diff options
-rw-r--r-- | Help/dev/source.rst | 5 | ||||
-rw-r--r-- | Help/manual/cmake-buildsystem.7.rst | 51 | ||||
-rw-r--r-- | Modules/CMakeDetermineCUDACompiler.cmake | 113 | ||||
-rw-r--r-- | Modules/CMakeDetermineCompilerId.cmake | 8 | ||||
-rw-r--r-- | Modules/CUDA/architectures.cmake | 60 | ||||
-rw-r--r-- | Modules/FindMPI.cmake | 8 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/cmGhsMultiTargetGenerator.cxx | 20 | ||||
-rw-r--r-- | Source/cmGhsMultiTargetGenerator.h | 3 | ||||
-rw-r--r-- | Tests/CMakeLib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Tests/CMakeLib/testCMExtEnumSet.cxx | 204 | ||||
-rw-r--r-- | Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt | 2 | ||||
-rw-r--r-- | Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt | 11 | ||||
-rw-r--r-- | Utilities/std/cmext/enum_set | 393 |
14 files changed, 674 insertions, 207 deletions
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/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/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..7d6a51d 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 20220428) #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/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/std/cmext/enum_set b/Utilities/std/cmext/enum_set new file mode 100644 index 0000000..f97a04c --- /dev/null +++ b/Utilities/std/cmext/enum_set @@ -0,0 +1,393 @@ +// -*-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--() + { + while (--this->Index >= 0 && !this->Set->test(this->Index)) + ; + + 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 |