diff options
25 files changed, 854 insertions, 217 deletions
@@ -4,6 +4,8 @@ *.user* *.pyc + +Help/_generated Testing CMakeUserPresets.json diff --git a/Auxiliary/bash-completion/cmake b/Auxiliary/bash-completion/cmake index d8d2c86..bed7248 100644 --- a/Auxiliary/bash-completion/cmake +++ b/Auxiliary/bash-completion/cmake @@ -96,7 +96,15 @@ _cmake() _filedir return ;; - --build|--install|--open) + --build) + # Seed the reply with non-directory arguments that we know are + # allowed to follow --build. _filedir will then prepend any valid + # directory matches to these. + COMPREPLY=( $( compgen -W "--preset --list-presets" -- "$cur" ) ) + _filedir -d + return + ;; + --install|--open) _filedir -d return ;; @@ -149,6 +157,34 @@ _cmake() 2>/dev/null | grep -v "^cmake version " )' -- "$cur" ) ) return ;; + --list-presets) + local IFS=$'\n' + local quoted + printf -v quoted %q "$cur" + + if [[ ! "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then + COMPREPLY=( $( compgen -W "configure${IFS}build${IFS}test${IFS}all" -- "$quoted" ) ) + fi + return + ;; + --preset) + local IFS=$'\n' + local quoted + printf -v quoted %q "$cur" + + local build_or_configure="configure" + if [[ "${IFS}${COMP_WORDS[*]}${IFS}" =~ "${IFS}--build${IFS}" ]]; then + build_or_configure="build" + fi + + local presets=$( cmake --list-presets="$build_or_configure" 2>/dev/null | + grep -o "^ \".*\"" | sed \ + -e "s/^ //g" \ + -e "s/\"//g" \ + -e 's/ /\\\\ /g' ) + COMPREPLY=( $( compgen -W "$presets" -- "$quoted" ) ) + return + ;; esac $split && return diff --git a/Auxiliary/bash-completion/ctest b/Auxiliary/bash-completion/ctest index 49343bb..3c629d2 100644 --- a/Auxiliary/bash-completion/ctest +++ b/Auxiliary/bash-completion/ctest @@ -103,6 +103,17 @@ _ctest() 2>/dev/null | grep -v "^ctest version " )' -- "$cur" ) ) return ;; + --preset) + local IFS=$'\n' + local quoted + printf -v quoted %q "$cur" + COMPREPLY=( $( compgen -W '$( ctest --list-presets 2>/dev/null | + grep -o "^ \".*\"" | sed \ + -e "s/^ //g" \ + -e "s/\"//g" \ + -e "s/ /\\\\ /g" )' -- "$quoted" ) ) + return + ;; esac if [[ "$cur" == -* ]]; then diff --git a/Help/dev/source.rst b/Help/dev/source.rst index 9be4451..0ee104f 100644 --- a/Help/dev/source.rst +++ b/Help/dev/source.rst @@ -117,6 +117,11 @@ These are: * ``cm::contains``: Checks if element or key is contained in container. +* ``<cmext/enum_set>`` + + * ``cm::enum_set``: + Container to manage set of elements from an ``enum class`` definition. + * ``<cmext/iterator>``: * ``cm::is_terator``: diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst index 8b20a2d..09553cb 100644 --- a/Help/guide/tutorial/index.rst +++ b/Help/guide/tutorial/index.rst @@ -11,8 +11,9 @@ work together in an example project can be very helpful. Steps ===== -The tutorial documentation and source code for examples can be found in -the ``Help/guide/tutorial`` directory of the CMake source code tree. +.. include:: source.txt + +|tutorial_source| Each step has its own subdirectory containing code that may be used as a starting point. The tutorial examples are progressive so that each step provides the complete solution for the previous step. diff --git a/Help/guide/tutorial/source.txt b/Help/guide/tutorial/source.txt new file mode 100644 index 0000000..bb45e86 --- /dev/null +++ b/Help/guide/tutorial/source.txt @@ -0,0 +1,3 @@ +.. |tutorial_source| replace:: + The tutorial documentation and source code examples can be found in + the ``Help/guide/tutorial`` directory of the CMake source code tree. diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst index f48313a..bceff2d 100644 --- a/Help/manual/cmake-buildsystem.7.rst +++ b/Help/manual/cmake-buildsystem.7.rst @@ -1040,24 +1040,26 @@ Additionally, IDEs will show the source files as part of the target for interactive reading and editing. A primary use-case for ``INTERFACE`` libraries is header-only libraries. +Since CMake 3.23, header files may be associated with a library by adding +them to a header set using the :command:`target_sources` command: .. code-block:: cmake - add_library(Eigen INTERFACE - src/eigen.h - src/vector.h - src/matrix.h - ) - target_include_directories(Eigen INTERFACE - $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src> - $<INSTALL_INTERFACE:include/Eigen> + add_library(Eigen INTERFACE) + + target_sources(Eigen INTERFACE + FILE_SET HEADERS + BASE_DIRS src + FILES src/eigen.h src/vector.h src/matrix.h ) add_executable(exe1 exe1.cpp) target_link_libraries(exe1 Eigen) -Here, the usage requirements from the ``Eigen`` target are consumed and used -when compiling, but it has no effect on linking. +When we specify the ``FILE_SET`` here, the ``BASE_DIRS`` we define automatically +become include directories in the usage requirements for the target ``Eigen``. +The usage requirements from the target are consumed and used when compiling, but +have no effect on linking. Another use-case is to employ an entirely target-focussed design for usage requirements: @@ -1081,26 +1083,25 @@ This way, the build specification of ``exe1`` is expressed entirely as linked targets, and the complexity of compiler-specific flags is encapsulated in an ``INTERFACE`` library target. -``INTERFACE`` libraries may be installed and exported. Any content they refer -to must be installed separately: +``INTERFACE`` libraries may be installed and exported. We can install the +default header set along with the target: .. code-block:: cmake - set(Eigen_headers - src/eigen.h - src/vector.h - src/matrix.h - ) - add_library(Eigen INTERFACE ${Eigen_headers}) - target_include_directories(Eigen INTERFACE - $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src> - $<INSTALL_INTERFACE:include/Eigen> + add_library(Eigen INTERFACE) + + target_sources(Eigen INTERFACE + FILE_SET HEADERS + BASE_DIRS src + FILES src/eigen.h src/vector.h src/matrix.h ) - install(TARGETS Eigen EXPORT eigenExport) + install(TARGETS Eigen EXPORT eigenExport + FILE_SET HEADERS DESTINATION include/Eigen) install(EXPORT eigenExport NAMESPACE Upstream:: DESTINATION lib/cmake/Eigen ) - install(FILES ${Eigen_headers} - DESTINATION include/Eigen - ) + +Here, the headers defined in the header set are installed to ``include/Eigen``. +The install destination automatically becomes an include directory that is a +usage requirement for consumers. diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 1ef1ec8..22c14df 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -599,7 +599,9 @@ String Transformations .. versionadded:: 3.15 - Removes duplicated items in the given ``list``. + Removes duplicated items in the given ``list``. The relative order of items + is preserved, but if duplicates are encountered, only the first instance is + preserved. .. genex:: $<FILTER:list,INCLUDE|EXCLUDE,regex> diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake index 66020e8..23da8ee 100644 --- a/Modules/CMakeDetermineCUDACompiler.cmake +++ b/Modules/CMakeDetermineCUDACompiler.cmake @@ -248,10 +248,6 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) if(CMAKE_CUDA_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=]) set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION "${CMAKE_MATCH_1}") endif() - - # Make the all, all-major, and native architecture information available. - # FIXME(#23161): Defer architecture detection until compiler testing. - include(${CMAKE_ROOT}/Modules/CUDA/architectures.cmake) endif() set(CMAKE_CUDA_COMPILER_ID_FLAGS_ALWAYS "-v") @@ -273,76 +269,34 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) endif() endif() - # FIXME(#23161): Defer architecture testing until compiler testing. - if(DEFINED CMAKE_CUDA_ARCHITECTURES) - if(CMAKE_CUDA_ARCHITECTURES MATCHES "^(all|all-major)$") - # For sufficiently new NVCC we can just use the all and all-major flags. - # For VS we don't test since we can't figure out the version this early (see #23161). - # For others select based on version. - if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.5) - string(APPEND nvcc_test_flags " -arch=${CMAKE_CUDA_ARCHITECTURES}") - set(architectures_tested "${CMAKE_CUDA_ARCHITECTURES}") - elseif(CMAKE_GENERATOR MATCHES "Visual Studio") - set(architectures_tested "${CMAKE_CUDA_ARCHITECTURES}") - else() - if(CMAKE_CUDA_ARCHITECTURES STREQUAL "all") - set(architectures_test ${CMAKE_CUDA_ARCHITECTURES_ALL}) - elseif(CMAKE_CUDA_ARCHITECTURES STREQUAL "all-major") - set(architectures_test ${CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR}) - endif() - endif() - elseif(CMAKE_CUDA_ARCHITECTURES STREQUAL "native") - # For sufficiently new NVCC we can just use the 'native' value directly. - # For VS we don't test since we can't find nvcc this early (see #23161). - if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" AND CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.6) - string(APPEND nvcc_test_flags " -arch=${CMAKE_CUDA_ARCHITECTURES}") - set(architectures_tested "${CMAKE_CUDA_ARCHITECTURES}") - elseif(CMAKE_GENERATOR MATCHES "Visual Studio") - set(architectures_tested "${CMAKE_CUDA_ARCHITECTURES}") - else() - set(architectures_test ${_CUDA_ARCHITECTURES_NATIVE}) - endif() - elseif(CMAKE_CUDA_ARCHITECTURES OR "${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "") - # Explicit architectures. Test them during detection. - set(architectures_explicit TRUE) - set(architectures_test ${CMAKE_CUDA_ARCHITECTURES}) - endif() - endif() - - foreach(arch ${architectures_test}) - # Strip specifiers as PTX vs binary doesn't matter. - string(REGEX MATCH "[0-9]+" arch_name "${arch}") - string(APPEND clang_test_flags " --cuda-gpu-arch=sm_${arch_name}") - string(APPEND nvcc_test_flags " -gencode=arch=compute_${arch_name},code=sm_${arch_name}") - list(APPEND architectures_tested "${arch_name}") - endforeach() - # Rest of the code treats an empty value as equivalent to "use the defaults". # Error out early to prevent confusing errors as a result of this. # Note that this also catches invalid non-numerical values such as "a". - if(DEFINED architectures_explicit AND "${architectures_tested}" STREQUAL "") - message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES must be valid if set.") + if(DEFINED CMAKE_CUDA_ARCHITECTURES) + if(CMAKE_CUDA_ARCHITECTURES STREQUAL "") + message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES must be non-empty if set.") + elseif(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^([0-9]+[;0-9]*|all|all-major|native)$") + message(FATAL_ERROR + "CMAKE_CUDA_ARCHITECTURES:\n" + " ${CMAKE_CUDA_ARCHITECTURES}\n" + "is not one of the following:\n" + "* a semicolon-separated list of integers\n" + "* a special value: all, all-major, native\n" + ) + endif() endif() if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang") - if(NOT CMAKE_CUDA_ARCHITECTURES) - # Clang doesn't automatically select an architecture supported by the SDK. - # Try in reverse order of deprecation with the most recent at front (i.e. the most likely to work for new setups). - foreach(arch "52" "30" "20") - list(APPEND CMAKE_CUDA_COMPILER_ID_TEST_FLAGS_FIRST "${clang_test_flags} --cuda-gpu-arch=sm_${arch}") - endforeach() - endif() - - # If the user specified CMAKE_CUDA_ARCHITECTURES this will include all the architecture flags. - # Otherwise this won't include any architecture flags and we'll fallback to Clang's defaults. - list(APPEND CMAKE_CUDA_COMPILER_ID_TEST_FLAGS_FIRST "${clang_test_flags}") + # Clang doesn't automatically select an architecture supported by the SDK. + # Try in reverse order of deprecation with the most recent at front (i.e. the most likely to work for new setups). + foreach(arch "52" "30" "20") + list(APPEND CMAKE_CUDA_COMPILER_ID_TEST_FLAGS_FIRST "${clang_test_flags} --cuda-gpu-arch=sm_${arch}") + endforeach() elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA") list(APPEND CMAKE_CUDA_COMPILER_ID_TEST_FLAGS_FIRST "${nvcc_test_flags}") endif() # We perform compiler identification for a second time to extract implicit linking info and host compiler for NVCC. - # We also use it to verify that CMAKE_CUDA_ARCHITECTURES and additionally on Clang that CUDA toolkit path works. - # The latter could be done during compiler testing in the future to avoid doing this for Clang. # We need to unset the compiler ID otherwise CMAKE_DETERMINE_COMPILER_ID() doesn't work. set(CMAKE_CUDA_COMPILER_ID) set(CMAKE_CUDA_PLATFORM_ID) @@ -357,12 +311,12 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN) get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY) set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}") - # We now know the version, so make the architecture variables available. + # The compiler comes with the toolkit, so the versions are the same. set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION ${CMAKE_CUDA_COMPILER_VERSION}) - # FIXME(#23161): Defer architecture detection until compiler testing. - include(${CMAKE_ROOT}/Modules/CUDA/architectures.cmake) endif() + include(${CMAKE_ROOT}/Modules/CUDA/architectures.cmake) + _cmake_find_compiler_sysroot(CUDA) endif() @@ -647,31 +601,6 @@ if("${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "") message(FATAL_ERROR "Failed to detect a default CUDA architecture.\n\nCompiler output:\n${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}") endif() endif() -elseif(CMAKE_CUDA_ARCHITECTURES AND NOT "${architectures_tested}" MATCHES "^(all|all-major|native)$") - # Sort since order mustn't matter. - list(SORT architectures_detected) - list(SORT architectures_tested) - - # We don't distinguish real/virtual architectures during testing. - # For "70-real;70-virtual" we detect "70" as working and architectures_tested is "70;70". - # Thus we need to remove duplicates before checking if they're equal. - list(REMOVE_DUPLICATES architectures_tested) - - # Print the actual architectures for generic values (all and all-major). - if(NOT DEFINED architectures_explicit) - set(architectures_error "${CMAKE_CUDA_ARCHITECTURES} (${architectures_tested})") - else() - set(architectures_error "${architectures_tested}") - endif() - - if(NOT "${architectures_detected}" STREQUAL "${architectures_tested}") - message(FATAL_ERROR - "The CMAKE_CUDA_ARCHITECTURES:\n" - " ${architectures_error}\n" - "do not all work with this compiler. Try:\n" - " ${architectures_detected}\n" - "instead.") - endif() endif() # configure all variables set in this file @@ -687,9 +616,7 @@ unset(_CUDA_LIBRARY_DIR) unset(_CUDA_TARGET_DIR) unset(_CUDA_TARGET_NAME) -unset(architectures_explicit) unset(architectures_detected) -unset(architectures_tested) set(CMAKE_CUDA_COMPILER_ENV_VAR "CUDACXX") set(CMAKE_CUDA_HOST_COMPILER_ENV_VAR "CUDAHOSTCXX") diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index a90fa5d..0e8b2af 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -495,13 +495,7 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS} if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(cuda_target "<TargetMachinePlatform>64</TargetMachinePlatform>") endif() - if(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^(all|all-major|native)$") - foreach(arch ${CMAKE_CUDA_ARCHITECTURES}) - string(REGEX MATCH "[0-9]+" arch_name "${arch}") - string(APPEND cuda_codegen "compute_${arch_name},sm_${arch_name};") - endforeach() - endif() - set(id_ItemDefinitionGroup_entry "<CudaCompile>${cuda_target}<AdditionalOptions>%(AdditionalOptions)-v</AdditionalOptions><CodeGeneration>${cuda_codegen}</CodeGeneration></CudaCompile>") + set(id_ItemDefinitionGroup_entry "<CudaCompile>${cuda_target}<AdditionalOptions>%(AdditionalOptions)-v</AdditionalOptions></CudaCompile>") set(id_PostBuildEvent_Command [[echo CMAKE_CUDA_COMPILER=$(CudaToolkitBinDir)\nvcc.exe]]) if(CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR) # check for legacy cuda custom toolkit folder structure diff --git a/Modules/CUDA/architectures.cmake b/Modules/CUDA/architectures.cmake index 9b1f2b5..fa3a5a1 100644 --- a/Modules/CUDA/architectures.cmake +++ b/Modules/CUDA/architectures.cmake @@ -44,63 +44,3 @@ if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4 AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")) list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87) endif() - -# FIXME(#23161): Detect architectures early since we test them during -# compiler detection. We already have code to detect them later during -# compiler testing, so we should not need to do this here. -if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") - set(_CUDA_ARCHS_EXE "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCUDACompilerArchs.bin") - execute_process( - COMMAND "${_CUDA_NVCC_EXECUTABLE}" -o "${_CUDA_ARCHS_EXE}" --cudart=static "${CMAKE_ROOT}/Modules/CMakeCUDACompilerABI.cu" - RESULT_VARIABLE _CUDA_ARCHS_RESULT - OUTPUT_VARIABLE _CUDA_ARCHS_OUTPUT - ERROR_VARIABLE _CUDA_ARCHS_OUTPUT - ) - if(_CUDA_ARCHS_RESULT EQUAL 0) - execute_process( - COMMAND "${_CUDA_ARCHS_EXE}" - RESULT_VARIABLE _CUDA_ARCHS_RESULT - OUTPUT_VARIABLE _CUDA_ARCHS_OUTPUT - ERROR_VARIABLE _CUDA_ARCHS_OUTPUT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - if(_CUDA_ARCHS_RESULT EQUAL 0) - if("$ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}") - # Undocumented hook used by CMake's CI. - # Clamp native architecture to version range supported by this CUDA. - list(GET CMAKE_CUDA_ARCHITECTURES_ALL 0 _CUDA_ARCH_MIN) - list(GET CMAKE_CUDA_ARCHITECTURES_ALL -1 _CUDA_ARCH_MAX) - set(_CUDA_ARCHITECTURES_NATIVE "") - foreach(_CUDA_ARCH IN LISTS _CUDA_ARCHS_OUTPUT) - if(_CUDA_ARCH LESS _CUDA_ARCH_MIN) - set(_CUDA_ARCH "${_CUDA_ARCH_MIN}") - endif() - if(_CUDA_ARCH GREATER _CUDA_ARCH_MAX) - set(_CUDA_ARCH "${_CUDA_ARCH_MAX}") - endif() - list(APPEND _CUDA_ARCHITECTURES_NATIVE ${_CUDA_ARCH}) - endforeach() - unset(_CUDA_ARCH) - unset(_CUDA_ARCH_MIN) - unset(_CUDA_ARCH_MAX) - else() - set(_CUDA_ARCHITECTURES_NATIVE "${_CUDA_ARCHS_OUTPUT}") - endif() - list(REMOVE_DUPLICATES _CUDA_ARCHITECTURES_NATIVE) - else() - if (NOT _CUDA_ARCHS_RESULT MATCHES "[0-9]+") - set(_CUDA_ARCHS_STATUS " (${_CUDA_ARCHS_RESULT})") - else() - set(_CUDA_ARCHS_STATUS "") - endif() - string(REPLACE "\n" "\n " _CUDA_ARCHS_OUTPUT " ${_CUDA_ARCHS_OUTPUT}") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Detecting the CUDA native architecture(s) failed with " - "the following output:\n${_CUDA_ARCHS_OUTPUT}\n\n") - set(_CUDA_ARCHS_OUTPUT "") - endif() - unset(_CUDA_ARCHS_EXE) - unset(_CUDA_ARCHS_RESULT) - unset(_CUDA_ARCHS_OUTPUT) -endif() diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake index c974d33..6b60deb 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake @@ -1452,21 +1452,21 @@ foreach(LANG IN ITEMS C CXX Fortran) if(CMAKE_${LANG}_COMPILER_LOADED) if(NOT MPI_FIND_COMPONENTS) set(_MPI_FIND_${LANG} TRUE) - elseif( ${LANG} IN_LIST MPI_FIND_COMPONENTS) + elseif( LANG IN_LIST MPI_FIND_COMPONENTS) set(_MPI_FIND_${LANG} TRUE) - elseif( ${LANG} STREQUAL CXX AND NOT MPI_CXX_SKIP_MPICXX AND MPICXX IN_LIST MPI_FIND_COMPONENTS ) + elseif( "${LANG}" STREQUAL "CXX" AND NOT MPI_CXX_SKIP_MPICXX AND MPICXX IN_LIST MPI_FIND_COMPONENTS ) set(_MPI_FIND_${LANG} TRUE) else() set(_MPI_FIND_${LANG} FALSE) endif() else() set(_MPI_FIND_${LANG} FALSE) - if(${LANG} IN_LIST MPI_FIND_COMPONENTS) + if(LANG IN_LIST MPI_FIND_COMPONENTS) string(APPEND _MPI_FAIL_REASON "MPI component '${LANG}' was requested, but language ${LANG} is not enabled. ") endif() endif() if(_MPI_FIND_${LANG}) - if( ${LANG} STREQUAL CXX AND NOT MPICXX IN_LIST MPI_FIND_COMPONENTS ) + if( "${LANG}" STREQUAL "CXX" AND NOT MPICXX IN_LIST MPI_FIND_COMPONENTS ) option(MPI_CXX_SKIP_MPICXX "If true, the MPI-2 C++ bindings are disabled using definitions." FALSE) mark_as_advanced(MPI_CXX_SKIP_MPICXX) endif() diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 3fff3db..74ebee0 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 23) -set(CMake_VERSION_PATCH 20220427) +set(CMake_VERSION_PATCH 20220503) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index a7796c9..bf019c3 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -634,13 +634,8 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) compile = false; } - *fout << comment << fname << '\n'; + *fout << comment << fname << WriteObjectLangOverride(si) << '\n'; if (compile) { - if ("ld" != si->GetExtension() && "int" != si->GetExtension() && - "bsp" != si->GetExtension()) { - WriteObjectLangOverride(*fout, si); - } - this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); @@ -736,17 +731,16 @@ void cmGhsMultiTargetGenerator::WriteCustomCommandLine( } } -void cmGhsMultiTargetGenerator::WriteObjectLangOverride( - std::ostream& fout, const cmSourceFile* sourceFile) +std::string cmGhsMultiTargetGenerator::WriteObjectLangOverride( + const cmSourceFile* sourceFile) { + std::string ret; cmValue rawLangProp = sourceFile->GetProperty("LANGUAGE"); if (rawLangProp) { - std::string sourceLangProp(*rawLangProp); - std::string const& extension = sourceFile->GetExtension(); - if ("CXX" == sourceLangProp && ("c" == extension || "C" == extension)) { - fout << " -dotciscxx\n"; - } + ret = cmStrCat(" [", *rawLangProp, "]"); } + + return ret; } bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index 9289a72..d3e80e6 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -65,8 +65,7 @@ private: void WriteSourceProperty(std::ostream& fout, const cmSourceFile* sf, std::string const& propName, std::string const& propFlag); - static void WriteObjectLangOverride(std::ostream& fout, - const cmSourceFile* sourceFile); + static std::string WriteObjectLangOverride(const cmSourceFile* sourceFile); bool DetermineIfIntegrityApp(); cmGeneratorTarget* GeneratorTarget; diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 0a394b5..adbdba8 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -76,6 +76,13 @@ static void MergeOptions(std::vector<std::string>& baseOpts, unsigned int const cmQtAutoGen::ParallelMax = 64; +#ifdef _WIN32 +// Actually 32767 (see +// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but we +// allow for a small margin +size_t const cmQtAutoGen::CommandLineLengthMax = 32000; +#endif + cm::string_view cmQtAutoGen::GeneratorName(GenT genType) { switch (genType) { diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index 5a23ae9..d111422 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -64,6 +64,11 @@ public: /// @brief Maximum number of parallel threads/processes in a generator static unsigned int const ParallelMax; +#ifdef _WIN32 + /// @brief Maximum number of characters on command line + static size_t const CommandLineLengthMax; +#endif + /// @brief Returns the generator name static cm::string_view GeneratorName(GenT genType); /// @brief Returns the generator name in upper case diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 4ed728e..0d38dfb 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -497,6 +497,10 @@ public: protected: ParseCacheT::FileHandleT CacheEntry; + + private: + void MaybeWriteMocResponseFile(std::string const& outputFile, + std::vector<std::string>& cmd) const; }; /** uic compiles a file. */ @@ -2025,6 +2029,8 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() cmd.push_back(outputFile); // Add source file cmd.push_back(sourceFile); + + MaybeWriteMocResponseFile(outputFile, cmd); } // Execute moc command @@ -2070,6 +2076,51 @@ void cmQtAutoMocUicT::JobCompileMocT::Process() } } +/* + * Check if command line exceeds maximum length supported by OS + * (if on Windows) and switch to using a response file instead. + */ +void cmQtAutoMocUicT::JobCompileMocT::MaybeWriteMocResponseFile( + std::string const& outputFile, std::vector<std::string>& cmd) const +{ +#ifdef _WIN32 + // Ensure cmd is less than CommandLineLengthMax characters + size_t commandLineLength = cmd.size(); // account for separating spaces + for (std::string const& str : cmd) { + commandLineLength += str.length(); + } + if (commandLineLength >= CommandLineLengthMax) { + // Command line exceeds maximum size allowed by OS + // => create response file + std::string const responseFile = cmStrCat(outputFile, ".rsp"); + + cmsys::ofstream fout(responseFile.c_str()); + if (!fout) { + this->LogError( + GenT::MOC, + cmStrCat("AUTOMOC was unable to create a response file at\n ", + this->MessagePath(responseFile))); + return; + } + + auto it = cmd.begin(); + while (++it != cmd.end()) { + fout << *it << "\n"; + } + fout.close(); + + // Keep all but executable + cmd.resize(1); + + // Specify response file + cmd.push_back(cmStrCat('@', responseFile)); + } +#else + static_cast<void>(outputFile); + static_cast<void>(cmd); +#endif +} + void cmQtAutoMocUicT::JobCompileUicT::Process() { std::string const& sourceFile = this->Mapping->SourceFile->FileName; diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 87925bd..1d45162 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -29,6 +29,7 @@ set(CMakeLib_TESTS testUVStreambuf.cxx testCMExtMemory.cxx testCMExtAlgorithm.cxx + testCMExtEnumSet.cxx ) if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM) list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx) diff --git a/Tests/CMakeLib/testCMExtEnumSet.cxx b/Tests/CMakeLib/testCMExtEnumSet.cxx new file mode 100644 index 0000000..64c437b --- /dev/null +++ b/Tests/CMakeLib/testCMExtEnumSet.cxx @@ -0,0 +1,204 @@ + +#include <cstdint> +#include <initializer_list> +#include <iostream> +#include <iterator> +#include <set> +#include <utility> + +#include <cmext/enum_set> + +namespace { + +int failed = 0; + +void testDeclaration() +{ + std::cout << "testDeclaration()" << std::endl; + + enum class Test : std::uint8_t + { + A, + B, + C, + D + }; + cm::enum_set<Test> testSet1; + cm::enum_set<Test> testSet2{ Test::A, Test::C }; + cm::enum_set<Test> testSet3 = testSet2; + + if (!testSet1.empty()) { + ++failed; + } + if (testSet2.size() != 2) { + ++failed; + } + if (testSet3.size() != 2) { + ++failed; + } +} + +void testIteration() +{ + std::cout << "testIteration()" << std::endl; + + enum class Test : std::uint8_t + { + A, + B, + C, + D + }; + cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B }; + + if (testSet.size() != 3) { + ++failed; + } + + std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A), + static_cast<std::uint8_t>(Test::B), + static_cast<std::uint8_t>(Test::C) }; + std::set<std::uint8_t> s; + + for (auto e : testSet) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + + s.clear(); + for (auto rit = testSet.rbegin(); rit != testSet.rend(); rit++) { + s.insert(static_cast<std::uint8_t>(*rit)); + } + if (s != reference) { + ++failed; + } +} + +void testEdition() +{ + std::cout << "testEdition()" << std::endl; + + enum class Test : std::uint8_t + { + A, + B, + C, + D, + E + }; + + { + cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B }; + + auto pos = testSet.insert(Test::E); + if (!pos.second || testSet.size() != 4 || *(pos.first) != Test::E || + testSet.find(Test::E) == testSet.end()) { + ++failed; + } + testSet.insert(Test::E); + if (testSet.size() != 4 || testSet.find(Test::E) == testSet.end()) { + ++failed; + } + + testSet.erase(Test::A); + if (testSet.size() != 3 || testSet.find(Test::A) != testSet.end()) { + ++failed; + } + testSet.erase(Test::A); + if (testSet.size() != 3 || testSet.find(Test::A) != testSet.end()) { + ++failed; + } + } + { + cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B }; + + testSet += { Test::D, Test::E }; + + std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A), + static_cast<std::uint8_t>(Test::B), + static_cast<std::uint8_t>(Test::C), + static_cast<std::uint8_t>(Test::D), + static_cast<std::uint8_t>(Test::E) }; + std::set<std::uint8_t> s; + for (auto e : testSet) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + + testSet -= { Test::D, Test::B }; + reference.erase(static_cast<std::uint8_t>(Test::D)); + reference.erase(static_cast<std::uint8_t>(Test::B)); + s.clear(); + for (auto e : testSet) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + } + { + cm::enum_set<Test> testSet1{ Test::A, Test::C, Test::B }; + cm::enum_set<Test> testSet2{ Test::A, Test::D, Test::E }; + testSet1.insert(testSet2.cbegin(), testSet2.cend()); + + std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::A), + static_cast<std::uint8_t>(Test::B), + static_cast<std::uint8_t>(Test::C), + static_cast<std::uint8_t>(Test::D), + static_cast<std::uint8_t>(Test::E) }; + std::set<std::uint8_t> s; + for (auto e : testSet1) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + + testSet1.erase(testSet2); + + reference.erase(static_cast<std::uint8_t>(Test::A)); + reference.erase(static_cast<std::uint8_t>(Test::D)); + reference.erase(static_cast<std::uint8_t>(Test::E)); + s.clear(); + for (auto e : testSet1) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + } + { + cm::enum_set<Test> testSet1{ Test::A, Test::C, Test::B }; + cm::enum_set<Test> testSet2{ Test::C, Test::E }; + + testSet1.flip(Test::A); + if (testSet1.size() != 2 || testSet1.contains(Test::A)) { + ++failed; + } + + testSet1.flip(testSet2); + std::set<std::uint8_t> reference{ static_cast<std::uint8_t>(Test::B), + static_cast<std::uint8_t>(Test::E) }; + std::set<std::uint8_t> s; + for (auto e : testSet1) { + s.insert(static_cast<std::uint8_t>(e)); + } + if (s != reference) { + ++failed; + } + } +} +} + +int testCMExtEnumSet(int /*unused*/, char* /*unused*/ []) +{ + testDeclaration(); + testIteration(); + testEdition(); + + return failed; +} diff --git a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt index 39640fa..6c42612 100644 --- a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt +++ b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt @@ -1,5 +1,5 @@ ^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\): - CMAKE_CUDA_ARCHITECTURES must be valid if set\. + CMAKE_CUDA_ARCHITECTURES must be non-empty if set\. Call Stack \(most recent call first\): architectures-empty\.cmake:2 \(enable_language\) CMakeLists\.txt:3 \(include\) diff --git a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt index 7608730..48f379c 100644 --- a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt +++ b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt @@ -1,5 +1,14 @@ ^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\): - CMAKE_CUDA_ARCHITECTURES must be valid if set\. + CMAKE_CUDA_ARCHITECTURES: + + invalid + + is not one of the following: + + \* a semicolon-separated list of integers + + \* a special value: all, all-major, native + Call Stack \(most recent call first\): architectures-invalid\.cmake:2 \(enable_language\) CMakeLists\.txt:3 \(include\)$ diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index c698e3b..fbce1c8 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt @@ -68,7 +68,7 @@ if(SPHINX_HTML) # we provide the path to the produced html output in the console # for tools that support URI protocol schemes - set(html_extra_commands + set(html_post_commands COMMAND ${CMAKE_COMMAND} -E echo "sphinx-build html: HTML documentation generated in file://${CMAKE_CURRENT_BINARY_DIR}/html/index.html" ) @@ -94,7 +94,7 @@ if(SPHINX_INFO) # Sphinx texinfo builder supports .info, .txt, .html and .pdf output. # SPHINX_INFO controls the .info output. - set(texinfo_extra_commands + set(texinfo_post_commands COMMAND ${MAKEINFO_EXECUTABLE} --no-split -o ${CMAKE_CURRENT_BINARY_DIR}/texinfo/cmake.info ${CMAKE_CURRENT_BINARY_DIR}/texinfo/cmake.texi @@ -112,7 +112,7 @@ if(SPHINX_QTHELP) endif() list(APPEND doc_formats qthelp) - set(qthelp_extra_commands + set(qthelp_post_commands # Workaround for assistant prior to # https://codereview.qt-project.org/#change,82250 in Qt 4. COMMAND ${CMAKE_COMMAND} "-DCSS_DIR=${CMAKE_CURRENT_BINARY_DIR}/qthelp/_static" @@ -148,7 +148,11 @@ if(CMake_SPHINX_CMAKE_ORG) list(APPEND doc_html_opts -A outdated=1) endif() - list(APPEND qthelp_extra_commands + list(APPEND html_pre_commands + COMMAND ${CMAKE_COMMAND} -Dversion=${CMake_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/tutorial_archive.cmake + ) + + list(APPEND qthelp_post_commands COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/qthelp/CMake.qch" "${CMAKE_CURRENT_BINARY_DIR}/html/CMake.qch" @@ -170,6 +174,7 @@ foreach(format ${doc_formats}) # arguments in peculiar order add_custom_command( OUTPUT ${doc_format_output} + ${${format}_pre_commands} COMMAND ${SPHINX_EXECUTABLE} -M ${format} ${CMake_SOURCE_DIR}/Help @@ -179,7 +184,7 @@ foreach(format ${doc_formats}) ${sphinx_flags} ${doc_${format}_opts} > ${doc_format_log} # log stdout, pass stderr - ${${format}_extra_commands} + ${${format}_post_commands} DEPENDS ${doc_format_last} COMMENT "sphinx-build ${format}: see Utilities/Sphinx/${doc_format_log}" VERBATIM @@ -188,6 +193,7 @@ foreach(format ${doc_formats}) # other formats use standard builder (-b) mode add_custom_command( OUTPUT ${doc_format_output} + ${${format}_pre_commands} COMMAND ${SPHINX_EXECUTABLE} -c ${CMAKE_CURRENT_BINARY_DIR} -d ${CMAKE_CURRENT_BINARY_DIR}/${doctrees} @@ -197,7 +203,7 @@ foreach(format ${doc_formats}) ${CMake_SOURCE_DIR}/Help ${CMAKE_CURRENT_BINARY_DIR}/${format} > ${doc_format_log} # log stdout, pass stderr - ${${format}_extra_commands} + ${${format}_post_commands} DEPENDS ${doc_format_last} COMMENT "sphinx-build ${format}: see Utilities/Sphinx/${doc_format_log}" VERBATIM diff --git a/Utilities/Sphinx/tutorial_archive.cmake b/Utilities/Sphinx/tutorial_archive.cmake new file mode 100644 index 0000000..212a622 --- /dev/null +++ b/Utilities/Sphinx/tutorial_archive.cmake @@ -0,0 +1,42 @@ +if(NOT version) + message(FATAL_ERROR "Pass -Dversion=") +endif() + +# Name of the archive and its top-level directory. +set(archive_name "cmake-${version}-tutorial-source") + +# Base directory for CMake Documentation. +set(help_dir "${CMAKE_CURRENT_LIST_DIR}/../../Help") +cmake_path(ABSOLUTE_PATH help_dir NORMALIZE) + +# Collect the non-documentation part of the tutorial directory. +file(COPY "${help_dir}/guide/tutorial/" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}" + NO_SOURCE_PERMISSIONS + PATTERN *.rst EXCLUDE + PATTERN source.txt EXCLUDE + ) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}/README.txt" [[ +This directory contains source code examples for the CMake Tutorial. +Each step has its own subdirectory containing code that may be used as a +starting point. The tutorial examples are progressive so that each step +provides the complete solution for the previous step. +]]) + +# Create an archive containing the tutorial source examples. +file(MAKE_DIRECTORY "${help_dir}/_generated") +file(ARCHIVE_CREATE + OUTPUT "${help_dir}/_generated/${archive_name}.zip" + PATHS "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}" + FORMAT zip + ) + +# Write a reStructuredText snippet included from the tutorial index. +file(WRITE "${help_dir}/guide/tutorial/source.txt" " +.. |tutorial_source| replace:: + The tutorial source code examples are available in + :download:`this archive </_generated/${archive_name}.zip>`. +") + +# Remove temporary directory. +file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/${archive_name}") diff --git a/Utilities/std/cmext/enum_set b/Utilities/std/cmext/enum_set new file mode 100644 index 0000000..4225b82 --- /dev/null +++ b/Utilities/std/cmext/enum_set @@ -0,0 +1,397 @@ +// -*-c++-*- +// vim: set ft=cpp: + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <bitset> +#include <cstddef> +#include <initializer_list> +#include <iterator> +#include <limits> +#include <utility> + +#include <cm/type_traits> + +// +// Class enum_set offers the capability to manage a set of enum values +// Only 'enum class' with unsigned base type are supported. +// +// The methods offered by 'enum_set' are close as possible to the 'std::set' +// container plus some useful methods from 'std::bitset' like 'flip'. +// +// Internally, this class use 'std::bitset' container to manage the +// set of enum. The size of the bitset is deduced from the underlying type of +// the enum. +// + +namespace cm { + +template <typename EnumSet> +class enum_set_iterator +{ +public: + enum_set_iterator() = default; + enum_set_iterator(const enum_set_iterator& other) = default; + + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename EnumSet::value_type; + using difference_type = typename EnumSet::difference_type; + using reference = typename EnumSet::reference; + using pointer = typename EnumSet::pointer; + + enum_set_iterator& operator++() + { + while (++this->Index < this->Set->max_size() && + !this->Set->test(this->Index)) + ; + + return *this; + } + enum_set_iterator operator++(int) + { + auto retval = *this; + ++(*this); + return retval; + } + + enum_set_iterator& operator--() + { + if (this->Index == 0) { + return *this; + } + + while (!this->Set->test(--this->Index) && this->Index != 0) + ; + + return *this; + } + enum_set_iterator operator--(int) + { + auto retval = *this; + --(*this); + return retval; + } + + reference operator*() const { return static_cast<reference>(this->Index); } + + bool operator==(enum_set_iterator other) const + { + return (this->Set == other.Set) && (this->Index == other.Index); + } + + bool operator!=(enum_set_iterator other) const { return !(*this == other); } + +private: + friend EnumSet; + + using size_type = typename EnumSet::size_type; + + enum_set_iterator(EnumSet* set, bool at_end = false) + : Set(set) + { + if (at_end || this->Set->empty()) { + this->Index = this->Set->max_size(); + } else { + while (!this->Set->test(this->Index) && + ++this->Index < this->Set->max_size()) + ; + } + } + enum_set_iterator(EnumSet* set, size_type pos) + : Index(pos) + , Set(set) + { + } + + std::size_t Index = 0; + EnumSet* Set = nullptr; +}; + +template < + typename Enum, + typename cm::enable_if_t< + std::is_enum<Enum>::value && + std::is_unsigned<typename std::underlying_type<Enum>::type>::value, + int> = 0> +class enum_set +{ +public: + using key_type = Enum; + using value_type = Enum; + using size_type = typename std::underlying_type<Enum>::type; + using difference_type = size_type; + using reference = Enum; + using const_reference = Enum; + using pointer = const Enum*; + using const_pointer = const Enum*; + + using iterator = enum_set_iterator<enum_set>; + using const_iterator = enum_set_iterator<const enum_set>; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + constexpr enum_set() noexcept = default; + enum_set(const enum_set& other) noexcept { this->insert(other); } + enum_set(std::initializer_list<value_type> list) { this->insert(list); } + + enum_set& operator=(const enum_set& other) noexcept + { + this->Set.reset(); + this->Set |= other.Set; + return *this; + } + enum_set& operator=(std::initializer_list<value_type> list) + { + this->Set.reset(); + this->insert(list); + } + + // Iterators + iterator begin() noexcept { return iterator(this); } + const_iterator begin() const noexcept { return const_iterator(this); } + const_iterator cbegin() const noexcept { return const_iterator(this); } + + iterator end() noexcept { return iterator(this, true); } + const_iterator end() const noexcept { return const_iterator(this, true); } + const_iterator cend() const noexcept { return const_iterator(this, true); } + + reverse_iterator rbegin() noexcept { return reverse_iterator(this->end()); } + const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(this->end()); + } + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(this->cend()); + } + + reverse_iterator rend() noexcept { return reverse_iterator(this->begin()); } + const_reverse_iterator rend() const noexcept + { + return const_reverse_iterator(this->begin()); + } + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(this->cbegin()); + } + + // Capacity + bool empty() const noexcept { return this->Set.none(); } + + size_type size() const noexcept { return this->Set.count(); } + + size_type max_size() const noexcept { return this->Set.size(); } + + // Modifiers + void clear() noexcept { this->Set.reset(); } + + enum_set& operator+=(key_type e) + { + this->insert(e); + return *this; + } + enum_set& operator+=(const enum_set& other) noexcept + { + this->erase(other); + return *this; + } + enum_set& operator+=(std::initializer_list<value_type> list) + { + this->insert(list); + return *this; + } + + enum_set& operator-=(key_type e) + { + this->erase(e); + return *this; + } + enum_set& operator-=(const enum_set& other) noexcept + { + this->erase(other); + return *this; + } + enum_set& operator-=(std::initializer_list<value_type> list) + { + this->erase(list); + return *this; + } + + std::pair<iterator, bool> insert(value_type value) + { + auto exist = this->contains(value); + if (!exist) { + this->Set.set(static_cast<size_type>(value)); + } + + return { iterator(this, static_cast<size_type>(value)), !exist }; + } + template <typename InputIt> + void insert(InputIt first, InputIt last) + { + for (auto i = first; i != last; i++) { + this->insert(*i); + } + } + void insert(const enum_set& other) noexcept { this->Set |= other.Set; } + void insert(std::initializer_list<value_type> list) + { + for (auto e : list) { + this->Set.set(static_cast<size_type>(e)); + } + } + + size_type erase(key_type key) + { + if (this->contains(key)) { + this->Set.reset(static_cast<size_type>(key)); + return 1; + } + + return 0; + } + iterator erase(iterator pos) + { + this->erase(*pos++); + return pos; + } + iterator erase(const_iterator pos) + { + this->erase(*pos++); + + return pos == this->cend() ? this->end() + : iterator(this, static_cast<size_type>(*pos)); + } + void erase(const enum_set& other) noexcept { this->Set &= ~other.Set; } + void erase(std::initializer_list<value_type> list) + { + for (auto e : list) { + this->Set.reset(static_cast<size_type>(e)); + } + } + + void swap(enum_set& other) noexcept + { + auto tmp = this->Set; + this->Set = other.Set; + other.Set = tmp; + } + + // toggle the specified enum + void flip(key_type key) { this->Set.flip(static_cast<size_type>(key)); } + // toggle all the enums stored in the other enum_set + void flip(const enum_set& other) noexcept { this->Set ^= other.Set; } + // toggle all the enums specified in the list + void flip(std::initializer_list<value_type> list) + { + for (auto e : list) { + this->Set.flip(static_cast<size_type>(e)); + } + } + + // Lookup + size_type count(key_type e) const { return this->contains(e) ? 1 : 0; } + + iterator find(key_type e) + { + if (this->contains(e)) { + return iterator(this, static_cast<size_type>(e)); + } else { + return this->end(); + } + } + const_iterator find(key_type e) const + { + if (this->contains(e)) { + return const_iterator(this, static_cast<size_type>(e)); + } else { + return this->end(); + } + } + + bool contains(key_type e) const + { + return this->Set.test(static_cast<size_type>(e)); + } + +private: + template <typename E, typename Predicate> + friend inline void erase_if(enum_set<E>& set, Predicate pred); + + friend class enum_set_iterator<enum_set>; + friend class enum_set_iterator<const enum_set>; + + bool test(size_type pos) const { return this->Set.test(pos); } + + std::bitset<std::numeric_limits<size_type>::digits> Set; +}; + +// non-member functions for enum_set +template <typename Enum> +inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, Enum rhs) +{ + return enum_set<Enum>(lhs) += rhs; +} +template <typename Enum> +inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, + const enum_set<Enum>& rhs) noexcept +{ + return enum_set<Enum>(lhs) += rhs; +} +template <typename Enum> +inline enum_set<Enum> operator+(const enum_set<Enum>& lhs, + const std::initializer_list<Enum> rhs) +{ + return enum_set<Enum>(lhs) += rhs; +} + +template <typename Enum> +inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, Enum rhs) +{ + return enum_set<Enum>(lhs) -= rhs; +} +template <typename Enum> +inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, + const enum_set<Enum>& rhs) noexcept +{ + return enum_set<Enum>(lhs) -= rhs; +} +template <typename Enum> +inline enum_set<Enum> operator-(const enum_set<Enum>& lhs, + const std::initializer_list<Enum> rhs) +{ + return enum_set<Enum>(lhs) -= rhs; +} + +template <typename Enum> +inline bool operator==(const enum_set<Enum>& lhs, + const enum_set<Enum>& rhs) noexcept +{ + return lhs == rhs; +} + +template <typename Enum> +inline bool operator!=(const enum_set<Enum>& lhs, + const enum_set<Enum>& rhs) noexcept +{ + return !(lhs == rhs); +} + +template <typename Enum> +inline void erase(enum_set<Enum>& set, Enum value) +{ + set.erase(value); +} + +template <typename Enum, typename Predicate> +inline void erase_if(enum_set<Enum>& set, Predicate pred) +{ + for (std::size_t index = 0; index < set.Set.size(); ++index) { + if (set.Set.test(index) && pred(static_cast<Enum>(index))) { + set.Set.reset(index); + } + } +} +} // namespace cm |