summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Help/command/cmake_path.rst4
-rw-r--r--Help/command/file.rst5
-rw-r--r--Help/command/find_package.rst25
-rw-r--r--Help/command/get_filename_component.rst2
-rw-r--r--Help/command/get_property.rst2
-rw-r--r--Help/command/install.rst4
-rw-r--r--Help/command/set_property.rst4
-rw-r--r--Help/command/string.rst91
-rw-r--r--Help/cpack_gen/deb.rst30
-rw-r--r--Help/cpack_gen/dmg.rst2
-rw-r--r--Help/cpack_gen/nsis.rst10
-rw-r--r--Help/cpack_gen/rpm.rst4
-rw-r--r--Help/envvar/CMAKE_INSTALL_MODE.rst37
-rw-r--r--Help/generator/Visual Studio 10 2010.rst9
-rw-r--r--Help/manual/cmake-env-variables.7.rst1
-rw-r--r--Help/manual/cmake-presets.7.rst5
-rw-r--r--Help/release/3.21.rst6
-rw-r--r--Help/release/dev/GoogleTest-gtest-filter.rst6
-rw-r--r--Help/release/dev/cmake-install-mode-symlink.rst16
-rw-r--r--Help/release/dev/cpack-deb-add-zstd-compression.rst6
-rw-r--r--Help/release/dev/nsis-minimal-version.rst4
-rw-r--r--Help/release/dev/nsis_ignore_install_page.rst6
-rw-r--r--Help/release/dev/string-TIMESTAMP-specifier-V.rst5
-rw-r--r--Help/release/dev/vs10-deprecate.rst5
-rw-r--r--Modules/CMakeFindBinUtils.cmake10
-rw-r--r--Modules/CheckCXXSymbolExists.cmake2
-rw-r--r--Modules/CheckFortranFunctionExists.cmake6
-rw-r--r--Modules/CheckLanguage.cmake5
-rw-r--r--Modules/CheckSymbolExists.cmake2
-rw-r--r--Modules/Compiler/ARMClang-C.cmake8
-rw-r--r--Modules/Compiler/ARMClang-CXX.cmake8
-rw-r--r--Modules/Compiler/Clang-FindBinUtils.cmake2
-rw-r--r--Modules/Compiler/GNU-FindBinUtils.cmake2
-rw-r--r--Modules/Compiler/NVHPC-C.cmake8
-rw-r--r--Modules/Compiler/NVHPC-CXX.cmake8
-rw-r--r--Modules/Compiler/NVHPC.cmake12
-rw-r--r--Modules/FetchContent.cmake452
-rw-r--r--Modules/FindBLAS.cmake30
-rw-r--r--Modules/FindCUDAToolkit.cmake3
-rw-r--r--Modules/FindLAPACK.cmake48
-rw-r--r--Modules/FindMPI.cmake3
-rw-r--r--Modules/FindPkgConfig.cmake8
-rw-r--r--Modules/FindX11.cmake6
-rw-r--r--Modules/GoogleTest.cmake11
-rw-r--r--Modules/GoogleTestAddTests.cmake11
-rw-r--r--Modules/InstallRequiredSystemLibraries.cmake33
-rw-r--r--Modules/Internal/CPack/CPackRPM.cmake4
-rw-r--r--Modules/Internal/CPack/NSIS.template.in2
-rw-r--r--Modules/Platform/Android-Determine.cmake2
-rw-r--r--Modules/Platform/Darwin.cmake4
-rw-r--r--Modules/Platform/Haiku.cmake5
-rw-r--r--Modules/VTKCompatibility.cmake4
-rw-r--r--Source/CMakeLists.txt3
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CPack/cmCPackDebGenerator.cxx27
-rw-r--r--Source/CPack/cmCPackNSISGenerator.cxx11
-rw-r--r--Source/CTest/cmCTestGenericHandler.cxx6
-rw-r--r--Source/CTest/cmCTestStartCommand.cxx8
-rw-r--r--Source/CTest/cmCTestSubmitCommand.cxx4
-rw-r--r--Source/QtDialog/QCMake.cxx6
-rw-r--r--Source/cmArchiveWrite.cxx3
-rw-r--r--Source/cmCMakePolicyCommand.cxx1
-rw-r--r--Source/cmCMakePresetsFile.cxx10
-rw-r--r--Source/cmCacheManager.cxx6
-rw-r--r--Source/cmCacheManager.h2
-rw-r--r--Source/cmCommandArgumentParserHelper.cxx2
-rw-r--r--Source/cmCommonTargetGenerator.cxx6
-rw-r--r--Source/cmCommonTargetGenerator.h5
-rw-r--r--Source/cmConditionEvaluator.cxx972
-rw-r--r--Source/cmConditionEvaluator.h30
-rw-r--r--Source/cmCoreTryCompile.cxx10
-rw-r--r--Source/cmDefinitions.cxx6
-rw-r--r--Source/cmDefinitions.h4
-rw-r--r--Source/cmExportBuildFileGenerator.cxx1
-rw-r--r--Source/cmExportFileGenerator.cxx2
-rw-r--r--Source/cmExportInstallFileGenerator.cxx1
-rw-r--r--Source/cmFileAPICodemodel.cxx3
-rw-r--r--Source/cmFileCommand.cxx4
-rw-r--r--Source/cmFileCopier.h5
-rw-r--r--Source/cmFileInstaller.cxx118
-rw-r--r--Source/cmFileInstaller.h5
-rw-r--r--Source/cmFindBase.cxx4
-rw-r--r--Source/cmForEachCommand.cxx1
-rw-r--r--Source/cmGeneratorExpressionNode.cxx2
-rw-r--r--Source/cmGeneratorTarget.cxx31
-rw-r--r--Source/cmGeneratorTarget.h2
-rw-r--r--Source/cmGetPropertyCommand.cxx19
-rw-r--r--Source/cmGetTestPropertyCommand.cxx5
-rw-r--r--Source/cmGhsMultiTargetGenerator.cxx2
-rw-r--r--Source/cmGlobalGenerator.cxx10
-rw-r--r--Source/cmGlobalGenerator.h3
-rw-r--r--Source/cmGlobalGhsMultiGenerator.cxx3
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx2
-rw-r--r--Source/cmGlobalVisualStudio7Generator.cxx19
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.cxx3
-rw-r--r--Source/cmIncludeGuardCommand.cxx1
-rw-r--r--Source/cmInstallMode.h17
-rw-r--r--Source/cmMakefile.cxx11
-rw-r--r--Source/cmMakefileTargetGenerator.cxx64
-rw-r--r--Source/cmMarkAsAdvancedCommand.cxx1
-rw-r--r--Source/cmNinjaNormalTargetGenerator.cxx10
-rw-r--r--Source/cmOptionCommand.cxx4
-rw-r--r--Source/cmPolicies.cxx1
-rw-r--r--Source/cmProperty.cxx113
-rw-r--r--Source/cmProperty.h210
-rw-r--r--Source/cmPropertyMap.cxx2
-rw-r--r--Source/cmQtAutoGen.cxx36
-rw-r--r--Source/cmQtAutoGen.h3
-rw-r--r--Source/cmQtAutoGenInitializer.cxx53
-rw-r--r--Source/cmQtAutoGenInitializer.h2
-rw-r--r--Source/cmQtAutoGenerator.cxx37
-rw-r--r--Source/cmQtAutoGenerator.h2
-rw-r--r--Source/cmQtAutoMocUic.cxx20
-rw-r--r--Source/cmQtAutoUicHelpers.cxx25
-rw-r--r--Source/cmQtAutoUicHelpers.h20
-rw-r--r--Source/cmSourceFile.cxx14
-rw-r--r--Source/cmStandardLevelResolver.cxx32
-rw-r--r--Source/cmStandardLevelResolver.h4
-rw-r--r--Source/cmState.cxx18
-rw-r--r--Source/cmStateDirectory.cxx30
-rw-r--r--Source/cmStateSnapshot.cxx3
-rw-r--r--Source/cmStateSnapshot.h3
-rw-r--r--Source/cmStringAlgorithms.cxx70
-rw-r--r--Source/cmStringAlgorithms.h64
-rw-r--r--Source/cmSystemTools.cxx5
-rw-r--r--Source/cmTarget.cxx43
-rw-r--r--Source/cmTargetPropertyComputer.h6
-rw-r--r--Source/cmTest.cxx6
-rw-r--r--Source/cmTest.h3
-rw-r--r--Source/cmTimestamp.cxx23
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx8
-rw-r--r--Source/cmWhileCommand.cxx89
-rw-r--r--Source/cmake.cxx6
-rw-r--r--Templates/MSBuild/FlagTables/v142_CL.json4
-rw-r--r--Templates/MSBuild/FlagTables/v143_CL.json4
-rw-r--r--Tests/CMakeLists.txt55
-rw-r--r--Tests/CPackNSISGenerator/CMakeLists.txt1
-rw-r--r--Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake9
-rw-r--r--Tests/CudaOnly/CMakeLists.txt2
-rw-r--r--Tests/CudaOnly/SeparateCompilation/CMakeLists.txt19
-rw-r--r--Tests/CudaOnly/SeparateCompilation/main/CMakeLists.txt18
-rw-r--r--Tests/CudaOnly/SeparateCompilation/main/main.cu (renamed from Tests/CudaOnly/SeparateCompilation/main.cu)4
-rw-r--r--Tests/EnforceConfig.cmake.in1
-rw-r--r--Tests/InstallMode/CMakeLists.txt124
-rw-r--r--Tests/InstallMode/README.txt43
-rw-r--r--Tests/InstallMode/Subproject.cmake76
-rw-r--r--Tests/InstallMode/Test.cmake38
-rw-r--r--Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt60
-rw-r--r--Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in8
-rw-r--r--Tests/InstallMode/subpro_a_static_lib/include/static_lib.h3
-rw-r--r--Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp10
-rw-r--r--Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt66
-rw-r--r--Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in8
-rw-r--r--Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h3
-rw-r--r--Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp10
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt10
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt61
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in8
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h3
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp10
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt68
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in11
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h3
-rw-r--r--Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp12
-rw-r--r--Tests/InstallMode/subpro_d_executable/CMakeLists.txt24
-rw-r--r--Tests/InstallMode/subpro_d_executable/src/main.cpp13
-rw-r--r--Tests/InstallMode/superpro/CMakeLists.txt29
-rw-r--r--Tests/InstallMode/superpro/file_copy.txt1
-rw-r--r--Tests/InstallMode/superpro/file_copy_file.txt1
-rw-r--r--Tests/InstallMode/superpro/file_create_link_symbolic.txt2
-rw-r--r--Tests/InstallMode/superpro/file_install.txt6
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt16
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in9
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp5
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/mainwindow.h13
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in7
-rw-r--r--Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp9
-rw-r--r--Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake9
-rw-r--r--Tests/RunCMake/BuildDepends/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/CMakePresets/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/V2InheritV3Optional.json.in8
-rw-r--r--Tests/RunCMake/CMakePresets/V2InheritV3OptionalUser.json.in11
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake0
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt5
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake0
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake7
-rw-r--r--Tests/RunCMake/FindPkgConfig/FindPkgConfig_CMP0126_NEW.cmake64
-rw-r--r--Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt16
-rw-r--r--Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt9
-rw-r--r--Tests/RunCMake/GoogleTest/GoogleTest.cmake18
-rw-r--r--Tests/RunCMake/GoogleTest/RunCMakeTest.cmake14
-rw-r--r--Tests/RunCMake/GoogleTest/fake_gtest.cpp55
-rw-r--r--Tests/RunCMake/InterfaceLibrary/IncludeDirectories.cmake7
-rw-r--r--Tests/RunCMake/InterfaceLibrary/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/LinkWhatYouUse/C.cmake1
-rw-r--r--Tests/RunCMake/LinkWhatYouUse/CXX.cmake1
-rw-r--r--Tests/RunCMake/LinkWhatYouUse/foo.c4
-rw-r--r--Tests/RunCMake/LinkWhatYouUse/foo.cxx4
-rw-r--r--Tests/RunCMake/ctest_test/RunCMakeTest.cmake13
-rw-r--r--Tests/RunCMake/ctest_test/test.cmake.in7
-rw-r--r--Tests/RunCMake/if/IncompleteMatches-stdout.txt6
-rw-r--r--Tests/RunCMake/if/IncompleteMatches.cmake36
-rw-r--r--Tests/RunCMake/if/IncompleteMatchesFail-result.txt1
-rw-r--r--Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt6
-rw-r--r--Tests/RunCMake/if/IncompleteMatchesFail.cmake2
-rw-r--r--Tests/RunCMake/if/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/if/unbalanced-parenthesis-result.txt1
-rw-r--r--Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt8
-rw-r--r--Tests/RunCMake/if/unbalanced-parenthesis.cmake8
-rw-r--r--Tests/RunCMake/string/Timestamp-stderr.txt2
-rw-r--r--Tests/RunCMake/string/Timestamp.cmake2
-rw-r--r--Tests/RunCMake/while/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/while/unbalanced-parenthesis-result.txt1
-rw-r--r--Tests/RunCMake/while/unbalanced-parenthesis-stderr.txt8
-rw-r--r--Tests/RunCMake/while/unbalanced-parenthesis.cmake8
-rw-r--r--Utilities/KWIML/include/kwiml/int.h17
-rw-r--r--Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c2
-rw-r--r--Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c4
-rwxr-xr-xbootstrap1
221 files changed, 3358 insertions, 1324 deletions
diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst
index 565a37b..454c860 100644
--- a/Help/command/cmake_path.rst
+++ b/Help/command/cmake_path.rst
@@ -69,8 +69,8 @@ Synopsis
`Native Conversion`_
cmake_path(`NATIVE_PATH`_ <path-var> [NORMALIZE] <out-var>)
- cmake_path(`CONVERT`_ <input> `TO_CMAKE_PATH_LIST`_ <out-var>)
- cmake_path(`CONVERT`_ <input> `TO_NATIVE_PATH_LIST`_ <out-var>)
+ cmake_path(`CONVERT`_ <input> `TO_CMAKE_PATH_LIST`_ <out-var> [NORMALIZE])
+ cmake_path(`CONVERT`_ <input> `TO_NATIVE_PATH_LIST`_ <out-var> [NORMALIZE])
`Hashing`_
cmake_path(`HASH`_ <path-var> <out-var>)
diff --git a/Help/command/file.rst b/Help/command/file.rst
index f038871..96a55ee 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -843,6 +843,11 @@ and ``NO_SOURCE_PERMISSIONS`` is default.
Installation scripts generated by the :command:`install` command
use this signature (with some undocumented options for internal use).
+.. versionchanged:: 3.22
+
+ The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
+ default copying behavior of :command:`file(INSTALL)`.
+
.. _SIZE:
.. code-block:: cmake
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index 7febd5d..bd26010 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -28,11 +28,26 @@ that the package cannot be found if it is not ``REQUIRED``. The ``REQUIRED``
option stops processing with an error message if the package cannot be found.
A package-specific list of required components may be listed after the
-``COMPONENTS`` option (or after the ``REQUIRED`` option if present).
+``COMPONENTS`` keyword. If any of these components are not able to be
+satisfied, the package overall is considered to be not found. If the
+``REQUIRED`` option is also present, this is treated as a fatal error,
+otherwise execution still continues. As a form of shorthand, if the
+``REQUIRED`` option is present, the ``COMPONENTS`` keyword can be omitted
+and the required components can be listed directly after ``REQUIRED``.
+
Additional optional components may be listed after
-``OPTIONAL_COMPONENTS``. Available components and their influence on
-whether a package is considered to be found are defined by the target
-package.
+``OPTIONAL_COMPONENTS``. If these cannot be satisfied, the package overall
+can still be considered found, as long as all required components are
+satisfied.
+
+The set of available components and their meaning are defined by the
+target package. Formally, it is up to the target package how to
+interpret the component information given to it, but it should follow
+the expectations stated above. For calls where no components are specified,
+there is no single expected behavior and target packages should clearly
+define what occurs in such cases. Common arrangements include assuming it
+should find all components, no components or some well-defined subset of the
+available components.
.. _FIND_PACKAGE_VERSION_FORMAT:
@@ -486,7 +501,7 @@ restores their original state before returning):
``<PackageName>_FIND_VERSION_EXACT``
True if ``EXACT`` option was given
``<PackageName>_FIND_COMPONENTS``
- List of requested components
+ List of specified components (required and optional)
``<PackageName>_FIND_REQUIRED_<c>``
True if component ``<c>`` is required,
false if component ``<c>`` is optional
diff --git a/Help/command/get_filename_component.rst b/Help/command/get_filename_component.rst
index be9d00a..4bdd388 100644
--- a/Help/command/get_filename_component.rst
+++ b/Help/command/get_filename_component.rst
@@ -3,7 +3,7 @@ get_filename_component
Get a specific component of a full filename.
-.. versionchanged:: 3.19
+.. versionchanged:: 3.20
This command been superseded by :command:`cmake_path` command, except
``REALPATH`` now offered by :ref:`file(REAL_PATH) <REAL_PATH>` command and
``PROGRAM`` now available in :command:`separate_arguments(PROGRAM)` command.
diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst
index f77d8af..46da285 100644
--- a/Help/command/get_property.rst
+++ b/Help/command/get_property.rst
@@ -9,7 +9,7 @@ Get a property.
<GLOBAL |
DIRECTORY [<dir>] |
TARGET <target> |
- SOURCE <source> |
+ SOURCE <source>
[DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
INSTALL <file> |
TEST <test> |
diff --git a/Help/command/install.rst b/Help/command/install.rst
index c6af489..1236f1d 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -30,6 +30,10 @@ are executed in order during installation.
with those in the parent directory to run in the order declared (see
policy :policy:`CMP0082`).
+.. versionchanged:: 3.22
+ The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
+ default copying behavior of :command:`install()`.
+
There are multiple signatures for this command. Some of them define
installation options for files and targets. Options common to
multiple signatures are covered here but they are valid only for
diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst
index bf437b4..555520d 100644
--- a/Help/command/set_property.rst
+++ b/Help/command/set_property.rst
@@ -9,8 +9,8 @@ Set a named property in a given scope.
DIRECTORY [<dir>] |
TARGET [<target1> ...] |
SOURCE [<src1> ...]
- [DIRECTORY <dirs> ...] |
- [TARGET_DIRECTORY <targets> ...]
+ [DIRECTORY <dirs> ...]
+ [TARGET_DIRECTORY <targets> ...] |
INSTALL [<file1> ...] |
TEST [<test1> ...] |
CACHE [<entry1> ...] >
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 8ad0089..29ad082 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -449,38 +449,73 @@ be in Coordinated Universal Time (UTC) rather than local time.
The optional ``<format_string>`` may contain the following format
specifiers:
-::
+``%%``
+ .. versionadded:: 3.8
- %% A literal percent sign (%).
- %d The day of the current month (01-31).
- %H The hour on a 24-hour clock (00-23).
- %I The hour on a 12-hour clock (01-12).
- %j The day of the current year (001-366).
- %m The month of the current year (01-12).
- %b Abbreviated month name (e.g. Oct).
- %B Full month name (e.g. October).
- %M The minute of the current hour (00-59).
- %s Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
- %S The second of the current minute.
- 60 represents a leap second. (00-60)
- %U The week number of the current year (00-53).
- %w The day of the current week. 0 is Sunday. (0-6)
- %a Abbreviated weekday name (e.g. Fri).
- %A Full weekday name (e.g. Friday).
- %y The last two digits of the current year (00-99)
- %Y The current year.
-
-.. versionadded:: 3.6
- ``%s`` format specifier (UNIX time).
+ A literal percent sign (%).
-.. versionadded:: 3.7
- ``%a`` and ``%b`` format specifiers (abbreviated month and weekday names).
+``%d``
+ The day of the current month (01-31).
-.. versionadded:: 3.8
- ``%%`` specifier (literal ``%``).
+``%H``
+ The hour on a 24-hour clock (00-23).
-.. versionadded:: 3.7
- ``%A`` and ``%B`` format specifiers (full month and weekday names).
+``%I``
+ The hour on a 12-hour clock (01-12).
+
+``%j``
+ The day of the current year (001-366).
+
+``%m``
+ The month of the current year (01-12).
+
+``%b``
+ .. versionadded:: 3.7
+
+ Abbreviated month name (e.g. Oct).
+
+``%B``
+ .. versionadded:: 3.10
+
+ Full month name (e.g. October).
+
+``%M``
+ The minute of the current hour (00-59).
+
+``%s``
+ .. versionadded:: 3.6
+
+ Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
+
+``%S``
+ The second of the current minute. 60 represents a leap second. (00-60)
+
+``%U``
+ The week number of the current year (00-53).
+
+``%V``
+ .. versionadded:: 3.22
+
+ The ISO 8601 week number of the current year (01-53).
+
+``%w``
+ The day of the current week. 0 is Sunday. (0-6)
+
+``%a``
+ .. versionadded:: 3.7
+
+ Abbreviated weekday name (e.g. Fri).
+
+``%A``
+ .. versionadded:: 3.10
+
+ Full weekday name (e.g. Friday).
+
+``%y``
+ The last two digits of the current year (00-99).
+
+``%Y``
+ The current year.
Unknown format specifiers will be ignored and copied to the output
as-is.
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst
index d8d53ec..f96ca32 100644
--- a/Help/cpack_gen/deb.rst
+++ b/Help/cpack_gen/deb.rst
@@ -274,10 +274,23 @@ List of CPack DEB generator specific variables:
Possible values are:
- - lzma
- - xz
- - bzip2
- - gzip
+ ``lzma``
+ Lempel–Ziv–Markov chain algorithm
+
+ ``xz``
+ XZ Utils compression
+
+ ``bzip2``
+ bzip2 Burrows–Wheeler algorithm
+
+ ``gzip``
+ GNU Gzip compression
+
+ ``zstd``
+ .. versionadded:: 3.22
+
+ Zstandard compression
+
.. variable:: CPACK_DEBIAN_PACKAGE_PRIORITY
CPACK_DEBIAN_<COMPONENT>_PACKAGE_PRIORITY
@@ -652,9 +665,18 @@ Dbgsym packaging has its own set of variables:
.. note::
+ Setting this also strips the ELF files in the generated non-dbgsym package,
+ which results in debuginfo only being available in the dbgsym package.
+
+.. note::
+
Binaries must contain debug symbols before packaging so use either ``Debug``
or ``RelWithDebInfo`` for :variable:`CMAKE_BUILD_TYPE` variable value.
+ Additionally, if :variable:`CPACK_STRIP_FILES` is set, the files will be stripped before
+ they get to the DEB generator, so will not contain debug symbols and
+ a dbgsym package will not get built. Do not use with :variable:`CPACK_STRIP_FILES`.
+
Building Debian packages on Windows
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/cpack_gen/dmg.rst b/Help/cpack_gen/dmg.rst
index 64e9769..1f05618 100644
--- a/Help/cpack_gen/dmg.rst
+++ b/Help/cpack_gen/dmg.rst
@@ -105,6 +105,8 @@ on macOS:
.. variable:: CPACK_DMG_FILESYSTEM
+ .. versionadded:: 3.21
+
The filesystem format. Common values are ``APFS`` and ``HFS+``.
See ``man hdiutil`` for a full list of supported formats.
Defaults to ``HFS+``.
diff --git a/Help/cpack_gen/nsis.rst b/Help/cpack_gen/nsis.rst
index 02e33ba..299cfec 100644
--- a/Help/cpack_gen/nsis.rst
+++ b/Help/cpack_gen/nsis.rst
@@ -3,8 +3,8 @@ CPack NSIS Generator
CPack Nullsoft Scriptable Install System (NSIS) generator specific options.
-.. versionchanged:: 3.17
- The NSIS generator requires NSIS 3.0 or newer.
+.. versionchanged:: 3.22
+ The NSIS generator requires NSIS 3.03 or newer.
Variables specific to CPack NSIS generator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -201,3 +201,9 @@ on Windows Nullsoft Scriptable Install System.
.. versionadded:: 3.21
If set, specify the name of the NSIS executable. Default is ``makensis``.
+
+.. variable:: CPACK_NSIS_IGNORE_LICENSE_PAGE
+
+ .. versionadded:: 3.22
+
+ If set, do not display the page containing the license during installation.
diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst
index f6274f9..823594a 100644
--- a/Help/cpack_gen/rpm.rst
+++ b/Help/cpack_gen/rpm.rst
@@ -840,6 +840,10 @@ Debuginfo RPM packaging has its own set of variables:
Binaries must contain debug symbols before packaging so use either ``Debug``
or ``RelWithDebInfo`` for :variable:`CMAKE_BUILD_TYPE` variable value.
+ Additionally, if :variable:`CPACK_STRIP_FILES` is set, the files will be stripped before
+ they get to the RPM generator, so will not contain debug symbols and
+ a debuginfo package will not get built. Do not use with :variable:`CPACK_STRIP_FILES`.
+
.. note::
Packages generated from packages without binary files, with binary files but
diff --git a/Help/envvar/CMAKE_INSTALL_MODE.rst b/Help/envvar/CMAKE_INSTALL_MODE.rst
new file mode 100644
index 0000000..37db0d7
--- /dev/null
+++ b/Help/envvar/CMAKE_INSTALL_MODE.rst
@@ -0,0 +1,37 @@
+CMAKE_INSTALL_MODE
+------------------
+
+.. versionadded:: 3.22
+
+.. include:: ENV_VAR.txt
+
+The ``CMAKE_INSTALL_MODE`` environment variable allows users to operate
+CMake in an alternate mode of :command:`file(INSTALL)` and :command:`install()`.
+
+The default behavior for an installation is to copy a source file from a
+source directory into a destination directory. This environment variable
+however allows the user to override this behavior, causing CMake to create
+symbolic links instead.
+
+.. note::
+ A symbolic link consists of a reference file path rather than contents of its own,
+ hence there are two ways to express the relation, either by a relative or an absolute path.
+
+The following values are allowed for ``CMAKE_INSTALL_MODE``:
+
+* empty, unset or ``COPY``: default behavior, duplicate the file at its destination
+* ``ABS_SYMLINK``: create an *absolute* symbolic link to the source file at the destination *or fail*
+* ``ABS_SYMLINK_OR_COPY``: like ``ABS_SYMLINK`` but silently copy on error
+* ``REL_SYMLINK``: create an *relative* symbolic link to the source file at the destination *or fail*
+* ``REL_SYMLINK_OR_COPY``: like ``REL_SYMLINK`` but silently copy on error
+* ``SYMLINK``: try as if through ``REL_SYMLINK`` and fall back to ``ABS_SYMLINK`` if the referenced
+ file cannot be expressed using a relative path. Fail on error.
+* ``SYMLINK_OR_COPY``: like ``SYMLINK`` but silently copy on error
+
+Installing symbolic links rather than copying files can help conserve storage space because files do
+not have to be duplicated on disk. However, modifications applied to the source immediately affects
+the symbolic link and vice versa. *Use with caution*.
+
+.. note:: ``CMAKE_INSTALL_MODE`` only affects files, *not* directories.
+
+.. note:: Symbolic links are not available on all platforms.
diff --git a/Help/generator/Visual Studio 10 2010.rst b/Help/generator/Visual Studio 10 2010.rst
index c065550..9ec33c3 100644
--- a/Help/generator/Visual Studio 10 2010.rst
+++ b/Help/generator/Visual Studio 10 2010.rst
@@ -1,7 +1,14 @@
Visual Studio 10 2010
---------------------
-Generates Visual Studio 10 (VS 2010) project files.
+Deprecated. Generates Visual Studio 10 (VS 2010) project files.
+
+.. note::
+ This generator is deprecated and will be removed in a future version
+ of CMake. It will still be possible to build with VS 10 2010 tools
+ using the :generator:`Visual Studio 11 2012` (or above) generator
+ with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v100``, or by
+ using the :generator:`NMake Makefiles` generator.
For compatibility with CMake versions prior to 3.0, one may specify this
generator using the name ``Visual Studio 10`` without the year component.
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index 3db189e..0799fdd 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -38,6 +38,7 @@ Environment Variables that Control the Build
/envvar/CMAKE_GENERATOR_INSTANCE
/envvar/CMAKE_GENERATOR_PLATFORM
/envvar/CMAKE_GENERATOR_TOOLSET
+ /envvar/CMAKE_INSTALL_MODE
/envvar/CMAKE_LANG_COMPILER_LAUNCHER
/envvar/CMAKE_LANG_LINKER_LAUNCHER
/envvar/CMAKE_MSVCIDE_RUN_PATH
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index db26b5a..74e9fae 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -187,8 +187,9 @@ that may contain the following fields:
An optional string representing the path to the toolchain file.
This field supports `macro expansion`_. If a relative path is specified,
it is calculated relative to the build directory, and if not found,
- relative to the source directory. Takes precedence over any `CMAKE_TOOLCHAIN_FILE`
- value. This is allowed in preset files specifying version ``3`` or above.
+ relative to the source directory. This field takes precedence over any
+ :variable:`CMAKE_TOOLCHAIN_FILE` value. It is allowed in preset files
+ specifying version ``3`` or above.
``binaryDir``
diff --git a/Help/release/3.21.rst b/Help/release/3.21.rst
index 3e70552..fc5d6ac 100644
--- a/Help/release/3.21.rst
+++ b/Help/release/3.21.rst
@@ -304,3 +304,9 @@ Changes made since CMake 3.21.0 include the following.
* The :generator:`Visual Studio 17 2022` generator is now based on
"Visual Studio 2022 Preview 2". Previously it was based on "Preview 1.1".
+
+3.21.2
+------
+
+* ``CUDA`` targets with :prop_tgt:`CUDA_SEPARABLE_COMPILATION` enabled are now
+ correctly generated in non-root directories.
diff --git a/Help/release/dev/GoogleTest-gtest-filter.rst b/Help/release/dev/GoogleTest-gtest-filter.rst
new file mode 100644
index 0000000..48b97a7
--- /dev/null
+++ b/Help/release/dev/GoogleTest-gtest-filter.rst
@@ -0,0 +1,6 @@
+GoogleTest-gtest-filter
+-----------------------
+
+* The :module:`GoogleTest` module :command:`gtest_discover_tests`
+ function gained a ``TEST_FILTER`` option to filter tests using
+ ``--gtest_filter`` during test discovery.
diff --git a/Help/release/dev/cmake-install-mode-symlink.rst b/Help/release/dev/cmake-install-mode-symlink.rst
new file mode 100644
index 0000000..a5f75ca
--- /dev/null
+++ b/Help/release/dev/cmake-install-mode-symlink.rst
@@ -0,0 +1,16 @@
+cmake-install-mode-symlink
+--------------------------
+
+* The :envvar:`CMAKE_INSTALL_MODE` environment variable was added to
+ allow users to override the default file-copying behavior of
+ :command:`install` and :command:`file(INSTALL)` into creating
+ symbolic links. This can aid in lowering storage space requirements
+ and avoiding redundancy.
+
+* The :command:`file(INSTALL)` can now be affected / modified by the
+ :envvar:`CMAKE_INSTALL_MODE` environment variable causing installation
+ of symbolic links instead of copying of files.
+
+* The :command:`install` can now be affected / modified by the
+ :envvar:`CMAKE_INSTALL_MODE` environment variable causing installation
+ of symbolic links instead of copying of files.
diff --git a/Help/release/dev/cpack-deb-add-zstd-compression.rst b/Help/release/dev/cpack-deb-add-zstd-compression.rst
new file mode 100644
index 0000000..a138455
--- /dev/null
+++ b/Help/release/dev/cpack-deb-add-zstd-compression.rst
@@ -0,0 +1,6 @@
+cpack-deb-add-zstd-compression
+------------------------------
+
+* The :cpack_gen:`CPack DEB Generator` gained the
+ option to set :variable:`CPACK_DEBIAN_COMPRESSION_TYPE` to zstd,
+ which enables Zstandard compression for deb packages.
diff --git a/Help/release/dev/nsis-minimal-version.rst b/Help/release/dev/nsis-minimal-version.rst
new file mode 100644
index 0000000..b19597a
--- /dev/null
+++ b/Help/release/dev/nsis-minimal-version.rst
@@ -0,0 +1,4 @@
+nsis-minimal-version.rst
+------------------------
+
+* The :cpack_gen:`CPack NSIS Generator` now requires NSIS 3.03 or later.
diff --git a/Help/release/dev/nsis_ignore_install_page.rst b/Help/release/dev/nsis_ignore_install_page.rst
new file mode 100644
index 0000000..2d8a8d0
--- /dev/null
+++ b/Help/release/dev/nsis_ignore_install_page.rst
@@ -0,0 +1,6 @@
+nsis_ignore_install_page
+------------------------
+
+* The :cpack_gen:`CPack NSIS Generator` gained a new variable
+ :variable:`CPACK_NSIS_IGNORE_LICENSE_PAGE` to ignore the
+ license page in the installer.
diff --git a/Help/release/dev/string-TIMESTAMP-specifier-V.rst b/Help/release/dev/string-TIMESTAMP-specifier-V.rst
new file mode 100644
index 0000000..afd3f03
--- /dev/null
+++ b/Help/release/dev/string-TIMESTAMP-specifier-V.rst
@@ -0,0 +1,5 @@
+string-TIMESTAMP-specifier-V
+----------------------------
+
+* The :command:`string(TIMESTAMP)` command now supports the ``%V``
+ specifier for ISO 8601 week numbers.
diff --git a/Help/release/dev/vs10-deprecate.rst b/Help/release/dev/vs10-deprecate.rst
new file mode 100644
index 0000000..7bec391
--- /dev/null
+++ b/Help/release/dev/vs10-deprecate.rst
@@ -0,0 +1,5 @@
+vs10-deprecate
+--------------
+
+* The :generator:`Visual Studio 10 2010` generator is now deprecated
+ and will be removed in a future version of CMake.
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index f139ff4..bb97f4a 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -33,7 +33,7 @@ function(__resolve_tool_path CMAKE_TOOL SEARCH_PATH DOCSTRING)
if(NOT _CMAKE_USER_TOOL_PATH)
# Find CMAKE_TOOL in the SEARCH_PATH directory by user-defined name.
- find_program(_CMAKE_TOOL_WITH_PATH NAMES ${${CMAKE_TOOL}} HINTS ${SEARCH_PATH})
+ find_program(_CMAKE_TOOL_WITH_PATH NAMES ${${CMAKE_TOOL}} HINTS ${SEARCH_PATH} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
if(_CMAKE_TOOL_WITH_PATH)
# Overwrite CMAKE_TOOL with full path found in SEARCH_PATH.
@@ -165,9 +165,9 @@ else()
# Prepend toolchain-specific names.
if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC")
- set(_CMAKE_LINKER_NAMES "lld-link")
+ list(PREPEND _CMAKE_LINKER_NAMES "lld-link")
else()
- set(_CMAKE_LINKER_NAMES "ld.lld")
+ list(PREPEND _CMAKE_LINKER_NAMES "ld.lld")
endif()
list(PREPEND _CMAKE_AR_NAMES "llvm-ar")
list(PREPEND _CMAKE_RANLIB_NAMES "llvm-ranlib")
@@ -202,7 +202,7 @@ foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS)
endforeach()
list(REMOVE_DUPLICATES _CMAKE_${_CMAKE_TOOL}_FIND_NAMES)
- find_program(CMAKE_${_CMAKE_TOOL} NAMES ${_CMAKE_${_CMAKE_TOOL}_FIND_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+ find_program(CMAKE_${_CMAKE_TOOL} NAMES ${_CMAKE_${_CMAKE_TOOL}_FIND_NAMES} HINTS ${_CMAKE_TOOLCHAIN_LOCATION} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
unset(_CMAKE_${_CMAKE_TOOL}_FIND_NAMES)
endforeach()
@@ -212,7 +212,7 @@ endif()
if(CMAKE_PLATFORM_HAS_INSTALLNAME)
- find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
+ find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
if(NOT CMAKE_INSTALL_NAME_TOOL)
message(FATAL_ERROR "Could not find install_name_tool, please check your installation.")
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index b4da4fa..7b13c3a 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -25,7 +25,7 @@ Check if a symbol exists as a function, variable, or macro in ``C++``.
as a function or variable then the symbol must also be available for
linking. If the symbol is a type, enum value, or C++ template it will
not be recognized: consider using the :module:`CheckTypeSize`
- or :module:`CheckCXXSourceCompiles` module instead.
+ or :module:`CheckSourceCompiles` module instead.
.. note::
diff --git a/Modules/CheckFortranFunctionExists.cmake b/Modules/CheckFortranFunctionExists.cmake
index ad72e2f..8f1ca9d 100644
--- a/Modules/CheckFortranFunctionExists.cmake
+++ b/Modules/CheckFortranFunctionExists.cmake
@@ -20,6 +20,12 @@ Check if a Fortran function exists.
``<result>``
variable to store the result; will be created as an internal cache variable.
+.. note::
+
+ This command does not detect functions in Fortran modules. In general it is
+ recommended to use :module:`CheckSourceCompiles` instead to determine if a
+ Fortran function or subroutine is available.
+
The following variables may be set before calling this macro to modify
the way the check is run:
diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake
index 928881c..559c103 100644
--- a/Modules/CheckLanguage.cmake
+++ b/Modules/CheckLanguage.cmake
@@ -36,6 +36,9 @@ Example:
include_guard(GLOBAL)
+cmake_policy(PUSH)
+cmake_policy(SET CMP0126 NEW)
+
macro(check_language lang)
if(NOT DEFINED CMAKE_${lang}_COMPILER)
set(_desc "Looking for a ${lang} compiler")
@@ -110,3 +113,5 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
endif()
endmacro()
+
+cmake_policy(POP)
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake
index f8ca584..48ee3c4 100644
--- a/Modules/CheckSymbolExists.cmake
+++ b/Modules/CheckSymbolExists.cmake
@@ -24,7 +24,7 @@ available and assumed to work. If the header files declare the symbol
as a function or variable then the symbol must also be available for
linking (so intrinsics may not be detected).
If the symbol is a type, enum value, or intrinsic it will not be recognized
-(consider using :module:`CheckTypeSize` or :module:`CheckCSourceCompiles`).
+(consider using :module:`CheckTypeSize` or :module:`CheckSourceCompiles`).
If the check needs to be done in C++, consider using
:module:`CheckCXXSymbolExists` instead.
diff --git a/Modules/Compiler/ARMClang-C.cmake b/Modules/Compiler/ARMClang-C.cmake
index 0a64a8a..01c4cea 100644
--- a/Modules/Compiler/ARMClang-C.cmake
+++ b/Modules/Compiler/ARMClang-C.cmake
@@ -2,6 +2,14 @@ include(Compiler/Clang-C)
include(Compiler/ARMClang)
__compiler_armclang(C)
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+ AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+ AND CMAKE_DEPFILE_FLAGS_C)
+ # dependencies are computed by the compiler itself
+ set(CMAKE_C_DEPFILE_FORMAT gcc)
+ set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
+endif()
+
set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
diff --git a/Modules/Compiler/ARMClang-CXX.cmake b/Modules/Compiler/ARMClang-CXX.cmake
index 5dfb401..045b783 100644
--- a/Modules/Compiler/ARMClang-CXX.cmake
+++ b/Modules/Compiler/ARMClang-CXX.cmake
@@ -1,3 +1,11 @@
include(Compiler/Clang-CXX)
include(Compiler/ARMClang)
__compiler_armclang(CXX)
+
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+ AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+ AND CMAKE_DEPFILE_FLAGS_CXX)
+ # dependencies are computed by the compiler itself
+ set(CMAKE_CXX_DEPFILE_FORMAT gcc)
+ set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
+endif()
diff --git a/Modules/Compiler/Clang-FindBinUtils.cmake b/Modules/Compiler/Clang-FindBinUtils.cmake
index e6c469a..125ae78 100644
--- a/Modules/Compiler/Clang-FindBinUtils.cmake
+++ b/Modules/Compiler/Clang-FindBinUtils.cmake
@@ -28,6 +28,7 @@ find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x}"
"${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar"
HINTS ${__clang_hints}
+ NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
DOC "LLVM archiver"
)
mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
@@ -38,6 +39,7 @@ find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x}"
"${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib"
HINTS ${__clang_hints}
+ NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
DOC "Generate index for LLVM archive"
)
mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB)
diff --git a/Modules/Compiler/GNU-FindBinUtils.cmake b/Modules/Compiler/GNU-FindBinUtils.cmake
index 097fbf3..4dcdd53 100644
--- a/Modules/Compiler/GNU-FindBinUtils.cmake
+++ b/Modules/Compiler/GNU-FindBinUtils.cmake
@@ -20,6 +20,7 @@ find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${__version_x}"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar${_CMAKE_COMPILER_SUFFIX}"
HINTS ${__gcc_hints}
+ NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
DOC "A wrapper around 'ar' adding the appropriate '--plugin' option for the GCC compiler"
)
mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
@@ -30,6 +31,7 @@ find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${__version_x}"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib${_CMAKE_COMPILER_SUFFIX}"
HINTS ${__gcc_hints}
+ NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
DOC "A wrapper around 'ranlib' adding the appropriate '--plugin' option for the GCC compiler"
)
mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB)
diff --git a/Modules/Compiler/NVHPC-C.cmake b/Modules/Compiler/NVHPC-C.cmake
index a734edf..e37aad5 100644
--- a/Modules/Compiler/NVHPC-C.cmake
+++ b/Modules/Compiler/NVHPC-C.cmake
@@ -4,9 +4,9 @@ include(Compiler/NVHPC)
# Needed so that we support `LANGUAGE` property correctly
set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
-# Required since as of NVHPC 21.03 the `-MD` flag implicitly
-# implies `-E` and therefore compilation and dependency generation
-# can't occur in the same invocation
-set(CMAKE_C_DEPENDS_EXTRA_COMMANDS "<CMAKE_C_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -x c -M <SOURCE> -MT <OBJECT> -MD<DEP_FILE>")
+if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 20.11)
+ set(CMAKE_C17_STANDARD_COMPILE_OPTION -std=c17)
+ set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=gnu17)
+endif()
__compiler_nvhpc(C)
diff --git a/Modules/Compiler/NVHPC-CXX.cmake b/Modules/Compiler/NVHPC-CXX.cmake
index 98d0022..534e822 100644
--- a/Modules/Compiler/NVHPC-CXX.cmake
+++ b/Modules/Compiler/NVHPC-CXX.cmake
@@ -4,9 +4,9 @@ include(Compiler/NVHPC)
# Needed so that we support `LANGUAGE` property correctly
set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
-# Required since as of NVHPC 21.03 the `-MD` flag implicitly
-# implies `-E` and therefore compilation and dependency generation
-# can't occur in the same invocation
-set(CMAKE_CXX_DEPENDS_EXTRA_COMMANDS "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -x c++ -M <SOURCE> -MT <OBJECT> -MD<DEP_FILE>")
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20.11)
+ set(CMAKE_CXX20_STANDARD_COMPILE_OPTION -std=c++20)
+ set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION -std=gnu++20)
+endif()
__compiler_nvhpc(CXX)
diff --git a/Modules/Compiler/NVHPC.cmake b/Modules/Compiler/NVHPC.cmake
index 7048670..a85df71 100644
--- a/Modules/Compiler/NVHPC.cmake
+++ b/Modules/Compiler/NVHPC.cmake
@@ -12,4 +12,16 @@ include(Compiler/PGI)
macro(__compiler_nvhpc lang)
# Logic specific to NVHPC.
+
+ if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 21.07)
+ set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>")
+ set(CMAKE_${lang}_DEPFILE_FORMAT gcc)
+ set(CMAKE_${lang}_DEPENDS_USE_COMPILER TRUE)
+ else()
+ # Before NVHPC 21.07 the `-MD` flag implicitly
+ # implies `-E` and therefore compilation and dependency generation
+ # can't occur in the same invocation
+ set(CMAKE_${lang}_DEPENDS_EXTRA_COMMANDS "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> ${CMAKE_${lang}_COMPILE_OPTIONS_EXPLICIT_LANGUAGE} -M <SOURCE> -MT <OBJECT> -MD<DEP_FILE>")
+ endif()
+
endmacro()
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index bd82a90..5ea8b2a 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -21,13 +21,14 @@ supported by the :module:`ExternalProject` module. Whereas
configure step to use the content in commands like :command:`add_subdirectory`,
:command:`include` or :command:`file` operations.
-Content population details would normally be defined separately from the
-command that performs the actual population. This separation ensures that
-all of the dependency details are defined before anything may try to use those
-details to populate content. This is particularly important in more complex
-project hierarchies where dependencies may be shared between multiple projects.
+Content population details should be defined separately from the command that
+performs the actual population. This separation ensures that all the
+dependency details are defined before anything might try to use them to
+populate content. This is particularly important in more complex project
+hierarchies where dependencies may be shared between multiple projects.
-The following shows a typical example of declaring content details:
+The following shows a typical example of declaring content details for some
+dependencies and then ensuring they are populated with a separate call:
.. code-block:: cmake
@@ -36,57 +37,67 @@ The following shows a typical example of declaring content details:
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
+ FetchContent_Declare(
+ myCompanyIcons
+ URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
+ URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
+ )
+
+ FetchContent_MakeAvailable(googletest secret_sauce)
+
+The :command:`FetchContent_MakeAvailable` command ensures the named
+dependencies have been populated, either by an earlier call or by populating
+them itself. When performing the population, it will also add them to the
+main build, if possible, so that the main build can use the populated
+projects' targets, etc. See the command's documentation for how these steps
+are performed.
+
+When using a hierarchical project arrangement, projects at higher levels in
+the hierarchy are able to override the declared details of content specified
+anywhere lower in the project hierarchy. The first details to be declared
+for a given dependency take precedence, regardless of where in the project
+hierarchy that occurs. Similarly, the first call that tries to populate a
+dependency "wins", with subsequent populations reusing the result of the
+first instead of repeating the population again.
+See the :ref:`Examples <fetch-content-examples>` which demonstrate
+this scenario.
-For most typical cases, populating the content can then be done with a single
-command like so:
+In some cases, the main project may need to have more precise control over
+the population, or it may be required to explicitly define the population
+steps in a way that cannot be captured by the declared details alone.
+For such situations, the lower level :command:`FetchContent_GetProperties` and
+:command:`FetchContent_Populate` commands can be used. These lack the richer
+features provided by :command:`FetchContent_MakeAvailable` though, so their
+direct use should be considered a last resort. The typical pattern of such
+custom steps looks like this:
.. code-block:: cmake
- FetchContent_MakeAvailable(googletest)
+ # NOTE: Where possible, prefer to use FetchContent_MakeAvailable()
+ # instead of custom logic like this
-The above command not only populates the content, it also adds it to the main
-build (if possible) so that the main build can use the populated project's
-targets, etc. In some cases, the main project may need to have more precise
-control over the population or may be required to explicitly define the
-population steps (e.g. if CMake versions earlier than 3.14 need to be
-supported). The typical pattern of such custom steps looks like this:
+ # Check if population has already been performed
+ FetchContent_GetProperties(depname)
+ if(NOT depname_POPULATED)
+ # Fetch the content using previously declared details
+ FetchContent_Populate(depname)
-.. code-block:: cmake
+ # Set custom variables, policies, etc.
+ # ...
- FetchContent_GetProperties(googletest)
- if(NOT googletest_POPULATED)
- FetchContent_Populate(googletest)
- add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
+ # Bring the populated content into the build
+ add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
endif()
-Regardless of which population method is used, when using the
-declare-populate pattern with a hierarchical project arrangement, projects at
-higher levels in the hierarchy are able to override the population details of
-content specified anywhere lower in the project hierarchy. The ability to
-detect whether content has already been populated ensures that even if
-multiple child projects want certain content to be available, the first one
-to populate it wins. The other child project can simply make use of the
-already available content instead of repeating the population for itself.
-See the :ref:`Examples <fetch-content-examples>` section which demonstrates
-this scenario.
-
The ``FetchContent`` module also supports defining and populating
content in a single call, with no check for whether the content has been
-populated elsewhere in the project already. This is a more low level
-operation and would not normally be the way the module is used, but it is
-sometimes useful as part of implementing some higher level feature or to
-populate some content in CMake's script mode.
-
-.. versionchanged:: 3.14
- ``FetchContent`` commands can access the terminal. This is necessary
- for password prompts and real-time progress displays to work.
+populated elsewhere already. This should not be done in projects, but may
+be appropriate for populating content in CMake's script mode.
+See :command:`FetchContent_Populate` for details.
Commands
^^^^^^^^
-Declaring Content Details
-"""""""""""""""""""""""""
-
.. command:: FetchContent_Declare
.. code-block:: cmake
@@ -94,7 +105,7 @@ Declaring Content Details
FetchContent_Declare(<name> <contentOptions>...)
The ``FetchContent_Declare()`` function records the options that describe
- how to populate the specified content, but if such details have already
+ how to populate the specified content. If such details have already
been recorded earlier in this project (regardless of where in the project
hierarchy), this and all later calls for the same content ``<name>`` are
ignored. This "first to record, wins" approach is what allows hierarchical
@@ -110,7 +121,7 @@ Declaring Content Details
projects needing that same content will use the same name, leading to
the content being populated multiple times.
- The ``<contentOptions>`` can be any of the download or update/patch options
+ The ``<contentOptions>`` can be any of the download, update or patch options
that the :command:`ExternalProject_Add` command understands. The configure,
build, install and test steps are explicitly disabled and therefore options
related to them will be ignored. The ``SOURCE_SUBDIR`` option is an
@@ -146,47 +157,88 @@ Declaring Content Details
than a branch or tag name. A commit hash is more secure and helps to
confirm that the downloaded contents are what you expected.
-Populating The Content
-""""""""""""""""""""""
+ .. versionchanged:: 3.14
+ Commands for the download, update or patch steps can access the terminal.
+ This may be needed for things like password prompts or real-time display
+ of command progress.
-For most common scenarios, population means making content available to the
-main build according to previously declared details for that dependency.
-There are two main patterns for populating content, one based on calling
-:command:`FetchContent_GetProperties` and
-:command:`FetchContent_Populate` for more precise control and the other on
-calling :command:`FetchContent_MakeAvailable` for a simpler, more automated
-approach. The former generally follows this canonical pattern:
+.. command:: FetchContent_MakeAvailable
-.. _`fetch-content-canonical-pattern`:
+ .. versionadded:: 3.14
-.. code-block:: cmake
+ .. code-block:: cmake
- # Check if population has already been performed
- FetchContent_GetProperties(<name>)
- string(TOLOWER "<name>" lcName)
- if(NOT ${lcName}_POPULATED)
- # Fetch the content using previously declared details
- FetchContent_Populate(<name>)
+ FetchContent_MakeAvailable(<name1> [<name2>...])
+
+ This command ensures that each of the named dependencies are populated and
+ potentially added to the build by the time it returns. It iterates over
+ the list, and for each dependency, the following logic is applied:
+
+ * If the dependency has already been populated earlier in this run, set
+ the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR`` and
+ ``<lowercaseName>_BINARY_DIR`` variables in the same way as a call to
+ :command:`FetchContent_GetProperties`, then skip the remaining steps
+ below and move on to the next dependency in the list.
+
+ * Call :command:`FetchContent_Populate` to populate the dependency using
+ the details recorded by an earlier call to :command:`FetchContent_Declare`.
+ Halt with a fatal error if no such details have been recorded.
+ :variable:`FETCHCONTENT_SOURCE_DIR_<uppercaseName>` can be used to override
+ the declared details and use content provided at the specified location
+ instead.
+
+ * If the top directory of the populated content contains a ``CMakeLists.txt``
+ file, call :command:`add_subdirectory` to add it to the main build.
+ It is not an error for there to be no ``CMakeLists.txt`` file, which
+ allows the command to be used for dependencies that make downloaded
+ content available at a known location, but which do not need or support
+ being added directly to the build.
+
+ .. versionadded:: 3.18
+ The ``SOURCE_SUBDIR`` option can be given in the declared details to
+ look somewhere below the top directory instead (i.e. the same way that
+ ``SOURCE_SUBDIR`` is used by the :command:`ExternalProject_Add`
+ command). The path provided with ``SOURCE_SUBDIR`` must be relative
+ and will be treated as relative to the top directory. It can also
+ point to a directory that does not contain a ``CMakeLists.txt`` file
+ or even to a directory that doesn't exist. This can be used to avoid
+ adding a project that contains a ``CMakeLists.txt`` file in its top
+ directory.
+
+ Projects should aim to declare the details of all dependencies they might
+ use before they call ``FetchContent_MakeAvailable()`` for any of them.
+ This ensures that if any of the dependencies are also sub-dependencies of
+ one or more of the others, the main project still controls the details
+ that will be used (because it will declare them first before the
+ dependencies get a chance to). In the following code samples, assume that
+ the ``uses_other`` dependency also uses ``FetchContent`` to add the ``other``
+ dependency internally:
- # Set custom variables, policies, etc.
- # ...
+ .. code-block:: cmake
- # Bring the populated content into the build
- add_subdirectory(${${lcName}_SOURCE_DIR} ${${lcName}_BINARY_DIR})
- endif()
+ # WRONG: Should declare all details first
+ FetchContent_Declare(uses_other ...)
+ FetchContent_MakeAvailable(uses_other)
+
+ FetchContent_Declare(other ...) # Will be ignored, uses_other beat us to it
+ FetchContent_MakeAvailable(other) # Would use details declared by uses_other
-The above is such a common pattern that, where no custom steps are needed
-between the calls to :command:`FetchContent_Populate` and
-:command:`add_subdirectory`, equivalent logic can be obtained by calling
-:command:`FetchContent_MakeAvailable` instead. Where it meets the needs of
-the project, :command:`FetchContent_MakeAvailable` should be preferred, as it
-is simpler and provides additional features over the pattern above.
+ .. code-block:: cmake
+
+ # CORRECT: All details declared first, so they will take priority
+ FetchContent_Declare(uses_other ...)
+ FetchContent_Declare(other ...)
+ FetchContent_MakeAvailable(uses_other other)
.. command:: FetchContent_Populate
+ .. note::
+ Where possible, prefer to use :command:`FetchContent_MakeAvailable`
+ instead of implementing population manually with this command.
+
.. code-block:: cmake
- FetchContent_Populate( <name> )
+ FetchContent_Populate(<name>)
In most cases, the only argument given to ``FetchContent_Populate()`` is the
``<name>``. When used this way, the command assumes the content details have
@@ -211,88 +263,29 @@ is simpler and provides additional features over the pattern above.
``FetchContent_Populate()``.
``FetchContent_Populate()`` will set three variables in the scope of the
- caller; ``<lcName>_POPULATED``, ``<lcName>_SOURCE_DIR`` and
- ``<lcName>_BINARY_DIR``, where ``<lcName>`` is the lowercased ``<name>``.
- ``<lcName>_POPULATED`` will always be set to ``True`` by the call.
- ``<lcName>_SOURCE_DIR`` is the location where the
- content can be found upon return (it will have already been populated), while
- ``<lcName>_BINARY_DIR`` is a directory intended for use as a corresponding
- build directory. The main use case for the two directory variables is to
- call :command:`add_subdirectory` immediately after population, i.e.:
+ caller:
+
+ ``<lowercaseName>_POPULATED``
+ This will always be set to ``TRUE`` by the call.
+
+ ``<lowercaseName>_SOURCE_DIR``
+ The location where the populated content can be found upon return.
+
+ ``<lowercaseName>_BINARY_DIR``
+ A directory intended for use as a corresponding build directory.
+
+ The main use case for the ``<lowercaseName>_SOURCE_DIR`` and
+ ``<lowercaseName>_BINARY_DIR`` variables is to call
+ :command:`add_subdirectory` immediately after population:
.. code-block:: cmake
- FetchContent_Populate(FooBar ...)
+ FetchContent_Populate(FooBar)
add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})
The values of the three variables can also be retrieved from anywhere in the
project hierarchy using the :command:`FetchContent_GetProperties` command.
- A number of cache variables influence the behavior of all content population
- performed using details saved from a :command:`FetchContent_Declare` call:
-
- ``FETCHCONTENT_BASE_DIR``
- In most cases, the saved details do not specify any options relating to the
- directories to use for the internal sub-build, final source and build areas.
- It is generally best to leave these decisions up to the ``FetchContent``
- module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR``
- cache variable controls the point under which all content population
- directories are collected, but in most cases developers would not need to
- change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if
- developers change this value, they should aim to keep the path short and
- just below the top level of the build tree to avoid running into path
- length problems on Windows.
-
- ``FETCHCONTENT_QUIET``
- The logging output during population can be quite verbose, making the
- configure stage quite noisy. This cache option (``ON`` by default) hides
- all population output unless an error is encountered. If experiencing
- problems with hung downloads, temporarily switching this option off may
- help diagnose which content population is causing the issue.
-
- ``FETCHCONTENT_FULLY_DISCONNECTED``
- When this option is enabled, no attempt is made to download or update
- any content. It is assumed that all content has already been populated in
- a previous run or the source directories have been pointed at existing
- contents the developer has provided manually (using options described
- further below). When the developer knows that no changes have been made to
- any content details, turning this option ``ON`` can significantly speed up
- the configure stage. It is ``OFF`` by default.
-
- ``FETCHCONTENT_UPDATES_DISCONNECTED``
- This is a less severe download/update control compared to
- ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and
- update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the
- update stage. Therefore, if content has not been downloaded previously,
- it will still be downloaded when this option is enabled. This can speed up
- the configure stage, but not as much as
- ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default.
-
- In addition to the above cache variables, the following cache variables are
- also defined for each content name (``<ucName>`` is the uppercased value of
- ``<name>``):
-
- ``FETCHCONTENT_SOURCE_DIR_<ucName>``
- If this is set, no download or update steps are performed for the specified
- content and the ``<lcName>_SOURCE_DIR`` variable returned to the caller is
- pointed at this location. This gives developers a way to have a separate
- checkout of the content that they can modify freely without interference
- from the build. The build simply uses that existing source, but it still
- defines ``<lcName>_BINARY_DIR`` to point inside its own build area.
- Developers are strongly encouraged to use this mechanism rather than
- editing the sources populated in the default location, as changes to
- sources in the default location can be lost when content population details
- are changed by the project.
-
- ``FETCHCONTENT_UPDATES_DISCONNECTED_<ucName>``
- This is the per-content equivalent of
- ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option
- is ``ON``, then updates will be disabled for the named content.
- Disabling updates for individual content can be useful for content whose
- details rarely change, while still leaving other frequently changing
- content with updates enabled.
-
-
The ``FetchContent_Populate()`` command also supports a syntax allowing the
content details to be specified directly rather than using any saved
details. This is more low-level and use of this form is generally to be
@@ -303,7 +296,8 @@ is simpler and provides additional features over the pattern above.
.. code-block:: cmake
- FetchContent_Populate( <name>
+ FetchContent_Populate(
+ <name>
[QUIET]
[SUBBUILD_DIR <subBuildDir>]
[SOURCE_DIR <srcDir>]
@@ -325,16 +319,17 @@ is simpler and provides additional features over the pattern above.
- The ``FETCHCONTENT_FULLY_DISCONNECTED`` and
``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored.
- The ``<lcName>_SOURCE_DIR`` and ``<lcName>_BINARY_DIR`` variables are still
- returned to the caller, but since these locations are not stored as global
- properties when this form is used, they are only available to the calling
- scope and below rather than the entire project hierarchy. No
- ``<lcName>_POPULATED`` variable is set in the caller's scope with this form.
+ The ``<lowercaseName>_SOURCE_DIR`` and ``<lowercaseName>_BINARY_DIR``
+ variables are still returned to the caller, but since these locations are
+ not stored as global properties when this form is used, they are only
+ available to the calling scope and below rather than the entire project
+ hierarchy. No ``<lowercaseName>_POPULATED`` variable is set in the caller's
+ scope with this form.
The supported options for ``FetchContent_Populate()`` are the same as those
for :command:`FetchContent_Declare()`. Those few options shown just
above are either specific to ``FetchContent_Populate()`` or their behavior is
- slightly modified from how :command:`ExternalProject_Add` treats them.
+ slightly modified from how :command:`ExternalProject_Add` treats them:
``QUIET``
The ``QUIET`` option can be given to hide the output associated with
@@ -347,9 +342,9 @@ is simpler and provides additional features over the pattern above.
``SUBBUILD_DIR``
The ``SUBBUILD_DIR`` argument can be provided to change the location of the
sub-build created to perform the population. The default value is
- ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-subbuild`` and it would be unusual
- to need to override this default. If a relative path is specified, it will
- be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`.
+ ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild`` and it would be
+ unusual to need to override this default. If a relative path is specified,
+ it will be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`.
This option should not be confused with the ``SOURCE_SUBDIR`` option which
only affects the :command:`FetchContent_MakeAvailable` command.
@@ -357,9 +352,9 @@ is simpler and provides additional features over the pattern above.
The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by
:command:`ExternalProject_Add`, but different default values are used by
``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to
- ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-src`` and ``BINARY_DIR`` defaults to
- ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-build``. If a relative path is
- specified, it will be interpreted as relative to
+ ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src`` and ``BINARY_DIR``
+ defaults to ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-build``.
+ If a relative path is specified, it will be interpreted as relative to
:variable:`CMAKE_CURRENT_BINARY_DIR`.
In addition to the above explicit options, any other unrecognized options are
@@ -380,11 +375,12 @@ is simpler and provides additional features over the pattern above.
on the command line invoking the script.
.. versionadded:: 3.18
- Added support for ``DOWNLOAD_NO_EXTRACT`` and ``SOURCE_SUBDIR`` options.
+ Added support for the ``DOWNLOAD_NO_EXTRACT`` option.
.. command:: FetchContent_GetProperties
- When using saved content details, a call to :command:`FetchContent_Populate`
+ When using saved content details, a call to
+ :command:`FetchContent_MakeAvailable` or :command:`FetchContent_Populate`
records information in global properties which can be queried at any time.
This information includes the source and binary directories associated with
the content and also whether or not the content population has been processed
@@ -392,7 +388,8 @@ is simpler and provides additional features over the pattern above.
.. code-block:: cmake
- FetchContent_GetProperties( <name>
+ FetchContent_GetProperties(
+ <name>
[SOURCE_DIR <srcDirVar>]
[BINARY_DIR <binDirVar>]
[POPULATED <doneVar>]
@@ -403,49 +400,104 @@ is simpler and provides additional features over the pattern above.
which is the name of the variable in which to store that property. Most of
the time though, only ``<name>`` is given, in which case the call will then
set the same variables as a call to
- :command:`FetchContent_Populate(name) <FetchContent_Populate>`. This allows
- the following canonical pattern to be used, which ensures that the relevant
- variables will always be defined regardless of whether or not the population
- has been performed elsewhere in the project already:
-
- .. code-block:: cmake
-
- FetchContent_GetProperties(foobar)
- if(NOT foobar_POPULATED)
- FetchContent_Populate(foobar)
- ...
- endif()
+ :command:`FetchContent_MakeAvailable(name) <FetchContent_MakeAvailable>` or
+ :command:`FetchContent_Populate(name) <FetchContent_Populate>`.
- The above pattern allows other parts of the overall project hierarchy to
- re-use the same content and ensure that it is only populated once.
-
-
-.. command:: FetchContent_MakeAvailable
+ This command is rarely needed when using
+ :command:`FetchContent_MakeAvailable`. It is more commonly used as part of
+ implementing the following pattern with :command:`FetchContent_Populate`,
+ which ensures that the relevant variables will always be defined regardless
+ of whether or not the population has been performed elsewhere in the project
+ already:
.. code-block:: cmake
- FetchContent_MakeAvailable( <name1> [<name2>...] )
+ # Check if population has already been performed
+ FetchContent_GetProperties(depname)
+ if(NOT depname_POPULATED)
+ # Fetch the content using previously declared details
+ FetchContent_Populate(depname)
- .. versionadded:: 3.14
+ # Set custom variables, policies, etc.
+ # ...
- This command implements the common pattern typically needed for most
- dependencies. It iterates over each of the named dependencies in turn
- and for each one it loosely follows the
- :ref:`canonical pattern <fetch-content-canonical-pattern>` as
- presented at the beginning of this section. An important difference is
- that :command:`add_subdirectory` will only be called on the
- populated content if there is a ``CMakeLists.txt`` file in its top level
- source directory. This allows the command to be used for dependencies
- that make downloaded content available at a known location but which do
- not need or support being added directly to the build.
-
- The ``SOURCE_SUBDIR`` option can be given in the declared details to
- instruct ``FetchContent_MakeAvailable()`` to look for a ``CMakeLists.txt``
- file in a subdirectory below the top level (i.e. the same way that
- ``SOURCE_SUBDIR`` is used by the :command:`ExternalProject_Add` command).
- ``SOURCE_SUBDIR`` must always be a relative path. See the next section
- for an example of this option.
+ # Bring the populated content into the build
+ add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
+ endif()
+Variables
+^^^^^^^^^
+
+A number of cache variables can influence the behavior where details from a
+:command:`FetchContent_Declare` call are used to populate content.
+The variables are all intended for the developer to customize behavior and
+should not normally be set by the project.
+
+.. variable:: FETCHCONTENT_BASE_DIR
+
+ In most cases, the saved details do not specify any options relating to the
+ directories to use for the internal sub-build, final source and build areas.
+ It is generally best to leave these decisions up to the ``FetchContent``
+ module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR``
+ cache variable controls the point under which all content population
+ directories are collected, but in most cases, developers would not need to
+ change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if
+ developers change this value, they should aim to keep the path short and
+ just below the top level of the build tree to avoid running into path
+ length problems on Windows.
+
+.. variable:: FETCHCONTENT_QUIET
+
+ The logging output during population can be quite verbose, making the
+ configure stage quite noisy. This cache option (``ON`` by default) hides
+ all population output unless an error is encountered. If experiencing
+ problems with hung downloads, temporarily switching this option off may
+ help diagnose which content population is causing the issue.
+
+.. variable:: FETCHCONTENT_FULLY_DISCONNECTED
+
+ When this option is enabled, no attempt is made to download or update
+ any content. It is assumed that all content has already been populated in
+ a previous run or the source directories have been pointed at existing
+ contents the developer has provided manually (using options described
+ further below). When the developer knows that no changes have been made to
+ any content details, turning this option ``ON`` can significantly speed up
+ the configure stage. It is ``OFF`` by default.
+
+.. variable:: FETCHCONTENT_UPDATES_DISCONNECTED
+
+ This is a less severe download/update control compared to
+ :variable:`FETCHCONTENT_FULLY_DISCONNECTED`. Instead of bypassing all
+ download and update logic, ``FETCHCONTENT_UPDATES_DISCONNECTED`` only
+ disables the update stage. Therefore, if content has not been downloaded
+ previously, it will still be downloaded when this option is enabled.
+ This can speed up the configure stage, but not as much as
+ :variable:`FETCHCONTENT_FULLY_DISCONNECTED`. It is ``OFF`` by default.
+
+In addition to the above cache variables, the following cache variables are
+also defined for each content name:
+
+.. variable:: FETCHCONTENT_SOURCE_DIR_<uppercaseName>
+
+ If this is set, no download or update steps are performed for the specified
+ content and the ``<lowercaseName>_SOURCE_DIR`` variable returned to the
+ caller is pointed at this location. This gives developers a way to have a
+ separate checkout of the content that they can modify freely without
+ interference from the build. The build simply uses that existing source,
+ but it still defines ``<lowercaseName>_BINARY_DIR`` to point inside its own
+ build area. Developers are strongly encouraged to use this mechanism rather
+ than editing the sources populated in the default location, as changes to
+ sources in the default location can be lost when content population details
+ are changed by the project.
+
+.. variable:: FETCHCONTENT_UPDATES_DISCONNECTED_<uppercaseName>
+
+ This is the per-content equivalent of
+ :variable:`FETCHCONTENT_UPDATES_DISCONNECTED`. If the global option or
+ this option is ``ON``, then updates will be disabled for the named content.
+ Disabling updates for individual content can be useful for content whose
+ details rarely change, while still leaving other frequently changing content
+ with updates enabled.
.. _`fetch-content-examples`:
@@ -470,7 +522,7 @@ frameworks are available to the main build:
)
# After the following call, the CMake targets defined by googletest and
- # Catch2 will be defined and available to the rest of the build
+ # Catch2 will be available to the rest of the build
FetchContent_MakeAvailable(googletest Catch2)
If the sub-project's ``CMakeLists.txt`` file is not at the top level of its
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake
index 799d179..a44af4d 100644
--- a/Modules/FindBLAS.cmake
+++ b/Modules/FindBLAS.cmake
@@ -109,7 +109,8 @@ BLAS/LAPACK Vendors
``Goto``
GotoBLAS
-``IBMESSL``
+``IBMESSL``, ``IBMESSL_SMP``
+
IBM Engineering and Scientific Subroutine Library
``Intel``
@@ -150,7 +151,7 @@ BLAS/LAPACK Vendors
``PhiPACK``
Portable High Performance ANSI C (PHiPAC)
-``SCSL``
+``SCSL``, ``SCSL_mp``
Scientific Computing Software Library
``SGIMATH``
@@ -862,19 +863,27 @@ if(BLA_VENDOR STREQUAL "SunPerf" OR BLA_VENDOR STREQUAL "All")
endif()
# BLAS in SCSL library? (SGI/Cray Scientific Library)
-if(BLA_VENDOR STREQUAL "SCSL" OR BLA_VENDOR STREQUAL "All")
+if(BLA_VENDOR MATCHES "SCSL" OR BLA_VENDOR STREQUAL "All")
+ set(_blas_scsl_lib "scs")
+
+ if(BLA_VENDOR MATCHES "_mp")
+ set(_blas_scsl_lib "${_blas_scsl_lib}_mp")
+ endif()
+
if(NOT BLAS_LIBRARIES)
check_blas_libraries(
BLAS_LIBRARIES
BLAS
sgemm
""
- "scsl"
+ "${_blas_scsl_lib}"
""
""
""
)
endif()
+
+ unset(_blas_scsl_lib)
endif()
# BLAS in SGIMATH library?
@@ -893,20 +902,27 @@ if(BLA_VENDOR STREQUAL "SGIMATH" OR BLA_VENDOR STREQUAL "All")
endif()
endif()
-# BLAS in IBM ESSL library? (requires generic BLAS lib, too)
-if(BLA_VENDOR STREQUAL "IBMESSL" OR BLA_VENDOR STREQUAL "All")
+# BLAS in IBM ESSL library?
+if(BLA_VENDOR MATCHES "IBMESSL" OR BLA_VENDOR STREQUAL "All")
+ set(_blas_essl_lib "essl")
+
+ if(BLA_VENDOR MATCHES "_SMP")
+ set(_blas_essl_lib "${_blas_essl_lib}smp")
+ endif()
if(NOT BLAS_LIBRARIES)
check_blas_libraries(
BLAS_LIBRARIES
BLAS
sgemm
""
- "essl;blas"
+ "${_blas_essl_lib}"
""
""
""
)
endif()
+
+ unset(_blas_essl_lib)
endif()
# BLAS in acml library?
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index e453109..92042d2 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -844,6 +844,9 @@ if(CUDAToolkit_FOUND)
HINTS ${CUDAToolkit_LIBRARY_DIR}
ENV CUDA_PATH
PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
+ # Support NVHPC splayed math library layout
+ ../../math_libs/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64
+ ../../math_libs/lib64
)
mark_as_advanced(CUDA_${lib_name}_LIBRARY)
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake
index aa5423a..f36acfd 100644
--- a/Modules/FindLAPACK.cmake
+++ b/Modules/FindLAPACK.cmake
@@ -491,6 +491,30 @@ if(NOT LAPACK_NOT_FOUND_MESSAGE)
)
endif()
+ # LAPACK in SCSL library? (SGI/Cray Scientific Library)
+ if(NOT LAPACK_LIBRARIES
+ AND (BLA_VENDOR MATCHES "SCSL" OR BLA_VENDOR STREQUAL "All"))
+ set(_lapack_scsl_lib "scs")
+
+ # Check for OpenMP support, VIA BLA_VENDOR of scs_mp
+ if(BLA_VENDOR MATCHES "_mp")
+ set(_lapack_scsl_lib "${_lapack_scsl_lib}_mp")
+ endif()
+
+ check_lapack_libraries(
+ LAPACK_LIBRARIES
+ LAPACK
+ cheev
+ ""
+ "${_lapack_scsl_lib}"
+ ""
+ ""
+ ""
+ "${BLAS_LIBRARIES}"
+ )
+ unset(_lapack_scsl_lib)
+ endif()
+
# BLAS in acml library?
if(BLA_VENDOR MATCHES "ACML" OR BLA_VENDOR STREQUAL "All")
if(BLAS_LIBRARIES MATCHES ".+acml.+")
@@ -586,6 +610,30 @@ if(NOT LAPACK_NOT_FOUND_MESSAGE)
unset(_ssl2_suffix)
endif()
+ # LAPACK in IBM ESSL library?
+ if(NOT LAPACK_LIBRARIES
+ AND (BLA_VENDOR MATCHES "IBMESSL" OR BLA_VENDOR STREQUAL "All"))
+ set(_lapack_essl_lib "essl")
+
+ # Check for OpenMP support, VIA BLA_VENDOR of esslsmp
+ if(BLA_VENDOR MATCHES "_SMP")
+ set(_lapack_essl_lib "${_lapack_essl_lib}smp")
+ endif()
+
+ check_lapack_libraries(
+ LAPACK_LIBRARIES
+ LAPACK
+ cheev
+ ""
+ "${_lapack_essl_lib}"
+ ""
+ ""
+ ""
+ "${BLAS_LIBRARIES}"
+ )
+ unset(_lapack_essl_lib)
+ endif()
+
# NVHPC Library?
if(NOT LAPACK_LIBRARIES
AND (BLA_VENDOR MATCHES "NVHPC" OR BLA_VENDOR STREQUAL "All"))
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake
index f64c4b8..d4b4a34 100644
--- a/Modules/FindMPI.cmake
+++ b/Modules/FindMPI.cmake
@@ -649,7 +649,8 @@ function (_MPI_interrogate_compiler LANG)
# we won't match the accompanying --param-ssp-size and -Wp,-D_FORTIFY_SOURCE flags and therefore
# produce inconsistent results with the regularly flags.
# Similarly, aliasing flags do not belong into our flag array.
- if(NOT "${_MPI_COMPILE_OPTION}" MATCHES "^-f((no-|)(stack-protector|strict-aliasing)|PI[CE]|pi[ce])")
+ # Also strip out `-framework` flags.
+ if(NOT "${_MPI_COMPILE_OPTION}" MATCHES "^-f((no-|)(stack-protector|strict-aliasing)|PI[CE]|pi[ce]|ramework)")
list(APPEND MPI_COMPILE_OPTIONS_WORK "${_MPI_COMPILE_OPTION}")
endif()
endforeach()
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index a28f6bc..4de5331 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -97,10 +97,18 @@ set(PKG_CONFIG_FOUND "${PKGCONFIG_FOUND}")
# Unsets the given variables
macro(_pkgconfig_unset var)
+ # Clear normal variable (possibly set by project code).
+ unset(${var})
+ # Store as cache variable.
+ # FIXME: Add a policy to switch to a normal variable.
set(${var} "" CACHE INTERNAL "")
endmacro()
macro(_pkgconfig_set var value)
+ # Clear normal variable (possibly set by project code).
+ unset(${var})
+ # Store as cache variable.
+ # FIXME: Add a policy to switch to a normal variable.
set(${var} ${value} CACHE INTERNAL "")
endmacro()
diff --git a/Modules/FindX11.cmake b/Modules/FindX11.cmake
index d480fc4..fd5ee53 100644
--- a/Modules/FindX11.cmake
+++ b/Modules/FindX11.cmake
@@ -828,6 +828,12 @@ if (UNIX)
X11_Xau_INCLUDE_PATH
X11_xcb_LIB
X11_xcb_INCLUDE_PATH
+ X11_xcb_icccm_LIB
+ X11_xcb_icccm_INCLUDE_PATH
+ X11_xcb_util_LIB
+ X11_xcb_util_INCLUDE_PATH
+ X11_xcb_xfixes_LIB
+ X11_xcb_xfixes_INCLUDE_PATH
X11_xcb_xkb_LIB
X11_X11_xcb_LIB
X11_X11_xcb_INCLUDE_PATH
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake
index 80d8e23..a483c03 100644
--- a/Modules/GoogleTest.cmake
+++ b/Modules/GoogleTest.cmake
@@ -151,6 +151,7 @@ same as the Google Test name (i.e. ``suite.testcase``); see also
[WORKING_DIRECTORY dir]
[TEST_PREFIX prefix]
[TEST_SUFFIX suffix]
+ [TEST_FILTER expr]
[NO_PRETTY_TYPES] [NO_PRETTY_VALUES]
[PROPERTIES name1 value1...]
[TEST_LIST var]
@@ -204,6 +205,12 @@ same as the Google Test name (i.e. ``suite.testcase``); see also
every discovered test case. Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
be specified.
+ ``TEST_FILTER expr``
+ .. versionadded:: 3.22
+
+ Filter expression to pass to ``--gtest_filter`` argument during test
+ discovery.
+
``NO_PRETTY_TYPES``
By default, the type index of type-parameterized tests is replaced by the
actual type name in the CTest test name. If this behavior is undesirable
@@ -411,7 +418,7 @@ function(gtest_discover_tests TARGET)
""
"NO_PRETTY_TYPES;NO_PRETTY_VALUES"
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
- "EXTRA_ARGS;PROPERTIES"
+ "EXTRA_ARGS;PROPERTIES;TEST_FILTER"
${ARGN}
)
@@ -475,6 +482,7 @@ function(gtest_discover_tests TARGET)
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
+ -D "TEST_FILTER=${_TEST_FILTER}"
-D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
-D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
-D "TEST_LIST=${_TEST_LIST}"
@@ -515,6 +523,7 @@ function(gtest_discover_tests TARGET)
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
+ " TEST_FILTER" " [==[" "${_TEST_FILTER}" "]==]" "\n"
" NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n"
" NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n"
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake
index 0f79c9a..6b3bf34 100644
--- a/Modules/GoogleTestAddTests.cmake
+++ b/Modules/GoogleTestAddTests.cmake
@@ -44,7 +44,7 @@ function(gtest_discover_tests_impl)
cmake_parse_arguments(
""
""
- "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
+ "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR;TEST_FILTER"
"TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR"
${ARGN}
)
@@ -58,6 +58,12 @@ function(gtest_discover_tests_impl)
set(tests)
set(tests_buffer)
+ if(_TEST_FILTER)
+ set(filter "--gtest_filter=${_TEST_FILTER}")
+ else()
+ set(filter)
+ endif()
+
# Run test executable to get list of available tests
if(NOT EXISTS "${_TEST_EXECUTABLE}")
message(FATAL_ERROR
@@ -66,7 +72,7 @@ function(gtest_discover_tests_impl)
)
endif()
execute_process(
- COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
+ COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests ${filter}
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
OUTPUT_VARIABLE output
@@ -178,6 +184,7 @@ if(CMAKE_SCRIPT_MODE_FILE)
TEST_WORKING_DIR ${TEST_WORKING_DIR}
TEST_PREFIX ${TEST_PREFIX}
TEST_SUFFIX ${TEST_SUFFIX}
+ TEST_FILTER ${TEST_FILTER}
TEST_LIST ${TEST_LIST}
CTEST_FILE ${CTEST_FILE}
TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake
index fa7b125..b2af6c9 100644
--- a/Modules/InstallRequiredSystemLibraries.cmake
+++ b/Modules/InstallRequiredSystemLibraries.cmake
@@ -80,19 +80,34 @@ foreach(LANG IN ITEMS C CXX Fortran)
set(_Intel_archdir ia32)
endif()
set(_Intel_compiler_ver ${CMAKE_${LANG}_COMPILER_VERSION})
- if(WIN32 AND EXISTS "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler")
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler" ABSOLUTE)
- elseif(WIN32)
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../redist/${_Intel_archdir}/compiler" ABSOLUTE)
+ if(WIN32)
+ set(_Intel_possible_redistdirs
+ "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler"
+ "${_Intel_basedir}/../../redist/${_Intel_archdir}/compiler"
+ )
elseif(APPLE)
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib" ABSOLUTE)
+ set(_Intel_possible_redistdirs
+ "${_Intel_basedir}/../../compiler/lib"
+ )
else()
- if(EXISTS "${_Intel_basedir}/../lib/${_Intel_archdir}_lin")
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../lib/${_Intel_archdir}" ABSOLUTE)
- else()
- get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib/${_Intel_archdir}_lin" ABSOLUTE)
+ set(_Intel_possible_redistdirs
+ "${_Intel_basedir}/../lib/${_Intel_archdir}"
+ "${_Intel_basedir}/../../compiler/lib/${_Intel_archdir}_lin"
+ )
+ endif()
+
+ set(_Intel_redistdir NOT-FOUND)
+ foreach(dir IN LISTS _Intel_possible_redistdirs)
+ if(EXISTS "${dir}")
+ set(_Intel_redistdir "${dir}")
+ break()
endif()
+ endforeach()
+ # Fall back to last dir
+ if(NOT _Intel_redistdir)
+ list(POP_BACK _Intel_possible_redistdirs _Intel_redistdir)
endif()
+ unset(_Intel_possible_redistdirs)
set(_IRSL_HAVE_Intel TRUE)
endif()
elseif("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "MSVC")
diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake
index bfb42f3..c72bf6d 100644
--- a/Modules/Internal/CPack/CPackRPM.cmake
+++ b/Modules/Internal/CPack/CPackRPM.cmake
@@ -11,9 +11,7 @@ function(set_spec_script_if_enabled TYPE PACKAGE_NAME VAR)
if(PACKAGE_NAME)
set(PACKAGE_NAME " -n ${PACKAGE_NAME}")
endif()
- set(${TYPE}_
- "%${TYPE}${PACKAGE_NAME}\n"
- "${VAR}\n" PARENT_SCOPE)
+ set(${TYPE}_ "%${TYPE}${PACKAGE_NAME}\n${VAR}\n" PARENT_SCOPE)
else()
set(${TYPE} "" PARENT_SCOPE)
endif()
diff --git a/Modules/Internal/CPack/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in
index 848691f..1df8a58 100644
--- a/Modules/Internal/CPack/NSIS.template.in
+++ b/Modules/Internal/CPack/NSIS.template.in
@@ -540,7 +540,7 @@ FunctionEnd
@CPACK_NSIS_INSTALLER_WELCOME_TITLE_3LINES_CODE@
!insertmacro MUI_PAGE_WELCOME
- !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
+ @CPACK_NSIS_LICENSE_PAGE@
Page custom InstallOptionsPage
!insertmacro MUI_PAGE_DIRECTORY
diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake
index 2d2cd5c..7b67f83 100644
--- a/Modules/Platform/Android-Determine.cmake
+++ b/Modules/Platform/Android-Determine.cmake
@@ -267,7 +267,7 @@ endif()
if(CMAKE_ANDROID_NDK)
# Identify the host platform.
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
- if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "darwin-x86_64")
else()
set(CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG "darwin-x86")
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index d9a7894..839dc81 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -47,7 +47,9 @@ set(CMAKE_SHARED_MODULE_PREFIX "lib")
set(CMAKE_SHARED_MODULE_SUFFIX ".so")
set(CMAKE_MODULE_EXISTS 1)
set(CMAKE_DL_LIBS "")
-set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
+if(NOT "${_CURRENT_OSX_VERSION}" VERSION_LESS "10.5")
+ set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
+endif()
foreach(lang C CXX OBJC OBJCXX)
set(CMAKE_${lang}_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
diff --git a/Modules/Platform/Haiku.cmake b/Modules/Platform/Haiku.cmake
index 7e0af61..b8e3ba6 100644
--- a/Modules/Platform/Haiku.cmake
+++ b/Modules/Platform/Haiku.cmake
@@ -1,8 +1,3 @@
-# process only once
-if(HAIKU)
- return()
-endif()
-
set(HAIKU 1)
set(UNIX 1)
diff --git a/Modules/VTKCompatibility.cmake b/Modules/VTKCompatibility.cmake
index 1b0815e..4ee7643 100644
--- a/Modules/VTKCompatibility.cmake
+++ b/Modules/VTKCompatibility.cmake
@@ -1,6 +1,10 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
+# Not needed for "modern" VTK.
+if (EXISTS "${VTK_SOURCE_DIR}/CMake/vtkModule.cmake")
+ return ()
+endif ()
if(APPLE)
set(CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 6775acb..7e37141 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -381,6 +381,7 @@ set(SRCS
cmProcessOutput.h
cmProcessTools.cxx
cmProcessTools.h
+ cmProperty.cxx
cmProperty.h
cmPropertyDefinition.cxx
cmPropertyDefinition.h
@@ -398,8 +399,6 @@ set(SRCS
cmQtAutoMocUic.h
cmQtAutoRcc.cxx
cmQtAutoRcc.h
- cmQtAutoUicHelpers.cxx
- cmQtAutoUicHelpers.h
cmRST.cxx
cmRST.h
cmRuntimeDependencyArchive.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index ee3ffb7..d10822f 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 21)
-set(CMake_VERSION_PATCH 20210727)
+set(CMake_VERSION_PATCH 20210824)
#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx
index 5b03185..829cef4 100644
--- a/Source/CPack/cmCPackDebGenerator.cxx
+++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -111,6 +111,9 @@ DebGenerator::DebGenerator(
} else if (!std::strcmp(debianCompressionType, "gzip")) {
this->CompressionSuffix = ".gz";
this->TarCompressionType = cmArchiveWrite::CompressGZip;
+ } else if (!std::strcmp(debianCompressionType, "zstd")) {
+ this->CompressionSuffix = ".zst";
+ this->TarCompressionType = cmArchiveWrite::CompressZstd;
} else if (!std::strcmp(debianCompressionType, "none")) {
this->CompressionSuffix.clear();
this->TarCompressionType = cmArchiveWrite::CompressNone;
@@ -189,7 +192,13 @@ bool DebGenerator::generateDataTar() const
cmArchiveWrite data_tar(fileStream_data_tar, this->TarCompressionType,
this->DebianArchiveType, 0,
static_cast<int>(this->NumThreads));
- data_tar.Open();
+ if (!data_tar.Open()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Error opening the archive \""
+ << filename_data_tar
+ << "\", ERROR = " << data_tar.GetError() << std::endl);
+ return false;
+ }
// uid/gid should be the one of the root user, and this root user has
// always uid/gid equal to 0.
@@ -314,7 +323,13 @@ bool DebGenerator::generateControlTar(std::string const& md5Filename) const
cmArchiveWrite control_tar(fileStream_control_tar,
cmArchiveWrite::CompressGZip,
this->DebianArchiveType);
- control_tar.Open();
+ if (!control_tar.Open()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Error opening the archive \""
+ << filename_control_tar
+ << "\", ERROR = " << control_tar.GetError() << std::endl);
+ return false;
+ }
// sets permissions and uid/gid for the files
control_tar.SetUIDAndGID(0u, 0u);
@@ -454,7 +469,13 @@ bool DebGenerator::generateDeb() const
cmGeneratedFileStream debStream;
debStream.Open(outputPath, false, true);
cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd");
- deb.Open();
+ if (!deb.Open()) {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Error opening the archive \""
+ << outputPath << "\", ERROR = " << deb.GetError()
+ << std::endl);
+ return false;
+ }
// uid/gid should be the one of the root user, and this root user has
// always uid/gid equal to 0.
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index 6bd0d1b..395b1df 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -235,6 +235,13 @@ int cmCPackNSISGenerator::PackageFiles()
brandingTextCode.c_str());
}
+ if (!this->IsSet("CPACK_NSIS_IGNORE_LICENSE_PAGE")) {
+ std::string licenceCode =
+ cmStrCat("!insertmacro MUI_PAGE_LICENSE \"",
+ this->GetOption("CPACK_RESOURCE_FILE_LICENSE"), "\"\n");
+ this->SetOptionIfNotSet("CPACK_NSIS_LICENSE_PAGE", licenceCode.c_str());
+ }
+
// Setup all of the component sections
if (this->Components.empty()) {
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
@@ -487,12 +494,12 @@ int cmCPackNSISGenerator::InitializeInternal()
}
if (versionRex.find(output)) {
double nsisVersion = atof(versionRex.match(1).c_str());
- double minNSISVersion = 3.0;
+ double minNSISVersion = 3.03;
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"NSIS Version: " << nsisVersion << std::endl);
if (nsisVersion < minNSISVersion) {
cmCPackLogger(cmCPackLog::LOG_ERROR,
- "CPack requires NSIS Version 3.0 or greater. "
+ "CPack requires NSIS Version 3.03 or greater. "
"NSIS found on the system was: "
<< nsisVersion << std::endl);
return 0;
diff --git a/Source/CTest/cmCTestGenericHandler.cxx b/Source/CTest/cmCTestGenericHandler.cxx
index cc756d7..48cc0e4 100644
--- a/Source/CTest/cmCTestGenericHandler.cxx
+++ b/Source/CTest/cmCTestGenericHandler.cxx
@@ -68,10 +68,8 @@ void cmCTestGenericHandler::Initialize()
{
this->AppendXML = false;
this->TestLoad = 0;
- this->Options.clear();
- for (auto const& po : this->PersistentOptions) {
- this->Options[po.first] = po.second;
- }
+ this->Options = this->PersistentOptions;
+ this->MultiOptions = this->PersistentMultiOptions;
}
const char* cmCTestGenericHandler::GetOption(const std::string& op)
diff --git a/Source/CTest/cmCTestStartCommand.cxx b/Source/CTest/cmCTestStartCommand.cxx
index 53e1b2f..a8c2403 100644
--- a/Source/CTest/cmCTestStartCommand.cxx
+++ b/Source/CTest/cmCTestStartCommand.cxx
@@ -30,8 +30,8 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args,
size_t cnt = 0;
const char* smodel = nullptr;
- const std::string* src_dir = nullptr;
- const std::string* bld_dir = nullptr;
+ cmProp src_dir;
+ cmProp bld_dir;
while (cnt < args.size()) {
if (args[cnt] == "GROUP" || args[cnt] == "TRACK") {
@@ -55,10 +55,10 @@ bool cmCTestStartCommand::InitialPass(std::vector<std::string> const& args,
smodel = args[cnt].c_str();
cnt++;
} else if (!src_dir) {
- src_dir = &args[cnt];
+ src_dir = cmProp(args[cnt]);
cnt++;
} else if (!bld_dir) {
- bld_dir = &args[cnt];
+ bld_dir = cmProp(args[cnt]);
cnt++;
} else {
this->SetError("Too many arguments");
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index bdba0e5..7aeb288 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -36,8 +36,8 @@ std::unique_ptr<cmCommand> cmCTestSubmitCommand::Clone()
cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
{
- const std::string* submitURL = !this->SubmitURL.empty()
- ? &this->SubmitURL
+ cmProp submitURL = !this->SubmitURL.empty()
+ ? cmProp(this->SubmitURL)
: this->Makefile->GetDefinition("CTEST_SUBMIT_URL");
if (submitURL) {
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index 859c18d..41e8a55 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -128,11 +128,11 @@ void QCMake::setBinaryDirectory(const QString& _dir)
}
cmProp gen = state->GetCacheEntryValue("CMAKE_GENERATOR");
if (gen) {
- const std::string* extraGen =
+ cmProp extraGen =
state->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
std::string curGen =
- cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
- *gen, extraGen ? *extraGen : "");
+ cmExternalMakefileProjectGenerator::CreateFullGeneratorName(*gen,
+ *extraGen);
this->setGenerator(QString::fromLocal8Bit(curGen.c_str()));
}
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index 54b2998..9e0d80c 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -250,6 +250,9 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
bool cmArchiveWrite::Open()
{
+ if (!this->Error.empty()) {
+ return false;
+ }
if (archive_write_open(
this->Archive, this, nullptr,
reinterpret_cast<archive_write_callback*>(&Callback::Write),
diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx
index 1f99043..b2830e2 100644
--- a/Source/cmCMakePolicyCommand.cxx
+++ b/Source/cmCMakePolicyCommand.cxx
@@ -6,6 +6,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx
index fd578fa..538b668 100644
--- a/Source/cmCMakePresetsFile.cxx
+++ b/Source/cmCMakePresetsFile.cxx
@@ -78,7 +78,8 @@ void InheritVector(std::vector<T>& child, const std::vector<T>& parent)
template <class T>
ReadFileResult VisitPreset(
T& preset, std::map<std::string, cmCMakePresetsFile::PresetPair<T>>& presets,
- std::map<std::string, CycleStatus> cycleStatus, int version)
+ std::map<std::string, CycleStatus> cycleStatus,
+ const cmCMakePresetsFile& file)
{
switch (cycleStatus[preset.Name]) {
case CycleStatus::InProgress:
@@ -108,7 +109,7 @@ ReadFileResult VisitPreset(
return ReadFileResult::USER_PRESET_INHERITANCE;
}
- auto result = VisitPreset(parentPreset, presets, cycleStatus, version);
+ auto result = VisitPreset(parentPreset, presets, cycleStatus, file);
if (result != ReadFileResult::READ_OK) {
return result;
}
@@ -128,7 +129,7 @@ ReadFileResult VisitPreset(
preset.ConditionEvaluator.reset();
}
- CHECK_OK(preset.VisitPresetAfterInherit(version))
+ CHECK_OK(preset.VisitPresetAfterInherit(file.GetVersion(preset)))
cycleStatus[preset.Name] = CycleStatus::Verified;
return ReadFileResult::READ_OK;
@@ -146,8 +147,7 @@ ReadFileResult ComputePresetInheritance(
for (auto& it : presets) {
auto& preset = it.second.Unexpanded;
- auto result =
- VisitPreset<T>(preset, presets, cycleStatus, file.GetVersion(preset));
+ auto result = VisitPreset<T>(preset, presets, cycleStatus, file);
if (result != ReadFileResult::READ_OK) {
return result;
}
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 1a950df..8fefaa6 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -500,7 +500,7 @@ cmProp cmCacheManager::GetInitializedCacheValue(const std::string& key) const
{
if (const auto* entry = this->GetCacheEntry(key)) {
if (entry->Initialized) {
- return &entry->GetValue();
+ return cmProp(entry->GetValue());
}
}
return nullptr;
@@ -568,10 +568,10 @@ std::vector<std::string> cmCacheManager::CacheEntry::GetPropertyList() const
cmProp cmCacheManager::CacheEntry::GetProperty(const std::string& prop) const
{
if (prop == "TYPE") {
- return &cmState::CacheEntryTypeToString(this->Type);
+ return cmProp(cmState::CacheEntryTypeToString(this->Type));
}
if (prop == "VALUE") {
- return &this->Value;
+ return cmProp(this->Value);
}
return this->Properties.GetPropertyValue(prop);
}
diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h
index 7a9a7dc..0238fb8 100644
--- a/Source/cmCacheManager.h
+++ b/Source/cmCacheManager.h
@@ -75,7 +75,7 @@ public:
cmProp GetCacheEntryValue(const std::string& key) const
{
if (const auto* entry = this->GetCacheEntry(key)) {
- return &entry->GetValue();
+ return cmProp(entry->GetValue());
}
return nullptr;
}
diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx
index deddba8..415f0c0 100644
--- a/Source/cmCommandArgumentParserHelper.cxx
+++ b/Source/cmCommandArgumentParserHelper.cxx
@@ -113,7 +113,7 @@ const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var)
if (this->EscapeQuotes && value) {
return this->AddString(cmEscapeQuotes(*value));
}
- return this->AddString(cmToCStrSafe(value));
+ return this->AddString(value);
}
const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var)
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 7c2e20c..59e4141 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -39,10 +39,10 @@ std::vector<std::string> const& cmCommonTargetGenerator::GetConfigNames() const
return this->ConfigNames;
}
-const char* cmCommonTargetGenerator::GetFeature(const std::string& feature,
- const std::string& config)
+cmProp cmCommonTargetGenerator::GetFeature(const std::string& feature,
+ const std::string& config)
{
- return this->GeneratorTarget->GetFeature(feature, config)->c_str();
+ return this->GeneratorTarget->GetFeature(feature, config);
}
void cmCommonTargetGenerator::AddModuleDefinitionFlag(
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h
index e1a4f8b..463a445 100644
--- a/Source/cmCommonTargetGenerator.h
+++ b/Source/cmCommonTargetGenerator.h
@@ -8,6 +8,8 @@
#include <string>
#include <vector>
+#include "cmProperty.h"
+
class cmGeneratorTarget;
class cmGlobalCommonGenerator;
class cmLinkLineComputer;
@@ -28,8 +30,7 @@ public:
protected:
// Feature query methods.
- const char* GetFeature(const std::string& feature,
- const std::string& config);
+ cmProp GetFeature(const std::string& feature, const std::string& config);
// Helper to add flag for windows .def file.
void AddModuleDefinitionFlag(cmLinkLineComputer* linkLineComputer,
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index f99592c..68bc4d8 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -2,16 +2,21 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmConditionEvaluator.h"
+#include <array>
#include <cstdio>
#include <cstdlib>
#include <functional>
+#include <iterator>
+#include <list>
#include <sstream>
#include <utility>
+#include <cm/string_view>
#include <cmext/algorithm>
#include "cmsys/RegularExpression.hxx"
+#include "cmExpandedCommandArgument.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmProperty.h"
@@ -20,40 +25,184 @@
#include "cmSystemTools.h"
#include "cmake.h"
-class cmTest;
-
-static std::string const keyAND = "AND";
-static std::string const keyCOMMAND = "COMMAND";
-static std::string const keyDEFINED = "DEFINED";
-static std::string const keyEQUAL = "EQUAL";
-static std::string const keyEXISTS = "EXISTS";
-static std::string const keyGREATER = "GREATER";
-static std::string const keyGREATER_EQUAL = "GREATER_EQUAL";
-static std::string const keyIN_LIST = "IN_LIST";
-static std::string const keyIS_ABSOLUTE = "IS_ABSOLUTE";
-static std::string const keyIS_DIRECTORY = "IS_DIRECTORY";
-static std::string const keyIS_NEWER_THAN = "IS_NEWER_THAN";
-static std::string const keyIS_SYMLINK = "IS_SYMLINK";
-static std::string const keyLESS = "LESS";
-static std::string const keyLESS_EQUAL = "LESS_EQUAL";
-static std::string const keyMATCHES = "MATCHES";
-static std::string const keyNOT = "NOT";
-static std::string const keyOR = "OR";
-static std::string const keyParenL = "(";
-static std::string const keyParenR = ")";
-static std::string const keyPOLICY = "POLICY";
-static std::string const keySTREQUAL = "STREQUAL";
-static std::string const keySTRGREATER = "STRGREATER";
-static std::string const keySTRGREATER_EQUAL = "STRGREATER_EQUAL";
-static std::string const keySTRLESS = "STRLESS";
-static std::string const keySTRLESS_EQUAL = "STRLESS_EQUAL";
-static std::string const keyTARGET = "TARGET";
-static std::string const keyTEST = "TEST";
-static std::string const keyVERSION_EQUAL = "VERSION_EQUAL";
-static std::string const keyVERSION_GREATER = "VERSION_GREATER";
-static std::string const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL";
-static std::string const keyVERSION_LESS = "VERSION_LESS";
-static std::string const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL";
+namespace {
+auto const keyAND = "AND"_s;
+auto const keyCOMMAND = "COMMAND"_s;
+auto const keyDEFINED = "DEFINED"_s;
+auto const keyEQUAL = "EQUAL"_s;
+auto const keyEXISTS = "EXISTS"_s;
+auto const keyGREATER = "GREATER"_s;
+auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
+auto const keyIN_LIST = "IN_LIST"_s;
+auto const keyIS_ABSOLUTE = "IS_ABSOLUTE"_s;
+auto const keyIS_DIRECTORY = "IS_DIRECTORY"_s;
+auto const keyIS_NEWER_THAN = "IS_NEWER_THAN"_s;
+auto const keyIS_SYMLINK = "IS_SYMLINK"_s;
+auto const keyLESS = "LESS"_s;
+auto const keyLESS_EQUAL = "LESS_EQUAL"_s;
+auto const keyMATCHES = "MATCHES"_s;
+auto const keyNOT = "NOT"_s;
+auto const keyOR = "OR"_s;
+auto const keyParenL = "("_s;
+auto const keyParenR = ")"_s;
+auto const keyPOLICY = "POLICY"_s;
+auto const keySTREQUAL = "STREQUAL"_s;
+auto const keySTRGREATER = "STRGREATER"_s;
+auto const keySTRGREATER_EQUAL = "STRGREATER_EQUAL"_s;
+auto const keySTRLESS = "STRLESS"_s;
+auto const keySTRLESS_EQUAL = "STRLESS_EQUAL"_s;
+auto const keyTARGET = "TARGET"_s;
+auto const keyTEST = "TEST"_s;
+auto const keyVERSION_EQUAL = "VERSION_EQUAL"_s;
+auto const keyVERSION_GREATER = "VERSION_GREATER"_s;
+auto const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL"_s;
+auto const keyVERSION_LESS = "VERSION_LESS"_s;
+auto const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL"_s;
+
+cmSystemTools::CompareOp const MATCH2CMPOP[5] = {
+ cmSystemTools::OP_LESS, cmSystemTools::OP_LESS_EQUAL,
+ cmSystemTools::OP_GREATER, cmSystemTools::OP_GREATER_EQUAL,
+ cmSystemTools::OP_EQUAL
+};
+
+// Run-Time to Compile-Time template selector
+template <template <typename> class Comp, template <typename> class... Ops>
+struct cmRt2CtSelector
+{
+ template <typename T>
+ static bool eval(int r, T lhs, T rhs)
+ {
+ switch (r) {
+ case 0:
+ return false;
+ case 1:
+ return Comp<T>()(lhs, rhs);
+ default:
+ return cmRt2CtSelector<Ops...>::eval(r - 1, lhs, rhs);
+ }
+ }
+};
+
+template <template <typename> class Comp>
+struct cmRt2CtSelector<Comp>
+{
+ template <typename T>
+ static bool eval(int r, T lhs, T rhs)
+ {
+ return r == 1 && Comp<T>()(lhs, rhs);
+ }
+};
+
+std::string bool2string(bool const value)
+{
+ return std::string(std::size_t(1), static_cast<char>('0' + int(value)));
+}
+
+bool looksLikeSpecialVariable(const std::string& var,
+ cm::static_string_view prefix,
+ const std::size_t varNameLen)
+{
+ // NOTE Expecting a variable name at least 1 char length:
+ // <prefix> + `{` + <varname> + `}`
+ return ((prefix.size() + 3) <= varNameLen) &&
+ cmHasPrefix(var, cmStrCat(prefix, '{')) && var[varNameLen - 1] == '}';
+}
+} // anonymous namespace
+
+#if defined(__SUNPRO_CC)
+# define CM_INHERIT_CTOR(Class, Base, Tpl) \
+ template <typename... Args> \
+ Class(Args&&... args) \
+ : Base Tpl(std::forward<Args>(args)...) \
+ { \
+ }
+#else
+# define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base;
+#endif
+
+// BEGIN cmConditionEvaluator::cmArgumentList
+class cmConditionEvaluator::cmArgumentList
+ : public std::list<cmExpandedCommandArgument>
+{
+ using base_t = std::list<cmExpandedCommandArgument>;
+
+public:
+ CM_INHERIT_CTOR(cmArgumentList, list, <cmExpandedCommandArgument>);
+
+ class CurrentAndNextIter
+ {
+ friend class cmConditionEvaluator::cmArgumentList;
+
+ public:
+ base_t::iterator current;
+ base_t::iterator next;
+
+ CurrentAndNextIter advance(base_t& args)
+ {
+ this->current = std::next(this->current);
+ this->next =
+ std::next(this->current, difference_type(this->current != args.end()));
+ return *this;
+ }
+
+ private:
+ CurrentAndNextIter(base_t& args)
+ : current(args.begin())
+ , next(std::next(this->current,
+ difference_type(this->current != args.end())))
+ {
+ }
+ };
+
+ class CurrentAndTwoMoreIter
+ {
+ friend class cmConditionEvaluator::cmArgumentList;
+
+ public:
+ base_t::iterator current;
+ base_t::iterator next;
+ base_t::iterator nextnext;
+
+ CurrentAndTwoMoreIter advance(base_t& args)
+ {
+ this->current = std::next(this->current);
+ this->next =
+ std::next(this->current, difference_type(this->current != args.end()));
+ this->nextnext =
+ std::next(this->next, difference_type(this->next != args.end()));
+ return *this;
+ }
+
+ private:
+ CurrentAndTwoMoreIter(base_t& args)
+ : current(args.begin())
+ , next(std::next(this->current,
+ difference_type(this->current != args.end())))
+ , nextnext(
+ std::next(this->next, difference_type(this->next != args.end())))
+ {
+ }
+ };
+
+ CurrentAndNextIter make2ArgsIterator() { return *this; }
+ CurrentAndTwoMoreIter make3ArgsIterator() { return *this; }
+
+ template <typename Iter>
+ void ReduceOneArg(const bool value, Iter args)
+ {
+ *args.current = cmExpandedCommandArgument(bool2string(value), true);
+ this->erase(args.next);
+ }
+
+ void ReduceTwoArgs(const bool value, CurrentAndTwoMoreIter args)
+ {
+ *args.current = cmExpandedCommandArgument(bool2string(value), true);
+ this->erase(args.nextnext);
+ this->erase(args.next);
+ }
+};
+
+// END cmConditionEvaluator::cmArgumentList
cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile,
cmListFileBacktrace bt)
@@ -99,25 +248,29 @@ bool cmConditionEvaluator::IsTrue(
// now loop through the arguments and see if we can reduce any of them
// we do this multiple times. Once for each level of precedence
// parens
- if (!this->HandleLevel0(newArgs, errorString, status)) {
- return false;
- }
- // predicates
- if (!this->HandleLevel1(newArgs, errorString, status)) {
- return false;
- }
- // binary ops
- if (!this->HandleLevel2(newArgs, errorString, status)) {
- return false;
- }
+ using handlerFn_t = bool (cmConditionEvaluator::*)(
+ cmArgumentList&, std::string&, MessageType&);
+ const std::array<handlerFn_t, 5> handlers = { {
+ &cmConditionEvaluator::HandleLevel0, // parenthesis
+ &cmConditionEvaluator::HandleLevel1, // predicates
+ &cmConditionEvaluator::HandleLevel2, // binary ops
+ &cmConditionEvaluator::HandleLevel3, // NOT
+ &cmConditionEvaluator::HandleLevel4 // AND OR
+ } };
+ for (auto fn : handlers) {
+ // Call the reducer 'till there is anything to reduce...
+ // (i.e., if after an iteration the size becomes smaller)
+ auto levelResult = true;
+ for (auto beginSize = newArgs.size();
+ (levelResult = (this->*fn)(newArgs, errorString, status)) &&
+ newArgs.size() < beginSize;
+ beginSize = newArgs.size()) {
+ }
- // NOT
- if (!this->HandleLevel3(newArgs, errorString, status)) {
- return false;
- }
- // AND OR
- if (!this->HandleLevel4(newArgs, errorString, status)) {
- return false;
+ if (!levelResult) {
+ // NOTE `errorString` supposed to be set already
+ return false;
+ }
}
// now at the end there should only be one argument left
@@ -147,11 +300,13 @@ cmProp cmConditionEvaluator::GetDefinitionIfUnquoted(
this->Policy54Status == cmPolicies::WARN) {
if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
std::ostringstream e;
- e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)) << "\n";
- e << "Quoted variables like \"" << argument.GetValue()
- << "\" will no longer be dereferenced "
- "when the policy is set to NEW. "
+ // clang-format off
+ e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054))
+ << "\n"
+ "Quoted variables like \"" << argument.GetValue() << "\" "
+ "will no longer be dereferenced when the policy is set to NEW. "
"Since the policy is not set the OLD behavior will be used.";
+ // clang-format on
this->Makefile.GetCMakeInstance()->IssueMessage(
MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
@@ -168,15 +323,16 @@ cmProp cmConditionEvaluator::GetVariableOrString(
cmProp def = this->GetDefinitionIfUnquoted(argument);
if (!def) {
- def = &argument.GetValue();
+ def = cmProp(argument.GetValue());
}
return def;
}
//=========================================================================
-bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
- cmExpandedCommandArgument& argument) const
+bool cmConditionEvaluator::IsKeyword(
+ cm::static_string_view keyword,
+ const cmExpandedCommandArgument& argument) const
{
if ((this->Policy54Status != cmPolicies::WARN &&
this->Policy54Status != cmPolicies::OLD) &&
@@ -184,17 +340,20 @@ bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
return false;
}
- bool isKeyword = argument.GetValue() == keyword;
+ const auto isKeyword = argument.GetValue() == keyword;
if (isKeyword && argument.WasQuoted() &&
this->Policy54Status == cmPolicies::WARN) {
if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
std::ostringstream e;
- e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054) << "\n";
- e << "Quoted keywords like \"" << argument.GetValue()
- << "\" will no longer be interpreted as keywords "
+ // clang-format off
+ e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)
+ << "\n"
+ "Quoted keywords like \"" << argument.GetValue() << "\" "
+ "will no longer be interpreted as keywords "
"when the policy is set to NEW. "
"Since the policy is not set the OLD behavior will be used.";
+ // clang-format on
this->Makefile.GetCMakeInstance()->IssueMessage(
MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
@@ -208,15 +367,7 @@ bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
bool cmConditionEvaluator::GetBooleanValue(
cmExpandedCommandArgument& arg) const
{
- // Check basic constants.
- if (arg == "0") {
- return false;
- }
- if (arg == "1") {
- return true;
- }
-
- // Check named constants.
+ // Check basic and named constants.
if (cmIsOn(arg.GetValue())) {
return true;
}
@@ -227,7 +378,7 @@ bool cmConditionEvaluator::GetBooleanValue(
// Check for numbers.
if (!arg.empty()) {
char* end;
- double d = strtod(arg.GetValue().c_str(), &end);
+ const double d = std::strtod(arg.GetValue().c_str(), &end);
if (*end == '\0') {
// The whole string is a number. Use C conversion to bool.
return static_cast<bool>(d);
@@ -242,7 +393,7 @@ bool cmConditionEvaluator::GetBooleanValue(
//=========================================================================
// Boolean value behavior from CMake 2.6.4 and below.
bool cmConditionEvaluator::GetBooleanValueOld(
- cmExpandedCommandArgument const& arg, bool one) const
+ cmExpandedCommandArgument const& arg, bool const one) const
{
if (one) {
// Old IsTrue behavior for single argument.
@@ -257,8 +408,8 @@ bool cmConditionEvaluator::GetBooleanValueOld(
}
// Old GetVariableOrNumber behavior.
cmProp def = this->GetDefinitionIfUnquoted(arg);
- if (!def && atoi(arg.GetValue().c_str())) {
- def = &arg.GetValue();
+ if (!def && std::atoi(arg.GetValue().c_str())) {
+ def = cmProp(arg.GetValue());
}
return !cmIsOff(def);
}
@@ -267,7 +418,7 @@ bool cmConditionEvaluator::GetBooleanValueOld(
// returns the resulting boolean value
bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
cmExpandedCommandArgument& newArg, std::string& errorString,
- MessageType& status, bool oneArg) const
+ MessageType& status, bool const oneArg) const
{
// Use the policy if it is set.
if (this->Policy12Status == cmPolicies::NEW) {
@@ -278,8 +429,8 @@ bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
}
// Check policy only if old and new results differ.
- bool newResult = this->GetBooleanValue(newArg);
- bool oldResult = this->GetBooleanValueOld(newArg, oneArg);
+ const auto newResult = this->GetBooleanValue(newArg);
+ const auto oldResult = this->GetBooleanValueOld(newArg, oneArg);
if (newResult != oldResult) {
switch (this->Policy12Status) {
case cmPolicies::WARN:
@@ -304,56 +455,31 @@ bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
return newResult;
}
-//=========================================================================
-void cmConditionEvaluator::IncrementArguments(
- cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2) const
+template <int N>
+inline int cmConditionEvaluator::matchKeysImpl(
+ const cmExpandedCommandArgument&)
{
- if (argP1 != newArgs.end()) {
- argP1++;
- argP2 = argP1;
- if (argP1 != newArgs.end()) {
- argP2++;
- }
- }
+ // Zero means "not found"
+ return 0;
}
-//=========================================================================
-// helper function to reduce code duplication
-void cmConditionEvaluator::HandlePredicate(
- bool value, int& reducible, cmArgumentList::iterator& arg,
- cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2) const
+template <int N, typename T, typename... Keys>
+inline int cmConditionEvaluator::matchKeysImpl(
+ const cmExpandedCommandArgument& arg, T current, Keys... key)
{
- if (value) {
- *arg = cmExpandedCommandArgument("1", true);
- } else {
- *arg = cmExpandedCommandArgument("0", true);
+ if (this->IsKeyword(current, arg)) {
+ // Stop searching as soon as smth has found
+ return N;
}
- newArgs.erase(argP1);
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- reducible = 1;
+ return matchKeysImpl<N + 1>(arg, key...);
}
-//=========================================================================
-// helper function to reduce code duplication
-void cmConditionEvaluator::HandleBinaryOp(bool value, int& reducible,
- cmArgumentList::iterator& arg,
- cmArgumentList& newArgs,
- cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2)
+template <typename... Keys>
+inline int cmConditionEvaluator::matchKeys(
+ const cmExpandedCommandArgument& arg, Keys... key)
{
- if (value) {
- *arg = cmExpandedCommandArgument("1", true);
- } else {
- *arg = cmExpandedCommandArgument("0", true);
- }
- newArgs.erase(argP2);
- newArgs.erase(argP1);
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- reducible = 1;
+ // Get index of the matched key (1-based)
+ return matchKeysImpl<1>(arg, key...);
}
//=========================================================================
@@ -362,55 +488,35 @@ bool cmConditionEvaluator::HandleLevel0(cmArgumentList& newArgs,
std::string& errorString,
MessageType& status)
{
- int reducible;
- do {
- reducible = 0;
- auto arg = newArgs.begin();
- while (arg != newArgs.end()) {
- if (this->IsKeyword(keyParenL, *arg)) {
- // search for the closing paren for this opening one
- cmArgumentList::iterator argClose;
- argClose = arg;
- argClose++;
- unsigned int depth = 1;
- while (argClose != newArgs.end() && depth) {
- if (this->IsKeyword(keyParenL, *argClose)) {
- depth++;
- }
- if (this->IsKeyword(keyParenR, *argClose)) {
- depth--;
- }
- argClose++;
- }
- if (depth) {
- errorString = "mismatched parenthesis in condition";
- status = MessageType::FATAL_ERROR;
- return false;
- }
- // store the reduced args in this vector
- std::vector<cmExpandedCommandArgument> newArgs2;
-
- // copy to the list structure
- auto argP1 = arg;
- argP1++;
- cm::append(newArgs2, argP1, argClose);
- newArgs2.pop_back();
- // now recursively invoke IsTrue to handle the values inside the
- // parenthetical expression
- bool value = this->IsTrue(newArgs2, errorString, status);
- if (value) {
- *arg = cmExpandedCommandArgument("1", true);
- } else {
- *arg = cmExpandedCommandArgument("0", true);
- }
- argP1 = arg;
- argP1++;
- // remove the now evaluated parenthetical expression
- newArgs.erase(argP1, argClose);
+ for (auto arg = newArgs.begin(); arg != newArgs.end(); ++arg) {
+ if (this->IsKeyword(keyParenL, *arg)) {
+ // search for the closing paren for this opening one
+ auto depth = 1;
+ auto argClose = std::next(arg);
+ for (; argClose != newArgs.end() && depth; ++argClose) {
+ depth += int(this->IsKeyword(keyParenL, *argClose)) -
+ int(this->IsKeyword(keyParenR, *argClose));
}
- ++arg;
+ if (depth) {
+ errorString = "mismatched parenthesis in condition";
+ status = MessageType::FATAL_ERROR;
+ return false;
+ }
+
+ // store the reduced args in this vector
+ auto argOpen = std::next(arg);
+ const std::vector<cmExpandedCommandArgument> subExpr(
+ argOpen, std::prev(argClose));
+
+ // now recursively invoke IsTrue to handle the values inside the
+ // parenthetical expression
+ const auto value = this->IsTrue(subExpr, errorString, status);
+ *arg = cmExpandedCommandArgument(bool2string(value), true);
+ argOpen = std::next(arg);
+ // remove the now evaluated parenthetical expression
+ newArgs.erase(argOpen, argClose);
}
- } while (reducible);
+ }
return true;
}
@@ -419,96 +525,104 @@ bool cmConditionEvaluator::HandleLevel0(cmArgumentList& newArgs,
bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
MessageType&)
{
- int reducible;
- do {
- reducible = 0;
- auto arg = newArgs.begin();
- cmArgumentList::iterator argP1;
- cmArgumentList::iterator argP2;
- while (arg != newArgs.end()) {
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- // does a file exist
- if (this->IsKeyword(keyEXISTS, *arg) && argP1 != newArgs.end()) {
- this->HandlePredicate(cmSystemTools::FileExists(argP1->GetValue()),
- reducible, arg, newArgs, argP1, argP2);
- }
- // does a directory with this name exist
- if (this->IsKeyword(keyIS_DIRECTORY, *arg) && argP1 != newArgs.end()) {
- this->HandlePredicate(
- cmSystemTools::FileIsDirectory(argP1->GetValue()), reducible, arg,
- newArgs, argP1, argP2);
- }
- // does a symlink with this name exist
- if (this->IsKeyword(keyIS_SYMLINK, *arg) && argP1 != newArgs.end()) {
- this->HandlePredicate(cmSystemTools::FileIsSymlink(argP1->GetValue()),
- reducible, arg, newArgs, argP1, argP2);
- }
- // is the given path an absolute path ?
- if (this->IsKeyword(keyIS_ABSOLUTE, *arg) && argP1 != newArgs.end()) {
- this->HandlePredicate(cmSystemTools::FileIsFullPath(argP1->GetValue()),
- reducible, arg, newArgs, argP1, argP2);
- }
- // does a command exist
- if (this->IsKeyword(keyCOMMAND, *arg) && argP1 != newArgs.end()) {
- cmState::Command command =
- this->Makefile.GetState()->GetCommand(argP1->GetValue());
- this->HandlePredicate(command != nullptr, reducible, arg, newArgs,
- argP1, argP2);
- }
- // does a policy exist
- if (this->IsKeyword(keyPOLICY, *arg) && argP1 != newArgs.end()) {
- cmPolicies::PolicyID pid;
- this->HandlePredicate(
- cmPolicies::GetPolicyID(argP1->GetValue().c_str(), pid), reducible,
- arg, newArgs, argP1, argP2);
- }
- // does a target exist
- if (this->IsKeyword(keyTARGET, *arg) && argP1 != newArgs.end()) {
- this->HandlePredicate(
- this->Makefile.FindTargetToUse(argP1->GetValue()) != nullptr,
- reducible, arg, newArgs, argP1, argP2);
- }
- // does a test exist
- if (this->Policy64Status != cmPolicies::OLD &&
- this->Policy64Status != cmPolicies::WARN) {
- if (this->IsKeyword(keyTEST, *arg) && argP1 != newArgs.end()) {
- const cmTest* haveTest = this->Makefile.GetTest(argP1->GetValue());
- this->HandlePredicate(haveTest != nullptr, reducible, arg, newArgs,
- argP1, argP2);
- }
- } else if (this->Policy64Status == cmPolicies::WARN &&
- this->IsKeyword(keyTEST, *arg)) {
+ const auto policy64IsOld = this->Policy64Status == cmPolicies::OLD ||
+ this->Policy64Status == cmPolicies::WARN;
+
+ for (auto args = newArgs.make2ArgsIterator(); args.current != newArgs.end();
+ args.advance(newArgs)) {
+
+ auto policyCheck = [&, this](const cmPolicies::PolicyID id,
+ const cmPolicies::PolicyStatus status,
+ const cm::static_string_view kw) {
+ if (status == cmPolicies::WARN && this->IsKeyword(kw, *args.current)) {
std::ostringstream e;
- e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0064) << "\n";
- e << "TEST will be interpreted as an operator "
+ e << cmPolicies::GetPolicyWarning(id) << "\n"
+ << kw
+ << " will be interpreted as an operator "
"when the policy is set to NEW. "
"Since the policy is not set the OLD behavior will be used.";
this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
}
- // is a variable defined
- if (this->IsKeyword(keyDEFINED, *arg) && argP1 != newArgs.end()) {
- size_t argP1len = argP1->GetValue().size();
- bool bdef = false;
- if (argP1len > 4 && cmHasLiteralPrefix(argP1->GetValue(), "ENV{") &&
- argP1->GetValue().operator[](argP1len - 1) == '}') {
- std::string env = argP1->GetValue().substr(4, argP1len - 5);
- bdef = cmSystemTools::HasEnv(env);
- } else if (argP1len > 6 &&
- cmHasLiteralPrefix(argP1->GetValue(), "CACHE{") &&
- argP1->GetValue().operator[](argP1len - 1) == '}') {
- std::string cache = argP1->GetValue().substr(6, argP1len - 7);
- bdef =
- this->Makefile.GetState()->GetCacheEntryValue(cache) != nullptr;
- } else {
- bdef = this->Makefile.IsDefinitionSet(argP1->GetValue());
- }
- this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
+ };
+
+ // NOTE Checking policies for warnings are not require an access to the
+ // next arg. Check them first!
+ policyCheck(cmPolicies::CMP0064, this->Policy64Status, keyTEST);
+
+ // NOTE Fail fast: All the predicates below require the next arg to be
+ // valid
+ if (args.next == newArgs.end()) {
+ continue;
+ }
+
+ // does a file exist
+ if (this->IsKeyword(keyEXISTS, *args.current)) {
+ newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
+ args);
+ }
+ // does a directory with this name exist
+ else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
+ newArgs.ReduceOneArg(
+ cmSystemTools::FileIsDirectory(args.next->GetValue()), args);
+ }
+ // does a symlink with this name exist
+ else if (this->IsKeyword(keyIS_SYMLINK, *args.current)) {
+ newArgs.ReduceOneArg(cmSystemTools::FileIsSymlink(args.next->GetValue()),
+ args);
+ }
+ // is the given path an absolute path ?
+ else if (this->IsKeyword(keyIS_ABSOLUTE, *args.current)) {
+ newArgs.ReduceOneArg(
+ cmSystemTools::FileIsFullPath(args.next->GetValue()), args);
+ }
+ // does a command exist
+ else if (this->IsKeyword(keyCOMMAND, *args.current)) {
+ newArgs.ReduceOneArg(
+ bool(this->Makefile.GetState()->GetCommand(args.next->GetValue())),
+ args);
+ }
+ // does a policy exist
+ else if (this->IsKeyword(keyPOLICY, *args.current)) {
+ cmPolicies::PolicyID pid;
+ newArgs.ReduceOneArg(
+ cmPolicies::GetPolicyID(args.next->GetValue().c_str(), pid), args);
+ }
+ // does a target exist
+ else if (this->IsKeyword(keyTARGET, *args.current)) {
+ newArgs.ReduceOneArg(
+ bool(this->Makefile.FindTargetToUse(args.next->GetValue())), args);
+ }
+ // is a variable defined
+ else if (this->IsKeyword(keyDEFINED, *args.current)) {
+ const auto& var = args.next->GetValue();
+ const auto varNameLen = var.size();
+
+ auto result = false;
+ if (looksLikeSpecialVariable(var, "ENV"_s, varNameLen)) {
+ const auto env = args.next->GetValue().substr(4, varNameLen - 5);
+ result = cmSystemTools::HasEnv(env);
+ }
+
+ else if (looksLikeSpecialVariable(var, "CACHE"_s, varNameLen)) {
+ const auto cache = args.next->GetValue().substr(6, varNameLen - 7);
+ result = bool(this->Makefile.GetState()->GetCacheEntryValue(cache));
}
- ++arg;
+
+ else {
+ result = this->Makefile.IsDefinitionSet(args.next->GetValue());
+ }
+ newArgs.ReduceOneArg(result, args);
+ }
+ // does a test exist
+ else if (this->IsKeyword(keyTEST, *args.current)) {
+ if (policy64IsOld) {
+ continue;
+ }
+ newArgs.ReduceOneArg(bool(this->Makefile.GetTest(args.next->GetValue())),
+ args);
}
- } while (reducible);
+ }
return true;
}
@@ -518,178 +632,143 @@ bool cmConditionEvaluator::HandleLevel2(cmArgumentList& newArgs,
std::string& errorString,
MessageType& status)
{
- int reducible;
- std::string def_buf;
- cmProp def;
- cmProp def2;
- do {
- reducible = 0;
- auto arg = newArgs.begin();
- cmArgumentList::iterator argP1;
- cmArgumentList::iterator argP2;
- while (arg != newArgs.end()) {
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- this->IsKeyword(keyMATCHES, *argP1)) {
- def = this->GetDefinitionIfUnquoted(*arg);
- if (!def) {
- def = &arg->GetValue();
- } else if (cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) {
- // The string to match is owned by our match result variables.
- // Move it to our own buffer before clearing them.
- def_buf = *def;
- def = &def_buf;
- }
- const std::string& rex = argP2->GetValue();
- this->Makefile.ClearMatches();
- cmsys::RegularExpression regEntry;
- if (!regEntry.compile(rex)) {
- std::ostringstream error;
- error << "Regular expression \"" << rex << "\" cannot compile";
- errorString = error.str();
- status = MessageType::FATAL_ERROR;
- return false;
- }
- if (regEntry.find(*def)) {
- this->Makefile.StoreMatches(regEntry);
- *arg = cmExpandedCommandArgument("1", true);
- } else {
- *arg = cmExpandedCommandArgument("0", true);
- }
- newArgs.erase(argP2);
- newArgs.erase(argP1);
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- reducible = 1;
- }
+ for (auto args = newArgs.make3ArgsIterator(); args.current != newArgs.end();
+ args.advance(newArgs)) {
- if (argP1 != newArgs.end() && this->IsKeyword(keyMATCHES, *arg)) {
- *arg = cmExpandedCommandArgument("0", true);
- newArgs.erase(argP1);
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- reducible = 1;
- }
+ int matchNo;
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- (this->IsKeyword(keyLESS, *argP1) ||
- this->IsKeyword(keyLESS_EQUAL, *argP1) ||
- this->IsKeyword(keyGREATER, *argP1) ||
- this->IsKeyword(keyGREATER_EQUAL, *argP1) ||
- this->IsKeyword(keyEQUAL, *argP1))) {
- def = this->GetVariableOrString(*arg);
- def2 = this->GetVariableOrString(*argP2);
- double lhs;
- double rhs;
- bool result;
- if (sscanf(def->c_str(), "%lg", &lhs) != 1 ||
- sscanf(def2->c_str(), "%lg", &rhs) != 1) {
- result = false;
- } else if (*(argP1) == keyLESS) {
- result = (lhs < rhs);
- } else if (*(argP1) == keyLESS_EQUAL) {
- result = (lhs <= rhs);
- } else if (*(argP1) == keyGREATER) {
- result = (lhs > rhs);
- } else if (*(argP1) == keyGREATER_EQUAL) {
- result = (lhs >= rhs);
- } else {
- result = (lhs == rhs);
- }
- this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
- }
+ // NOTE Handle special case `if(... BLAH_BLAH MATCHES)`
+ // (i.e., w/o regex to match which is possibly result of
+ // variable expansion to an empty string)
+ if (args.next != newArgs.end() &&
+ this->IsKeyword(keyMATCHES, *args.current)) {
+ newArgs.ReduceOneArg(false, args);
+ }
+
+ // NOTE Fail fast: All the binary ops below require 2 arguments.
+ else if (args.next == newArgs.end() || args.nextnext == newArgs.end()) {
+ continue;
+ }
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- (this->IsKeyword(keySTRLESS, *argP1) ||
- this->IsKeyword(keySTRLESS_EQUAL, *argP1) ||
- this->IsKeyword(keySTRGREATER, *argP1) ||
- this->IsKeyword(keySTRGREATER_EQUAL, *argP1) ||
- this->IsKeyword(keySTREQUAL, *argP1))) {
- def = this->GetVariableOrString(*arg);
- def2 = this->GetVariableOrString(*argP2);
- int val = (*def).compare(*def2);
- bool result;
- if (*(argP1) == keySTRLESS) {
- result = (val < 0);
- } else if (*(argP1) == keySTRLESS_EQUAL) {
- result = (val <= 0);
- } else if (*(argP1) == keySTRGREATER) {
- result = (val > 0);
- } else if (*(argP1) == keySTRGREATER_EQUAL) {
- result = (val >= 0);
- } else // strequal
- {
- result = (val == 0);
- }
- this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
+ else if (this->IsKeyword(keyMATCHES, *args.next)) {
+ cmProp def = this->GetDefinitionIfUnquoted(*args.current);
+
+ std::string def_buf;
+ if (!def) {
+ def = cmProp(args.current->GetValue());
+ } else if (cmHasLiteralPrefix(args.current->GetValue(),
+ "CMAKE_MATCH_")) {
+ // The string to match is owned by our match result variables.
+ // Move it to our own buffer before clearing them.
+ def_buf = *def;
+ def = cmProp(def_buf);
}
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- (this->IsKeyword(keyVERSION_LESS, *argP1) ||
- this->IsKeyword(keyVERSION_LESS_EQUAL, *argP1) ||
- this->IsKeyword(keyVERSION_GREATER, *argP1) ||
- this->IsKeyword(keyVERSION_GREATER_EQUAL, *argP1) ||
- this->IsKeyword(keyVERSION_EQUAL, *argP1))) {
- def = this->GetVariableOrString(*arg);
- def2 = this->GetVariableOrString(*argP2);
- cmSystemTools::CompareOp op;
- if (*argP1 == keyVERSION_LESS) {
- op = cmSystemTools::OP_LESS;
- } else if (*argP1 == keyVERSION_LESS_EQUAL) {
- op = cmSystemTools::OP_LESS_EQUAL;
- } else if (*argP1 == keyVERSION_GREATER) {
- op = cmSystemTools::OP_GREATER;
- } else if (*argP1 == keyVERSION_GREATER_EQUAL) {
- op = cmSystemTools::OP_GREATER_EQUAL;
- } else { // version_equal
- op = cmSystemTools::OP_EQUAL;
- }
- bool result =
- cmSystemTools::VersionCompare(op, def->c_str(), def2->c_str());
- this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
+ this->Makefile.ClearMatches();
+
+ const auto& rex = args.nextnext->GetValue();
+ cmsys::RegularExpression regEntry;
+ if (!regEntry.compile(rex)) {
+ std::ostringstream error;
+ error << "Regular expression \"" << rex << "\" cannot compile";
+ errorString = error.str();
+ status = MessageType::FATAL_ERROR;
+ return false;
}
- // is file A newer than file B
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- this->IsKeyword(keyIS_NEWER_THAN, *argP1)) {
- int fileIsNewer = 0;
- cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
- arg->GetValue(), (argP2)->GetValue(), &fileIsNewer);
- this->HandleBinaryOp(
- (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
- newArgs, argP1, argP2);
+ const auto match = regEntry.find(*def);
+ if (match) {
+ this->Makefile.StoreMatches(regEntry);
}
+ newArgs.ReduceTwoArgs(match, args);
+ }
+
+ else if ((matchNo =
+ this->matchKeys(*args.next, keyLESS, keyLESS_EQUAL, keyGREATER,
+ keyGREATER_EQUAL, keyEQUAL))) {
+
+ cmProp ldef = this->GetVariableOrString(*args.current);
+ cmProp rdef = this->GetVariableOrString(*args.nextnext);
+
+ double lhs;
+ double rhs;
+ auto parseDoubles = [&]() {
+ return std::sscanf(ldef->c_str(), "%lg", &lhs) == 1 &&
+ std::sscanf(rdef->c_str(), "%lg", &rhs) == 1;
+ };
+ // clang-format off
+ const auto result = parseDoubles() &&
+ cmRt2CtSelector<
+ std::less, std::less_equal,
+ std::greater, std::greater_equal,
+ std::equal_to
+ >::eval(matchNo, lhs, rhs);
+ // clang-format on
+ newArgs.ReduceTwoArgs(result, args);
+ }
+
+ else if ((matchNo = this->matchKeys(*args.next, keySTRLESS,
+ keySTRLESS_EQUAL, keySTRGREATER,
+ keySTRGREATER_EQUAL, keySTREQUAL))) {
+
+ const cmProp lhs = this->GetVariableOrString(*args.current);
+ const cmProp rhs = this->GetVariableOrString(*args.nextnext);
+ const auto val = (*lhs).compare(*rhs);
+ // clang-format off
+ const auto result = cmRt2CtSelector<
+ std::less, std::less_equal,
+ std::greater, std::greater_equal,
+ std::equal_to
+ >::eval(matchNo, val, 0);
+ // clang-format on
+ newArgs.ReduceTwoArgs(result, args);
+ }
+
+ else if ((matchNo =
+ this->matchKeys(*args.next, keyVERSION_LESS,
+ keyVERSION_LESS_EQUAL, keyVERSION_GREATER,
+ keyVERSION_GREATER_EQUAL, keyVERSION_EQUAL))) {
+ const auto op = MATCH2CMPOP[matchNo - 1];
+ const cmProp lhs = this->GetVariableOrString(*args.current);
+ const cmProp rhs = this->GetVariableOrString(*args.nextnext);
+ const auto result =
+ cmSystemTools::VersionCompare(op, lhs->c_str(), rhs->c_str());
+ newArgs.ReduceTwoArgs(result, args);
+ }
+
+ // is file A newer than file B
+ else if (this->IsKeyword(keyIS_NEWER_THAN, *args.next)) {
+ auto fileIsNewer = 0;
+ cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
+ args.current->GetValue(), args.nextnext->GetValue(), &fileIsNewer);
+ newArgs.ReduceTwoArgs(
+ (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), args);
+ }
+
+ else if (this->IsKeyword(keyIN_LIST, *args.next)) {
- if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
- this->IsKeyword(keyIN_LIST, *argP1)) {
- if (this->Policy57Status != cmPolicies::OLD &&
- this->Policy57Status != cmPolicies::WARN) {
- bool result = false;
-
- def = this->GetVariableOrString(*arg);
- def2 = this->Makefile.GetDefinition(argP2->GetValue());
-
- if (def2) {
- std::vector<std::string> list = cmExpandedList(*def2, true);
- result = cm::contains(list, *def);
- }
-
- this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
- } else if (this->Policy57Status == cmPolicies::WARN) {
- std::ostringstream e;
- e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057) << "\n";
- e << "IN_LIST will be interpreted as an operator "
- "when the policy is set to NEW. "
- "Since the policy is not set the OLD behavior will be used.";
-
- this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
- }
+ if (this->Policy57Status != cmPolicies::OLD &&
+ this->Policy57Status != cmPolicies::WARN) {
+
+ cmProp lhs = this->GetVariableOrString(*args.current);
+ cmProp rhs = this->Makefile.GetDefinition(args.nextnext->GetValue());
+
+ newArgs.ReduceTwoArgs(
+ rhs && cm::contains(cmExpandedList(*rhs, true), *lhs), args);
}
- ++arg;
+ else if (this->Policy57Status == cmPolicies::WARN) {
+ std::ostringstream e;
+ e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057)
+ << "\n"
+ "IN_LIST will be interpreted as an operator "
+ "when the policy is set to NEW. "
+ "Since the policy is not set the OLD behavior will be used.";
+
+ this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+ }
}
- } while (reducible);
+ }
return true;
}
@@ -699,23 +778,14 @@ bool cmConditionEvaluator::HandleLevel3(cmArgumentList& newArgs,
std::string& errorString,
MessageType& status)
{
- int reducible;
- do {
- reducible = 0;
- auto arg = newArgs.begin();
- cmArgumentList::iterator argP1;
- cmArgumentList::iterator argP2;
- while (arg != newArgs.end()) {
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- if (argP1 != newArgs.end() && this->IsKeyword(keyNOT, *arg)) {
- bool rhs = this->GetBooleanValueWithAutoDereference(
- *argP1, errorString, status);
- this->HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
- }
- ++arg;
+ for (auto args = newArgs.make2ArgsIterator(); args.next != newArgs.end();
+ args.advance(newArgs)) {
+ if (this->IsKeyword(keyNOT, *args.current)) {
+ const auto rhs = this->GetBooleanValueWithAutoDereference(
+ *args.next, errorString, status);
+ newArgs.ReduceOneArg(!rhs, args);
}
- } while (reducible);
+ }
return true;
}
@@ -725,38 +795,24 @@ bool cmConditionEvaluator::HandleLevel4(cmArgumentList& newArgs,
std::string& errorString,
MessageType& status)
{
- int reducible;
- bool lhs;
- bool rhs;
- do {
- reducible = 0;
- auto arg = newArgs.begin();
- cmArgumentList::iterator argP1;
- cmArgumentList::iterator argP2;
- while (arg != newArgs.end()) {
- argP1 = arg;
- this->IncrementArguments(newArgs, argP1, argP2);
- if (argP1 != newArgs.end() && this->IsKeyword(keyAND, *argP1) &&
- argP2 != newArgs.end()) {
- lhs =
- this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
- rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
- status);
- this->HandleBinaryOp((lhs && rhs), reducible, arg, newArgs, argP1,
- argP2);
- }
-
- if (argP1 != newArgs.end() && this->IsKeyword(keyOR, *argP1) &&
- argP2 != newArgs.end()) {
- lhs =
- this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
- rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
- status);
- this->HandleBinaryOp((lhs || rhs), reducible, arg, newArgs, argP1,
- argP2);
- }
- ++arg;
+ for (auto args = newArgs.make3ArgsIterator(); args.nextnext != newArgs.end();
+ args.advance(newArgs)) {
+
+ int matchNo;
+
+ if ((matchNo = this->matchKeys(*args.next, keyAND, keyOR))) {
+ const auto lhs = this->GetBooleanValueWithAutoDereference(
+ *args.current, errorString, status);
+ const auto rhs = this->GetBooleanValueWithAutoDereference(
+ *args.nextnext, errorString, status);
+ // clang-format off
+ const auto result =
+ cmRt2CtSelector<
+ std::logical_and, std::logical_or
+ >::eval(matchNo, lhs, rhs);
+ // clang-format on
+ newArgs.ReduceTwoArgs(result, args);
}
- } while (reducible);
+ }
return true;
}
diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h
index cf00ede..00d896b 100644
--- a/Source/cmConditionEvaluator.h
+++ b/Source/cmConditionEvaluator.h
@@ -4,23 +4,22 @@
#include "cmConfigure.h" // IWYU pragma: keep
-#include <list>
#include <string>
#include <vector>
-#include "cmExpandedCommandArgument.h"
+#include <cmext/string_view>
+
#include "cmListFileCache.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmProperty.h"
+class cmExpandedCommandArgument;
class cmMakefile;
class cmConditionEvaluator
{
public:
- using cmArgumentList = std::list<cmExpandedCommandArgument>;
-
cmConditionEvaluator(cmMakefile& makefile, cmListFileBacktrace bt);
// this is a shared function for both If and Else to determine if the
@@ -30,14 +29,16 @@ public:
std::string& errorString, MessageType& status);
private:
+ class cmArgumentList;
+
// Filter the given variable definition based on policy CMP0054.
cmProp GetDefinitionIfUnquoted(
const cmExpandedCommandArgument& argument) const;
cmProp GetVariableOrString(const cmExpandedCommandArgument& argument) const;
- bool IsKeyword(std::string const& keyword,
- cmExpandedCommandArgument& argument) const;
+ bool IsKeyword(cm::static_string_view keyword,
+ const cmExpandedCommandArgument& argument) const;
bool GetBooleanValue(cmExpandedCommandArgument& arg) const;
@@ -49,19 +50,14 @@ private:
MessageType& status,
bool oneArg = false) const;
- void IncrementArguments(cmArgumentList& newArgs,
- cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2) const;
+ template <int N>
+ int matchKeysImpl(const cmExpandedCommandArgument&);
- void HandlePredicate(bool value, int& reducible,
- cmArgumentList::iterator& arg, cmArgumentList& newArgs,
- cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2) const;
+ template <int N, typename T, typename... Keys>
+ int matchKeysImpl(const cmExpandedCommandArgument&, T, Keys...);
- void HandleBinaryOp(bool value, int& reducible,
- cmArgumentList::iterator& arg, cmArgumentList& newArgs,
- cmArgumentList::iterator& argP1,
- cmArgumentList::iterator& argP2);
+ template <typename... Keys>
+ int matchKeys(const cmExpandedCommandArgument&, Keys...);
bool HandleLevel0(cmArgumentList& newArgs, std::string& errorString,
MessageType& status);
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index bf18143..0fd48f0 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -606,7 +606,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
std::string langFlags = "CMAKE_" + li + "_FLAGS";
cmProp flags = this->Makefile->GetDefinition(langFlags);
fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
- cmOutputConverter::EscapeForCMake(cmToCStrSafe(flags)).c_str());
+ cmOutputConverter::EscapeForCMake(flags).c_str());
fprintf(fout,
"set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
" ${COMPILE_DEFINITIONS}\")\n",
@@ -644,9 +644,8 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
std::string const langFlagsCfg =
cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
cmProp flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
- fprintf(
- fout, "set(%s %s)\n", langFlagsCfg.c_str(),
- cmOutputConverter::EscapeForCMake(cmToCStrSafe(flagsCfg)).c_str());
+ fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
+ cmOutputConverter::EscapeForCMake(flagsCfg).c_str());
}
} break;
}
@@ -679,8 +678,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
cmProp exeLinkFlags =
this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
- cmOutputConverter::EscapeForCMake(cmToCStrSafe(exeLinkFlags))
- .c_str());
+ cmOutputConverter::EscapeForCMake(exeLinkFlags).c_str());
}
break;
}
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index 4a4f87d..9e2d7b9 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -34,11 +34,11 @@ cmDefinitions::Def const& cmDefinitions::GetInternal(const std::string& key,
return begin->Map.emplace(key, def).first->second;
}
-const std::string* cmDefinitions::Get(const std::string& key, StackIter begin,
- StackIter end)
+cmProp cmDefinitions::Get(const std::string& key, StackIter begin,
+ StackIter end)
{
Def const& def = cmDefinitions::GetInternal(key, begin, end, false);
- return def.Value ? def.Value.str_if_stable() : nullptr;
+ return def.Value ? cmProp(def.Value.str_if_stable()) : nullptr;
}
void cmDefinitions::Raise(const std::string& key, StackIter begin,
diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h
index b650aa8..ee1db7a 100644
--- a/Source/cmDefinitions.h
+++ b/Source/cmDefinitions.h
@@ -12,6 +12,7 @@
#include <cm/string_view>
#include "cmLinkedTree.h"
+#include "cmProperty.h"
#include "cmString.hxx"
/** \class cmDefinitions
@@ -28,8 +29,7 @@ class cmDefinitions
public:
// -- Static member functions
- static const std::string* Get(const std::string& key, StackIter begin,
- StackIter end);
+ static cmProp Get(const std::string& key, StackIter begin, StackIter end);
static void Raise(const std::string& key, StackIter begin, StackIter end);
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 4a9bf28..6e3f918 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -18,6 +18,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmTarget.h"
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index ebdbe38..8b06a15 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -917,7 +917,7 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os)
// Protect that file against use with older CMake versions.
/* clang-format off */
os << "# Generated by CMake\n\n";
- os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.5)\n"
+ os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.6)\n"
<< " message(FATAL_ERROR \"CMake >= 2.6.0 required\")\n"
<< "endif()\n";
/* clang-format on */
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 4249cfe..46d2d31 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -16,6 +16,7 @@
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index a2b5460..adeba74 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -813,8 +813,7 @@ Json::Value CodemodelConfig::DumpProject(Project& p)
Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
{
Json::Value minimumCMakeVersion;
- if (std::string const* def =
- s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
+ if (cmProp def = s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
minimumCMakeVersion = Json::objectValue;
minimumCMakeVersion["string"] = *def;
}
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 1e3076f..2730a07 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -1850,7 +1850,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
} else if (*i == "TLS_CAINFO") {
++i;
if (i != args.end()) {
- cainfo = &(*i);
+ cainfo = cmProp(*i);
} else {
status.SetError("DOWNLOAD missing file value for TLS_CAINFO.");
return false;
@@ -2236,7 +2236,7 @@ bool HandleUploadCommand(std::vector<std::string> const& args,
} else if (*i == "TLS_CAINFO") {
++i;
if (i != args.end()) {
- cainfo = &(*i);
+ cainfo = cmProp(*i);
} else {
status.SetError("UPLOAD missing file value for TLS_CAINFO.");
return false;
diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h
index 217d58d..ee9872d 100644
--- a/Source/cmFileCopier.h
+++ b/Source/cmFileCopier.h
@@ -67,8 +67,9 @@ protected:
bool InstallSymlinkChain(std::string& fromFile, std::string& toFile);
bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
- bool InstallFile(const std::string& fromFile, const std::string& toFile,
- MatchProperties match_properties);
+ virtual bool InstallFile(const std::string& fromFile,
+ const std::string& toFile,
+ MatchProperties match_properties);
bool InstallDirectory(const std::string& source,
const std::string& destination,
MatchProperties match_properties);
diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx
index c89be96..0d8ba2d 100644
--- a/Source/cmFileInstaller.cxx
+++ b/Source/cmFileInstaller.cxx
@@ -3,7 +3,12 @@
#include "cmFileInstaller.h"
+#include <map>
#include <sstream>
+#include <utility>
+
+#include <cm/string_view>
+#include <cmext/string_view>
#include "cm_sys_stat.h"
@@ -18,6 +23,7 @@ using namespace cmFSPermissions;
cmFileInstaller::cmFileInstaller(cmExecutionStatus& status)
: cmFileCopier(status, "INSTALL")
, InstallType(cmInstallType_FILES)
+ , InstallMode(cmInstallMode::COPY)
, Optional(false)
, MessageAlways(false)
, MessageLazy(false)
@@ -82,6 +88,93 @@ bool cmFileInstaller::Install(const std::string& fromFile,
return this->cmFileCopier::Install(fromFile, toFile);
}
+bool cmFileInstaller::InstallFile(const std::string& fromFile,
+ const std::string& toFile,
+ MatchProperties match_properties)
+{
+ if (this->InstallMode == cmInstallMode::COPY) {
+ return this->cmFileCopier::InstallFile(fromFile, toFile, match_properties);
+ }
+
+ std::string newFromFile;
+
+ if (this->InstallMode == cmInstallMode::REL_SYMLINK ||
+ this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY ||
+ this->InstallMode == cmInstallMode::SYMLINK ||
+ this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+ // Try to get a relative path.
+ std::string toDir = cmSystemTools::GetParentDirectory(toFile);
+ newFromFile = cmSystemTools::ForceToRelativePath(toDir, fromFile);
+
+ // Double check that we can restore the original path.
+ std::string reassembled =
+ cmSystemTools::CollapseFullPath(newFromFile, toDir);
+ if (!cmSystemTools::ComparePath(reassembled, fromFile)) {
+ if (this->InstallMode == cmInstallMode::SYMLINK ||
+ this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+ // User does not mind, silently proceed with absolute path.
+ newFromFile = fromFile;
+ } else if (this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY) {
+ // User expects a relative symbolic link or a copy.
+ // Since an absolute symlink won't do, copy instead.
+ return this->cmFileCopier::InstallFile(fromFile, toFile,
+ match_properties);
+ } else {
+ // We cannot meet user's expectation (REL_SYMLINK)
+ auto e = cmStrCat(this->Name,
+ " cannot determine relative path for symlink to \"",
+ newFromFile, "\" at \"", toFile, "\".");
+ this->Status.SetError(e);
+ return false;
+ }
+ }
+ } else {
+ newFromFile = fromFile; // stick with absolute path
+ }
+
+ // Compare the symlink value to that at the destination if not
+ // always installing.
+ bool copy = true;
+ if (!this->Always) {
+ std::string oldSymlinkTarget;
+ if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
+ if (newFromFile == oldSymlinkTarget) {
+ copy = false;
+ }
+ }
+ }
+
+ // Inform the user about this file installation.
+ this->ReportCopy(toFile, TypeLink, copy);
+
+ if (copy) {
+ // Remove the destination file so we can always create the symlink.
+ cmSystemTools::RemoveFile(toFile);
+
+ // Create destination directory if it doesn't exist
+ cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
+
+ // Create the symlink.
+ if (!cmSystemTools::CreateSymlink(newFromFile, toFile)) {
+ if (this->InstallMode == cmInstallMode::ABS_SYMLINK_OR_COPY ||
+ this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY ||
+ this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+ // Failed to create a symbolic link, fall back to copying.
+ return this->cmFileCopier::InstallFile(newFromFile, toFile,
+ match_properties);
+ }
+
+ auto e = cmStrCat(this->Name, " cannot create symlink to \"",
+ newFromFile, "\" at \"", toFile,
+ "\": ", cmSystemTools::GetLastSystemError(), "\".");
+ this->Status.SetError(e);
+ return false;
+ }
+ }
+
+ return true;
+}
+
void cmFileInstaller::DefaultFilePermissions()
{
this->cmFileCopier::DefaultFilePermissions();
@@ -141,6 +234,31 @@ bool cmFileInstaller::Parse(std::vector<std::string> const& args)
return false;
}
+ static const std::map<cm::string_view, cmInstallMode> install_mode_dict{
+ { "ABS_SYMLINK"_s, cmInstallMode::ABS_SYMLINK },
+ { "ABS_SYMLINK_OR_COPY"_s, cmInstallMode::ABS_SYMLINK_OR_COPY },
+ { "REL_SYMLINK"_s, cmInstallMode::REL_SYMLINK },
+ { "REL_SYMLINK_OR_COPY"_s, cmInstallMode::REL_SYMLINK_OR_COPY },
+ { "SYMLINK"_s, cmInstallMode::SYMLINK },
+ { "SYMLINK_OR_COPY"_s, cmInstallMode::SYMLINK_OR_COPY }
+ };
+
+ std::string install_mode;
+ cmSystemTools::GetEnv("CMAKE_INSTALL_MODE", install_mode);
+ if (install_mode.empty() || install_mode == "COPY"_s) {
+ this->InstallMode = cmInstallMode::COPY;
+ } else {
+ auto it = install_mode_dict.find(install_mode);
+ if (it != install_mode_dict.end()) {
+ this->InstallMode = it->second;
+ } else {
+ auto e = cmStrCat("Unrecognized value '", install_mode,
+ "' for environment variable CMAKE_INSTALL_MODE");
+ this->Status.SetError(e);
+ return false;
+ }
+ }
+
return true;
}
diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h
index 3a905d3..3f6bd45 100644
--- a/Source/cmFileInstaller.h
+++ b/Source/cmFileInstaller.h
@@ -8,6 +8,7 @@
#include <vector>
#include "cmFileCopier.h"
+#include "cmInstallMode.h"
#include "cmInstallType.h"
class cmExecutionStatus;
@@ -19,6 +20,7 @@ struct cmFileInstaller : public cmFileCopier
protected:
cmInstallType InstallType;
+ cmInstallMode InstallMode;
bool Optional;
bool MessageAlways;
bool MessageLazy;
@@ -35,7 +37,8 @@ protected:
bool ReportMissing(const std::string& fromFile) override;
bool Install(const std::string& fromFile,
const std::string& toFile) override;
-
+ bool InstallFile(const std::string& fromFile, const std::string& toFile,
+ MatchProperties match_properties) override;
bool Parse(std::vector<std::string> const& args) override;
enum
{
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 1038ac2..f4e1763 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -311,7 +311,7 @@ bool cmFindBase::CheckForVariableDefined()
if (cached && cacheType != cmStateEnums::UNINITIALIZED) {
this->VariableType = cacheType;
- if (const auto* hs =
+ if (const auto& hs =
state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) {
this->VariableDocumentation = *hs;
}
@@ -336,7 +336,7 @@ void cmFindBase::NormalizeFindResult()
if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) ==
cmPolicies::NEW) {
// ensure the path returned by find_* command is absolute
- const auto* existingValue =
+ const auto& existingValue =
this->Makefile->GetDefinition(this->VariableName);
std::string value;
if (!existingValue->empty()) {
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index 4845a6d..b44f797 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -27,6 +27,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 217ebe5..3e90ead 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -1777,7 +1777,7 @@ static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
testedFeatures[lang].push_back(p);
if (availableFeatures.find(lang) == availableFeatures.end()) {
- const char* featuresKnown =
+ cmProp featuresKnown =
standardResolver.CompileFeaturesAvailable(lang, &error);
if (!featuresKnown) {
reportError(context, content->GetOriginalExpression(), error);
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 17237bb..4208689 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -62,7 +62,7 @@ cmProp cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
cmGeneratorTarget const* tgt, cmMessenger* /* messenger */,
cmListFileBacktrace const& /* context */)
{
- return &tgt->GetSourcesProperty();
+ return tgt->GetSourcesProperty();
}
template <>
@@ -332,7 +332,7 @@ cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
cmGeneratorTarget::~cmGeneratorTarget() = default;
-const std::string& cmGeneratorTarget::GetSourcesProperty() const
+cmProp cmGeneratorTarget::GetSourcesProperty() const
{
std::vector<std::string> values;
for (auto const& se : this->SourceEntries) {
@@ -341,7 +341,7 @@ const std::string& cmGeneratorTarget::GetSourcesProperty() const
static std::string value;
value.clear();
value = cmJoin(values, ";");
- return value;
+ return cmProp(value);
}
cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const
@@ -396,13 +396,7 @@ cmProp cmGeneratorTarget::GetProperty(const std::string& prop) const
std::string const& cmGeneratorTarget::GetSafeProperty(
std::string const& prop) const
{
- cmProp ret = this->GetProperty(prop);
- if (ret) {
- return *ret;
- }
-
- static std::string const s_empty;
- return s_empty;
+ return this->GetProperty(prop);
}
const char* cmGeneratorTarget::GetOutputTargetType(
@@ -564,7 +558,7 @@ std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
// framework postfix.
frameworkPostfix = this->GetFrameworkMultiConfigPostfix(config);
if (!frameworkPostfix.empty()) {
- postfix = &frameworkPostfix;
+ postfix = cmProp(frameworkPostfix);
}
}
return postfix ? *postfix : std::string();
@@ -966,7 +960,7 @@ cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
this->GetLanguageStandardProperty(lang, config);
if (languageStandard) {
- return &(languageStandard->Value);
+ return cmProp(languageStandard->Value);
}
return nullptr;
@@ -5121,13 +5115,13 @@ void cmGeneratorTarget::GetFullNameInternal(
if (this->IsFrameworkOnApple()) {
fw_prefix =
cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
- targetPrefix = &fw_prefix;
+ targetPrefix = cmProp(fw_prefix);
targetSuffix = nullptr;
}
if (this->IsCFBundleOnApple()) {
fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/');
- targetPrefix = &fw_prefix;
+ targetPrefix = cmProp(fw_prefix);
targetSuffix = nullptr;
}
@@ -5143,7 +5137,7 @@ void cmGeneratorTarget::GetFullNameInternal(
// EXECUTABLE_SUFFIX attribute.
if (this->IsFrameworkOnApple() &&
this->GetGlobalGenerator()->GetName() == "Xcode") {
- targetSuffix = &configPostfix;
+ targetSuffix = cmProp(configPostfix);
} else {
outBase += configPostfix;
}
@@ -5674,6 +5668,11 @@ std::string valueAsString<std::string>(std::string value)
return value;
}
template <>
+std::string valueAsString<cmProp>(cmProp value)
+{
+ return value ? value : std::string("(unset)");
+}
+template <>
std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
{
return "(unset)";
@@ -5749,7 +5748,7 @@ std::string getTypedProperty<std::string>(
cmProp value = tgt->GetProperty(prop);
if (genexInterpreter == nullptr) {
- return valueAsString(cmToCStr(value));
+ return valueAsString(value);
}
return genexInterpreter->Evaluate(value ? *value : "", prop);
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index bd4e08f..fb1b2e6 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -837,7 +837,7 @@ public:
std::string GetFortranModuleDirectory(std::string const& working_dir) const;
bool IsFortranBuildingInstrinsicModules() const;
- const std::string& GetSourcesProperty() const;
+ cmProp GetSourcesProperty() const;
void AddISPCGeneratedHeader(std::string const& header,
std::string const& config);
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx
index cb657f9..bb3a40b 100644
--- a/Source/cmGetPropertyCommand.cxx
+++ b/Source/cmGetPropertyCommand.cxx
@@ -268,6 +268,11 @@ bool StoreResult(OutType infoType, cmMakefile& makefile,
}
return true;
}
+bool StoreResult(OutType infoType, cmMakefile& makefile,
+ const std::string& variable, cmProp value)
+{
+ return StoreResult(infoType, makefile, variable, value.GetCStr());
+}
bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
OutType infoType, const std::string& variable,
@@ -280,9 +285,8 @@ bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
// Get the property.
cmake* cm = status.GetMakefile().GetCMakeInstance();
- return StoreResult(
- infoType, status.GetMakefile(), variable,
- cmToCStr(cm->GetState()->GetGlobalProperty(propertyName)));
+ return StoreResult(infoType, status.GetMakefile(), variable,
+ cm->GetState()->GetGlobalProperty(propertyName));
}
bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
@@ -329,7 +333,7 @@ bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
// Get the property.
return StoreResult(infoType, status.GetMakefile(), variable,
- cmToCStr(mf->GetProperty(propertyName)));
+ mf->GetProperty(propertyName));
}
bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
@@ -365,8 +369,7 @@ bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
if (!prop) {
prop = target->GetProperty(propertyName);
}
- return StoreResult(infoType, status.GetMakefile(), variable,
- cmToCStr(prop));
+ return StoreResult(infoType, status.GetMakefile(), variable, prop);
}
status.SetError(cmStrCat("could not find TARGET ", name,
". Perhaps it has not yet been created."));
@@ -391,7 +394,7 @@ bool HandleSourceMode(cmExecutionStatus& status, const std::string& name,
if (cmSourceFile* sf =
directory_makefile.GetOrCreateSource(source_file_absolute_path)) {
return StoreResult(infoType, status.GetMakefile(), variable,
- cmToCStr(sf->GetPropertyForUser(propertyName)));
+ sf->GetPropertyForUser(propertyName));
}
status.SetError(
cmStrCat("given SOURCE name that could not be found or created: ",
@@ -447,7 +450,7 @@ bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
value = status.GetMakefile().GetState()->GetCacheEntryProperty(
name, propertyName);
}
- StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(value));
+ StoreResult(infoType, status.GetMakefile(), variable, value);
return true;
}
diff --git a/Source/cmGetTestPropertyCommand.cxx b/Source/cmGetTestPropertyCommand.cxx
index cf8c1d5..077353e 100644
--- a/Source/cmGetTestPropertyCommand.cxx
+++ b/Source/cmGetTestPropertyCommand.cxx
@@ -4,6 +4,7 @@
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
+#include "cmProperty.h"
#include "cmTest.h"
bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
@@ -19,12 +20,12 @@ bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
cmMakefile& mf = status.GetMakefile();
cmTest* test = mf.GetTest(testName);
if (test) {
- const char* prop = nullptr;
+ cmProp prop;
if (!args[1].empty()) {
prop = test->GetProperty(args[1]);
}
if (prop) {
- mf.AddDefinition(var, prop);
+ mf.AddDefinition(var, prop->c_str());
return true;
}
}
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 32238e4..12fd9c4 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -376,7 +376,7 @@ void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper(
#ifdef _WIN32
std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%";
#else
- std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi";
+ std::string check_error = "if [ $? -ne 0 ]; then exit 1; fi";
#endif
#ifdef _WIN32
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 15a7304..3561deb 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -242,7 +242,7 @@ void cmGlobalGenerator::ResolveLanguageCompiler(const std::string& lang,
std::vector<std::string> cnameArgVec;
if (cname && !cname->empty()) {
cmExpandList(*cname, cnameArgVec);
- cname = &cnameArgVec.front();
+ cname = cmProp(cnameArgVec.front());
}
std::string changeVars;
@@ -1179,10 +1179,10 @@ void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l,
}
}
-const char* cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
+cmProp cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
{
assert(!this->Makefiles.empty());
- return cmToCStr(this->Makefiles[0]->GetDefinition(name));
+ return this->Makefiles[0]->GetDefinition(name);
}
bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const
@@ -1195,7 +1195,7 @@ std::string cmGlobalGenerator::GetSafeGlobalSetting(
std::string const& name) const
{
assert(!this->Makefiles.empty());
- return this->Makefiles[0]->GetSafeDefinition(name);
+ return this->Makefiles[0]->GetDefinition(name);
}
bool cmGlobalGenerator::IgnoreFile(const char* ext) const
@@ -2749,7 +2749,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install(
singleLine.push_back(cfgArg);
cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
} else {
- cfgArg += cmToCStr(mf->GetDefinition("CMAKE_CFG_INTDIR"));
+ cfgArg += *mf->GetDefinition("CMAKE_CFG_INTDIR");
}
singleLine.push_back(cfgArg);
}
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index f0b59bf..34646d9 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -23,6 +23,7 @@
#include "cmCustomCommandLines.h"
#include "cmDuration.h"
#include "cmExportSet.h"
+#include "cmProperty.h"
#include "cmStateSnapshot.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -308,7 +309,7 @@ public:
cmExportSetMap& GetExportSets() { return this->ExportSets; }
- const char* GetGlobalSetting(std::string const& name) const;
+ cmProp GetGlobalSetting(std::string const& name) const;
bool GlobalSettingIsOn(std::string const& name) const;
std::string GetSafeGlobalSetting(std::string const& name) const;
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 7cf3e93..b81f82e 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -186,8 +186,7 @@ void cmGlobalGhsMultiGenerator::EnableLanguage(
mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
- const char* tgtPlatform =
- cmToCStrSafe(mf->GetDefinition("GHS_TARGET_PLATFORM"));
+ const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM")->c_str();
if (!tgtPlatform) {
cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not "
"specified; defaulting to \"integrity\"");
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index fdb7155..488ff2e 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -88,7 +88,7 @@ public:
void GetDocumentation(cmDocumentationEntry& entry) const override
{
entry.Name = std::string(vs10generatorName) + " [arch]";
- entry.Brief = "Generates Visual Studio 2010 project files. "
+ entry.Brief = "Deprecated. Generates Visual Studio 2010 project files. "
"Optional [arch] can be \"Win64\" or \"IA64\".";
}
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index f8aa172..34dba1e 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -296,6 +296,25 @@ void cmGlobalVisualStudio7Generator::Generate()
this->CallVisualStudioMacro(MacroReload,
GetSLNFile(this->LocalGenerators[0].get()));
}
+
+ if (this->Version == VS10 && !this->CMakeInstance->GetIsInTryCompile()) {
+ std::string cmakeWarnVS10;
+ if (cmProp cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
+ "CMAKE_WARN_VS10")) {
+ this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS10");
+ cmakeWarnVS10 = *cached;
+ } else {
+ cmSystemTools::GetEnv("CMAKE_WARN_VS10", cmakeWarnVS10);
+ }
+ if (cmakeWarnVS10.empty() || !cmIsOff(cmakeWarnVS10)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::WARNING,
+ "The \"Visual Studio 10 2010\" generator is deprecated "
+ "and will be removed in a future version of CMake."
+ "\n"
+ "Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.");
+ }
+ }
}
void cmGlobalVisualStudio7Generator::OutputSLNFile(
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 79c06d6..d0ad53e 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -618,6 +618,9 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
return AuxToolset::Default;
}
+ if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
+ return AuxToolset::Default;
+ }
// The first two components of the default toolset version typically
// match the name used by later VS versions for the SxS props files.
diff --git a/Source/cmIncludeGuardCommand.cxx b/Source/cmIncludeGuardCommand.cxx
index aefd098..d48c823 100644
--- a/Source/cmIncludeGuardCommand.cxx
+++ b/Source/cmIncludeGuardCommand.cxx
@@ -4,6 +4,7 @@
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
+#include "cmProperty.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
#include "cmSystemTools.h"
diff --git a/Source/cmInstallMode.h b/Source/cmInstallMode.h
new file mode 100644
index 0000000..5343d34
--- /dev/null
+++ b/Source/cmInstallMode.h
@@ -0,0 +1,17 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+/**
+ * Enumerate types known to file(INSTALL).
+ */
+enum class cmInstallMode
+{
+ COPY,
+ ABS_SYMLINK,
+ ABS_SYMLINK_OR_COPY,
+ REL_SYMLINK,
+ REL_SYMLINK_OR_COPY,
+ SYMLINK,
+ SYMLINK_OR_COPY
+};
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 6d477a7..0b8778f 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -2579,12 +2579,7 @@ cmProp cmMakefile::GetDefinition(const std::string& name) const
const std::string& cmMakefile::GetSafeDefinition(const std::string& name) const
{
- static std::string const empty;
- cmProp def = this->GetDefinition(name);
- if (!def) {
- return empty;
- }
- return *def;
+ return this->GetDefinition(name);
}
bool cmMakefile::GetDefExpandList(const std::string& name,
@@ -2967,7 +2962,7 @@ MessageType cmMakefile::ExpandVariablesInStringNew(
break;
case ENVIRONMENT:
if (cmSystemTools::GetEnv(lookup, svalue)) {
- value = &svalue;
+ value = cmProp(svalue);
}
break;
case CACHE:
@@ -4012,7 +4007,7 @@ cmProp cmMakefile::GetProperty(const std::string& prop) const
return pair.first;
});
output = cmJoin(keys, ";");
- return &output;
+ return cmProp(output);
}
return this->StateSnapshot.GetDirectory().GetProperty(prop);
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index d6145f8..626453f 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -324,9 +324,25 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules()
this->LocalGenerator->MaybeRelativeToTopBinDir(dependFileNameFull))
<< "\n";
+ // Scan any custom commands to check if DEPFILE option is specified
+ bool ccGenerateDeps = false;
+ std::vector<cmSourceFile const*> customCommands;
+ this->GeneratorTarget->GetCustomCommands(customCommands,
+ this->GetConfigName());
+ for (cmSourceFile const* sf : customCommands) {
+ if (!sf->GetCustomCommand()->GetDepfile().empty()) {
+ ccGenerateDeps = true;
+ break;
+ }
+ }
+
std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER";
- if (!this->Makefile->IsDefinitionSet(depsUseCompiler) ||
- this->Makefile->IsOn(depsUseCompiler)) {
+ bool compilerGenerateDeps =
+ this->GlobalGenerator->SupportsCompilerDependencies() &&
+ (!this->Makefile->IsDefinitionSet(depsUseCompiler) ||
+ this->Makefile->IsOn(depsUseCompiler));
+
+ if (compilerGenerateDeps || ccGenerateDeps) {
std::string compilerDependFile =
cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
*this->BuildFileStream << "# Include any dependencies generated by the "
@@ -361,7 +377,9 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules()
"management for "
<< this->GeneratorTarget->GetName() << ".\n";
}
+ }
+ if (compilerGenerateDeps) {
// deactivate no longer needed legacy dependency files
// Write an empty dependency file.
cmGeneratedFileStream legacyDepFileStream(
@@ -1491,14 +1509,18 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
}
std::vector<std::string> architectures = cmExpandedList(architecturesStr);
+ std::string const& relPath =
+ this->LocalGenerator->GetHomeRelativeOutputPath();
// Ensure there are no duplicates.
const std::vector<std::string> linkDeps = [&]() -> std::vector<std::string> {
std::vector<std::string> deps;
this->AppendTargetDepends(deps, true);
this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA");
- std::copy(this->Objects.begin(), this->Objects.end(),
- std::back_inserter(deps));
+
+ for (std::string const& obj : this->Objects) {
+ deps.emplace_back(cmStrCat(relPath, obj));
+ }
std::unordered_set<std::string> depsSet(deps.begin(), deps.end());
deps.clear();
@@ -1517,33 +1539,34 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
std::string profiles;
std::vector<std::string> fatbinaryDepends;
- std::string registerFile = cmStrCat(objectDir, "cmake_cuda_register.h");
+ std::string const registerFile =
+ cmStrCat(objectDir, "cmake_cuda_register.h");
// Link device code for each architecture.
for (const std::string& architectureKind : architectures) {
- // Clang always generates real code, so strip the specifier.
- const std::string architecture =
- architectureKind.substr(0, architectureKind.find('-'));
- const std::string cubin =
- cmStrCat(relObjectDir, "sm_", architecture, ".cubin");
-
- profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
- fatbinaryDepends.emplace_back(cubin);
-
std::string registerFileCmd;
// The generated register file contains macros that when expanded
// register the device routines. Because the routines are the same for
// all architectures the register file will be the same too. Thus
// generate it only on the first invocation to reduce overhead.
- if (fatbinaryDepends.size() == 1) {
- std::string registerFileRel =
- this->LocalGenerator->MaybeRelativeToCurBinDir(registerFile);
+ if (fatbinaryDepends.empty()) {
+ std::string const registerFileRel =
+ cmStrCat(relPath, relObjectDir, "cmake_cuda_register.h");
registerFileCmd =
cmStrCat(" --register-link-binaries=", registerFileRel);
cleanFiles.push_back(registerFileRel);
}
+ // Clang always generates real code, so strip the specifier.
+ const std::string architecture =
+ architectureKind.substr(0, architectureKind.find('-'));
+ const std::string cubin =
+ cmStrCat(objectDir, "sm_", architecture, ".cubin");
+
+ profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
+ fatbinaryDepends.emplace_back(cubin);
+
std::string command = cmStrCat(
this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
" -arch=sm_", architecture, registerFileCmd, " -o=$@ ",
@@ -1562,7 +1585,7 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
const std::string fatbinaryOutput =
cmStrCat(objectDir, "cmake_cuda_fatbin.h");
const std::string fatbinaryOutputRel =
- this->LocalGenerator->MaybeRelativeToCurBinDir(fatbinaryOutput);
+ cmStrCat(relPath, relObjectDir, "cmake_cuda_fatbin.h");
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
fatbinaryOutputRel, fatbinaryDepends,
@@ -1590,9 +1613,8 @@ void cmMakefileTargetGenerator::WriteDeviceLinkRule(
compileCmd, vars);
commands.emplace_back(compileCmd);
- this->LocalGenerator->WriteMakeRule(
- *this->BuildFileStream, nullptr, output,
- { cmStrCat(relObjectDir, "cmake_cuda_fatbin.h") }, commands, false);
+ this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, output,
+ { fatbinaryOutputRel }, commands, false);
// Clean all the possible executable names and symlinks.
this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end());
diff --git a/Source/cmMarkAsAdvancedCommand.cxx b/Source/cmMarkAsAdvancedCommand.cxx
index 45043fa..5908f74 100644
--- a/Source/cmMarkAsAdvancedCommand.cxx
+++ b/Source/cmMarkAsAdvancedCommand.cxx
@@ -6,6 +6,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 93a54c2..96e9142 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -759,10 +759,6 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
const std::string cubin =
cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
- fatbinary.Variables["PROFILES"] +=
- cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
- fatbinary.ExplicitDeps.emplace_back(cubin);
-
cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config));
dlink.ExplicitDeps = explicitDeps;
dlink.Outputs = { cubin };
@@ -772,11 +768,15 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
// the device routines. Because the routines are the same for all
// architectures the register file will be the same too. Thus generate it
// only on the first invocation to reduce overhead.
- if (fatbinary.ExplicitDeps.size() == 1) {
+ if (fatbinary.ExplicitDeps.empty()) {
dlink.Variables["REGISTER"] = cmStrCat(
"--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
}
+ fatbinary.Variables["PROFILES"] +=
+ cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
+ fatbinary.ExplicitDeps.emplace_back(cubin);
+
this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), dlink);
}
diff --git a/Source/cmOptionCommand.cxx b/Source/cmOptionCommand.cxx
index bae67e0..014da4d 100644
--- a/Source/cmOptionCommand.cxx
+++ b/Source/cmOptionCommand.cxx
@@ -29,7 +29,7 @@ bool cmOptionCommand(std::vector<std::string> const& args,
{
auto policyStatus =
status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0077);
- const auto* existsBeforeSet =
+ const auto& existsBeforeSet =
status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
switch (policyStatus) {
case cmPolicies::WARN:
@@ -77,7 +77,7 @@ bool cmOptionCommand(std::vector<std::string> const& args,
}
if (checkAndWarn) {
- const auto* existsAfterSet =
+ const auto& existsAfterSet =
status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
if (!existsAfterSet) {
status.GetMakefile().IssueMessage(
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index 01e8c04..fc839c5 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -10,6 +10,7 @@
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmProperty.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
#include "cmStateTypes.h"
diff --git a/Source/cmProperty.cxx b/Source/cmProperty.cxx
new file mode 100644
index 0000000..d012630
--- /dev/null
+++ b/Source/cmProperty.cxx
@@ -0,0 +1,113 @@
+
+#include "cmProperty.h"
+
+#include <string>
+
+#include <cmext/string_view>
+
+#include "cmStringAlgorithms.h"
+
+std::string cmProp::Empty;
+
+bool cmProp::IsOn(cm::string_view value) noexcept
+{
+ switch (value.size()) {
+ case 1:
+ return value[0] == '1' || value[0] == 'Y' || value[0] == 'y';
+ case 2:
+ return //
+ (value[0] == 'O' || value[0] == 'o') && //
+ (value[1] == 'N' || value[1] == 'n');
+ case 3:
+ return //
+ (value[0] == 'Y' || value[0] == 'y') && //
+ (value[1] == 'E' || value[1] == 'e') && //
+ (value[2] == 'S' || value[2] == 's');
+ case 4:
+ return //
+ (value[0] == 'T' || value[0] == 't') && //
+ (value[1] == 'R' || value[1] == 'r') && //
+ (value[2] == 'U' || value[2] == 'u') && //
+ (value[3] == 'E' || value[3] == 'e');
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool cmProp::IsOff(cm::string_view value) noexcept
+{
+ switch (value.size()) {
+ case 0:
+ return true;
+ case 1:
+ return value[0] == '0' || value[0] == 'N' || value[0] == 'n';
+ case 2:
+ return //
+ (value[0] == 'N' || value[0] == 'n') && //
+ (value[1] == 'O' || value[1] == 'o');
+ case 3:
+ return //
+ (value[0] == 'O' || value[0] == 'o') && //
+ (value[1] == 'F' || value[1] == 'f') && //
+ (value[2] == 'F' || value[2] == 'f');
+ case 5:
+ return //
+ (value[0] == 'F' || value[0] == 'f') && //
+ (value[1] == 'A' || value[1] == 'a') && //
+ (value[2] == 'L' || value[2] == 'l') && //
+ (value[3] == 'S' || value[3] == 's') && //
+ (value[4] == 'E' || value[4] == 'e');
+ case 6:
+ return //
+ (value[0] == 'I' || value[0] == 'i') && //
+ (value[1] == 'G' || value[1] == 'g') && //
+ (value[2] == 'N' || value[2] == 'n') && //
+ (value[3] == 'O' || value[3] == 'o') && //
+ (value[4] == 'R' || value[4] == 'r') && //
+ (value[5] == 'E' || value[5] == 'e');
+ default:
+ break;
+ }
+
+ return IsNOTFOUND(value);
+}
+bool cmProp::IsNOTFOUND(cm::string_view value) noexcept
+{
+ return (value == "NOTFOUND"_s) || cmHasSuffix(value, "-NOTFOUND"_s);
+}
+
+int cmProp::Compare(cmProp value) const noexcept
+{
+ if (this->Value == nullptr && !value) {
+ return 0;
+ }
+ if (this->Value == nullptr) {
+ return -1;
+ }
+ if (!value) {
+ return 1;
+ }
+ return this->Value->compare(*value);
+}
+
+int cmProp::Compare(cm::string_view value) const noexcept
+{
+ if (this->Value == nullptr && value.data() == nullptr) {
+ return 0;
+ }
+ if (this->Value == nullptr) {
+ return -1;
+ }
+ if (value.data() == nullptr) {
+ return 1;
+ }
+ return cm::string_view(*this->Value).compare(value);
+}
+
+std::ostream& operator<<(std::ostream& o, cmProp v)
+{
+ o << *v;
+ return o;
+}
diff --git a/Source/cmProperty.h b/Source/cmProperty.h
index 1e03c3f..3a0a5be 100644
--- a/Source/cmProperty.h
+++ b/Source/cmProperty.h
@@ -4,8 +4,12 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#include <cstddef>
+#include <iosfwd>
#include <string>
+#include <cm/string_view>
+
class cmProperty
{
public:
@@ -23,14 +27,210 @@ public:
};
};
-using cmProp = const std::string*;
+class cmProp
+{
+public:
+ cmProp() noexcept = default;
+ cmProp(std::nullptr_t) noexcept {}
+ explicit cmProp(const std::string* value) noexcept
+ : Value(value)
+ {
+ }
+ explicit cmProp(const std::string& value) noexcept
+ : Value(&value)
+ {
+ }
+ cmProp(const cmProp& other) noexcept = default;
-inline const char* cmToCStr(cmProp p)
+ cmProp& operator=(const cmProp& other) noexcept = default;
+ cmProp& operator=(std::nullptr_t) noexcept
+ {
+ this->Value = nullptr;
+ return *this;
+ }
+
+ const std::string* Get() const noexcept { return this->Value; }
+ const char* GetCStr() const noexcept
+ {
+ return this->Value == nullptr ? nullptr : this->Value->c_str();
+ }
+
+ const std::string* operator->() const noexcept
+ {
+ return this->Value == nullptr ? &cmProp::Empty : this->Value;
+ }
+ const std::string& operator*() const noexcept
+ {
+ return this->Value == nullptr ? cmProp::Empty : *this->Value;
+ }
+
+ explicit operator bool() const noexcept { return this->Value != nullptr; }
+ operator const std::string&() const noexcept { return this->operator*(); }
+ operator cm::string_view() const noexcept { return this->operator*(); }
+
+ /**
+ * Does the value indicate a true or ON value?
+ */
+ bool IsOn() const noexcept
+ {
+ return this->Value != nullptr &&
+ cmProp::IsOn(cm::string_view(*this->Value));
+ }
+ /**
+ * Does the value indicate a false or off value ? Note that this is
+ * not the same as !IsOn(...) because there are a number of
+ * ambiguous values such as "/usr/local/bin" a path will result in
+ * IsOn and IsOff both returning false. Note that the special path
+ * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+ */
+ bool IsOff() const noexcept
+ {
+ return this->Value == nullptr ||
+ cmProp::IsOff(cm::string_view(*this->Value));
+ }
+ /** Return true if value is NOTFOUND or ends in -NOTFOUND. */
+ bool IsNOTFOUND() const noexcept
+ {
+ return this->Value != nullptr &&
+ cmProp::IsNOTFOUND(cm::string_view(*this->Value));
+ }
+ bool IsEmpty() const noexcept
+ {
+ return this->Value == nullptr || this->Value->empty();
+ }
+
+ bool IsSet() const noexcept
+ {
+ return !this->IsEmpty() && !this->IsNOTFOUND();
+ }
+
+ /**
+ * Does a string indicate a true or ON value?
+ */
+ static bool IsOn(const char* value) noexcept
+ {
+ return value != nullptr && IsOn(cm::string_view(value));
+ }
+ static bool IsOn(cm::string_view) noexcept;
+
+ /**
+ * Compare method has same semantic as std::optional::compare
+ */
+ int Compare(cmProp value) const noexcept;
+ int Compare(cm::string_view value) const noexcept;
+
+ /**
+ * Does a string indicate a false or off value ? Note that this is
+ * not the same as !IsOn(...) because there are a number of
+ * ambiguous values such as "/usr/local/bin" a path will result in
+ * IsOn and IsOff both returning false. Note that the special path
+ * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+ */
+ static bool IsOff(const char* value) noexcept
+ {
+ return value == nullptr || IsOff(cm::string_view(value));
+ }
+ static bool IsOff(cm::string_view) noexcept;
+
+ /** Return true if value is NOTFOUND or ends in -NOTFOUND. */
+ static bool IsNOTFOUND(const char* value) noexcept
+ {
+ return value == nullptr || IsNOTFOUND(cm::string_view(value));
+ }
+ static bool IsNOTFOUND(cm::string_view) noexcept;
+
+ static bool IsEmpty(const char* value) noexcept
+ {
+ return value == nullptr || *value == '\0';
+ }
+ static bool IsEmpty(cm::string_view value) noexcept { return value.empty(); }
+
+private:
+ static std::string Empty;
+ const std::string* Value = nullptr;
+};
+
+std::ostream& operator<<(std::ostream& o, cmProp v);
+
+inline bool operator==(cmProp l, cmProp r) noexcept
+{
+ return l.Compare(r) == 0;
+}
+inline bool operator!=(cmProp l, cmProp r) noexcept
{
- return p ? p->c_str() : nullptr;
+ return l.Compare(r) != 0;
+}
+inline bool operator<(cmProp l, cmProp r) noexcept
+{
+ return l.Compare(r) < 0;
+}
+inline bool operator<=(cmProp l, cmProp r) noexcept
+{
+ return l.Compare(r) <= 0;
+}
+inline bool operator>(cmProp l, cmProp r) noexcept
+{
+ return l.Compare(r) > 0;
+}
+inline bool operator>=(cmProp l, cmProp r) noexcept
+{
+ return l.Compare(r) >= 0;
}
-inline const char* cmToCStrSafe(cmProp p)
+inline bool operator==(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) == 0;
+}
+inline bool operator!=(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) != 0;
+}
+inline bool operator<(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) < 0;
+}
+inline bool operator<=(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) <= 0;
+}
+inline bool operator>(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) > 0;
+}
+inline bool operator>=(cmProp l, cm::string_view r) noexcept
+{
+ return l.Compare(r) >= 0;
+}
+
+inline bool operator==(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) == 0;
+}
+inline bool operator!=(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) != 0;
+}
+inline bool operator<(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) < 0;
+}
+inline bool operator<=(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) <= 0;
+}
+inline bool operator>(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) > 0;
+}
+inline bool operator>=(cmProp l, std::nullptr_t) noexcept
+{
+ return l.Compare(cmProp{}) >= 0;
+}
+
+/**
+ * Temporary wrapper
+ */
+inline const char* cmToCStr(cmProp p)
{
- return p ? p->c_str() : "";
+ return p.GetCStr();
}
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 06e151a..3e3a44b 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -46,7 +46,7 @@ cmProp cmPropertyMap::GetPropertyValue(const std::string& name) const
{
auto it = this->Map_.find(name);
if (it != this->Map_.end()) {
- return &it->second;
+ return cmProp(it->second);
}
return nullptr;
}
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 898d862..57fcd2d 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -384,39 +384,3 @@ bool cmQtAutoGen::RccLister::list(std::string const& qrcFile,
}
return true;
}
-
-bool cmQtAutoGen::FileRead(std::string& content, std::string const& filename,
- std::string* error)
-{
- content.clear();
- if (!cmSystemTools::FileExists(filename, true)) {
- if (error != nullptr) {
- *error = "Not a file.";
- }
- return false;
- }
-
- unsigned long const length = cmSystemTools::FileLength(filename);
- cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
-
- // Use lambda to save destructor calls of ifs
- return [&ifs, length, &content, error]() -> bool {
- if (!ifs) {
- if (error != nullptr) {
- *error = "Opening the file for reading failed.";
- }
- return false;
- }
- content.reserve(length);
- using IsIt = std::istreambuf_iterator<char>;
- content.assign(IsIt{ ifs }, IsIt{});
- if (!ifs) {
- content.clear();
- if (error != nullptr) {
- *error = "Reading from the file failed.";
- }
- return false;
- }
- return true;
- }();
-}
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index b9ae360..466a954 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -100,9 +100,6 @@ public:
std::vector<std::string> const& newOpts,
bool isQt5);
- static bool FileRead(std::string& content, std::string const& filename,
- std::string* error = nullptr);
-
/** @class RccLister
* @brief Lists files in qrc resource files
*/
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 6cc8328..4dd78e5 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -902,13 +902,6 @@ bool cmQtAutoGenInitializer::InitScanFiles()
// The reason is that their file names might be discovered from source files
// at generation time.
if (this->MocOrUicEnabled()) {
- std::set<std::string> uicIncludes;
- auto collectUicIncludes = [&](std::unique_ptr<cmSourceFile> const& sf) {
- std::string content;
- FileRead(content, sf->GetFullPath());
- this->AutoUicHelpers.CollectUicIncludes(uicIncludes, content);
- };
-
for (const auto& sf : this->Makefile->GetSourceFiles()) {
// sf->GetExtension() is only valid after sf->ResolveFullPath() ...
// Since we're iterating over source files that might be not in the
@@ -921,10 +914,6 @@ bool cmQtAutoGenInitializer::InitScanFiles()
std::string const& extLower =
cmSystemTools::LowerCase(sf->GetExtension());
- bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
- bool const skipUic =
- (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC) ||
- !this->Uic.Enabled);
if (cm->IsAHeaderExtension(extLower)) {
if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
auto muf = makeMUFile(sf.get(), fullPath, {}, false);
@@ -932,9 +921,6 @@ bool cmQtAutoGenInitializer::InitScanFiles()
addMUHeader(std::move(muf), extLower);
}
}
- if (!skipUic && !sf->GetIsGenerated()) {
- collectUicIncludes(sf);
- }
} else if (cm->IsACLikeSourceExtension(extLower)) {
if (!cm::contains(this->AutogenTarget.Sources, sf.get())) {
auto muf = makeMUFile(sf.get(), fullPath, {}, false);
@@ -942,11 +928,11 @@ bool cmQtAutoGenInitializer::InitScanFiles()
addMUSource(std::move(muf));
}
}
- if (!skipUic && !sf->GetIsGenerated()) {
- collectUicIncludes(sf);
- }
} else if (this->Uic.Enabled && (extLower == kw.ui)) {
// .ui file
+ bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
+ bool const skipUic =
+ (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
if (!skipUic) {
// Check if the .ui file has uic options
std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
@@ -956,22 +942,35 @@ bool cmQtAutoGenInitializer::InitScanFiles()
this->Uic.UiFilesWithOptions.emplace_back(fullPath,
cmExpandedList(uicOpts));
}
+
+ auto uiHeaderRelativePath = cmSystemTools::RelativePath(
+ this->LocalGen->GetCurrentSourceDirectory(),
+ cmSystemTools::GetFilenamePath(fullPath));
+
+ // Avoid creating a path containing adjacent slashes
+ if (!uiHeaderRelativePath.empty() &&
+ uiHeaderRelativePath.back() != '/') {
+ uiHeaderRelativePath += '/';
+ }
+
+ auto uiHeaderFilePath = cmStrCat(
+ '/', uiHeaderRelativePath, "ui_"_s,
+ cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s);
+
+ ConfigString uiHeader;
+ std::string uiHeaderGenex;
+ this->ConfigFileNamesAndGenex(
+ uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s),
+ uiHeaderFilePath);
+
+ this->Uic.UiHeaders.emplace_back(
+ std::make_pair(uiHeader, uiHeaderGenex));
} else {
// Register skipped .ui file
this->Uic.SkipUi.insert(fullPath);
}
}
}
-
- for (const auto& include : uicIncludes) {
- ConfigString uiHeader;
- std::string uiHeaderGenex;
- this->ConfigFileNamesAndGenex(uiHeader, uiHeaderGenex,
- cmStrCat(this->Dir.Build, "/include"_s),
- cmStrCat("/"_s, include));
- this->Uic.UiHeaders.emplace_back(
- std::make_pair(uiHeader, uiHeaderGenex));
- }
}
// Process GENERATED sources and headers
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index 3ec87d2..e76817b 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -17,7 +17,6 @@
#include "cmFilePathChecksum.h"
#include "cmQtAutoGen.h"
-#include "cmQtAutoUicHelpers.h"
class cmGeneratorTarget;
class cmGlobalGenerator;
@@ -171,7 +170,6 @@ private:
std::string ConfigDefault;
std::vector<std::string> ConfigsList;
std::string TargetsFolder;
- cmQtAutoUicHelpers AutoUicHelpers;
/** Common directories. */
struct
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index 0c6b5e6..568926e 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -121,6 +121,43 @@ bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename)
return success;
}
+bool cmQtAutoGenerator::FileRead(std::string& content,
+ std::string const& filename,
+ std::string* error)
+{
+ content.clear();
+ if (!cmSystemTools::FileExists(filename, true)) {
+ if (error != nullptr) {
+ *error = "Not a file.";
+ }
+ return false;
+ }
+
+ unsigned long const length = cmSystemTools::FileLength(filename);
+ cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary));
+
+ // Use lambda to save destructor calls of ifs
+ return [&ifs, length, &content, error]() -> bool {
+ if (!ifs) {
+ if (error != nullptr) {
+ *error = "Opening the file for reading failed.";
+ }
+ return false;
+ }
+ content.reserve(length);
+ using IsIt = std::istreambuf_iterator<char>;
+ content.assign(IsIt{ ifs }, IsIt{});
+ if (!ifs) {
+ content.clear();
+ if (error != nullptr) {
+ *error = "Reading from the file failed.";
+ }
+ return false;
+ }
+ return true;
+ }();
+}
+
bool cmQtAutoGenerator::FileWrite(std::string const& filename,
std::string const& content,
std::string* error)
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 66399d7..5c3a8ad 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -70,6 +70,8 @@ public:
// -- File system methods
static bool MakeParentDirectory(std::string const& filename);
+ static bool FileRead(std::string& content, std::string const& filename,
+ std::string* error = nullptr);
static bool FileWrite(std::string const& filename,
std::string const& content,
std::string* error = nullptr);
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 86d54f9..2753fd5 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -30,7 +30,6 @@
#include "cmGeneratedFileStream.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenerator.h"
-#include "cmQtAutoUicHelpers.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmWorkerPool.h"
@@ -282,7 +281,7 @@ public:
std::vector<std::string> Options;
std::unordered_map<std::string, UiFile> UiFiles;
std::vector<std::string> SearchPaths;
- cmQtAutoUicHelpers AutoUicHelpers;
+ cmsys::RegularExpression RegExpInclude;
};
/** Uic shared variables. */
@@ -762,7 +761,11 @@ std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const
return res;
}
-cmQtAutoMocUicT::UicSettingsT::UicSettingsT() = default;
+cmQtAutoMocUicT::UicSettingsT::UicSettingsT()
+{
+ this->RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+}
cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default;
@@ -1053,7 +1056,16 @@ void cmQtAutoMocUicT::JobParseT::UicIncludes()
}
std::set<std::string> includes;
- this->UicConst().AutoUicHelpers.CollectUicIncludes(includes, this->Content);
+ {
+ const char* contentChars = this->Content.c_str();
+ cmsys::RegularExpression const& regExp = this->UicConst().RegExpInclude;
+ cmsys::RegularExpressionMatch match;
+ while (regExp.find(contentChars, match)) {
+ includes.emplace(match.match(2));
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
this->CreateKeys(this->FileHandle->ParseData->Uic.Include, includes,
UiUnderscoreLength);
}
diff --git a/Source/cmQtAutoUicHelpers.cxx b/Source/cmQtAutoUicHelpers.cxx
deleted file mode 100644
index 751ae08..0000000
--- a/Source/cmQtAutoUicHelpers.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
-#include "cmQtAutoUicHelpers.h"
-
-cmQtAutoUicHelpers::cmQtAutoUicHelpers()
-{
- RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
-}
-
-void cmQtAutoUicHelpers::CollectUicIncludes(std::set<std::string>& includes,
- const std::string& content) const
-{
- if (content.find("ui_") == std::string::npos) {
- return;
- }
-
- const char* contentChars = content.c_str();
- cmsys::RegularExpressionMatch match;
- while (this->RegExpInclude.find(contentChars, match)) {
- includes.emplace(match.match(2));
- // Forward content pointer
- contentChars += match.end();
- }
-}
diff --git a/Source/cmQtAutoUicHelpers.h b/Source/cmQtAutoUicHelpers.h
deleted file mode 100644
index 6b09a31..0000000
--- a/Source/cmQtAutoUicHelpers.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
-#pragma once
-
-#include <set>
-#include <string>
-
-#include "cmsys/RegularExpression.hxx"
-
-class cmQtAutoUicHelpers
-{
-public:
- cmQtAutoUicHelpers();
- virtual ~cmQtAutoUicHelpers() = default;
- void CollectUicIncludes(std::set<std::string>& includes,
- const std::string& content) const;
-
-private:
- cmsys::RegularExpression RegExpInclude;
-};
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index 3f3c8d5..f2b5cc4 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -342,7 +342,7 @@ cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
// if it is requested by the user.
if (prop == propLANGUAGE) {
// The pointer is valid until `this->Language` is modified.
- return &this->GetOrDetermineLanguage();
+ return cmProp(this->GetOrDetermineLanguage());
}
// Special handling for GENERATED property.
@@ -355,9 +355,9 @@ cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
(policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)
? CheckScope::GlobalAndLocal
: CheckScope::Global)) {
- return &propTRUE;
+ return cmProp(propTRUE);
}
- return &propFALSE;
+ return cmProp(propFALSE);
}
// Perform the normal property lookup.
@@ -371,7 +371,7 @@ cmProp cmSourceFile::GetProperty(const std::string& prop) const
if (this->FullPath.empty()) {
return nullptr;
}
- return &this->FullPath;
+ return cmProp(this->FullPath);
}
// Check for the properties with backtraces.
@@ -382,7 +382,7 @@ cmProp cmSourceFile::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->IncludeDirectories, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propCOMPILE_OPTIONS) {
@@ -392,7 +392,7 @@ cmProp cmSourceFile::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->CompileOptions, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propCOMPILE_DEFINITIONS) {
@@ -402,7 +402,7 @@ cmProp cmSourceFile::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->CompileDefinitions, ";");
- return &output;
+ return cmProp(output);
}
cmProp retVal = this->Properties.GetPropertyValue(prop);
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx
index 74b9d6f..499317d 100644
--- a/Source/cmStandardLevelResolver.cxx
+++ b/Source/cmStandardLevelResolver.cxx
@@ -57,10 +57,10 @@ int ParseStd(std::string const& level)
return -1;
}
-struct StanardLevelComputer
+struct StandardLevelComputer
{
- explicit StanardLevelComputer(std::string lang, std::vector<int> levels,
- std::vector<std::string> levelsStr)
+ explicit StandardLevelComputer(std::string lang, std::vector<int> levels,
+ std::vector<std::string> levelsStr)
: Language(std::move(lang))
, Levels(std::move(levels))
, LevelsAsStrings(std::move(levelsStr))
@@ -308,31 +308,33 @@ struct StanardLevelComputer
std::vector<std::string> LevelsAsStrings;
};
-std::unordered_map<std::string, StanardLevelComputer> StandardComputerMapping =
- { { "C",
- StanardLevelComputer{
+std::unordered_map<std::string, StandardLevelComputer>
+ StandardComputerMapping = {
+ { "C",
+ StandardLevelComputer{
"C", std::vector<int>{ 90, 99, 11, 17, 23 },
std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
{ "CXX",
- StanardLevelComputer{
+ StandardLevelComputer{
"CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
{ "CUDA",
- StanardLevelComputer{
+ StandardLevelComputer{
"CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23 },
std::vector<std::string>{ "03", "11", "14", "17", "20", "23" } } },
{ "OBJC",
- StanardLevelComputer{
+ StandardLevelComputer{
"OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
{ "OBJCXX",
- StanardLevelComputer{
+ StandardLevelComputer{
"OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
{ "HIP",
- StanardLevelComputer{
+ StandardLevelComputer{
"HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
- std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } } };
+ std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }
+ };
}
std::string cmStandardLevelResolver::GetCompileOptionDef(
@@ -391,7 +393,7 @@ bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
return true;
}
- const char* features = this->CompileFeaturesAvailable(lang, error);
+ cmProp features = this->CompileFeaturesAvailable(lang, error);
if (!features) {
return false;
}
@@ -469,7 +471,7 @@ bool cmStandardLevelResolver::CompileFeatureKnown(
return false;
}
-const char* cmStandardLevelResolver::CompileFeaturesAvailable(
+cmProp cmStandardLevelResolver::CompileFeaturesAvailable(
const std::string& lang, std::string* error) const
{
if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
@@ -511,7 +513,7 @@ const char* cmStandardLevelResolver::CompileFeaturesAvailable(
}
return nullptr;
}
- return cmToCStr(featuresKnown);
+ return featuresKnown;
}
bool cmStandardLevelResolver::GetNewRequiredStandard(
diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h
index d84fbcb..c01a3b1 100644
--- a/Source/cmStandardLevelResolver.h
+++ b/Source/cmStandardLevelResolver.h
@@ -30,8 +30,8 @@ public:
const std::string& feature, std::string& lang,
std::string* error) const;
- const char* CompileFeaturesAvailable(const std::string& lang,
- std::string* error) const;
+ cmProp CompileFeaturesAvailable(const std::string& lang,
+ std::string* error) const;
bool GetNewRequiredStandard(const std::string& targetName,
const std::string& feature,
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index bdb5bda..bde3e2e 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -603,47 +603,47 @@ cmProp cmState::GetGlobalProperty(const std::string& prop)
if (prop == "CMAKE_C_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_C90_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_C90_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_C99_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_C99_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_C11_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_C11_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_CXX_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_CXX98_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_CXX98_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_CXX11_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_CXX11_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_CXX14_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_CXX14_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
if (prop == "CMAKE_CUDA_KNOWN_FEATURES") {
static const std::string s_out(
&FOR_EACH_CUDA_FEATURE(STRING_LIST_ELEMENT)[1]);
- return &s_out;
+ return cmProp(s_out);
}
#undef STRING_LIST_ELEMENT
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index c898dd4..b90cf7e 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -450,17 +450,17 @@ cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const
if (prop == "PARENT_DIRECTORY") {
cmStateSnapshot parent = this->Snapshot_.GetBuildsystemDirectoryParent();
if (parent.IsValid()) {
- return &parent.GetDirectory().GetCurrentSource();
+ return cmProp(parent.GetDirectory().GetCurrentSource());
}
- return &output;
+ return cmProp(output);
}
if (prop == kBINARY_DIR) {
output = this->GetCurrentBinary();
- return &output;
+ return cmProp(output);
}
if (prop == kSOURCE_DIR) {
output = this->GetCurrentSource();
- return &output;
+ return cmProp(output);
}
if (prop == kSUBDIRECTORIES) {
std::vector<std::string> child_dirs;
@@ -471,15 +471,15 @@ cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const
child_dirs.push_back(ci.GetDirectory().GetCurrentSource());
}
output = cmJoin(child_dirs, ";");
- return &output;
+ return cmProp(output);
}
if (prop == kBUILDSYSTEM_TARGETS) {
output = cmJoin(this->DirectoryState->NormalTargetNames, ";");
- return &output;
+ return cmProp(output);
}
if (prop == "IMPORTED_TARGETS"_s) {
output = cmJoin(this->DirectoryState->ImportedTargetNames, ";");
- return &output;
+ return cmProp(output);
}
if (prop == "LISTFILE_STACK") {
@@ -491,38 +491,38 @@ cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const
}
std::reverse(listFiles.begin(), listFiles.end());
output = cmJoin(listFiles, ";");
- return &output;
+ return cmProp(output);
}
if (prop == "CACHE_VARIABLES") {
output = cmJoin(this->Snapshot_.State->GetCacheEntryKeys(), ";");
- return &output;
+ return cmProp(output);
}
if (prop == "VARIABLES") {
std::vector<std::string> res = this->Snapshot_.ClosureKeys();
cm::append(res, this->Snapshot_.State->GetCacheEntryKeys());
std::sort(res.begin(), res.end());
output = cmJoin(res, ";");
- return &output;
+ return cmProp(output);
}
if (prop == "INCLUDE_DIRECTORIES") {
output = cmJoin(this->GetIncludeDirectoriesEntries(), ";");
- return &output;
+ return cmProp(output);
}
if (prop == "COMPILE_OPTIONS") {
output = cmJoin(this->GetCompileOptionsEntries(), ";");
- return &output;
+ return cmProp(output);
}
if (prop == "COMPILE_DEFINITIONS") {
output = cmJoin(this->GetCompileDefinitionsEntries(), ";");
- return &output;
+ return cmProp(output);
}
if (prop == "LINK_OPTIONS") {
output = cmJoin(this->GetLinkOptionsEntries(), ";");
- return &output;
+ return cmProp(output);
}
if (prop == "LINK_DIRECTORIES") {
output = cmJoin(this->GetLinkDirectoriesEntries(), ";");
- return &output;
+ return cmProp(output);
}
cmProp retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index fbf47ef..6f13d89 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -201,8 +201,7 @@ bool cmStateSnapshot::HasDefinedPolicyCMP0011()
return !this->Position->Policies->IsEmpty();
}
-std::string const* cmStateSnapshot::GetDefinition(
- std::string const& name) const
+cmProp cmStateSnapshot::GetDefinition(std::string const& name) const
{
assert(this->Position->Vars.IsValid());
return cmDefinitions::Get(name, this->Position->Vars, this->Position->Root);
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h
index d06cba3..a5fe7e2 100644
--- a/Source/cmStateSnapshot.h
+++ b/Source/cmStateSnapshot.h
@@ -12,6 +12,7 @@
#include "cmLinkedTree.h"
#include "cmPolicies.h"
+#include "cmProperty.h"
#include "cmStateTypes.h"
class cmState;
@@ -23,7 +24,7 @@ public:
cmStateSnapshot(cmState* state = nullptr);
cmStateSnapshot(cmState* state, cmStateDetail::PositionType position);
- std::string const* GetDefinition(std::string const& name) const;
+ cmProp GetDefinition(std::string const& name) const;
bool IsInitialized(std::string const& name) const;
void SetDefinition(std::string const& name, cm::string_view value);
void RemoveDefinition(std::string const& name);
diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx
index 5bb6e7b..acb5e5b 100644
--- a/Source/cmStringAlgorithms.cxx
+++ b/Source/cmStringAlgorithms.cxx
@@ -227,76 +227,6 @@ bool cmIsInternallyOn(cm::string_view val)
(val[3] == 'N' || val[3] == 'n');
}
-bool cmIsNOTFOUND(cm::string_view val)
-{
- return (val == "NOTFOUND") || cmHasLiteralSuffix(val, "-NOTFOUND");
-}
-
-bool cmIsOn(cm::string_view val)
-{
- switch (val.size()) {
- case 1:
- return val[0] == '1' || val[0] == 'Y' || val[0] == 'y';
- case 2:
- return //
- (val[0] == 'O' || val[0] == 'o') && //
- (val[1] == 'N' || val[1] == 'n');
- case 3:
- return //
- (val[0] == 'Y' || val[0] == 'y') && //
- (val[1] == 'E' || val[1] == 'e') && //
- (val[2] == 'S' || val[2] == 's');
- case 4:
- return //
- (val[0] == 'T' || val[0] == 't') && //
- (val[1] == 'R' || val[1] == 'r') && //
- (val[2] == 'U' || val[2] == 'u') && //
- (val[3] == 'E' || val[3] == 'e');
- default:
- break;
- }
-
- return false;
-}
-
-bool cmIsOff(cm::string_view val)
-{
- switch (val.size()) {
- case 0:
- return true;
- case 1:
- return val[0] == '0' || val[0] == 'N' || val[0] == 'n';
- case 2:
- return //
- (val[0] == 'N' || val[0] == 'n') && //
- (val[1] == 'O' || val[1] == 'o');
- case 3:
- return //
- (val[0] == 'O' || val[0] == 'o') && //
- (val[1] == 'F' || val[1] == 'f') && //
- (val[2] == 'F' || val[2] == 'f');
- case 5:
- return //
- (val[0] == 'F' || val[0] == 'f') && //
- (val[1] == 'A' || val[1] == 'a') && //
- (val[2] == 'L' || val[2] == 'l') && //
- (val[3] == 'S' || val[3] == 's') && //
- (val[4] == 'E' || val[4] == 'e');
- case 6:
- return //
- (val[0] == 'I' || val[0] == 'i') && //
- (val[1] == 'G' || val[1] == 'g') && //
- (val[2] == 'N' || val[2] == 'n') && //
- (val[3] == 'O' || val[3] == 'o') && //
- (val[4] == 'R' || val[4] == 'r') && //
- (val[5] == 'E' || val[5] == 'e');
- default:
- break;
- }
-
- return cmIsNOTFOUND(val);
-}
-
bool cmStrToLong(const char* str, long* value)
{
errno = 0;
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 6b458ec..531678a 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -14,25 +14,12 @@
#include <cm/string_view>
+#include "cmProperty.h"
#include "cmRange.h"
/** String range type. */
using cmStringRange = cmRange<std::vector<std::string>::const_iterator>;
-/** Check for non-empty string. */
-inline bool cmNonempty(const char* str)
-{
- return str && *str;
-}
-inline bool cmNonempty(cm::string_view str)
-{
- return !str.empty();
-}
-inline bool cmNonempty(std::string const* str)
-{
- return str && !str->empty();
-}
-
/** Returns length of a literal string. */
template <size_t N>
constexpr size_t cmStrLen(const char (&/*str*/)[N])
@@ -175,6 +162,10 @@ public:
cmAlphaNum(unsigned long long int val);
cmAlphaNum(float val);
cmAlphaNum(double val);
+ cmAlphaNum(cmProp value)
+ : View_(*value)
+ {
+ }
cm::string_view View() const { return this->View_; }
@@ -227,20 +218,44 @@ inline bool cmIsInternallyOn(const char* val)
return cmIsInternallyOn(cm::string_view(val));
}
+/** Check for non-empty Property/Variable value. */
+inline bool cmNonempty(cm::string_view val)
+{
+ return !cmProp::IsEmpty(val);
+}
+inline bool cmNonempty(const char* val)
+{
+ return !cmProp::IsEmpty(val);
+}
+inline bool cmNonempty(cmProp val)
+{
+ return !val.IsEmpty();
+}
+
/** Return true if value is NOTFOUND or ends in -NOTFOUND. */
-bool cmIsNOTFOUND(cm::string_view val);
+inline bool cmIsNOTFOUND(cm::string_view val)
+{
+ return cmProp::IsNOTFOUND(val);
+}
+inline bool cmIsNOTFOUND(cmProp val)
+{
+ return val.IsNOTFOUND();
+}
/**
* Does a string indicate a true or ON value? This is not the same as ifdef.
*/
-bool cmIsOn(cm::string_view val);
+inline bool cmIsOn(cm::string_view val)
+{
+ return cmProp::IsOn(val);
+}
inline bool cmIsOn(const char* val)
{
- return val && cmIsOn(cm::string_view(val));
+ return cmProp::IsOn(val);
}
-inline bool cmIsOn(std::string const* val)
+inline bool cmIsOn(cmProp val)
{
- return val && cmIsOn(*val);
+ return val.IsOn();
}
/**
@@ -250,14 +265,17 @@ inline bool cmIsOn(std::string const* val)
* IsON and IsOff both returning false. Note that the special path
* NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
*/
-bool cmIsOff(cm::string_view val);
+inline bool cmIsOff(cm::string_view val)
+{
+ return cmProp::IsOff(val);
+}
inline bool cmIsOff(const char* val)
{
- return !val || cmIsOff(cm::string_view(val));
+ return cmProp::IsOff(val);
}
-inline bool cmIsOff(std::string const* val)
+inline bool cmIsOff(cmProp val)
{
- return !val || cmIsOff(*val);
+ return val.IsOff();
}
/** Returns true if string @a str starts with the character @a prefix. */
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index f082ae8..54fe7a1 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -1629,7 +1629,10 @@ bool cmSystemTools::CreateTar(const std::string& outFileName,
cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format,
compressionLevel);
- a.Open();
+ if (!a.Open()) {
+ cmSystemTools::Error(a.GetError());
+ return false;
+ }
a.SetMTime(mtime);
a.SetVerbose(verbose);
bool tarCreatedSuccessfully = true;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 70e11a2..656afc6 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -157,7 +157,7 @@ cmProp cmTargetPropertyComputer::GetSources<cmTarget>(
}
static std::string srcs;
srcs = ss.str();
- return &srcs;
+ return cmProp(srcs);
}
class cmTargetInternals
@@ -1749,7 +1749,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
if (propertyIter == this->impl->LanguageStandardProperties.end()) {
return nullptr;
}
- return &(propertyIter->second.Value);
+ return cmProp(propertyIter->second.Value);
}
if (prop == propLINK_LIBRARIES) {
if (this->impl->LinkImplementationPropertyEntries.empty()) {
@@ -1758,11 +1758,11 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->LinkImplementationPropertyEntries, ";");
- return &output;
+ return cmProp(output);
}
// the type property returns what type the target is
if (prop == propTYPE) {
- return &cmState::GetTargetTypeName(this->GetType());
+ return cmProp(cmState::GetTargetTypeName(this->GetType()));
}
if (prop == propINCLUDE_DIRECTORIES) {
if (this->impl->IncludeDirectoriesEntries.empty()) {
@@ -1771,7 +1771,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->IncludeDirectoriesEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propCOMPILE_FEATURES) {
if (this->impl->CompileFeaturesEntries.empty()) {
@@ -1780,7 +1780,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->CompileFeaturesEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propCOMPILE_OPTIONS) {
if (this->impl->CompileOptionsEntries.empty()) {
@@ -1789,7 +1789,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->CompileOptionsEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propCOMPILE_DEFINITIONS) {
if (this->impl->CompileDefinitionsEntries.empty()) {
@@ -1798,7 +1798,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->CompileDefinitionsEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propLINK_OPTIONS) {
if (this->impl->LinkOptionsEntries.empty()) {
@@ -1807,7 +1807,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->LinkOptionsEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propLINK_DIRECTORIES) {
if (this->impl->LinkDirectoriesEntries.empty()) {
@@ -1817,7 +1817,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->LinkDirectoriesEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
if (this->impl->Utilities.empty()) {
@@ -1834,7 +1834,7 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
return item.Value.first;
});
output = cmJoin(utilities, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propPRECOMPILE_HEADERS) {
if (this->impl->PrecompileHeadersEntries.empty()) {
@@ -1843,26 +1843,27 @@ cmProp cmTarget::GetProperty(const std::string& prop) const
static std::string output;
output = cmJoin(this->impl->PrecompileHeadersEntries, ";");
- return &output;
+ return cmProp(output);
}
if (prop == propIMPORTED) {
- return this->IsImported() ? &propTRUE : &propFALSE;
+ return this->IsImported() ? cmProp(propTRUE) : cmProp(propFALSE);
}
if (prop == propIMPORTED_GLOBAL) {
- return this->IsImportedGloballyVisible() ? &propTRUE : &propFALSE;
+ return this->IsImportedGloballyVisible() ? cmProp(propTRUE)
+ : cmProp(propFALSE);
}
if (prop == propNAME) {
- return &this->GetName();
+ return cmProp(this->GetName());
}
if (prop == propBINARY_DIR) {
- return &this->impl->Makefile->GetStateSnapshot()
- .GetDirectory()
- .GetCurrentBinary();
+ return cmProp(this->impl->Makefile->GetStateSnapshot()
+ .GetDirectory()
+ .GetCurrentBinary());
}
if (prop == propSOURCE_DIR) {
- return &this->impl->Makefile->GetStateSnapshot()
- .GetDirectory()
- .GetCurrentSource();
+ return cmProp(this->impl->Makefile->GetStateSnapshot()
+ .GetDirectory()
+ .GetCurrentSource());
}
}
diff --git a/Source/cmTargetPropertyComputer.h b/Source/cmTargetPropertyComputer.h
index f2be318..a749b53 100644
--- a/Source/cmTargetPropertyComputer.h
+++ b/Source/cmTargetPropertyComputer.h
@@ -65,7 +65,7 @@ private:
context)) {
return nullptr;
}
- return &ComputeLocationForBuild(tgt);
+ return cmProp(ComputeLocationForBuild(tgt));
}
// Support "LOCATION_<CONFIG>".
@@ -76,7 +76,7 @@ private:
return nullptr;
}
std::string configName = prop.substr(9);
- return &ComputeLocation(tgt, configName);
+ return cmProp(ComputeLocation(tgt, configName));
}
// Support "<CONFIG>_LOCATION".
@@ -89,7 +89,7 @@ private:
context)) {
return nullptr;
}
- return &ComputeLocation(tgt, configName);
+ return cmProp(ComputeLocation(tgt, configName));
}
}
}
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index a26bef3..5bc10c2 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -32,7 +32,7 @@ void cmTest::SetCommand(std::vector<std::string> const& command)
this->Command = command;
}
-const char* cmTest::GetProperty(const std::string& prop) const
+cmProp cmTest::GetProperty(const std::string& prop) const
{
cmProp retVal = this->Properties.GetPropertyValue(prop);
if (!retVal) {
@@ -40,12 +40,12 @@ const char* cmTest::GetProperty(const std::string& prop) const
this->Makefile->GetState()->IsPropertyChained(prop, cmProperty::TEST);
if (chain) {
if (cmProp p = this->Makefile->GetProperty(prop, chain)) {
- return p->c_str();
+ return p;
}
}
return nullptr;
}
- return retVal->c_str();
+ return retVal;
}
bool cmTest::GetPropertyAsBool(const std::string& prop) const
diff --git a/Source/cmTest.h b/Source/cmTest.h
index f33b7e2..63e5e87 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -8,6 +8,7 @@
#include <vector>
#include "cmListFileCache.h"
+#include "cmProperty.h"
#include "cmPropertyMap.h"
class cmMakefile;
@@ -36,7 +37,7 @@ public:
void SetProperty(const std::string& prop, const char* value);
void AppendProperty(const std::string& prop, const std::string& value,
bool asString = false);
- const char* GetProperty(const std::string& prop) const;
+ cmProp GetProperty(const std::string& prop) const;
bool GetPropertyAsBool(const std::string& prop) const;
cmPropertyMap& GetProperties() { return this->Properties; }
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx
index 056696d..cfea4cf 100644
--- a/Source/cmTimestamp.cxx
+++ b/Source/cmTimestamp.cxx
@@ -18,6 +18,10 @@
#include <cstring>
#include <sstream>
+#ifdef __MINGW32__
+# include <libloaderapi.h>
+#endif
+
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -159,6 +163,7 @@ std::string cmTimestamp::AddTimestampComponent(char flag,
case 'M':
case 'S':
case 'U':
+ case 'V':
case 'w':
case 'y':
case 'Y':
@@ -187,6 +192,24 @@ std::string cmTimestamp::AddTimestampComponent(char flag,
}
}
+#ifdef __MINGW32__
+ /* See a bug in MinGW: https://sourceforge.net/p/mingw-w64/bugs/793/. A work
+ * around is to try to use strftime() from ucrtbase.dll. */
+ using T = size_t(WINAPI*)(char*, size_t, const char*, const struct tm*);
+ auto loadStrftime = [] {
+ auto handle =
+ LoadLibraryExA("ucrtbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (handle) {
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
+ return reinterpret_cast<T>(GetProcAddress(handle, "strftime"));
+# pragma GCC diagnostic pop
+ }
+ return strftime;
+ };
+ static T strftime = loadStrftime();
+#endif
+
char buffer[16];
size_t size =
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 82880a9..03d1bbb 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -490,10 +490,7 @@ void cmVisualStudio10TargetGenerator::Generate()
e1.Element("Platform", this->Platform);
cmProp projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL");
- if (!projLabel) {
- projLabel = &this->Name;
- }
- e1.Element("ProjectName", *projLabel);
+ e1.Element("ProjectName", projLabel ? projLabel : this->Name);
{
cmProp targetFramework =
this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK");
@@ -4091,6 +4088,9 @@ void cmVisualStudio10TargetGenerator::WriteMidlOptions(
if (this->ProjectType == csproj) {
return;
}
+ if (this->GeneratorTarget->GetType() > cmStateEnums::UTILITY) {
+ return;
+ }
// This processes *any* of the .idl files specified in the project's file
// list (and passed as the item metadata %(Filename) expressing the rule
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index 327c1c7..b8297ce 100644
--- a/Source/cmWhileCommand.cxx
+++ b/Source/cmWhileCommand.cxx
@@ -16,13 +16,14 @@
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmOutputConverter.h"
#include "cmSystemTools.h"
#include "cmake.h"
class cmWhileFunctionBlocker : public cmFunctionBlocker
{
public:
- cmWhileFunctionBlocker(cmMakefile* mf);
+ cmWhileFunctionBlocker(cmMakefile* mf, std::vector<cmListFileArgument> args);
~cmWhileFunctionBlocker() override;
cm::string_view StartCommandName() const override { return "while"_s; }
@@ -34,14 +35,15 @@ public:
bool Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus) override;
- std::vector<cmListFileArgument> Args;
-
private:
cmMakefile* Makefile;
+ std::vector<cmListFileArgument> Args;
};
-cmWhileFunctionBlocker::cmWhileFunctionBlocker(cmMakefile* mf)
- : Makefile(mf)
+cmWhileFunctionBlocker::cmWhileFunctionBlocker(
+ cmMakefile* const mf, std::vector<cmListFileArgument> args)
+ : Makefile{ mf }
+ , Args{ std::move(args) }
{
this->Makefile->PushLoopBlock();
}
@@ -60,39 +62,29 @@ bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus)
{
- cmMakefile& mf = inStatus.GetMakefile();
- std::string errorString;
-
- std::vector<cmExpandedCommandArgument> expandedArguments;
- mf.ExpandArguments(this->Args, expandedArguments);
- MessageType messageType;
+ auto& mf = inStatus.GetMakefile();
cmListFileBacktrace whileBT =
mf.GetBacktrace().Push(this->GetStartingContext());
- cmConditionEvaluator conditionEvaluator(mf, whileBT);
-
- bool isTrue =
- conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
-
- while (isTrue) {
- if (!errorString.empty()) {
- std::string err = "had incorrect arguments: ";
- for (cmListFileArgument const& arg : this->Args) {
- err += (arg.Delim ? "\"" : "");
- err += arg.Value;
- err += (arg.Delim ? "\"" : "");
- err += " ";
- }
- err += "(";
- err += errorString;
- err += ").";
- mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
- if (messageType == MessageType::FATAL_ERROR) {
- cmSystemTools::SetFatalErrorOccured();
- return true;
- }
- }
+ std::vector<cmExpandedCommandArgument> expandedArguments;
+ // At least same size expected for `expandedArguments` as `Args`
+ expandedArguments.reserve(this->Args.size());
+
+ auto expandArgs = [&mf](std::vector<cmListFileArgument> const& args,
+ std::vector<cmExpandedCommandArgument>& out)
+ -> std::vector<cmExpandedCommandArgument>& {
+ out.clear();
+ mf.ExpandArguments(args, out);
+ return out;
+ };
+
+ std::string errorString;
+ MessageType messageType;
+
+ for (cmConditionEvaluator conditionEvaluator(mf, whileBT);
+ conditionEvaluator.IsTrue(expandArgs(this->Args, expandedArguments),
+ errorString, messageType);) {
// Invoke all the functions that were collected in the block.
for (cmListFileFunction const& fn : functions) {
cmExecutionStatus status(mf);
@@ -111,11 +103,22 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
return true;
}
}
- expandedArguments.clear();
- mf.ExpandArguments(this->Args, expandedArguments);
- isTrue =
- conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
}
+
+ if (!errorString.empty()) {
+ std::string err = "had incorrect arguments:\n ";
+ for (auto const& i : expandedArguments) {
+ err += " ";
+ err += cmOutputConverter::EscapeForCMake(i.GetValue());
+ }
+ err += "\n";
+ err += errorString;
+ mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
+ if (messageType == MessageType::FATAL_ERROR) {
+ cmSystemTools::SetFatalErrorOccured();
+ }
+ }
+
return true;
}
@@ -128,11 +131,9 @@ bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
}
// create a function blocker
- {
- cmMakefile& makefile = status.GetMakefile();
- auto fb = cm::make_unique<cmWhileFunctionBlocker>(&makefile);
- fb->Args = args;
- makefile.AddFunctionBlocker(std::move(fb));
- }
+ auto& makefile = status.GetMakefile();
+ makefile.AddFunctionBlocker(
+ cm::make_unique<cmWhileFunctionBlocker>(&makefile, args));
+
return true;
}
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index ab8309d..7f8f654 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -114,7 +114,9 @@
# include "cmExtraSublimeTextGenerator.h"
#endif
-#if defined(__linux__) || defined(_WIN32)
+// NOTE: the __linux__ macro is predefined on Android host too, but
+// main CMakeLists.txt filters out this generator by host name.
+#if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
# include "cmGlobalGhsMultiGenerator.h"
#endif
@@ -2528,7 +2530,7 @@ void cmake::AddDefaultGenerators()
this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
#endif
#if !defined(CMAKE_BOOTSTRAP)
-# if defined(__linux__) || defined(_WIN32)
+# if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
# endif
this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
diff --git a/Templates/MSBuild/FlagTables/v142_CL.json b/Templates/MSBuild/FlagTables/v142_CL.json
index 64eeac4..4c65ad9 100644
--- a/Templates/MSBuild/FlagTables/v142_CL.json
+++ b/Templates/MSBuild/FlagTables/v142_CL.json
@@ -1214,7 +1214,7 @@
"comment": "Additional Module Dependencies",
"value": "",
"flags": [
- "UserValue",
+ "UserFollowing",
"SemicolonAppendable"
]
},
@@ -1224,7 +1224,7 @@
"comment": "Additional Header Unit Dependencies",
"value": "",
"flags": [
- "UserValue",
+ "UserFollowing",
"SemicolonAppendable"
]
},
diff --git a/Templates/MSBuild/FlagTables/v143_CL.json b/Templates/MSBuild/FlagTables/v143_CL.json
index 8b700aa..96f74b1 100644
--- a/Templates/MSBuild/FlagTables/v143_CL.json
+++ b/Templates/MSBuild/FlagTables/v143_CL.json
@@ -1214,7 +1214,7 @@
"comment": "Additional Module Dependencies",
"value": "",
"flags": [
- "UserValue",
+ "UserFollowing",
"SemicolonAppendable"
]
},
@@ -1224,7 +1224,7 @@
"comment": "Additional Header Unit Dependencies",
"value": "",
"flags": [
- "UserValue",
+ "UserFollowing",
"SemicolonAppendable"
]
},
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 388ff20..9dab5b9 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -39,6 +39,13 @@ set(ENV{HOME} \"${TEST_HOME}\")
")
endif()
+# Suppress generator deprecation warnings in test suite.
+if(CMAKE_GENERATOR MATCHES "^Visual Studio 10 2010")
+ set(TEST_WARN_VS10_CODE "set(ENV{CMAKE_WARN_VS10} OFF)")
+else()
+ set(TEST_WARN_VS10_CODE "")
+endif()
+
# 3.9 or later provides a definitive answer to whether we are multi-config
# through a global property. Prior to 3.9, CMAKE_CONFIGURATION_TYPES being set
# is assumed to mean multi-config, but developers might modify it so it is
@@ -1641,6 +1648,54 @@ if(BUILD_TESTING)
WORKING_DIRECTORY ${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate
DEPENDS ExternalProjectUpdateSetup )
+ execute_process(
+ COMMAND ${CMAKE_CMAKE_COMMAND}
+ "-E" create_symlink
+ "${CMake_SOURCE_DIR}/Tests/CMakeLists.txt" # random source file that exists
+ "${CMake_BINARY_DIR}/Tests/try_to_create_symlink" # random target file in existing directory
+ RESULT_VARIABLE _failed
+ )
+ if(_failed)
+ message("Failed to create a simple symlink on this machine. Skipping InstallMode tests.")
+ else()
+ function(add_installmode_test _mode)
+ set(ENV{CMAKE_INSTALL_MODE} _mode)
+ set(_maybe_InstallMode_CTEST_OPTIONS)
+ set(_maybe_BUILD_OPTIONS)
+ if(_isMultiConfig)
+ set(_maybe_CTEST_OPTIONS -C $<CONFIGURATION>)
+ else()
+ set(_maybe_BUILD_OPTIONS "-DCMAKE_BUILD_TYPE=$<CONFIGURATION>")
+ endif()
+ add_test(
+ NAME "InstallMode-${_mode}"
+ COMMAND
+ ${CMAKE_CTEST_COMMAND} -V ${_maybe_CTEST_OPTIONS}
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/InstallMode"
+ "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}"
+ ${build_generator_args}
+ --build-project superpro
+ --build-exe-dir "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}"
+ --force-new-ctest-process
+ --build-options
+ ${_maybe_BUILD_OPTIONS}
+ "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}/install"
+ )
+ list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}")
+ unset(ENV{CMAKE_INSTALL_MODE})
+ endfunction()
+
+ add_installmode_test(COPY)
+ add_installmode_test(REL_SYMLINK)
+ add_installmode_test(REL_SYMLINK_OR_COPY)
+ add_installmode_test(ABS_SYMLINK)
+ add_installmode_test(ABS_SYMLINK_OR_COPY)
+ add_installmode_test(SYMLINK)
+ add_installmode_test(SYMLINK_OR_COPY)
+ endif()
+
+
# do each of the tutorial steps
function(add_tutorial_test step_name use_mymath tutorial_arg pass_regex)
set(tutorial_test_name Tutorial${step_name})
diff --git a/Tests/CPackNSISGenerator/CMakeLists.txt b/Tests/CPackNSISGenerator/CMakeLists.txt
index 64a8ef6..5d6320b 100644
--- a/Tests/CPackNSISGenerator/CMakeLists.txt
+++ b/Tests/CPackNSISGenerator/CMakeLists.txt
@@ -19,5 +19,6 @@ set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
set(CPACK_NSIS_MANIFEST_DPI_AWARE ON)
set(CPACK_NSIS_BRANDING_TEXT "CMake branding text")
set(CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION "RIGHT")
+set(CPACK_NSIS_IGNORE_LICENSE_PAGE ON)
include(CPack)
diff --git a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
index 8bfcf26..31a2560 100644
--- a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
+++ b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
@@ -60,3 +60,12 @@ if("${output_index}" EQUAL "-1")
else()
message(STATUS "Found BrandingText")
endif()
+
+# license page should not be present
+file(STRINGS "${project_file}" line REGEX "!insertmacro MUI_PAGE_LICENSE")
+string(FIND "${line}" "MUI_PAGE_LICENSE" output_index)
+if("${output_index}" EQUAL "-1")
+ message(STATUS "License not found in the project")
+else()
+ message(FATAL_ERROR "License found in the project")
+endif()
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index fdb7a6e..a3fb409 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -15,7 +15,7 @@ add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang)
add_cuda_test_macro(CudaOnly.WithDefs CudaOnlyWithDefs)
add_cuda_test_macro(CudaOnly.CircularLinkLine CudaOnlyCircularLinkLine)
add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols)
-add_cuda_test_macro(CudaOnly.SeparateCompilation CudaOnlySeparateCompilation)
+add_cuda_test_macro(CudaOnly.SeparateCompilation main/CudaOnlySeparateCompilation)
if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang")
# Clang doesn't have flags for selecting the runtime.
diff --git a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
index 864ecbf..17069e3 100644
--- a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
+++ b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt
@@ -34,26 +34,9 @@ add_library(CUDASeparateLibB STATIC file4.cu file5.cu)
target_compile_features(CUDASeparateLibB PRIVATE cuda_std_11)
target_link_libraries(CUDASeparateLibB PRIVATE CUDASeparateLibA)
-add_executable(CudaOnlySeparateCompilation main.cu)
-target_link_libraries(CudaOnlySeparateCompilation
- PRIVATE CUDASeparateLibB)
-set_target_properties(CudaOnlySeparateCompilation PROPERTIES CUDA_STANDARD 11)
-set_target_properties(CudaOnlySeparateCompilation PROPERTIES CUDA_STANDARD_REQUIRED TRUE)
-
set_target_properties(CUDASeparateLibA
CUDASeparateLibB
PROPERTIES CUDA_SEPARABLE_COMPILATION ON
POSITION_INDEPENDENT_CODE ON)
-if (CMAKE_GENERATOR MATCHES "^Visual Studio")
- #Visual Studio CUDA integration will not perform device linking
- #on a target that itself does not have GenerateRelocatableDeviceCode
- #enabled.
- set_target_properties(CudaOnlySeparateCompilation
- PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
-endif()
-
-if(APPLE)
- # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
- set_property(TARGET CudaOnlySeparateCompilation PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
-endif()
+add_subdirectory(main)
diff --git a/Tests/CudaOnly/SeparateCompilation/main/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilation/main/CMakeLists.txt
new file mode 100644
index 0000000..c181078
--- /dev/null
+++ b/Tests/CudaOnly/SeparateCompilation/main/CMakeLists.txt
@@ -0,0 +1,18 @@
+add_executable(CudaOnlySeparateCompilation main.cu)
+target_link_libraries(CudaOnlySeparateCompilation PRIVATE CUDASeparateLibB)
+set_target_properties(CudaOnlySeparateCompilation PROPERTIES
+ CUDA_STANDARD 11
+ CUDA_STANDARD_REQUIRED TRUE
+)
+
+if(CMAKE_GENERATOR MATCHES "^Visual Studio")
+ # Visual Studio CUDA integration will not perform device linking
+ # on a target that itself does not have GenerateRelocatableDeviceCode
+ # enabled.
+ set_property(TARGET CudaOnlySeparateCompilation PROPERTY CUDA_SEPARABLE_COMPILATION ON)
+endif()
+
+if(APPLE)
+ # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
+ set_property(TARGET CudaOnlySeparateCompilation PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
+endif()
diff --git a/Tests/CudaOnly/SeparateCompilation/main.cu b/Tests/CudaOnly/SeparateCompilation/main/main.cu
index 40dbe5d..2b6e8f4 100644
--- a/Tests/CudaOnly/SeparateCompilation/main.cu
+++ b/Tests/CudaOnly/SeparateCompilation/main/main.cu
@@ -1,8 +1,8 @@
#include <iostream>
-#include "file1.h"
-#include "file2.h"
+#include "../file1.h"
+#include "../file2.h"
int file4_launch_kernel(int x);
int file5_launch_kernel(int x);
diff --git a/Tests/EnforceConfig.cmake.in b/Tests/EnforceConfig.cmake.in
index 7781ded..7c6f76a 100644
--- a/Tests/EnforceConfig.cmake.in
+++ b/Tests/EnforceConfig.cmake.in
@@ -36,3 +36,4 @@ unset(ENV{CMAKE_GENERATOR_TOOLSET})
unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS})
@TEST_HOME_ENV_CODE@
+@TEST_WARN_VS10_CODE@
diff --git a/Tests/InstallMode/CMakeLists.txt b/Tests/InstallMode/CMakeLists.txt
new file mode 100644
index 0000000..96c83a0
--- /dev/null
+++ b/Tests/InstallMode/CMakeLists.txt
@@ -0,0 +1,124 @@
+cmake_minimum_required(VERSION 3.20.0)
+
+project(superpro LANGUAGES NONE)
+
+add_subdirectory(superpro)
+
+include(Subproject.cmake)
+add_subproject(static_lib DIR subpro_a_static_lib)
+add_subproject(shared_lib DIR subpro_b_shared_lib)
+add_subproject(nested_lib DIR subpro_c_nested_lib NO_INSTALL)
+add_subproject(executable DIR subpro_d_executable
+ DEPENDS
+ static_lib
+ shared_lib
+ nested_lib
+)
+
+include(CTest)
+if(BUILD_TESTING)
+ enable_language(CXX) # required by GNUInstallDirs
+ include(GNUInstallDirs)
+
+ macro(testme _name _path _symlink)
+ add_test(
+ NAME "${_name}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMAND
+ "${CMAKE_COMMAND}"
+ "-DFILE_PATH=${CMAKE_INSTALL_PREFIX}/${_path}"
+ "-DEXPECT_SYMLINK:BOOL=${_symlink}"
+ "-DEXPECT_ABSOLUTE:BOOL=${ARGN}"
+ "-P" "${CMAKE_SOURCE_DIR}/Test.cmake"
+ )
+ endmacro()
+
+ set(_mode $ENV{CMAKE_INSTALL_MODE})
+ if(NOT "${_mode}" OR "${_mode}" STREQUAL "COPY")
+ set(expect_symlink NO)
+ elseif("${_mode}" MATCHES "(REL_)?SYMLINK(_OR_COPY)?")
+ set(expect_symlink YES)
+ set(expect_absolute NO)
+ elseif("${_mode}" MATCHES "ABS_SYMLINK(_OR_COPY)?")
+ set(expect_symlink YES)
+ set(expect_absolute YES)
+ endif()
+
+ # toplevel project should respect CMAKE_INSTALL_MODE
+
+ testme(superproj_file_copy
+ "file_copy.txt" NO)
+ testme(superproj_file_copy_file
+ "file_copy_file.txt" NO)
+ testme(superproj_file_install
+ "file_install.txt"
+ ${expect_symlink}
+ ${expect_absolute})
+ testme(superproj_file_create_link_symbolic
+ "file_create_link_symbolic.txt" YES YES)
+
+ # subprojects should receive and respect CMAKE_INSTALL_MODE too
+
+ testme(subpro_a_static_lib_header
+ "${CMAKE_INSTALL_INCLUDEDIR}/static_lib.h"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+ testme(subpro_a_static_lib_libfile
+ "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_static_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+
+ testme(subpro_b_shared_lib_header
+ "${CMAKE_INSTALL_INCLUDEDIR}/shared_lib.h"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+
+ if(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG AND
+ "${CMAKE_CXX_CREATE_SHARED_MODULE}" MATCHES "SONAME_FLAG")
+ # due to semver, this is always a link
+ testme(subpro_b_shared_lib_libfile
+ "${CMAKE_INSTALL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}the_shared_lib${CMAKE_SHARED_LIBRARY_SUFFIX}"
+ YES
+ ${expect_absolute}
+ )
+ # this is the actual shared lib, so should follow CMAKE_INSTALL_MODE rules
+ testme(subpro_b_shared_lib_libfile_versuffix
+ "${CMAKE_INSTALL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}the_shared_lib${CMAKE_SHARED_LIBRARY_SUFFIX}.2.3.4"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+ endif()
+
+ testme(subpro_d_executable_exefile
+ "${CMAKE_INSTALL_BINDIR}/the_executable${CMAKE_EXECUTABLE_SUFFIX}"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+
+ # nested subprojects should receive and respect CMAKE_INSTALL_MODE too
+
+ testme(subsubpro_c1_header
+ "${CMAKE_INSTALL_INCLUDEDIR}/c1_lib.h"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+ testme(subsubpro_c1_libfile
+ "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_c1_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+
+ testme(subsubpro_c2_header
+ "${CMAKE_INSTALL_INCLUDEDIR}/c2_lib.h"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+ testme(subsubpro_c2_libfile
+ "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_c2_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+ ${expect_symlink}
+ ${expect_absolute}
+ )
+endif()
diff --git a/Tests/InstallMode/README.txt b/Tests/InstallMode/README.txt
new file mode 100644
index 0000000..a4316eb
--- /dev/null
+++ b/Tests/InstallMode/README.txt
@@ -0,0 +1,43 @@
+This is an example superbuild project to demonstrate the use of the
+CMAKE_INSTALL_MODE environment variable on.
+
+The project hierarchy is like (B = Builds / D = Link Dependency):
+
++---------------------------------------------------------------------+
+| Superbuild (Top) |
++---------------------------------------------------------------------+
+ | | | |
+ | | | |
+ (B) (B) (B) (B)
+ | | | |
+ v v v v
++---------------+ +---------------+ +---------------+ +---------------+
+| A: Static Lib | | B: Shared Lib | | C: Nested | | D: Executable |
+| Project | | Project | | Superbuild | | Project |
++---------------+ +---------------+ +---------------+ +---------------+
+ ^ ^ | | | | |
+ | | (B) (B) | | |
+ | | | | | | |
+ | | v | | | |
+ | | +----------------+ | | | |
+ | | | C1: Static Lib | | | | |
+ | | | Project | | (D) (D) (D)
+ | | +----------------+ | | | |
+ | | ^ | | | |
+ | | | v | | |
+ | | (D) +----------------+ | | |
+ | | | | C2: Static Lib |<---+ | |
+ | | +--| Project | | |
+ | | +----------------+ | |
+ | | | |
+ | +------------------------------------+ |
+ | |
+ +--------------------------------------------------------+
+
+The superbuild system is built on top of ExternalProject_Add().
+
+NOTE that the subprojects will configure, build and install
+during the build phase ('make') of the top-level project.
+There is no install target in the top-level project!
+The CMAKE_INSTALL_PREFIX is therefore populated during the build
+phase already.
diff --git a/Tests/InstallMode/Subproject.cmake b/Tests/InstallMode/Subproject.cmake
new file mode 100644
index 0000000..e4354d6
--- /dev/null
+++ b/Tests/InstallMode/Subproject.cmake
@@ -0,0 +1,76 @@
+include(ExternalProject)
+
+# add_subproject(<name> [NO_INSTALL] [DIR <dirname>] [DEPENDS [subpro_dep ...]])
+function(add_subproject _name)
+ cmake_parse_arguments(_arg "NO_INSTALL" "DIR" "DEPENDS" ${ARGN})
+
+ if(_arg_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "There are unparsed arguments")
+ endif()
+
+ set(_maybe_NO_INSTALL)
+ if(_arg_NO_INSTALL)
+ set(_maybe_NO_INSTALL "INSTALL_COMMAND")
+ else()
+ # This is a trick to get a valid call.
+ # Since we set UPDATE_COMMAND to ""
+ # explicitly below, this won't harm.
+ set(_maybe_NO_INSTALL "UPDATE_COMMAND")
+ endif()
+
+ if(CMAKE_GENERATOR MATCHES "Ninja Multi-Config")
+ # Replace list separator before passing on to ExternalProject_Add
+ string(REPLACE ";" "|" _CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}")
+ string(REPLACE ";" "|" _CROSS_CONFIGS "${CMAKE_CROSS_CONFIGS}")
+ string(REPLACE ";" "|" _DEFAULT_CONFIGS "${CMAKE_DEFAULT_CONFIGS}")
+
+ set(_maybe_NINJA_MULTICONFIG_ARGS
+ "-DCMAKE_CONFIGURATION_TYPES:STRINGS=${_CONFIGURATION_TYPES}"
+ "-DCMAKE_CROSS_CONFIGS:STRINGS=${_CROSS_CONFIGS}"
+ "-DCMAKE_DEFAULT_BUILD_TYPE:STRING=${CMAKE_DEFAULT_BUILD_TYPE}"
+ "-DCMAKE_DEFAULT_CONFIGS:STRINGS=${_DEFAULT_CONFIGS}"
+ )
+ endif()
+
+ ExternalProject_Add("${_name}"
+ DOWNLOAD_COMMAND ""
+ UPDATE_COMMAND ""
+ ${_maybe_NO_INSTALL} ""
+
+ BUILD_ALWAYS ON
+
+ LOG_DOWNLOAD OFF
+ LOG_UPDATE OFF
+ LOG_PATCH OFF
+ LOG_CONFIGURE OFF
+ LOG_BUILD OFF
+ LOG_INSTALL OFF
+
+ SOURCE_DIR "${PROJECT_SOURCE_DIR}/${_arg_DIR}"
+
+ # Private build directory per subproject
+ BINARY_DIR "${PROJECT_BINARY_DIR}/subproject/${_arg_DIR}"
+
+ # Common install directory, populated immediately
+ # during build (during build - not install - of superproject)
+ INSTALL_DIR "${CMAKE_INSTALL_PREFIX}"
+
+ DEPENDS
+ ${_arg_DEPENDS}
+
+ LIST_SEPARATOR "|"
+ CMAKE_ARGS
+ "-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>"
+
+ # We can rely on ExternalProject to pick the right
+ # generator (and architecture/toolset where applicable),
+ # however, we need to explicitly inherit other parent
+ # project's build settings.
+ "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
+ ${_maybe_NINJA_MULTICONFIG_ARGS}
+
+ # Subproject progress reports clutter up the output, disable
+ "-DCMAKE_TARGET_MESSAGES:BOOL=OFF"
+ "-DCMAKE_RULE_MESSAGES:BOOL=OFF"
+ )
+endfunction()
diff --git a/Tests/InstallMode/Test.cmake b/Tests/InstallMode/Test.cmake
new file mode 100644
index 0000000..46c8fa1
--- /dev/null
+++ b/Tests/InstallMode/Test.cmake
@@ -0,0 +1,38 @@
+message("Testing...")
+message("FILE_PATH = ${FILE_PATH}")
+message("EXPECT_SYMLINK = ${EXPECT_SYMLINK}")
+message("EXPECT_ABSOLUTE = ${EXPECT_ABSOLUTE}")
+
+if(NOT DEFINED FILE_PATH)
+ message(FATAL_ERROR "FILE_PATH variable must be defined")
+endif()
+
+if(NOT EXISTS "${FILE_PATH}")
+ message(FATAL_ERROR "File ${FILE_PATH} does not exist")
+endif()
+
+if(NOT DEFINED EXPECT_SYMLINK)
+ message(FATAL_ERROR "EXPECT_SYMLINK must be defined")
+endif()
+
+if(EXPECT_SYMLINK)
+ if(NOT DEFINED EXPECT_ABSOLUTE)
+ message(FATAL_ERROR "EXPECT_ABSOLUTE variable must be defined")
+ endif()
+
+ if(NOT IS_SYMLINK "${FILE_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} must be a symlink")
+ endif()
+
+ file(READ_SYMLINK "${FILE_PATH}" TARGET_PATH)
+
+ if(EXPECT_ABSOLUTE AND NOT IS_ABSOLUTE "${TARGET_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} must be an absolute symlink")
+ elseif(NOT EXPECT_ABSOLUTE AND IS_ABSOLUTE "${TARGET_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} must be a relative symlink")
+ endif()
+else()
+ if(IS_SYMLINK "${FILE_PATH}")
+ message(FATAL_ERROR "${FILE_PATH} must NOT be a symlink")
+ endif()
+endif()
diff --git a/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt b/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt
new file mode 100644
index 0000000..7cd32cc
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt
@@ -0,0 +1,60 @@
+# This CMakeLists.txt is part of the subproject A (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(static_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+
+add_library(the_static_lib STATIC
+ "include/static_lib.h"
+ "src/static_lib.cpp"
+)
+
+target_include_directories(the_static_lib PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+ DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+ TARGETS
+ the_static_lib
+ EXPORT main
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ "cmake/PackageConfig.cmake.in"
+ "${PROJECT_NAME}Config.cmake"
+ INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY SameMajorVersion
+)
+
+install(
+ EXPORT main
+ FILE "${PROJECT_NAME}Targets.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h b/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h
new file mode 100644
index 0000000..bd82d2e
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void static_hello();
diff --git a/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp b/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp
new file mode 100644
index 0000000..fe1cd85
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <static_lib.h>
+
+using namespace std;
+
+void static_hello()
+{
+ cout << "Hello from static_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt b/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt
new file mode 100644
index 0000000..eb118c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt
@@ -0,0 +1,66 @@
+# This CMakeLists.txt is part of the subproject B (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(shared_lib_project VERSION 2.3.4 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+
+add_library(the_shared_lib SHARED
+ "include/shared_lib.h"
+ "src/shared_lib.cpp"
+)
+
+set_target_properties(the_shared_lib
+ PROPERTIES
+ VERSION "${PROJECT_VERSION}"
+ SOVERSION "${PROJECT_VERSION}"
+)
+
+target_include_directories(the_shared_lib PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+ DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+ TARGETS
+ the_shared_lib
+ EXPORT main
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ "cmake/PackageConfig.cmake.in"
+ "${PROJECT_NAME}Config.cmake"
+ INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY SameMajorVersion
+)
+
+install(
+ EXPORT main
+ FILE "${PROJECT_NAME}Targets.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h b/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h
new file mode 100644
index 0000000..fd960db
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void shared_hello();
diff --git a/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp b/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp
new file mode 100644
index 0000000..2820d5d
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <shared_lib.h>
+
+using namespace std;
+
+void shared_hello()
+{
+ cout << "Hello from shared_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt
new file mode 100644
index 0000000..e397c02
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.20.0)
+
+project(subpro_c_nested_lib LANGUAGES NONE)
+
+include(../Subproject.cmake)
+add_subproject(c1_lib DIR subsubpro_c1_lib)
+add_subproject(c2_lib DIR subsubpro_c2_lib
+ DEPENDS
+ c1_lib
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt
new file mode 100644
index 0000000..89f3755
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt
@@ -0,0 +1,61 @@
+# This CMakeLists.txt is a nested subproject of the
+# subproject C (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(c1_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+
+add_library(the_c1_lib STATIC
+ "include/c1_lib.h"
+ "src/c1_lib.cpp"
+)
+
+target_include_directories(the_c1_lib PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+ DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+ TARGETS
+ the_c1_lib
+ EXPORT main
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ "cmake/PackageConfig.cmake.in"
+ "${PROJECT_NAME}Config.cmake"
+ INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY SameMajorVersion
+)
+
+install(
+ EXPORT main
+ FILE "${PROJECT_NAME}Targets.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h
new file mode 100644
index 0000000..245f9d4
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void c1_hello();
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp
new file mode 100644
index 0000000..c405056
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <c1_lib.h>
+
+using namespace std;
+
+void c1_hello()
+{
+ cout << "Hello from c1_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt
new file mode 100644
index 0000000..e139446
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt
@@ -0,0 +1,68 @@
+# This CMakeLists.txt is a nested subproject of the
+# subproject C (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(c2_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+find_package(c1_lib_project REQUIRED)
+
+include(GNUInstallDirs)
+
+add_library(the_c2_lib STATIC
+ "include/c2_lib.h"
+ "src/c2_lib.cpp"
+)
+
+target_link_libraries(the_c2_lib
+ PUBLIC
+ the_c1_lib
+)
+
+target_include_directories(the_c2_lib PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+ DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+ TARGETS
+ the_c2_lib
+ EXPORT main
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+ "cmake/PackageConfig.cmake.in"
+ "${PROJECT_NAME}Config.cmake"
+ INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+ PATH_VARS
+ CMAKE_INSTALL_INCLUDEDIR
+ CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+ VERSION "${PROJECT_VERSION}"
+ COMPATIBILITY SameMajorVersion
+)
+
+install(
+ EXPORT main
+ FILE "${PROJECT_NAME}Targets.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+ DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..45a177a
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,11 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
+
+include(CMakeFindDependencyMacro)
+find_dependency(c1_lib_project REQUIRED)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h
new file mode 100644
index 0000000..5056814
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void c2_hello();
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp
new file mode 100644
index 0000000..cd2c932
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp
@@ -0,0 +1,12 @@
+#include <iostream>
+
+#include <c1_lib.h>
+#include <c2_lib.h>
+
+using namespace std;
+
+void c2_hello()
+{
+ cout << "Hello from c2_lib and also..." << endl;
+ c1_hello();
+}
diff --git a/Tests/InstallMode/subpro_d_executable/CMakeLists.txt b/Tests/InstallMode/subpro_d_executable/CMakeLists.txt
new file mode 100644
index 0000000..9847227
--- /dev/null
+++ b/Tests/InstallMode/subpro_d_executable/CMakeLists.txt
@@ -0,0 +1,24 @@
+# This CMakeLists.txt is part of the subproject B (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(subpro_d_executable LANGUAGES CXX)
+
+find_package(static_lib_project REQUIRED)
+find_package(shared_lib_project REQUIRED)
+find_package(c2_lib_project REQUIRED)
+
+add_executable(the_executable
+ "src/main.cpp"
+)
+
+target_link_libraries(the_executable PRIVATE
+ the_static_lib
+ the_shared_lib
+ the_c2_lib
+)
+
+install(
+ TARGETS
+ the_executable
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
diff --git a/Tests/InstallMode/subpro_d_executable/src/main.cpp b/Tests/InstallMode/subpro_d_executable/src/main.cpp
new file mode 100644
index 0000000..ec12004
--- /dev/null
+++ b/Tests/InstallMode/subpro_d_executable/src/main.cpp
@@ -0,0 +1,13 @@
+#include <cstdlib>
+
+#include <c2_lib.h>
+#include <shared_lib.h>
+#include <static_lib.h>
+
+int main()
+{
+ static_hello();
+ shared_hello();
+ c2_hello();
+ return EXIT_SUCCESS;
+}
diff --git a/Tests/InstallMode/superpro/CMakeLists.txt b/Tests/InstallMode/superpro/CMakeLists.txt
new file mode 100644
index 0000000..ae4d25c
--- /dev/null
+++ b/Tests/InstallMode/superpro/CMakeLists.txt
@@ -0,0 +1,29 @@
+# This CMakeLists.txt is part of the superproject (add_subdirectory).
+
+# Below file transfers are executed at configuration time!
+
+file(
+ COPY
+ "file_copy.txt"
+ DESTINATION
+ "${CMAKE_INSTALL_PREFIX}"
+)
+
+file(COPY_FILE
+ "${CMAKE_CURRENT_SOURCE_DIR}/file_copy_file.txt"
+ "${CMAKE_INSTALL_PREFIX}/file_copy_file.txt"
+)
+
+file(
+ INSTALL
+ "file_install.txt"
+ DESTINATION
+ "${CMAKE_INSTALL_PREFIX}"
+)
+
+file(
+ CREATE_LINK
+ "${CMAKE_CURRENT_SOURCE_DIR}/file_create_link_symbolic.txt"
+ "${CMAKE_INSTALL_PREFIX}/file_create_link_symbolic.txt"
+ SYMBOLIC
+)
diff --git a/Tests/InstallMode/superpro/file_copy.txt b/Tests/InstallMode/superpro/file_copy.txt
new file mode 100644
index 0000000..aacbb96
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_copy.txt
@@ -0,0 +1 @@
+This file should always be copied into CMAKE_INSTALL_PREFIX.
diff --git a/Tests/InstallMode/superpro/file_copy_file.txt b/Tests/InstallMode/superpro/file_copy_file.txt
new file mode 100644
index 0000000..aacbb96
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_copy_file.txt
@@ -0,0 +1 @@
+This file should always be copied into CMAKE_INSTALL_PREFIX.
diff --git a/Tests/InstallMode/superpro/file_create_link_symbolic.txt b/Tests/InstallMode/superpro/file_create_link_symbolic.txt
new file mode 100644
index 0000000..16a481b
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_create_link_symbolic.txt
@@ -0,0 +1,2 @@
+This file should always be installed into CMAKE_INSTALL_PREFIX
+as a symbolic link to the original file.
diff --git a/Tests/InstallMode/superpro/file_install.txt b/Tests/InstallMode/superpro/file_install.txt
new file mode 100644
index 0000000..eac9782
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_install.txt
@@ -0,0 +1,6 @@
+This file should be placed in CMAKE_INSTALL_PREFIX
+as a copy if the CMAKE_INSTALL_MODE environment variable
+is unset or equals "COPY".
+If the variable's value is "SYMLINK" or "SYMLINK_OR_COPY",
+the CMAKE_INSTALL_PREFIX should rather receive a symbolic
+link to this file.
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
index a9ccece..9b114e9 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
@@ -26,13 +26,12 @@ macro(rebuild buildName)
endmacro()
configure_file("${testProjectTemplateDir}/mocwidget.h" "${testProjectSrc}/mocwidget.h" COPYONLY)
+configure_file("${testProjectTemplateDir}/mainwindow.h" "${testProjectSrc}/mainwindow.h" COPYONLY)
configure_file("${testProjectTemplateDir}/main.cpp" "${testProjectSrc}/main.cpp" COPYONLY)
-configure_file("${testProjectTemplateDir}/subdir/subdircheck.cpp" "${testProjectSrc}/subdir/subdircheck.cpp" COPYONLY)
configure_file("${testProjectTemplateDir}/CMakeLists.txt.in" "${testProjectSrc}/CMakeLists.txt" @ONLY)
set(Num 1)
configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY)
-configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY)
if(CMAKE_GENERATOR_INSTANCE)
set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}")
@@ -96,10 +95,21 @@ sleep()
set(Num 2)
configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY)
-configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY)
rebuild(2)
execute_process(COMMAND "${testProjectBinDir}/${extra_bin_path}UicOnFileChange" RESULT_VARIABLE result)
if(NOT result EQUAL "0")
message(FATAL_ERROR "Rebuild of UicOnFileChange test result is: ${result}")
endif()
+
+# Check if the generated ui_mainwindow.h rules introduce circular dependency between the generated
+# ui_mainwinow.h and timestamp.
+#
+# The first rebuild updates a timestamp dependency file after "touching" mainwindow.h.
+sleep()
+execute_process(COMMAND ${CMAKE_COMMAND} -E touch "${testProjectSrc}/mainwindow.h")
+rebuild(3)
+
+# The second rebuild detects if cycling dependency is introduced by deps file.
+sleep()
+rebuild(4)
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
index 2a1998d..c787db1 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
@@ -5,9 +5,12 @@ include("@CMAKE_CURRENT_LIST_DIR@/../AutogenGuiTest.cmake")
# Enable CMAKE_AUTOUIC for all targets
set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTOMOC ON)
-add_executable(UicOnFileChange main.cpp mainwindow.ui
- subdir/subdircheck.cpp subdir/mainwindowsubdir.ui
+add_executable(UicOnFileChange main.cpp mainwindow.ui mainwindow.h
+)
+target_include_directories(UicOnFileChange PRIVATE
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${CMAKE_CURRENT_BINARY_DIR}/UicOnFileChange_autogen/include"
)
-target_include_directories(UicOnFileChange PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(UicOnFileChange ${QT_QTCORE_TARGET} ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
index 3981268..4aab2ad 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp
@@ -1,11 +1,10 @@
+#include "mainwindow.h"
#include "ui_mainwindow.h"
-extern bool subdircheck();
-
int main(int argc, char* argv[])
{
MocWidget mw;
Ui::Widget mwUi;
mwUi.setupUi(&mw);
- return mw.objectName() == "Widget2" && subdircheck() ? 0 : 1;
+ return mw.objectName() == "Widget2" ? 0 : 1;
}
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/mainwindow.h b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/mainwindow.h
new file mode 100644
index 0000000..24621ff
--- /dev/null
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/mainwindow.h
@@ -0,0 +1,13 @@
+#include <QObject>
+
+#include "ui_mainwindow.h"
+
+class MainWindow : public QObject
+{
+ Q_OBJECT
+public:
+ MainWindow() { mwUi.setupUi(&mw); }
+
+ MocWidget mw;
+ Ui::Widget mwUi;
+};
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in
deleted file mode 100644
index a6a31f6..0000000
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>WidgetSubdir</class>
- <widget class="MocWidget" name="WidgetSubdir@Num@"/>
- <resources/>
- <connections/>
-</ui>
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp
deleted file mode 100644
index 3b36a10..0000000
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include "ui_mainwindowsubdir.h"
-
-bool subdircheck()
-{
- MocWidget mw;
- Ui::WidgetSubdir mwUi;
- mwUi.setupUi(&mw);
- return mw.objectName() == "WidgetSubdir2";
-}
diff --git a/Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake b/Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake
new file mode 100644
index 0000000..e13efb3
--- /dev/null
+++ b/Tests/RunCMake/BuildDepends/CustomCommandDependencies-compiler-deps-legacy.cmake
@@ -0,0 +1,9 @@
+enable_language(C)
+
+add_custom_command(OUTPUT main.c
+ DEPFILE main.c.d
+ COMMAND "${CMAKE_COMMAND}" -DINFILE=${CMAKE_CURRENT_SOURCE_DIR}/main.c -DOUTFILE=main.c -DDEPFILE=main.c.d
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/GenerateDepFile.cmake"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+
+add_executable(main ${CMAKE_CURRENT_BINARY_DIR}/main.c)
diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
index f8c20c2..27bbff6 100644
--- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
+++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
@@ -170,6 +170,10 @@ endif()
if (RunCMake_GENERATOR MATCHES "Makefiles")
run_cmake(CustomCommandDependencies-BadArgs)
+ run_cmake_with_options(CustomCommandDependencies-compiler-deps-legacy -DCMAKE_DEPENDS_USE_COMPILER=FALSE)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ run_cmake_command(CustomCommandDependencies-compiler-deps-legacy ${CMAKE_COMMAND} --build . --config Debug)
+ unset(RunCMake_TEST_NO_CLEAN)
endif()
if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode" AND
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index 31bd8a4..c31a645 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -260,6 +260,7 @@ unset(CMakePresets_FILE)
run_cmake_presets(GoodUserOnly)
run_cmake_presets(GoodUserFromMain)
run_cmake_presets(GoodUserFromUser)
+run_cmake_presets(V2InheritV3Optional)
# Test CMakeUserPresets.json errors
run_cmake_presets(UserDuplicateInUser)
diff --git a/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake b/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/V2InheritV3Optional.cmake
diff --git a/Tests/RunCMake/CMakePresets/V2InheritV3Optional.json.in b/Tests/RunCMake/CMakePresets/V2InheritV3Optional.json.in
new file mode 100644
index 0000000..957b157
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/V2InheritV3Optional.json.in
@@ -0,0 +1,8 @@
+{
+ "version": 3,
+ "configurePresets": [
+ {
+ "name": "default"
+ }
+ ]
+}
diff --git a/Tests/RunCMake/CMakePresets/V2InheritV3OptionalUser.json.in b/Tests/RunCMake/CMakePresets/V2InheritV3OptionalUser.json.in
new file mode 100644
index 0000000..f18c1b9
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/V2InheritV3OptionalUser.json.in
@@ -0,0 +1,11 @@
+{
+ "version": 2,
+ "configurePresets": [
+ {
+ "name": "V2InheritV3Optional",
+ "inherits": "default",
+ "generator": "@RunCMake_GENERATOR@",
+ "binaryDir": "${sourceDir}/build"
+ }
+ ]
+}
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt
new file mode 100644
index 0000000..202ef80
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Warning:
+ The "Visual Studio 10 2010" generator is deprecated and will be removed in
+ a future version of CMake.
+
+ Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 0b26b89..cea5b1b 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -918,3 +918,10 @@ set(ProfilingTestOutput ${RunCMake_TEST_BINARY_DIR}/output.json)
set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${ProfilingTestOutput})
run_cmake(ProfilingTest)
unset(RunCMake_TEST_OPTIONS)
+
+if(RunCMake_GENERATOR MATCHES "^Visual Studio 10 2010")
+ run_cmake_with_options(DeprecateVS10-WARN-ON -DCMAKE_WARN_VS10=ON)
+ unset(ENV{CMAKE_WARN_VS10})
+ run_cmake(DeprecateVS10-WARN-ON)
+ run_cmake_with_options(DeprecateVS10-WARN-OFF -DCMAKE_WARN_VS10=OFF)
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_CMP0126_NEW.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_CMP0126_NEW.cmake
new file mode 100644
index 0000000..a419ab3
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_CMP0126_NEW.cmake
@@ -0,0 +1,64 @@
+
+cmake_policy(SET CMP0126 NEW)
+
+# Needed for CMAKE_SYSTEM_NAME, CMAKE_LIBRARY_ARCHITECTURE, FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS
+enable_language(C)
+
+# Prepare environment and variables
+set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE)
+set(CMAKE_PREFIX_PATH "${CMAKE_CURRENT_SOURCE_DIR}/pc-foo")
+if(WIN32)
+ set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}\\dummy-pkg-config.bat")
+ set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}\\pc-bar;X:\\this\\directory\\should\\not\\exist\\in\\the\\filesystem")
+ set(ENV{PKG_CONFIG_PATH} "C:\\baz")
+else()
+ set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/dummy-pkg-config.sh")
+ set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/pc-bar:/this/directory/should/not/exist/in/the/filesystem")
+ set(ENV{PKG_CONFIG_PATH} "/baz")
+endif()
+
+
+find_package(PkgConfig)
+
+
+if(NOT DEFINED CMAKE_SYSTEM_NAME
+ OR (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
+ AND NOT CMAKE_CROSSCOMPILING))
+ if(EXISTS "/etc/debian_version") # is this a debian system ?
+ if(CMAKE_LIBRARY_ARCHITECTURE MATCHES "^(i386-linux-gnu|x86_64-linux-gnu)$")
+ # Cannot create directories for all the existing architectures...
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ else()
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ endif()
+ else()
+ # not debian, check the FIND_LIBRARY_USE_LIB32_PATHS and FIND_LIBRARY_USE_LIB64_PATHS properties
+ get_property(uselibx32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIBX32_PATHS)
+ get_property(uselib32 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS)
+ get_property(uselib64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS)
+ if(uselibx32 AND CMAKE_INTERNAL_PLATFORM_ABI STREQUAL "ELF X32")
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/libx32/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/libx32/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ elseif(uselib32 AND CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib32/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib32/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ elseif(uselib64 AND CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib64/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib64/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ else()
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ endif()
+ endif()
+else()
+ if(WIN32)
+ set(expected_path "C:\\baz;${CMAKE_CURRENT_SOURCE_DIR}\\pc-foo\\lib\\pkgconfig;${CMAKE_CURRENT_SOURCE_DIR}\\pc-bar\\lib\\pkgconfig")
+ else()
+ set(expected_path "/baz:${CMAKE_CURRENT_SOURCE_DIR}/pc-foo/lib/pkgconfig:${CMAKE_CURRENT_SOURCE_DIR}/pc-bar/lib/pkgconfig")
+ endif()
+endif()
+
+
+set(FOO_FOUND FALSE)
+
+pkg_check_modules(FOO "${expected_path}")
+
+if(NOT FOO_FOUND)
+ message(FATAL_ERROR "Expected FOO_FOUND == 1.")
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
index 81a4732..f36d1eb 100644
--- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -7,6 +7,7 @@ set(ENV{CMAKE_FRAMEWORK_PATH} "")
run_cmake(PkgConfigDoesNotExist)
+run_cmake(FindPkgConfig_CMP0126_NEW)
run_cmake(FindPkgConfig_NO_PKGCONFIG_PATH)
run_cmake(FindPkgConfig_PKGCONFIG_PATH)
run_cmake(FindPkgConfig_PKGCONFIG_PATH_NO_CMAKE_PATH)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt
new file mode 100644
index 0000000..cf08267
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt
@@ -0,0 +1,16 @@
+Test project .*
+ Start 27: TEST:basic\.case_foo!3
+1/4 Test #27: TEST:basic\.case_foo!3 \.+ +Passed +[0-9.]+ sec
+ Start 28: TEST:basic\.case_bar!3
+2/4 Test #28: TEST:basic\.case_bar!3 \.+ +Passed +[0-9.]+ sec
+ Start 29: TEST:basic\.disabled_case!3
+3/4 Test #29: TEST:basic\.disabled_case!3 \.+\*+Not Run \(Disabled\) +[0-9.]+ sec
+ Start 30: TEST:basic\.DISABLEDnot_really_case!3
+4/4 Test #30: TEST:basic\.DISABLEDnot_really_case!3 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 3
+
+Total Test time \(real\) = +[0-9.]+ sec
+
+The following tests did not run:
+.*29 - TEST:basic.disabled_case!3 \(Disabled\)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
new file mode 100644
index 0000000..4a9d75b
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
@@ -0,0 +1,9 @@
+Test project .*
+ Start 31: TEST:typed/short\.case!4
+1/2 Test #31: TEST:typed/short\.case!4 \.+ +Passed +[0-9.]+ sec
+ Start 32: TEST:typed/float\.case!4
+2/2 Test #32: TEST:typed/float\.case!4 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 2
+
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
index 8efd117..221d6ad 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake
+++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
@@ -24,6 +24,24 @@ gtest_discover_tests(
PROPERTIES LABELS TEST2
)
+gtest_discover_tests(
+ fake_gtest
+ TEST_PREFIX TEST:
+ TEST_SUFFIX !3
+ TEST_FILTER basic*
+ EXTRA_ARGS how now "\"brown\" cow"
+ PROPERTIES LABELS TEST3
+)
+
+gtest_discover_tests(
+ fake_gtest
+ TEST_PREFIX TEST:
+ TEST_SUFFIX !4
+ TEST_FILTER typed*
+ EXTRA_ARGS how now "\"brown\" cow"
+ PROPERTIES LABELS TEST4
+)
+
add_executable(no_tests_defined no_tests_defined.cpp)
xcode_sign_adhoc(no_tests_defined)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index 530c8ab..c5c5925 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -40,6 +40,20 @@ function(run_GoogleTest DISCOVERY_MODE)
--no-label-summary
)
+ run_cmake_command(GoogleTest-test3
+ ${CMAKE_CTEST_COMMAND}
+ -C Debug
+ -L TEST3
+ --no-label-summary
+ )
+
+ run_cmake_command(GoogleTest-test4
+ ${CMAKE_CTEST_COMMAND}
+ -C Debug
+ -L TEST4
+ --no-label-summary
+ )
+
run_cmake_command(GoogleTest-test-missing
${CMAKE_CTEST_COMMAND}
-C Debug
diff --git a/Tests/RunCMake/GoogleTest/fake_gtest.cpp b/Tests/RunCMake/GoogleTest/fake_gtest.cpp
index 1956c37..b2a5cb4 100644
--- a/Tests/RunCMake/GoogleTest/fake_gtest.cpp
+++ b/Tests/RunCMake/GoogleTest/fake_gtest.cpp
@@ -7,27 +7,42 @@ int main(int argc, char** argv)
// it only requires that we produces output in the expected format when
// invoked with --gtest_list_tests. Thus, we fake that here. This allows us
// to test the module without actually needing Google Test.
+ bool is_filtered =
+ argc > 2 && std::string(argv[2]).find("--gtest_filter=") == 0;
+ bool is_basic_only =
+ is_filtered && std::string(argv[2]).find("basic*") != std::string::npos;
+ bool is_typed_only =
+ is_filtered && std::string(argv[2]).find("typed*") != std::string::npos;
+
if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
- std::cout << "basic." << std::endl;
- std::cout << " case_foo" << std::endl;
- std::cout << " case_bar" << std::endl;
- std::cout << " DISABLED_disabled_case" << std::endl;
- std::cout << " DISABLEDnot_really_case" << std::endl;
- std::cout << "DISABLED_disabled." << std::endl;
- std::cout << " case" << std::endl;
- std::cout << "DISABLEDnotreally." << std::endl;
- std::cout << " case" << std::endl;
- std::cout << "typed/0. # TypeParam = short" << std::endl;
- std::cout << " case" << std::endl;
- std::cout << "typed/1. # TypeParam = float" << std::endl;
- std::cout << " case" << std::endl;
- std::cout << "value/test." << std::endl;
- std::cout << " case/0 # GetParam() = 1" << std::endl;
- std::cout << " case/1 # GetParam() = \"foo\"" << std::endl;
- std::cout << "param/special." << std::endl;
- std::cout << " case/0 # GetParam() = \"semicolon;\"" << std::endl;
- std::cout << " case/1 # GetParam() = \"backslash\\\"" << std::endl;
- std::cout << " case/2 # GetParam() = \"${var}\"" << std::endl;
+ if (!is_typed_only) {
+ std::cout << "basic." << std::endl;
+ std::cout << " case_foo" << std::endl;
+ std::cout << " case_bar" << std::endl;
+ std::cout << " DISABLED_disabled_case" << std::endl;
+ std::cout << " DISABLEDnot_really_case" << std::endl;
+ }
+ if (!is_basic_only && !is_typed_only) {
+ std::cout << "DISABLED_disabled." << std::endl;
+ std::cout << " case" << std::endl;
+ std::cout << "DISABLEDnotreally." << std::endl;
+ std::cout << " case" << std::endl;
+ }
+ if (!is_basic_only) {
+ std::cout << "typed/0. # TypeParam = short" << std::endl;
+ std::cout << " case" << std::endl;
+ std::cout << "typed/1. # TypeParam = float" << std::endl;
+ std::cout << " case" << std::endl;
+ }
+ if (!is_basic_only && !is_typed_only) {
+ std::cout << "value/test." << std::endl;
+ std::cout << " case/0 # GetParam() = 1" << std::endl;
+ std::cout << " case/1 # GetParam() = \"foo\"" << std::endl;
+ std::cout << "param/special." << std::endl;
+ std::cout << " case/0 # GetParam() = \"semicolon;\"" << std::endl;
+ std::cout << " case/1 # GetParam() = \"backslash\\\"" << std::endl;
+ std::cout << " case/2 # GetParam() = \"${var}\"" << std::endl;
+ }
return 0;
}
diff --git a/Tests/RunCMake/InterfaceLibrary/IncludeDirectories.cmake b/Tests/RunCMake/InterfaceLibrary/IncludeDirectories.cmake
new file mode 100644
index 0000000..b94eac0
--- /dev/null
+++ b/Tests/RunCMake/InterfaceLibrary/IncludeDirectories.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0076 NEW)
+include_directories(Inc1 Inc2)
+add_library(iface INTERFACE)
+target_sources(iface PRIVATE iface.c)
+# Ensure the INCLUDE_DIRECTORIES property is populated.
+# Since interface libraries do not actually compile anything, this should be ignored.
+set_property(TARGET iface APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/Inc3 ${CMAKE_CURRENT_SOURCE_DIR}/Inc4)
diff --git a/Tests/RunCMake/InterfaceLibrary/RunCMakeTest.cmake b/Tests/RunCMake/InterfaceLibrary/RunCMakeTest.cmake
index 834b3c8..10a2d51 100644
--- a/Tests/RunCMake/InterfaceLibrary/RunCMakeTest.cmake
+++ b/Tests/RunCMake/InterfaceLibrary/RunCMakeTest.cmake
@@ -34,3 +34,4 @@ run_WithSources(ConfigSources "build1:iface")
run_WithSources(EmptySources "build1:iface" "build2:iface2,merge")
run_WithSources(ExcludeFromAll "build1" "build2:iface" "build3:iface2,merge")
run_WithSources(PublicSources "build1" "build2:iface" "build3:iface2,merge")
+run_WithSources(IncludeDirectories "build1:iface")
diff --git a/Tests/RunCMake/LinkWhatYouUse/C.cmake b/Tests/RunCMake/LinkWhatYouUse/C.cmake
index 4c3f428..f00db30 100644
--- a/Tests/RunCMake/LinkWhatYouUse/C.cmake
+++ b/Tests/RunCMake/LinkWhatYouUse/C.cmake
@@ -2,3 +2,4 @@ enable_language(C)
set(CMAKE_LINK_WHAT_YOU_USE TRUE)
add_executable(main main.c)
target_link_libraries(main m)
+add_library(foo STATIC foo.c)
diff --git a/Tests/RunCMake/LinkWhatYouUse/CXX.cmake b/Tests/RunCMake/LinkWhatYouUse/CXX.cmake
index 9555832..5762e94 100644
--- a/Tests/RunCMake/LinkWhatYouUse/CXX.cmake
+++ b/Tests/RunCMake/LinkWhatYouUse/CXX.cmake
@@ -2,3 +2,4 @@ enable_language(CXX)
set(CMAKE_LINK_WHAT_YOU_USE TRUE)
add_executable(main main.cxx)
target_link_libraries(main m)
+add_library(foo STATIC foo.cxx)
diff --git a/Tests/RunCMake/LinkWhatYouUse/foo.c b/Tests/RunCMake/LinkWhatYouUse/foo.c
new file mode 100644
index 0000000..c83d856
--- /dev/null
+++ b/Tests/RunCMake/LinkWhatYouUse/foo.c
@@ -0,0 +1,4 @@
+int foo(void)
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/LinkWhatYouUse/foo.cxx b/Tests/RunCMake/LinkWhatYouUse/foo.cxx
new file mode 100644
index 0000000..e05eb7e
--- /dev/null
+++ b/Tests/RunCMake/LinkWhatYouUse/foo.cxx
@@ -0,0 +1,4 @@
+int foo()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
index a211c66..de81049 100644
--- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -194,6 +194,19 @@ add_test(
endfunction()
run_completion_status()
+# Verify that running ctest_test() multiple times with different label arguments
+# doesn't break.
+function(run_changing_labels)
+ set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_test(NAME a COMMAND ${CMAKE_COMMAND} -E true)
+set_property(TEST a PROPERTY LABELS a)
+add_test(NAME b COMMAND ${CMAKE_COMMAND} -E true)
+set_property(TEST b PROPERTY LABELS b)
+ ]])
+ run_ctest(TestChangingLabels)
+endfunction()
+run_changing_labels()
+
# Verify that test output can add additional labels
function(run_extra_labels)
set(CASE_CMAKELISTS_SUFFIX_CODE [[
diff --git a/Tests/RunCMake/ctest_test/test.cmake.in b/Tests/RunCMake/ctest_test/test.cmake.in
index 50b936d..36b1dbd 100644
--- a/Tests/RunCMake/ctest_test/test.cmake.in
+++ b/Tests/RunCMake/ctest_test/test.cmake.in
@@ -15,4 +15,9 @@ set(ctest_test_args "@CASE_CTEST_TEST_ARGS@")
ctest_start(Experimental)
ctest_configure()
ctest_build()
-ctest_test(${ctest_test_args})
+if("@CASE_NAME@" STREQUAL "TestChangingLabels")
+ ctest_test(${ctest_test_args} INCLUDE_LABEL "^a$")
+ ctest_test(${ctest_test_args} INCLUDE_LABEL "^b$")
+else()
+ ctest_test(${ctest_test_args})
+endif()
diff --git a/Tests/RunCMake/if/IncompleteMatches-stdout.txt b/Tests/RunCMake/if/IncompleteMatches-stdout.txt
new file mode 100644
index 0000000..634d988
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatches-stdout.txt
@@ -0,0 +1,6 @@
+-- Test #1 passed
+-- Test #2 passed
+-- Test #3 passed
+-- Test #4 passed
+-- Test #5 passed
+-- Test #6 passed
diff --git a/Tests/RunCMake/if/IncompleteMatches.cmake b/Tests/RunCMake/if/IncompleteMatches.cmake
new file mode 100644
index 0000000..7142cfc
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatches.cmake
@@ -0,0 +1,36 @@
+if(MATCHES)
+ message(SEND_ERROR "Test #1 failed")
+else()
+ message(STATUS "Test #1 passed")
+endif()
+
+if("" MATCHES "")
+ message(STATUS "Test #2 passed")
+else()
+ message(SEND_ERROR "Test #2 failed")
+endif()
+
+if(MATCHES RHS)
+ message(SEND_ERROR "Test #3 failed")
+else()
+ message(STATUS "Test #3 passed")
+endif()
+
+set(RHS "")
+if(MATCHES RHS)
+ message(SEND_ERROR "Test #4 failed")
+else()
+ message(STATUS "Test #4 passed")
+endif()
+
+if(MATCHES "^$")
+ message(SEND_ERROR "Test #5 failed")
+else()
+ message(STATUS "Test #5 passed")
+endif()
+
+if("" MATCHES "^$")
+ message(STATUS "Test #6 passed")
+else()
+ message(SEND_ERROR "Test #6 failed")
+endif()
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail-result.txt b/Tests/RunCMake/if/IncompleteMatchesFail-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt b/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt
new file mode 100644
index 0000000..6e8ac80
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at IncompleteMatchesFail\.cmake:1 \(if\):
+ if given arguments:
+
+ "LHS" "MATCHES"
+
+ Unknown arguments specified
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail.cmake b/Tests/RunCMake/if/IncompleteMatchesFail.cmake
new file mode 100644
index 0000000..a3ef2b8
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail.cmake
@@ -0,0 +1,2 @@
+if(LHS MATCHES)
+endif()
diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake
index f54edf7..6baa840 100644
--- a/Tests/RunCMake/if/RunCMakeTest.cmake
+++ b/Tests/RunCMake/if/RunCMakeTest.cmake
@@ -9,7 +9,11 @@ run_cmake(duplicate-else-after-elseif)
run_cmake(elseif-message)
run_cmake(misplaced-elseif)
+run_cmake(unbalanced-parenthesis)
+
run_cmake(MatchesSelf)
+run_cmake(IncompleteMatches)
+run_cmake(IncompleteMatchesFail)
run_cmake(TestNameThatExists)
run_cmake(TestNameThatDoesNotExist)
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis-result.txt b/Tests/RunCMake/if/unbalanced-parenthesis-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt b/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt
new file mode 100644
index 0000000..770ccb8
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at unbalanced-parenthesis\.cmake:[0-9]+ \(if\):
+ if given arguments:
+
+ "NOT" "\(" "IN_LIST" "some_list"
+
+ mismatched parenthesis in condition
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis.cmake b/Tests/RunCMake/if/unbalanced-parenthesis.cmake
new file mode 100644
index 0000000..c51c755
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis.cmake
@@ -0,0 +1,8 @@
+set(var_with_paren "(")
+set(some_list "")
+
+if(NOT ${var_with_paren} IN_LIST some_list)
+ message(STATUS "Never prints")
+else()
+ message(STATUS "Never prints")
+endif()
diff --git a/Tests/RunCMake/string/Timestamp-stderr.txt b/Tests/RunCMake/string/Timestamp-stderr.txt
index cd4dcb3..d54777b 100644
--- a/Tests/RunCMake/string/Timestamp-stderr.txt
+++ b/Tests/RunCMake/string/Timestamp-stderr.txt
@@ -1 +1 @@
-RESULT=2005-08-07 23:19:49 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 %I=11 epoch=1123456789
+RESULT=2005-08-07 23:19:49 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 w_iso=31 %I=11 epoch=1123456789
diff --git a/Tests/RunCMake/string/Timestamp.cmake b/Tests/RunCMake/string/Timestamp.cmake
index cba258d..7fd6d72 100644
--- a/Tests/RunCMake/string/Timestamp.cmake
+++ b/Tests/RunCMake/string/Timestamp.cmake
@@ -1,3 +1,3 @@
set(ENV{SOURCE_DATE_EPOCH} "1123456789")
-string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S %A=%a %B=%b %y day=%j wd=%w week=%U %%I=%I epoch=%s" UTC)
+string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s" UTC)
message("RESULT=${RESULT}")
diff --git a/Tests/RunCMake/while/RunCMakeTest.cmake b/Tests/RunCMake/while/RunCMakeTest.cmake
index 7da80ac..bb9b991 100644
--- a/Tests/RunCMake/while/RunCMakeTest.cmake
+++ b/Tests/RunCMake/while/RunCMakeTest.cmake
@@ -5,3 +5,5 @@ run_cmake(EndMissing)
run_cmake(EndMismatch)
run_cmake(EndAlone)
run_cmake(EndAloneArgs)
+
+run_cmake(unbalanced-parenthesis)
diff --git a/Tests/RunCMake/while/unbalanced-parenthesis-result.txt b/Tests/RunCMake/while/unbalanced-parenthesis-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/while/unbalanced-parenthesis-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/while/unbalanced-parenthesis-stderr.txt b/Tests/RunCMake/while/unbalanced-parenthesis-stderr.txt
new file mode 100644
index 0000000..9d4132c
--- /dev/null
+++ b/Tests/RunCMake/while/unbalanced-parenthesis-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at unbalanced-parenthesis.cmake:[0-9]+ \(while\):
+ had incorrect arguments:
+
+ "NOT" "\(" "IN_LIST" "some_list"
+
+ mismatched parenthesis in condition
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/while/unbalanced-parenthesis.cmake b/Tests/RunCMake/while/unbalanced-parenthesis.cmake
new file mode 100644
index 0000000..7a12701
--- /dev/null
+++ b/Tests/RunCMake/while/unbalanced-parenthesis.cmake
@@ -0,0 +1,8 @@
+set(var_with_paren "(")
+set(some_list "")
+
+while(NOT ${var_with_paren} IN_LIST some_list)
+ message(STATUS "Never prints")
+endwhile()
+
+message(STATUS "Never prints")
diff --git a/Utilities/KWIML/include/kwiml/int.h b/Utilities/KWIML/include/kwiml/int.h
index b2e14d5..25a063a 100644
--- a/Utilities/KWIML/include/kwiml/int.h
+++ b/Utilities/KWIML/include/kwiml/int.h
@@ -162,7 +162,11 @@ An includer may test the following macros after inclusion:
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
# define KWIML_INT_HAVE_INTTYPES_H 1
#elif defined(_MSC_VER) /* MSVC */
-# define KWIML_INT_NO_INTTYPES_H 1
+# if _MSC_VER >= 1800
+# define KWIML_INT_HAVE_INTTYPES_H 1
+# else
+# define KWIML_INT_NO_INTTYPES_H 1
+# endif
#elif defined(__BORLANDC__) /* Borland */
# define KWIML_INT_NO_INTTYPES_H 1
#elif defined(__WATCOMC__) /* Watcom */
@@ -272,6 +276,15 @@ An includer may test the following macros after inclusion:
# define KWIML_INT_BROKEN_PRIXPTR 1
#endif
+#if defined(_MSC_VER) && _MSC_VER < 1900
+ /* MSVC scanf seems broken on 8-bit sizes until 19.00 */
+# define KWIML_INT_BROKEN_SCNd8 1
+# define KWIML_INT_BROKEN_SCNi8 1
+# define KWIML_INT_BROKEN_SCNo8 1
+# define KWIML_INT_BROKEN_SCNu8 1
+# define KWIML_INT_BROKEN_SCNx8 1
+#endif
+
#if (defined(__SUNPRO_C)||defined(__SUNPRO_CC)) && defined(_CHAR_IS_UNSIGNED)
# define KWIML_INT_BROKEN_INT8_T 1 /* system type defined incorrectly */
#elif defined(__BORLANDC__) && defined(_CHAR_UNSIGNED)
@@ -303,7 +316,7 @@ An includer may test the following macros after inclusion:
#elif defined(__BORLANDC__)
# define KWIML_INT_private_NO_SCN8
# define KWIML_INT_private_NO_SCN64
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) && _MSC_VER < 1900
# define KWIML_INT_private_NO_SCN8
#elif defined(__WATCOMC__)
# define KWIML_INT_private_NO_SCN8
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
index 9489e51..bcb67fe 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
@@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$");
#elif HAVE_BSDXML_H
#include <bsdxml.h>
#elif HAVE_EXPAT_H
-#include <expat.h>
+#include <cm3p/expat.h>
#endif
#ifdef HAVE_BZLIB_H
#include <cm3p/bzlib.h>
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
index 4d71f98..62f98a4 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
@@ -53,10 +53,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102
#include <cm3p/zlib.h>
#endif
#ifdef HAVE_BZLIB_H
-#include <bzlib.h>
+#include <cm3p/bzlib.h>
#endif
#ifdef HAVE_LZMA_H
-#include <lzma.h>
+#include <cm3p/lzma.h>
#endif
#include "archive.h"
diff --git a/bootstrap b/bootstrap
index 4ef0fe5..d0329fb 100755
--- a/bootstrap
+++ b/bootstrap
@@ -426,6 +426,7 @@ CMAKE_CXX_SOURCES="\
cmPolicies \
cmProcessOutput \
cmProjectCommand \
+ cmProperty \
cmPropertyDefinition \
cmPropertyMap \
cmGccDepfileLexerHelper \