summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Auxiliary/bash-completion/cmake38
-rw-r--r--Auxiliary/bash-completion/ctest11
-rw-r--r--Help/dev/source.rst5
-rw-r--r--Help/guide/tutorial/index.rst5
-rw-r--r--Help/guide/tutorial/source.txt3
-rw-r--r--Help/manual/cmake-buildsystem.7.rst51
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst4
-rw-r--r--Modules/CMakeDetermineCUDACompiler.cmake113
-rw-r--r--Modules/CMakeDetermineCompilerId.cmake8
-rw-r--r--Modules/CUDA/architectures.cmake60
-rw-r--r--Modules/FindMPI.cmake8
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/cmGhsMultiTargetGenerator.cxx20
-rw-r--r--Source/cmGhsMultiTargetGenerator.h3
-rw-r--r--Source/cmQtAutoGen.cxx7
-rw-r--r--Source/cmQtAutoGen.h5
-rw-r--r--Source/cmQtAutoMocUic.cxx51
-rw-r--r--Tests/CMakeLib/CMakeLists.txt1
-rw-r--r--Tests/CMakeLib/testCMExtEnumSet.cxx204
-rw-r--r--Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt2
-rw-r--r--Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt11
-rw-r--r--Utilities/Sphinx/CMakeLists.txt18
-rw-r--r--Utilities/Sphinx/tutorial_archive.cmake42
-rw-r--r--Utilities/std/cmext/enum_set397
25 files changed, 854 insertions, 217 deletions
diff --git a/.gitignore b/.gitignore
index d39684d..be848fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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