summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitlab/ci/wix.ps15
-rw-r--r--.gitlab/os-macos.yml12
-rw-r--r--CTestCustom.cmake.in1
-rw-r--r--Help/command/cmake_path.rst5
-rw-r--r--Help/command/execute_process.rst11
-rw-r--r--Help/command/find_package.rst14
-rw-r--r--Help/command/foreach.rst8
-rw-r--r--Help/command/function.rst6
-rw-r--r--Help/command/if.rst24
-rw-r--r--Help/command/target_sources.rst4
-rw-r--r--Help/command/try_run.rst13
-rw-r--r--Help/command/while.rst8
-rw-r--r--Help/guide/tutorial/A Basic Starting Point.rst26
-rw-r--r--Help/guide/tutorial/Adding Support for a Testing Dashboard.rst20
-rw-r--r--Help/guide/tutorial/Adding System Introspection.rst10
-rw-r--r--Help/guide/tutorial/Adding a Custom Command and Generated File.rst12
-rw-r--r--Help/guide/tutorial/Packaging an Installer.rst4
-rw-r--r--Help/guide/tutorial/Step5/CTestConfig.cmake7
-rw-r--r--Help/guide/tutorial/Step6/CMakeLists.txt2
-rw-r--r--Help/guide/tutorial/Step6/CTestConfig.cmake7
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt23
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx25
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step7/CMakeLists.txt2
-rw-r--r--Help/guide/tutorial/Step7/CTestConfig.cmake7
-rw-r--r--Help/guide/tutorial/Step7/License.txt2
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt43
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx17
-rw-r--r--Help/guide/tutorial/Step8/CMakeLists.txt10
-rw-r--r--Help/guide/tutorial/Step9/CMakeLists.txt1
-rw-r--r--Help/guide/tutorial/index.rst2
-rw-r--r--Help/guide/using-dependencies/index.rst2
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst1922
-rw-r--r--Help/manual/cmake-modules.7.rst1
-rw-r--r--Help/module/FindSDL_gfx.rst1
-rw-r--r--Help/policy/CMP0097.rst6
-rw-r--r--Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst2
-rw-r--r--Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst24
-rw-r--r--Help/release/3.22.rst4
-rw-r--r--Help/release/3.23.rst7
-rw-r--r--Help/release/3.24.rst4
-rw-r--r--Help/release/dev/FindVulkan-dxc.rst5
-rw-r--r--Help/release/dev/try_run_split_output.rst6
-rw-r--r--Modules/FetchContent.cmake71
-rw-r--r--Modules/FindCURL.cmake3
-rw-r--r--Modules/FindCoin3D.cmake4
-rw-r--r--Modules/FindGTest.cmake2
-rw-r--r--Modules/FindOpenACC.cmake10
-rw-r--r--Modules/FindOpenMP.cmake41
-rw-r--r--Modules/FindPython/Support.cmake10
-rw-r--r--Modules/FindSDL_gfx.cmake86
-rw-r--r--Modules/FindSDL_image.cmake4
-rw-r--r--Modules/FindSDL_mixer.cmake4
-rw-r--r--Modules/FindSDL_net.cmake4
-rw-r--r--Modules/FindSDL_sound.cmake25
-rw-r--r--Modules/FindSDL_ttf.cmake4
-rw-r--r--Modules/FindVulkan.cmake103
-rw-r--r--Modules/Platform/AIX-GNU.cmake4
-rw-r--r--Modules/Platform/AIX-XL.cmake4
-rwxr-xr-xModules/Platform/AIX/ExportImportList46
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CTest/cmCTestHandlerCommand.cxx12
-rw-r--r--Source/CTest/cmCTestHandlerCommand.h3
-rw-r--r--Source/cmArgumentParser.cxx177
-rw-r--r--Source/cmArgumentParser.h373
-rw-r--r--Source/cmArgumentParserTypes.h45
-rw-r--r--Source/cmCMakeHostSystemInformationCommand.cxx12
-rw-r--r--Source/cmCMakePathCommand.cxx134
-rw-r--r--Source/cmCommands.cxx6
-rw-r--r--Source/cmCoreTryCompile.cxx68
-rw-r--r--Source/cmCoreTryCompile.h12
-rw-r--r--Source/cmExecuteProcessCommand.cxx12
-rw-r--r--Source/cmFileCommand.cxx220
-rw-r--r--Source/cmFindPackageCommand.cxx1249
-rw-r--r--Source/cmFindPackageCommand.h2
-rw-r--r--Source/cmGeneratedFileStream.cxx6
-rw-r--r--Source/cmGeneratorTarget.cxx12
-rw-r--r--Source/cmGlobalGenerator.cxx75
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx8
-rw-r--r--Source/cmGlobalVisualStudio10Generator.h2
-rw-r--r--Source/cmGlobalVisualStudio71Generator.cxx2
-rw-r--r--Source/cmGlobalVisualStudioGenerator.cxx6
-rw-r--r--Source/cmGlobalVisualStudioGenerator.h3
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.cxx126
-rw-r--r--Source/cmInstallCommand.cxx4
-rw-r--r--Source/cmNinjaTargetGenerator.cxx8
-rw-r--r--Source/cmParseArgumentsCommand.cxx9
-rw-r--r--Source/cmQtAutoGenInitializer.cxx28
-rw-r--r--Source/cmSystemTools.cxx52
-rw-r--r--Source/cmSystemTools.h8
-rw-r--r--Source/cmTarget.cxx45
-rw-r--r--Source/cmTarget.h6
-rw-r--r--Source/cmTryCompileCommand.cxx27
-rw-r--r--Source/cmTryCompileCommand.h30
-rw-r--r--Source/cmTryRunCommand.cxx245
-rw-r--r--Source/cmTryRunCommand.h44
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx274
-rw-r--r--Source/cmVisualStudio10TargetGenerator.h11
-rw-r--r--Source/cmake.cxx23
-rw-r--r--Source/cmakemain.cxx2
-rw-r--r--Source/cmcldeps.cxx1
-rw-r--r--Source/cmcmd.cxx13
-rw-r--r--Source/kwsys/Directory.cxx39
-rw-r--r--Source/kwsys/SystemTools.cxx63
-rw-r--r--Templates/MSBuild/FlagTables/v10_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v11_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v12_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v140_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v141_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v142_CSharp.json7
-rw-r--r--Templates/MSBuild/FlagTables/v143_CSharp.json7
-rw-r--r--Tests/CMakeLib/testArgumentParser.cxx297
-rw-r--r--Tests/CMakeLists.txt2
-rw-r--r--Tests/FindVulkan/Test/CMakeLists.txt15
-rw-r--r--Tests/FindVulkan/Test/Run-dxc_exe.cmake20
-rw-r--r--Tests/FindVulkan/Test/main-dxc_lib.cxx23
-rw-r--r--Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt5
-rw-r--r--Tests/RunCMake/CommandLine/P_arbitrary_args.cmake1
-rw-r--r--Tests/RunCMake/CommandLine/P_args-stdout.txt6
-rw-r--r--Tests/RunCMake/CommandLine/P_args.cmake6
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake3
-rw-r--r--Tests/RunCMake/File_Archive/create-missing-args-stderr.txt20
-rw-r--r--Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt10
-rw-r--r--Tests/RunCMake/File_Configure/BadArgContent-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt5
-rw-r--r--Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt5
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake28
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake3
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake5
-rw-r--r--Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake9
-rw-r--r--Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake6
-rw-r--r--Tests/RunCMake/VerifyHeaderSets/a.h4
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt6
-rw-r--r--Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt6
-rw-r--r--Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt4
-rw-r--r--Tests/RunCMake/cmake_path/RunCMakeTest.cmake11
-rw-r--r--Tests/RunCMake/execute_process/EchoCommand3-stderr.txt4
-rw-r--r--Tests/RunCMake/execute_process/EncodingMissing-stderr.txt5
-rw-r--r--Tests/RunCMake/export/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/export/TryCompileExport.cmake9
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt5
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake6
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt6
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake4
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt4
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake5
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt4
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake5
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt4
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake5
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake5
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-override.cmake6
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt6
-rw-r--r--Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake6
-rw-r--r--Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake18
-rw-r--r--Tests/RunCMake/file-CHMOD/all-perms-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/all-perms-stderr.txt4
-rw-r--r--Tests/RunCMake/file-CHMOD/all-perms.cmake3
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-path-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt6
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-path.cmake1
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-perms-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt3
-rw-r--r--Tests/RunCMake/file-CHMOD/invalid-perms.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt5
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt5
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-file-perms.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-perms-result.txt (renamed from Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt)0
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt5
-rw-r--r--Tests/RunCMake/file-CHMOD/missing-perms.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/no-perms-result.txt1
-rw-r--r--Tests/RunCMake/file-CHMOD/no-perms-stderr.txt3
-rw-r--r--Tests/RunCMake/file-CHMOD/no-perms.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/ok.cmake2
-rw-r--r--Tests/RunCMake/file-CHMOD/override.cmake3
-rw-r--r--Tests/RunCMake/file-CHMOD/write-only-result.txt1
-rw-r--r--Tests/RunCMake/file-CHMOD/write-only-stderr.txt6
-rw-r--r--Tests/RunCMake/file-CHMOD/write-only.cmake3
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt20
-rw-r--r--Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt9
-rw-r--r--Tests/RunCMake/find_package/RunCMakeTest.cmake16
-rw-r--r--Tests/RunCMake/find_package/SearchPaths.cmake2
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake0
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt10
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt14
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake1
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt12
-rw-r--r--Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake1
-rw-r--r--Tests/RunCMake/include_external_msproject/Program.cs9
-rw-r--r--Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake11
-rw-r--r--Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake9
-rw-r--r--Tests/RunCMake/include_external_msproject/consoleapp.csproj14
-rw-r--r--Tests/RunCMake/try_compile/BinDirEmpty-result.txt1
-rw-r--r--Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt4
-rw-r--r--Tests/RunCMake/try_compile/BinDirEmpty.cmake1
-rw-r--r--Tests/RunCMake/try_compile/BinDirRelative-result.txt1
-rw-r--r--Tests/RunCMake/try_compile/BinDirRelative-stderr.txt6
-rw-r--r--Tests/RunCMake/try_compile/BinDirRelative.cmake1
-rw-r--r--Tests/RunCMake/try_compile/EmptyListArgs.cmake8
-rw-r--r--Tests/RunCMake/try_compile/EmptyValueArgs-result.txt1
-rw-r--r--Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt9
-rw-r--r--Tests/RunCMake/try_compile/EmptyValueArgs.cmake4
-rw-r--r--Tests/RunCMake/try_compile/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt5
-rw-r--r--Tests/RunCMake/try_run/BadStdErrVariable.cmake5
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt5
-rw-r--r--Tests/RunCMake/try_run/BadStdOutVariable.cmake5
-rw-r--r--Tests/RunCMake/try_run/BinDirEmpty-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BinDirEmpty-stderr.txt4
-rw-r--r--Tests/RunCMake/try_run/BinDirEmpty.cmake1
-rw-r--r--Tests/RunCMake/try_run/BinDirRelative-result.txt1
-rw-r--r--Tests/RunCMake/try_run/BinDirRelative-stderr.txt6
-rw-r--r--Tests/RunCMake/try_run/BinDirRelative.cmake1
-rw-r--r--Tests/RunCMake/try_run/RunCMakeTest.cmake5
-rw-r--r--Tests/TryCompile/CMakeLists.txt23
-rw-r--r--Tests/TryCompile/stdout_and_stderr.c8
-rw-r--r--Utilities/IWYU/mapping.imp2
-rw-r--r--Utilities/Release/WiX/CustomAction/CMakeLists.txt4
-rw-r--r--Utilities/cmcurl/CMakeLists.txt2
270 files changed, 4465 insertions, 2949 deletions
diff --git a/.gitlab/ci/wix.ps1 b/.gitlab/ci/wix.ps1
index a69053303e..b7cb3f3402 100755
--- a/.gitlab/ci/wix.ps1
+++ b/.gitlab/ci/wix.ps1
@@ -2,13 +2,14 @@ $erroractionpreference = "stop"
$release = "v3.14.0.6526"
$sha256sum = "4C89898DF3BCAB13E12F7CA54399C35AD273475AD2CB6284611D00AE2D063C2C"
-$filename = "wix314-binaries"
+$filename = "wix-3.14.0.6526-win-i386"
$tarball = "$filename.zip"
$outdir = $pwd.Path
$outdir = "$outdir\.gitlab"
$ProgressPreference = 'SilentlyContinue'
-Invoke-WebRequest -Uri "https://wixtoolset.org/downloads/$release/$tarball" -OutFile "$outdir\$tarball"
+#Invoke-WebRequest -Uri "https://wixtoolset.org/downloads/$release/$tarball" -OutFile "$outdir\$tarball"
+Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/$tarball" -OutFile "$outdir\$tarball"
$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256
if ($hash.Hash -ne $sha256sum) {
exit 1
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 3183bf7ff9..5632f6df3a 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -7,7 +7,7 @@
GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID"
# TODO: Factor this out so that each job selects the Xcode version to
# use so that different versions can be tested in a single pipeline.
- DEVELOPER_DIR: "/Applications/Xcode-13.3.app/Contents/Developer"
+ DEVELOPER_DIR: "/Applications/Xcode-13.4.app/Contents/Developer"
# Avoid conflicting with other projects running on the same machine.
SCCACHE_SERVER_PORT: 4227
@@ -95,7 +95,7 @@
- cmake # Since this is a bare runner, pin to a project.
- macos
- shell
- - xcode-13.3
+ - xcode-13.4
- nonconcurrent
.macos_x86_64_builder_tags_package:
@@ -103,7 +103,7 @@
- cmake # Since this is a bare runner, pin to a project.
- macos
- shell
- - xcode-13.3
+ - xcode-13.4
- nonconcurrent
- finder
@@ -112,7 +112,7 @@
- cmake # Since this is a bare runner, pin to a project.
- macos
- shell
- - xcode-13.3
+ - xcode-13.4
- concurrent
.macos_arm64_builder_tags:
@@ -120,7 +120,7 @@
- cmake # Since this is a bare runner, pin to a project.
- macos-arm64
- shell
- - xcode-13.3
+ - xcode-13.4
- nonconcurrent
.macos_arm64_builder_ext_tags:
@@ -128,7 +128,7 @@
- cmake # Since this is a bare runner, pin to a project.
- macos-arm64
- shell
- - xcode-13.3
+ - xcode-13.4
- concurrent
## macOS-specific scripts
diff --git a/CTestCustom.cmake.in b/CTestCustom.cmake.in
index 49026a33d1..967e1bfbe0 100644
--- a/CTestCustom.cmake.in
+++ b/CTestCustom.cmake.in
@@ -74,6 +74,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION
"cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*may return deterministic values"
"cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*isn.*t random" # we do not do crypto
"cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*srand.*seed choices are.*poor" # we do not do crypto
+ "cmFindPackageCommand.cxx.*: warning #177-D: parameter .* was declared but never referenced"
"IPA warning: function.*multiply defined in"
"LICENSE WARNING" # PGI license expiry. Not useful in nightly testing.
diff --git a/Help/command/cmake_path.rst b/Help/command/cmake_path.rst
index 6fd3cf6888..eb7da07e38 100644
--- a/Help/command/cmake_path.rst
+++ b/Help/command/cmake_path.rst
@@ -482,8 +482,9 @@ are :ref:`normalized <Normalization>` before the check.
cmake_path(COMPARE <input1> NOT_EQUAL <input2> <out-var>)
Compares the lexical representations of two paths provided as string literals.
-No normalization is performed on either path. Equality is determined
-according to the following pseudo-code logic:
+No normalization is performed on either path, except multiple consecutive
+directory separators are effectively collapsed into a single separator.
+Equality is determined according to the following pseudo-code logic:
::
diff --git a/Help/command/execute_process.rst b/Help/command/execute_process.rst
index 82fcd46a51..d4ba4654db 100644
--- a/Help/command/execute_process.rst
+++ b/Help/command/execute_process.rst
@@ -37,8 +37,15 @@ Options:
``COMMAND``
A child process command line.
- CMake executes the child process using operating system APIs directly.
- All arguments are passed VERBATIM to the child process.
+ CMake executes the child process using operating system APIs directly:
+
+ * On POSIX platforms, the command line is passed to the
+ child process in an ``argv[]`` style array.
+
+ * On Windows platforms, the command line is encoded as a string such
+ that child processes using ``CommandLineToArgvW`` will decode the
+ original arguments.
+
No intermediate shell is used, so shell operators such as ``>``
are treated as normal arguments.
(Use the ``INPUT_*``, ``OUTPUT_*``, and ``ERROR_*`` options to
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index 35e3c51a92..ee52c4189c 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -103,8 +103,9 @@ Basic Signature
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
+ [GLOBAL]
[NO_POLICY_SCOPE]
- [GLOBAL])
+ [BYPASS_PROVIDER])
The basic signature is supported by both Module and Config modes.
The ``MODULE`` keyword implies that only Module mode can be used to find
@@ -181,6 +182,14 @@ only take the single version at the lower end of the range into account.
See the :command:`cmake_policy` command documentation for discussion
of the ``NO_POLICY_SCOPE`` option.
+.. versionadded:: 3.24
+ The ``BYPASS_PROVIDER`` keyword is only allowed when ``find_package()`` is
+ being called by a :ref:`dependency provider <dependency_providers>`.
+ It can be used by providers to call the built-in ``find_package()``
+ implementation directly and prevent that call from being re-routed back to
+ itself. Future versions of CMake may detect attempts to use this keyword
+ from places other than a dependency provider and halt with a fatal error.
+
.. _`full signature`:
Full Signature
@@ -192,8 +201,9 @@ Full Signature
[REQUIRED] [[COMPONENTS] [components...]]
[OPTIONAL_COMPONENTS components...]
[CONFIG|NO_MODULE]
- [NO_POLICY_SCOPE]
[GLOBAL]
+ [NO_POLICY_SCOPE]
+ [BYPASS_PROVIDER]
[NAMES name1 [name2 ...]]
[CONFIGS config1 [config2 ...]]
[HINTS path1 [path2 ... ]]
diff --git a/Help/command/foreach.rst b/Help/command/foreach.rst
index d9f54ca750..ddf8dfa84f 100644
--- a/Help/command/foreach.rst
+++ b/Help/command/foreach.rst
@@ -130,3 +130,11 @@ yields
-- en=two, ba=dua
-- en=three, ba=tiga
-- en=four, ba=
+
+See Also
+^^^^^^^^
+
+* :command:`break`
+* :command:`continue`
+* :command:`endforeach`
+* :command:`while`
diff --git a/Help/command/function.rst b/Help/command/function.rst
index 3d25aa45f0..fc55c034ff 100644
--- a/Help/command/function.rst
+++ b/Help/command/function.rst
@@ -73,3 +73,9 @@ argument. Referencing to ``ARGV#`` arguments beyond ``ARGC`` have
undefined behavior. Checking that ``ARGC`` is greater than ``#`` is
the only way to ensure that ``ARGV#`` was passed to the function as an
extra argument.
+
+See Also
+^^^^^^^^
+
+* :command:`endfunction`
+* :command:`return`
diff --git a/Help/command/if.rst b/Help/command/if.rst
index c096725236..b72769f6d1 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -320,13 +320,18 @@ Path Comparisons
.. _PATH_EQUAL:
``if(<variable|string> PATH_EQUAL <variable|string>)``
- .. versionadded:: 3.24
- Compares the lexical representations of two paths provided as string
- literals or variables. No normalization is performed on either path.
+ .. versionadded:: 3.24
- Lexical comparison has the advantage over string comparison to have a
- knowledge of the structure of the path. So, the following comparison is
- ``TRUE`` using ``PATH_EQUAL`` operator, but ``FALSE`` with ``STREQUAL``:
+ Compares the two paths component-by-component. Only if every component of
+ both paths match will the two paths compare equal. Multiple path separators
+ are effectively collapsed into a single separator, but note that backslashes
+ are not converted to forward slashes. No other
+ :ref:`path normalization <Normalization>` is performed.
+
+ Component-wise comparison is superior to string-based comparison due to the
+ handling of multiple path separators. In the following example, the
+ expression evaluates to true using ``PATH_EQUAL``, but false with
+ ``STREQUAL``:
.. code-block:: cmake
@@ -419,3 +424,10 @@ There is no automatic evaluation for environment or cache
:ref:`Variable References`. Their values must be referenced as
``$ENV{<name>}`` or ``$CACHE{<name>}`` wherever the above-documented
condition syntax accepts ``<variable|string>``.
+
+See also
+^^^^^^^^
+
+ * :command:`else`
+ * :command:`elseif`
+ * :command:`endif`
diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst
index 4699a08574..a079307508 100644
--- a/Help/command/target_sources.rst
+++ b/Help/command/target_sources.rst
@@ -52,10 +52,10 @@ expressions to ensure the sources are correctly assigned to the target.
.. code-block:: cmake
# WRONG: starts with generator expression, but relative path used
- target_sources(MyTarget "$<$<CONFIG:Debug>:dbgsrc.cpp>")
+ target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:dbgsrc.cpp>")
# CORRECT: absolute path used inside the generator expression
- target_sources(MyTarget "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/dbgsrc.cpp>")
+ target_sources(MyTarget PRIVATE "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/dbgsrc.cpp>")
See the :manual:`cmake-buildsystem(7)` manual for more on defining
buildsystem properties.
diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst
index fc41cdd7b7..125b0acb32 100644
--- a/Help/command/try_run.rst
+++ b/Help/command/try_run.rst
@@ -19,6 +19,8 @@ Try Compiling and Running Source Files
[LINK_LIBRARIES <libs>...]
[COMPILE_OUTPUT_VARIABLE <var>]
[RUN_OUTPUT_VARIABLE <var>]
+ [RUN_OUTPUT_STDOUT_VARIABLE <var>]
+ [RUN_OUTPUT_STDERR_VARIABLE <var>]
[OUTPUT_VARIABLE <var>]
[WORKING_DIRECTORY <var>]
[ARGS <args>...])
@@ -70,6 +72,16 @@ The options are:
``RUN_OUTPUT_VARIABLE <var>``
Report the output from running the executable in a given variable.
+``RUN_OUTPUT_STDOUT_VARIABLE <var>``
+ .. versionadded:: 3.25
+
+ Report the output of stdout from running the executable in a given variable.
+
+``RUN_OUTPUT_STDERR_VARIABLE <var>``
+ .. versionadded:: 3.25
+
+ Report the output of stderr from running the executable in a given variable.
+
``WORKING_DIRECTORY <var>``
.. versionadded:: 3.20
@@ -110,6 +122,7 @@ These cache entries are:
In order to make cross compiling your project easier, use ``try_run``
only if really required. If you use ``try_run``, use the
+``RUN_OUTPUT_STDOUT_VARIABLE``, ``RUN_OUTPUT_STDERR_VARIABLE``,
``RUN_OUTPUT_VARIABLE`` or ``OUTPUT_VARIABLE`` options only if really
required. Using them will require that when cross-compiling, the cache
variables will have to be set manually to the output of the executable.
diff --git a/Help/command/while.rst b/Help/command/while.rst
index a4957c1dbc..0bafae5ada 100644
--- a/Help/command/while.rst
+++ b/Help/command/while.rst
@@ -23,3 +23,11 @@ Per legacy, the :command:`endwhile` command admits
an optional ``<condition>`` argument.
If used, it must be a verbatim repeat of the argument of the opening
``while`` command.
+
+See Also
+^^^^^^^^
+
+ * :command:`break`
+ * :command:`continue`
+ * :command:`foreach`
+ * :command:`endwhile`
diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst
index cf1ec01860..d57cc358b7 100644
--- a/Help/guide/tutorial/A Basic Starting Point.rst
+++ b/Help/guide/tutorial/A Basic Starting Point.rst
@@ -19,8 +19,19 @@ required. This will be the starting point for our tutorial. Create a
add_executable(Tutorial tutorial.cxx)
-Note that this example uses lower case commands in the ``CMakeLists.txt`` file.
-Upper, lower, and mixed case commands are supported by CMake. The source
+Any project's top most ``CMakeLists.txt`` must start by specifying
+a minimum CMake version using :command:`cmake_minimum_required`. This ensures
+that the later CMake functions are run with a compatible version of CMake.
+
+To start a project, we use :command:`project` to set the project name. This
+call is required with every project and should be called soon after
+:command:`cmake_minimum_required`.
+
+Lastly, we use :command:`add_executable` to specify we want an executable
+named Tutorial generated using ``tutorial.cxx`` as the source.
+
+Note that this example uses lower case commands in the ``CMakeLists.txt``
+file. Upper, lower, and mixed case commands are supported by CMake. The source
code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be
used to compute the square root of a number.
@@ -79,7 +90,7 @@ to set the project name and version number.
:language: cmake
:end-before: # specify the C++ standard
-Then, configure a header file to pass the version number to the source
+Then use :command:`configure_file` to pass the version number to the source
code:
.. literalinclude:: Step2/CMakeLists.txt
@@ -91,7 +102,8 @@ code:
Since the configured file will be written into the binary tree, we
must add that directory to the list of paths to search for include
-files. Add the following lines to the end of the ``CMakeLists.txt`` file:
+files. Use :command:`target_include_directories` to add the following lines to
+the end of the ``CMakeLists.txt`` file:
.. literalinclude:: Step2/CMakeLists.txt
:caption: CMakeLists.txt
@@ -107,9 +119,9 @@ directory with the following contents:
:name: TutorialConfig.h.in
:language: c++
-When CMake configures this header file the values for
+When CMake configures this header file, the values for
``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will be
-replaced.
+replaced with the corresponding version numbers from the project.
Next modify ``tutorial.cxx`` to include the configured header file,
``TutorialConfig.h``.
@@ -141,7 +153,7 @@ Next let's add some C++11 features to our project by replacing ``atof`` with
We will need to explicitly state in the CMake code that it should use the
correct flags. The easiest way to enable support for a specific C++ standard
in CMake is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this
-tutorial, set the :variable:`CMAKE_CXX_STANDARD` variable in the
+tutorial, :command:`set` the :variable:`CMAKE_CXX_STANDARD` variable in the
``CMakeLists.txt`` file to ``11`` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`
to ``True``. Make sure to add the ``CMAKE_CXX_STANDARD`` declarations above the
call to ``add_executable``.
diff --git a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
index c6e0fd0b3b..9ec195c8f7 100644
--- a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
+++ b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
@@ -1,4 +1,4 @@
-Step 8: Adding Support for a Testing Dashboard
+Step 5: Adding Support for a Testing Dashboard
==============================================
Adding support for submitting our test results to a dashboard is simple. We
@@ -9,21 +9,21 @@ we include the :module:`CTest` module in our top-level ``CMakeLists.txt``.
Replace:
-.. code-block:: cmake
+.. literalinclude:: Step5/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-enable_testing-remove
-
- # enable testing
- enable_testing()
+ :language: cmake
+ :start-after: # enable testing
+ :end-before: # does the application run
With:
-.. code-block:: cmake
+.. literalinclude:: Step6/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-include-CTest
-
- # enable dashboard scripting
- include(CTest)
+ :language: cmake
+ :start-after: # enable testing
+ :end-before: # does the application run
The :module:`CTest` module will automatically call ``enable_testing()``, so we
can remove it from our CMake files.
@@ -46,7 +46,7 @@ downloaded from the ``Settings`` page of the project on the CDash
instance that will host and display the test results. Once downloaded from
CDash, the file should not be modified locally.
-.. literalinclude:: Step9/CTestConfig.cmake
+.. literalinclude:: Step6/CTestConfig.cmake
:caption: CTestConfig.cmake
:name: CTestConfig.cmake
:language: cmake
diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst
index 8db0cb8f33..710e9793af 100644
--- a/Help/guide/tutorial/Adding System Introspection.rst
+++ b/Help/guide/tutorial/Adding System Introspection.rst
@@ -1,4 +1,4 @@
-Step 5: Adding System Introspection
+Step 6: Adding System Introspection
===================================
Let us consider adding some code to our project that depends on features the
@@ -15,7 +15,7 @@ these functions using the :module:`CheckCXXSourceCompiles` module in
Add the checks for ``log`` and ``exp`` to ``MathFunctions/CMakeLists.txt``,
after the call to :command:`target_include_directories`:
-.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-check_cxx_source_compiles
:language: cmake
@@ -25,7 +25,7 @@ after the call to :command:`target_include_directories`:
If available, use :command:`target_compile_definitions` to specify
``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions.
-.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-target_compile_definitions
:language: cmake
@@ -37,7 +37,7 @@ compute the square root in the ``mysqrt`` function. Add the following code to
the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the
``#endif`` before returning the result!):
-.. literalinclude:: Step6/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step7/MathFunctions/mysqrt.cxx
:caption: MathFunctions/mysqrt.cxx
:name: MathFunctions/mysqrt.cxx-ifdef
:language: c++
@@ -46,7 +46,7 @@ the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the
We will also need to modify ``mysqrt.cxx`` to include ``cmath``.
-.. literalinclude:: Step6/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step7/MathFunctions/mysqrt.cxx
:caption: MathFunctions/mysqrt.cxx
:name: MathFunctions/mysqrt.cxx-include-cmath
:language: c++
diff --git a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst
index 70c6695623..6dc47617f1 100644
--- a/Help/guide/tutorial/Adding a Custom Command and Generated File.rst
+++ b/Help/guide/tutorial/Adding a Custom Command and Generated File.rst
@@ -1,4 +1,4 @@
-Step 6: Adding a Custom Command and Generated File
+Step 7: Adding a Custom Command and Generated File
==================================================
Suppose, for the purpose of this tutorial, we decide that we never want to use
@@ -26,7 +26,7 @@ accomplish this.
First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for
``MakeTable`` is added as any other executable would be added.
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-add_executable-MakeTable
:language: cmake
@@ -36,7 +36,7 @@ First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for
Then we add a custom command that specifies how to produce ``Table.h``
by running MakeTable.
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h
:language: cmake
@@ -47,7 +47,7 @@ Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated
file ``Table.h``. This is done by adding the generated ``Table.h`` to the list
of sources for the library MathFunctions.
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-add_library-Table.h
:language: cmake
@@ -57,7 +57,7 @@ of sources for the library MathFunctions.
We also have to add the current binary directory to the list of include
directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``.
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h
:language: cmake
@@ -67,7 +67,7 @@ directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``.
Now let's use the generated table. First, modify ``mysqrt.cxx`` to include
``Table.h``. Next, we can rewrite the ``mysqrt`` function to use the table:
-.. literalinclude:: Step7/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step8/MathFunctions/mysqrt.cxx
:caption: MathFunctions/mysqrt.cxx
:name: MathFunctions/mysqrt.cxx
:language: c++
diff --git a/Help/guide/tutorial/Packaging an Installer.rst b/Help/guide/tutorial/Packaging an Installer.rst
index 0ee5db2aca..5f0b27a174 100644
--- a/Help/guide/tutorial/Packaging an Installer.rst
+++ b/Help/guide/tutorial/Packaging an Installer.rst
@@ -1,4 +1,4 @@
-Step 7: Packaging an Installer
+Step 8: Packaging an Installer
==============================
Next suppose that we want to distribute our project to other people so that
@@ -11,7 +11,7 @@ installations and package management features. To accomplish this we will use
CPack to create platform specific installers. Specifically we need to add a
few lines to the bottom of our top-level ``CMakeLists.txt`` file.
-.. literalinclude:: Step8/CMakeLists.txt
+.. literalinclude:: Step9/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-include-CPack
:language: cmake
diff --git a/Help/guide/tutorial/Step5/CTestConfig.cmake b/Help/guide/tutorial/Step5/CTestConfig.cmake
new file mode 100644
index 0000000000..73efdb1f6e
--- /dev/null
+++ b/Help/guide/tutorial/Step5/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt
index 82d00c8d47..3ac5cd675a 100644
--- a/Help/guide/tutorial/Step6/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/CMakeLists.txt
@@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# enable testing
-enable_testing()
+include(CTest)
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
diff --git a/Help/guide/tutorial/Step6/CTestConfig.cmake b/Help/guide/tutorial/Step6/CTestConfig.cmake
new file mode 100644
index 0000000000..73efdb1f6e
--- /dev/null
+++ b/Help/guide/tutorial/Step6/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
index 42e098af80..b12f27d94a 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
@@ -6,29 +6,6 @@ target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
-# does this system provide the log and exp functions?
-include(CheckCXXSourceCompiles)
-check_cxx_source_compiles("
- #include <cmath>
- int main() {
- std::log(1.0);
- return 0;
- }
-" HAVE_LOG)
-check_cxx_source_compiles("
- #include <cmath>
- int main() {
- std::exp(1.0);
- return 0;
- }
-" HAVE_EXP)
-
-# add compile definitions
-if(HAVE_LOG AND HAVE_EXP)
- target_compile_definitions(MathFunctions
- PRIVATE "HAVE_LOG" "HAVE_EXP")
-endif()
-
# install rules
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx
deleted file mode 100644
index ee585568cb..0000000000
--- a/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that builds a sqrt table
-#include <cmath>
-#include <fstream>
-#include <iostream>
-
-int main(int argc, char* argv[])
-{
- // make sure we have enough arguments
- if (argc < 2) {
- return 1;
- }
-
- std::ofstream fout(argv[1], std::ios_base::out);
- const bool fileOpen = fout.is_open();
- if (fileOpen) {
- fout << "double sqrtTable[] = {" << std::endl;
- for (int i = 0; i < 10; ++i) {
- fout << sqrt(static_cast<double>(i)) << "," << std::endl;
- }
- // close the table with a zero
- fout << "0};" << std::endl;
- fout.close();
- }
- return fileOpen ? 0 : 1; // return 0 if wrote the file
-}
diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
index 7eecd26b5d..abe767d5ae 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
@@ -1,4 +1,3 @@
-#include <cmath>
#include <iostream>
#include "MathFunctions.h"
@@ -10,12 +9,6 @@ double mysqrt(double x)
return 0;
}
- // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
- double result = std::exp(std::log(x) * 0.5);
- std::cout << "Computing sqrt of " << x << " to be " << result
- << " using log and exp" << std::endl;
-#else
double result = x;
// do ten iterations
@@ -27,6 +20,5 @@ double mysqrt(double x)
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
-#endif
return result;
}
diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt
index 82d00c8d47..3ac5cd675a 100644
--- a/Help/guide/tutorial/Step7/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/CMakeLists.txt
@@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# enable testing
-enable_testing()
+include(CTest)
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
diff --git a/Help/guide/tutorial/Step7/CTestConfig.cmake b/Help/guide/tutorial/Step7/CTestConfig.cmake
new file mode 100644
index 0000000000..73efdb1f6e
--- /dev/null
+++ b/Help/guide/tutorial/Step7/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Help/guide/tutorial/Step7/License.txt b/Help/guide/tutorial/Step7/License.txt
deleted file mode 100644
index c62d00b9ea..0000000000
--- a/Help/guide/tutorial/Step7/License.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
index 9ede4b327c..42e098af80 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
@@ -1,29 +1,34 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
- COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
- DEPENDS MakeTable
- )
-
-# add the main library
-add_library(MathFunctions
- mysqrt.cxx
- ${CMAKE_CURRENT_BINARY_DIR}/Table.h
- )
+add_library(MathFunctions mysqrt.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-# state that we depend on our binary dir to find Table.h
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
+# does this system provide the log and exp functions?
+include(CheckCXXSourceCompiles)
+check_cxx_source_compiles("
+ #include <cmath>
+ int main() {
+ std::log(1.0);
+ return 0;
+ }
+" HAVE_LOG)
+check_cxx_source_compiles("
+ #include <cmath>
+ int main() {
+ std::exp(1.0);
+ return 0;
+ }
+" HAVE_EXP)
+
+# add compile definitions
+if(HAVE_LOG AND HAVE_EXP)
+ target_compile_definitions(MathFunctions
+ PRIVATE "HAVE_LOG" "HAVE_EXP")
+endif()
+
# install rules
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
index 7d80ee9648..7eecd26b5d 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
@@ -1,10 +1,8 @@
+#include <cmath>
#include <iostream>
#include "MathFunctions.h"
-// include the generated table
-#include "Table.h"
-
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -12,12 +10,13 @@ double mysqrt(double x)
return 0;
}
- // use the table to help find an initial value
+ // if we have both log and exp then use them
+#if defined(HAVE_LOG) && defined(HAVE_EXP)
+ double result = std::exp(std::log(x) * 0.5);
+ std::cout << "Computing sqrt of " << x << " to be " << result
+ << " using log and exp" << std::endl;
+#else
double result = x;
- if (x >= 1 && x < 10) {
- std::cout << "Use the table to help find an initial value " << std::endl;
- result = sqrtTable[static_cast<int>(x)];
- }
// do ten iterations
for (int i = 0; i < 10; ++i) {
@@ -28,6 +27,6 @@ double mysqrt(double x)
result = result + 0.5 * delta / result;
std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
}
-
+#endif
return result;
}
diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt
index 4c78b94c0b..3ac5cd675a 100644
--- a/Help/guide/tutorial/Step8/CMakeLists.txt
+++ b/Help/guide/tutorial/Step8/CMakeLists.txt
@@ -37,7 +37,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# enable testing
-enable_testing()
+include(CTest)
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)
@@ -64,11 +64,3 @@ do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-# setup installer
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-set(CPACK_SOURCE_GENERATOR "TGZ")
-include(CPack)
diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt
index 6bae26e42b..b13a662837 100644
--- a/Help/guide/tutorial/Step9/CMakeLists.txt
+++ b/Help/guide/tutorial/Step9/CMakeLists.txt
@@ -65,6 +65,7 @@ do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
+# setup installer
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
diff --git a/Help/guide/tutorial/index.rst b/Help/guide/tutorial/index.rst
index 09553cb28f..65d4829f8d 100644
--- a/Help/guide/tutorial/index.rst
+++ b/Help/guide/tutorial/index.rst
@@ -25,10 +25,10 @@ provides the complete solution for the previous step.
Adding a Library
Adding Usage Requirements for a Library
Installing and Testing
+ Adding Support for a Testing Dashboard
Adding System Introspection
Adding a Custom Command and Generated File
Packaging an Installer
- Adding Support for a Testing Dashboard
Selecting Static or Shared Libraries
Adding Generator Expressions
Adding Export Configuration
diff --git a/Help/guide/using-dependencies/index.rst b/Help/guide/using-dependencies/index.rst
index 6cab7ef479..bb519ad135 100644
--- a/Help/guide/using-dependencies/index.rst
+++ b/Help/guide/using-dependencies/index.rst
@@ -388,7 +388,7 @@ can still be built out-of-the-box.
The developer, on the other hand, may be much more interested in controlling
*how* a dependency is provided to the project. You might want to use a
-particular version of a package that you built themself. You might want
+particular version of a package that you built yourself. You might want
to use a third party package manager. You might want to redirect some
requests to a different URL on a system you control for security or
performance reasons. CMake supports these sort of scenarios through
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 25e581ad21..7a6188aafe 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -11,40 +11,189 @@ Introduction
============
Generator expressions are evaluated during build system generation to produce
-information specific to each build configuration.
+information specific to each build configuration. They have the form
+``$<...>``. For example:
+
+.. code-block:: cmake
+
+ target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>)
+
+This would expand to ``/opt/include/GNU``, ``/opt/include/Clang``, etc.
+depending on the C++ compiler used.
Generator expressions are allowed in the context of many target properties,
such as :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INCLUDE_DIRECTORIES`,
:prop_tgt:`COMPILE_DEFINITIONS` and others. They may also be used when using
commands to populate those properties, such as :command:`target_link_libraries`,
:command:`target_include_directories`, :command:`target_compile_definitions`
-and others.
+and others. They enable conditional linking, conditional definitions used when
+compiling, conditional include directories, and more. The conditions may be
+based on the build configuration, target properties, platform information,
+or any other queryable information.
-They enable conditional linking, conditional definitions used when compiling,
-conditional include directories, and more. The conditions may be based on
-the build configuration, target properties, platform information or any other
-queryable information.
+Generator expressions can be nested:
-Generator expressions have the form ``$<...>``. To avoid confusion, this page
-deviates from most of the CMake documentation in that it omits angular brackets
-``<...>`` around placeholders like ``condition``, ``string``, ``target``,
-among others.
+.. code-block:: cmake
-Generator expressions can be nested, as shown in most of the examples below.
+ target_compile_definitions(tgt PRIVATE
+ $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,4.2.0>:OLD_COMPILER>
+ )
-.. _`Boolean Generator Expressions`:
+The above would expand to ``OLD_COMPILER`` if the
+:variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less
+than 4.2.0.
-Boolean Generator Expressions
-=============================
+Whitespace And Quoting
+======================
-Boolean expressions evaluate to either ``0`` or ``1``.
-They are typically used to construct the condition in a :ref:`conditional
-generator expression<Conditional Generator Expressions>`.
+Generator expressions are typically parsed after command arguments.
+If a generator expression contains spaces, new lines, semicolons or
+other characters that may be interpreted as command argument separators,
+the whole expression should be surrounded by quotes when passed to a
+command. Failure to do so may result in the expression being split and
+it may no longer be recognized as a generator expression.
-Available boolean expressions are:
+When using :command:`add_custom_command` or :command:`add_custom_target`,
+use the ``VERBATIM`` and ``COMMAND_EXPAND_LISTS`` options to obtain robust
+argument splitting and quoting.
-Logical Operators
------------------
+.. code-block:: cmake
+
+ # WRONG: Embedded space will be treated as an argument separator.
+ # This ends up not being seen as a generator expression at all.
+ add_custom_target(run_some_tool
+ COMMAND some_tool -I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>
+ VERBATIM
+ )
+
+.. code-block:: cmake
+
+ # Better, but still not robust. Quotes prevent the space from splitting the
+ # expression. However, the tool will receive the expanded value as a single
+ # argument.
+ add_custom_target(run_some_tool
+ COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>, -I>"
+ VERBATIM
+ )
+
+.. code-block:: cmake
+
+ # Nearly correct. Using a semicolon to separate arguments and adding the
+ # COMMAND_EXPAND_LISTS option means that paths with spaces will be handled
+ # correctly. Quoting the whole expression ensures it is seen as a generator
+ # expression. But if the target property is empty, we will get a bare -I
+ # with nothing after it.
+ add_custom_target(run_some_tool
+ COMMAND some_tool "-I$<JOIN:$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>,;-I>"
+ COMMAND_EXPAND_LISTS
+ VERBATIM
+ )
+
+Using variables to build up a more complex generator expression is also a
+good way to reduce errors and improve readability. The above example can be
+improved further like so:
+
+.. code-block:: cmake
+
+ # The $<BOOL:...> check prevents adding anything if the property is empty,
+ # assuming the property value cannot be one of CMake's false constants.
+ set(prop "$<TARGET_PROPERTY:tgt,INCLUDE_DIRECTORIES>")
+ add_custom_target(run_some_tool
+ COMMAND some_tool "$<$<BOOL:${prop}>:-I$<JOIN:${prop},;-I>>"
+ COMMAND_EXPAND_LISTS
+ VERBATIM
+ )
+
+A common mistake is to try to split a generator expression across multiple
+lines with indenting:
+
+.. code-block:: cmake
+
+ # WRONG: New lines and spaces all treated as argument separators, so the
+ # generator expression is split and not recognized correctly.
+ target_compile_definitions(tgt PRIVATE
+ $<$<AND:
+ $<CXX_COMPILER_ID:GNU>,
+ $<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>
+ >:HAVE_5_OR_LATER>
+ )
+
+Again, use helper variables with well-chosen names to build up a readable
+expression instead:
+
+.. code-block:: cmake
+
+ set(is_gnu "$<CXX_COMPILER_ID:GNU>")
+ set(v5_or_later "$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5>")
+ set(meet_requirements "$<AND:${is_gnu},${v5_or_later}>")
+ target_compile_definitions(tgt PRIVATE
+ "$<${meet_requirements}:HAVE_5_OR_LATER>"
+ )
+
+Debugging
+=========
+
+Since generator expressions are evaluated during generation of the buildsystem,
+and not during processing of ``CMakeLists.txt`` files, it is not possible to
+inspect their result with the :command:`message()` command. One possible way
+to generate debug messages is to add a custom target:
+
+.. code-block:: cmake
+
+ add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<...>")
+
+After running ``cmake``, you can then build the ``genexdebug`` target to print
+the result of the ``$<...>`` expression (i.e. run the command
+``cmake --build ... --target genexdebug``).
+
+Another way is to write debug messages to a file with :command:`file(GENERATE)`:
+
+.. code-block:: cmake
+
+ file(GENERATE OUTPUT filename CONTENT "$<...>")
+
+Generator Expression Reference
+==============================
+
+.. note::
+
+ This reference deviates from most of the CMake documentation in that it
+ omits angular brackets ``<...>`` around placeholders like ``condition``,
+ ``string``, ``target``, etc. This is to prevent an opportunity for those
+ placeholders to be misinterpreted as generator expressions.
+
+.. _`Conditional Generator Expressions`:
+
+Conditional Expressions
+-----------------------
+
+A fundamental category of generator expressions relates to conditional logic.
+Two forms of conditional generator expressions are supported:
+
+.. genex:: $<condition:true_string>
+
+ Evaluates to ``true_string`` if ``condition`` is ``1``, or an empty string
+ if ``condition`` evaluates to ``0``. Any other value for ``condition``
+ results in an error.
+
+.. genex:: $<IF:condition,true_string,false_string>
+
+ .. versionadded:: 3.8
+
+ Evaluates to ``true_string`` if ``condition`` is ``1``, or ``false_string``
+ if ``condition`` is ``0``. Any other value for ``condition`` results in an
+ error.
+
+Typically, the ``condition`` is itself a generator expression. For instance,
+the following expression expands to ``DEBUG_MODE`` when the ``Debug``
+configuration is used, and the empty string for all other configurations:
+
+.. code-block:: cmake
+
+ $<$<CONFIG:Debug>:DEBUG_MODE>
+
+Boolean-like ``condition`` values other than ``1`` or ``0`` can be handled
+by wrapping them with the ``$<BOOL:...>`` generator expression:
.. genex:: $<BOOL:string>
@@ -58,49 +207,71 @@ Logical Operators
Otherwise evaluates to ``1``.
+The ``$<BOOL:...>`` generator expression is often used when a ``condition``
+is provided by a CMake variable:
+
+.. code-block:: cmake
+
+ $<$<BOOL:${HAVE_SOME_FEATURE}>:-DENABLE_SOME_FEATURE>
+
+
+.. _`Boolean Generator Expressions`:
+
+Logical Operators
+-----------------
+
+The common boolean logic operators are supported:
+
.. genex:: $<AND:conditions>
- where ``conditions`` is a comma-separated list of boolean expressions.
- Evaluates to ``1`` if all conditions are ``1``.
- Otherwise evaluates to ``0``.
+ where ``conditions`` is a comma-separated list of boolean expressions,
+ all of which must evaluate to either ``1`` or ``0``. The whole expression
+ evaluates to ``1`` if all conditions are ``1``. If any condition is ``0``,
+ the whole expression evaluates to ``0``.
.. genex:: $<OR:conditions>
where ``conditions`` is a comma-separated list of boolean expressions.
- Evaluates to ``1`` if at least one of the conditions is ``1``.
- Otherwise evaluates to ``0``.
+ all of which must evaluate to either ``1`` or ``0``. The whole expression
+ evaluates to ``1`` if at least one of the ``conditions`` is ``1``. If all
+ ``conditions`` evaluate to ``0``, the whole expression evaluates to ``0``.
.. genex:: $<NOT:condition>
+ ``condition`` must be ``0`` or ``1``. The result of the expression is
``0`` if ``condition`` is ``1``, else ``1``.
+.. _`Comparison Expressions`:
+
+Primary Comparison Expressions
+------------------------------
+
+CMake supports a variety of generator expressions that compare things.
+This section covers the primary and most widely used comparison types.
+Other more specific comparison types are documented in their own separate
+sections further below.
+
String Comparisons
-------------------
+^^^^^^^^^^^^^^^^^^
.. genex:: $<STREQUAL:string1,string2>
``1`` if ``string1`` and ``string2`` are equal, else ``0``.
The comparison is case-sensitive. For a case-insensitive comparison,
combine with a :ref:`string transforming generator expression
- <String Transforming Generator Expressions>`,
+ <String Transforming Generator Expressions>`. For example, the following
+ evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
- $<STREQUAL:$<UPPER_CASE:${foo}>,"BAR"> # "1" if ${foo} is any of "BAR", "Bar", "bar", ...
+ $<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
.. genex:: $<EQUAL:value1,value2>
``1`` if ``value1`` and ``value2`` are numerically equal, else ``0``.
-.. genex:: $<IN_LIST:string,list>
-
- .. versionadded:: 3.12
-
- ``1`` if ``string`` is member of the semicolon-separated ``list``, else ``0``.
- Uses case-sensitive comparisons.
-
Version Comparisons
--------------------
+^^^^^^^^^^^^^^^^^^^
.. genex:: $<VERSION_LESS:v1,v2>
@@ -126,9 +297,70 @@ Version Comparisons
``1`` if ``v1`` is a version greater than or equal to ``v2``, else ``0``.
-Path Comparisons
+.. _`String Transforming Generator Expressions`:
+
+String Transformations
+----------------------
+
+.. genex:: $<LOWER_CASE:string>
+
+ Content of ``string`` converted to lower case.
+
+.. genex:: $<UPPER_CASE:string>
+
+ Content of ``string`` converted to upper case.
+
+.. genex:: $<MAKE_C_IDENTIFIER:...>
+
+ Content of ``...`` converted to a C identifier. The conversion follows the
+ same behavior as :command:`string(MAKE_C_IDENTIFIER)`.
+
+List Expressions
----------------
+.. genex:: $<IN_LIST:string,list>
+
+ .. versionadded:: 3.12
+
+ ``1`` if ``string`` is an item in the semicolon-separated ``list``, else ``0``.
+ It uses case-sensitive comparisons.
+
+.. genex:: $<JOIN:list,string>
+
+ Joins the list with the content of ``string`` inserted between each item.
+
+.. genex:: $<REMOVE_DUPLICATES:list>
+
+ .. versionadded:: 3.15
+
+ Removes duplicated items in the given ``list``. The relative order of items
+ is preserved, but if duplicates are encountered, only the first instance is
+ preserved.
+
+.. genex:: $<FILTER:list,INCLUDE|EXCLUDE,regex>
+
+ .. versionadded:: 3.15
+
+ Includes or removes items from ``list`` that match the regular expression
+ ``regex``.
+
+Path Expressions
+----------------
+
+Most of the expressions in this section are closely associated with the
+:command:`cmake_path` command, providing the same capabilities, but in
+the form of a generator expression.
+
+For all generator expressions in this section, paths are expected to be in
+cmake-style format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>`
+generator expression can be used to convert a native path to a cmake-style
+one.
+
+.. _GenEx Path Comparisons:
+
+Path Comparisons
+^^^^^^^^^^^^^^^^
+
.. genex:: $<PATH_EQUAL:path1,path2>
.. versionadded:: 3.24
@@ -142,18 +374,11 @@ Path Comparisons
.. _GenEx Path Queries:
Path Queries
-------------
+^^^^^^^^^^^^
-The ``$<PATH>`` generator expression offers the same capabilities as the
-:command:`cmake_path` command, for the :ref:`Query <Path Query>` options.
-
-For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
-format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
-expression can be used to convert a native path to a cmake-style one.
-
-The ``$<PATH>`` generator expression can also be used for path
-:ref:`Decomposition <GenEx Path Decomposition>` and
-:ref:`Transformations <GenEx Path Transformations>`.
+These expressions provide the generation-time capabilities equivalent to the
+:ref:`Query <Path Query>` options of the :command:`cmake_path` command.
+All paths are expected to be in cmake-style format.
.. genex:: $<PATH:HAS_*,path>
@@ -199,211 +424,421 @@ The ``$<PATH>`` generator expression can also be used for path
.. versionadded:: 3.24
- Returns ``1`` if ``path`` is the prefix of ``input``,``0`` otherwise.
+ Returns ``1`` if ``path`` is the prefix of ``input``, ``0`` otherwise.
When the ``NORMALIZE`` option is specified, ``path`` and ``input`` are
:ref:`normalized <Normalization>` before the check.
-Variable Queries
-----------------
+.. _GenEx Path Decomposition:
-.. genex:: $<TARGET_EXISTS:target>
+Path Decomposition
+^^^^^^^^^^^^^^^^^^
- .. versionadded:: 3.12
+These expressions provide the generation-time capabilities equivalent to the
+:ref:`Decomposition <Path Decomposition>` options of the :command:`cmake_path`
+command. All paths are expected to be in cmake-style format.
- ``1`` if ``target`` exists, else ``0``.
+.. genex:: $<PATH:GET_*,...>
-.. genex:: $<CONFIG:cfgs>
+ .. versionadded:: 3.24
- ``1`` if config is any one of the entries in comma-separated list
- ``cfgs``, else ``0``. This is a case-insensitive comparison. The mapping in
- :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this
- expression when it is evaluated on a property on an :prop_tgt:`IMPORTED`
- target.
+ The following operations retrieve a different component or group of
+ components from a path. See :ref:`Path Structure And Terminology` for the
+ meaning of each path component.
-.. genex:: $<PLATFORM_ID:platform_ids>
+ ::
- where ``platform_ids`` is a comma-separated list.
- ``1`` if the CMake's platform id matches any one of the entries in
- ``platform_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_SYSTEM_NAME` variable.
+ $<PATH:GET_ROOT_NAME,path>
+ $<PATH:GET_ROOT_DIRECTORY,path>
+ $<PATH:GET_ROOT_PATH,path>
+ $<PATH:GET_FILENAME,path>
+ $<PATH:GET_EXTENSION[,LAST_ONLY],path>
+ $<PATH:GET_STEM[,LAST_ONLY],path>
+ $<PATH:GET_RELATIVE_PART,path>
+ $<PATH:GET_PARENT_PATH,path>
-.. genex:: $<C_COMPILER_ID:compiler_ids>
+ If a requested component is not present in the path, an empty string is
+ returned.
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the C compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+.. _GenEx Path Transformations:
-.. genex:: $<CXX_COMPILER_ID:compiler_ids>
+Path Transformations
+^^^^^^^^^^^^^^^^^^^^
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the CXX compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+These expressions provide the generation-time capabilities equivalent to the
+:ref:`Modification <Path Modification>` and :ref:`Generation <Path Generation>`
+options of the :command:`cmake_path` command. All paths are expected to be
+in cmake-style format.
-.. genex:: $<CUDA_COMPILER_ID:compiler_ids>
+.. _GenEx PATH-CMAKE_PATH:
- .. versionadded:: 3.15
+.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path>
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the CUDA compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ .. versionadded:: 3.24
-.. genex:: $<OBJC_COMPILER_ID:compiler_ids>
+ Returns ``path``. If ``path`` is a native path, it is converted into a
+ cmake-style path with forward-slashes (``/``). On Windows, the long filename
+ marker is taken into account.
- .. versionadded:: 3.16
+ When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
+ <Normalization>` after the conversion.
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the Objective-C compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+.. genex:: $<PATH:APPEND,path,input,...>
-.. genex:: $<OBJCXX_COMPILER_ID:compiler_ids>
+ .. versionadded:: 3.24
- .. versionadded:: 3.16
+ Returns all the ``input`` arguments appended to ``path`` using ``/`` as the
+ ``directory-separator``. Depending on the ``input``, the value of ``path``
+ may be discarded.
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the Objective-C++ compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ See :ref:`cmake_path(APPEND) <APPEND>` for more details.
-.. genex:: $<Fortran_COMPILER_ID:compiler_ids>
+.. genex:: $<PATH:REMOVE_FILENAME,path>
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the Fortran compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ .. versionadded:: 3.24
-.. genex:: $<HIP_COMPILER_ID:compiler_ids>
+ Returns ``path`` with filename component (as returned by
+ ``$<PATH:GET_FILENAME>``) removed. After removal, any trailing
+ ``directory-separator`` is left alone, if present.
- .. versionadded:: 3.21
+ See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details.
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the HIP compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+.. genex:: $<PATH:REPLACE_FILENAME,path,input>
-.. genex:: $<ISPC_COMPILER_ID:compiler_ids>
+ .. versionadded:: 3.24
- .. versionadded:: 3.19
+ Returns ``path`` with the filename component replaced by ``input``. If
+ ``path`` has no filename component (i.e. ``$<PATH:HAS_FILENAME>`` returns
+ ``0``), ``path`` is unchanged.
- where ``compiler_ids`` is a comma-separated list.
- ``1`` if the CMake's compiler id of the ISPC compiler matches any one
- of the entries in ``compiler_ids``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details.
+
+.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path>
+
+ .. versionadded:: 3.24
+
+ Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` removed, if any.
+
+ See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details.
+
+.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path,input>
+
+ .. versionadded:: 3.24
+
+ Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` replaced by
+ ``input``, if any.
+
+ See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details.
+
+.. genex:: $<PATH:NORMAL_PATH,path>
+
+ .. versionadded:: 3.24
+
+ Returns ``path`` normalized according to the steps described in
+ :ref:`Normalization`.
+
+.. genex:: $<PATH:RELATIVE_PATH,path,base_directory>
+
+ .. versionadded:: 3.24
+
+ Returns ``path``, modified to make it relative to the ``base_directory``
+ argument.
+
+ See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more
+ details.
+
+.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory>
+
+ .. versionadded:: 3.24
+
+ Returns ``path`` as absolute. If ``path`` is a relative path
+ (``$<PATH:IS_RELATIVE>`` returns ``1``), it is evaluated relative to the
+ given base directory specified by ``base_directory`` argument.
+
+ When the ``NORMALIZE`` option is specified, the path is
+ :ref:`normalized <Normalization>` after the path computation.
+
+ See :ref:`cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>` for more details.
+
+Shell Paths
+^^^^^^^^^^^
+
+.. genex:: $<SHELL_PATH:...>
+
+ .. versionadded:: 3.4
+
+ Content of ``...`` converted to shell path style. For example, slashes are
+ converted to backslashes in Windows shells and drive letters are converted
+ to posix paths in MSYS shells. The ``...`` must be an absolute path.
+
+ .. versionadded:: 3.14
+ The ``...`` may be a :ref:`semicolon-separated list <CMake Language Lists>`
+ of paths, in which case each path is converted individually and a result
+ list is generated using the shell path separator (``:`` on POSIX and
+ ``;`` on Windows). Be sure to enclose the argument containing this genex
+ in double quotes in CMake source code so that ``;`` does not split arguments.
+
+Configuration Expressions
+-------------------------
+
+.. genex:: $<CONFIG>
+
+ Configuration name. Use this instead of the deprecated :genex:`CONFIGURATION`
+ generator expression.
+
+.. genex:: $<CONFIG:cfgs>
+
+ ``1`` if config is any one of the entries in comma-separated list
+ ``cfgs``, else ``0``. This is a case-insensitive comparison. The mapping in
+ :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this
+ expression when it is evaluated on a property of an :prop_tgt:`IMPORTED`
+ target.
+
+.. genex:: $<OUTPUT_CONFIG:...>
+
+ .. versionadded:: 3.20
+
+ Only valid in :command:`add_custom_command` and :command:`add_custom_target`
+ as the outer-most generator expression in an argument.
+ With the :generator:`Ninja Multi-Config` generator, generator expressions
+ in ``...`` are evaluated using the custom command's "output config".
+ With other generators, the content of ``...`` is evaluated normally.
+
+.. genex:: $<COMMAND_CONFIG:...>
+
+ .. versionadded:: 3.20
+
+ Only valid in :command:`add_custom_command` and :command:`add_custom_target`
+ as the outer-most generator expression in an argument.
+ With the :generator:`Ninja Multi-Config` generator, generator expressions
+ in ``...`` are evaluated using the custom command's "command config".
+ With other generators, the content of ``...`` is evaluated normally.
+
+Toolchain And Language Expressions
+----------------------------------
+
+Platform
+^^^^^^^^
+
+.. genex:: $<PLATFORM_ID>
+
+ The current system's CMake platform id.
+ See also the :variable:`CMAKE_SYSTEM_NAME` variable.
+
+.. genex:: $<PLATFORM_ID:platform_ids>
+
+ where ``platform_ids`` is a comma-separated list.
+ ``1`` if CMake's platform id matches any one of the entries in
+ ``platform_ids``, otherwise ``0``.
+ See also the :variable:`CMAKE_SYSTEM_NAME` variable.
+
+Compiler Version
+^^^^^^^^^^^^^^^^
+
+See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable, which is
+closely related to the expressions in this sub-section.
+
+.. genex:: $<C_COMPILER_VERSION>
+
+ The version of the C compiler used.
.. genex:: $<C_COMPILER_VERSION:version>
``1`` if the version of the C compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<CXX_COMPILER_VERSION>
+
+ The version of the CXX compiler used.
.. genex:: $<CXX_COMPILER_VERSION:version>
``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<CUDA_COMPILER_VERSION>
+
+ .. versionadded:: 3.15
+
+ The version of the CUDA compiler used.
.. genex:: $<CUDA_COMPILER_VERSION:version>
.. versionadded:: 3.15
``1`` if the version of the CXX compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<OBJC_COMPILER_VERSION>
+
+ .. versionadded:: 3.16
+
+ The version of the OBJC compiler used.
.. genex:: $<OBJC_COMPILER_VERSION:version>
.. versionadded:: 3.16
``1`` if the version of the OBJC compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<OBJCXX_COMPILER_VERSION>
+
+ .. versionadded:: 3.16
+
+ The version of the OBJCXX compiler used.
.. genex:: $<OBJCXX_COMPILER_VERSION:version>
.. versionadded:: 3.16
``1`` if the version of the OBJCXX compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<Fortran_COMPILER_VERSION>
+
+ The version of the Fortran compiler used.
.. genex:: $<Fortran_COMPILER_VERSION:version>
``1`` if the version of the Fortran compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<HIP_COMPILER_VERSION>
+
+ .. versionadded:: 3.21
+
+ The version of the HIP compiler used.
.. genex:: $<HIP_COMPILER_VERSION:version>
.. versionadded:: 3.21
``1`` if the version of the HIP compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+
+.. genex:: $<ISPC_COMPILER_VERSION>
+
+ .. versionadded:: 3.19
+
+ The version of the ISPC compiler used.
.. genex:: $<ISPC_COMPILER_VERSION:version>
.. versionadded:: 3.19
``1`` if the version of the ISPC compiler matches ``version``, otherwise ``0``.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
-.. genex:: $<TARGET_POLICY:policy>
+Compiler Language And ID
+^^^^^^^^^^^^^^^^^^^^^^^^
- ``1`` if the ``policy`` was NEW when the 'head' target was created,
- else ``0``. If the ``policy`` was not set, the warning message for the policy
- will be emitted. This generator expression only works for a subset of
- policies.
+See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable, which is closely
+related to most of the expressions in this sub-section.
-.. genex:: $<COMPILE_FEATURES:features>
+.. genex:: $<C_COMPILER_ID>
- .. versionadded:: 3.1
+ CMake's compiler id of the C compiler used.
- where ``features`` is a comma-spearated list.
- Evaluates to ``1`` if all of the ``features`` are available for the 'head'
- target, and ``0`` otherwise. If this expression is used while evaluating
- the link implementation of a target and if any dependency transitively
- increases the required :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD`
- for the 'head' target, an error is reported. See the
- :manual:`cmake-compile-features(7)` manual for information on
- compile features and a list of supported compilers.
+.. genex:: $<C_COMPILER_ID:compiler_ids>
-.. _`Boolean COMPILE_LANGUAGE Generator Expression`:
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the C compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
-.. genex:: $<COMPILE_LANG_AND_ID:language,compiler_ids>
+.. genex:: $<CXX_COMPILER_ID>
+
+ CMake's compiler id of the CXX compiler used.
+
+.. genex:: $<CXX_COMPILER_ID:compiler_ids>
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the CXX compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<CUDA_COMPILER_ID>
.. versionadded:: 3.15
- ``1`` when the language used for compilation unit matches ``language`` and
- the CMake's compiler id of the language compiler matches any one of the
- entries in ``compiler_ids``, otherwise ``0``. This expression is a short form
- for the combination of ``$<COMPILE_LANGUAGE:language>`` and
- ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
- compile options, compile definitions, and include directories for source files of a
- particular language and compiler combination in a target. For example:
+ CMake's compiler id of the CUDA compiler used.
- .. code-block:: cmake
+.. genex:: $<CUDA_COMPILER_ID:compiler_ids>
- add_executable(myapp main.cpp foo.c bar.cpp zot.cu)
- target_compile_definitions(myapp
- PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG>
- $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL>
- $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG>
- )
+ .. versionadded:: 3.15
- This specifies the use of different compile definitions based on both
- the compiler id and compilation language. This example will have a
- ``COMPILING_CXX_WITH_CLANG`` compile definition when Clang is the CXX
- compiler, and ``COMPILING_CXX_WITH_INTEL`` when Intel is the CXX compiler.
- Likewise when the C compiler is Clang it will only see the ``COMPILING_C_WITH_CLANG``
- definition.
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the CUDA compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
- Without the ``COMPILE_LANG_AND_ID`` generator expression the same logic
- would be expressed as:
+.. genex:: $<OBJC_COMPILER_ID>
- .. code-block:: cmake
+ .. versionadded:: 3.16
- target_compile_definitions(myapp
- PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:COMPILING_CXX_WITH_CLANG>
- $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Intel>>:COMPILING_CXX_WITH_INTEL>
- $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG>
- )
+ CMake's compiler id of the OBJC compiler used.
+
+.. genex:: $<OBJC_COMPILER_ID:compiler_ids>
+
+ .. versionadded:: 3.16
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the Objective-C compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<OBJCXX_COMPILER_ID>
+
+ .. versionadded:: 3.16
+
+ CMake's compiler id of the OBJCXX compiler used.
+
+.. genex:: $<OBJCXX_COMPILER_ID:compiler_ids>
+
+ .. versionadded:: 3.16
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the Objective-C++ compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<Fortran_COMPILER_ID>
+
+ CMake's compiler id of the Fortran compiler used.
+
+.. genex:: $<Fortran_COMPILER_ID:compiler_ids>
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the Fortran compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<HIP_COMPILER_ID>
+
+ .. versionadded:: 3.21
+
+ CMake's compiler id of the HIP compiler used.
+
+.. genex:: $<HIP_COMPILER_ID:compiler_ids>
+
+ .. versionadded:: 3.21
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the HIP compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<ISPC_COMPILER_ID>
+
+ .. versionadded:: 3.19
+
+ CMake's compiler id of the ISPC compiler used.
+
+.. genex:: $<ISPC_COMPILER_ID:compiler_ids>
+
+ .. versionadded:: 3.19
+
+ where ``compiler_ids`` is a comma-separated list.
+ ``1`` if CMake's compiler id of the ISPC compiler matches any one
+ of the entries in ``compiler_ids``, otherwise ``0``.
+
+.. genex:: $<COMPILE_LANGUAGE>
+
+ .. versionadded:: 3.3
+
+ The compile language of source files when evaluating compile options.
+ See :ref:`the related boolean expression
+ <Boolean COMPILE_LANGUAGE Generator Expression>`
+ ``$<COMPILE_LANGUAGE:language>``
+ for notes about the portability of this generator expression.
+
+.. _`Boolean COMPILE_LANGUAGE Generator Expression`:
.. genex:: $<COMPILE_LANGUAGE:languages>
@@ -411,8 +846,8 @@ Variable Queries
``1`` when the language used for compilation unit matches any of the entries
in ``languages``, otherwise ``0``. This expression may be used to specify
- compile options, compile definitions, and include directories for source files of a
- particular language in a target. For example:
+ compile options, compile definitions, and include directories for source
+ files of a particular language in a target. For example:
.. code-block:: cmake
@@ -451,49 +886,82 @@ Variable Queries
add_executable(myapp main.cpp)
target_link_libraries(myapp myapp_c myapp_cxx)
-.. _`Boolean LINK_LANGUAGE Generator Expression`:
-
-.. genex:: $<LINK_LANG_AND_ID:language,compiler_ids>
+.. genex:: $<COMPILE_LANG_AND_ID:language,compiler_ids>
- .. versionadded:: 3.18
+ .. versionadded:: 3.15
- ``1`` when the language used for link step matches ``language`` and the
- CMake's compiler id of the language linker matches any one of the entries
- in ``compiler_ids``, otherwise ``0``. This expression is a short form for the
- combination of ``$<LINK_LANGUAGE:language>`` and
+ ``1`` when the language used for compilation unit matches ``language`` and
+ CMake's compiler id of the ``language`` compiler matches any one of the
+ entries in ``compiler_ids``, otherwise ``0``. This expression is a short form
+ for the combination of ``$<COMPILE_LANGUAGE:language>`` and
``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
- link libraries, link options, link directories and link dependencies of a
- particular language and linker combination in a target. For example:
+ compile options, compile definitions, and include directories for source
+ files of a particular language and compiler combination in a target.
+ For example:
.. code-block:: cmake
- add_library(libC_Clang ...)
- add_library(libCXX_Clang ...)
- add_library(libC_Intel ...)
- add_library(libCXX_Intel ...)
+ add_executable(myapp main.cpp foo.c bar.cpp zot.cu)
+ target_compile_definitions(myapp
+ PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:COMPILING_CXX_WITH_CLANG>
+ $<$<COMPILE_LANG_AND_ID:CXX,Intel>:COMPILING_CXX_WITH_INTEL>
+ $<$<COMPILE_LANG_AND_ID:C,Clang>:COMPILING_C_WITH_CLANG>
+ )
- add_executable(myapp main.c)
- if (CXX_CONFIG)
- target_sources(myapp PRIVATE file.cxx)
- endif()
- target_link_libraries(myapp
- PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang>
- $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang>
- $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel>
- $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>)
+ This specifies the use of different compile definitions based on both
+ the compiler id and compilation language. This example will have a
+ ``COMPILING_CXX_WITH_CLANG`` compile definition when Clang is the CXX
+ compiler, and ``COMPILING_CXX_WITH_INTEL`` when Intel is the CXX compiler.
+ Likewise, when the C compiler is Clang, it will only see the
+ ``COMPILING_C_WITH_CLANG`` definition.
- This specifies the use of different link libraries based on both the
- compiler id and link language. This example will have target ``libCXX_Clang``
- as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX``
- linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker.
- Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target
- ``libC_Clang`` will be added as link dependency and ``libC_Intel`` when
- ``Intel`` is the ``C`` linker.
+ Without the ``COMPILE_LANG_AND_ID`` generator expression, the same logic
+ would be expressed as:
- See :ref:`the note related to
- <Constraints LINK_LANGUAGE Generator Expression>`
- ``$<LINK_LANGUAGE:language>`` for constraints about the usage of this
- generator expression.
+ .. code-block:: cmake
+
+ target_compile_definitions(myapp
+ PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:AppleClang,Clang>>:COMPILING_CXX_WITH_CLANG>
+ $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:Intel>>:COMPILING_CXX_WITH_INTEL>
+ $<$<AND:$<COMPILE_LANGUAGE:C>,$<C_COMPILER_ID:Clang>>:COMPILING_C_WITH_CLANG>
+ )
+
+Compile Features
+^^^^^^^^^^^^^^^^
+
+.. genex:: $<COMPILE_FEATURES:features>
+
+ .. versionadded:: 3.1
+
+ where ``features`` is a comma-separated list.
+ Evaluates to ``1`` if all of the ``features`` are available for the 'head'
+ target, and ``0`` otherwise. If this expression is used while evaluating
+ the link implementation of a target and if any dependency transitively
+ increases the required :prop_tgt:`C_STANDARD` or :prop_tgt:`CXX_STANDARD`
+ for the 'head' target, an error is reported. See the
+ :manual:`cmake-compile-features(7)` manual for information on
+ compile features and a list of supported compilers.
+
+Linker Language And ID
+^^^^^^^^^^^^^^^^^^^^^^
+
+.. genex:: $<LINK_LANGUAGE>
+
+ .. versionadded:: 3.18
+
+ The link language of the target when evaluating link options.
+ See :ref:`the related boolean expression
+ <Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:languages>``
+ for notes about the portability of this generator expression.
+
+ .. note::
+
+ This generator expression is not supported by the link libraries
+ properties to avoid side-effects due to the double evaluation of
+ these properties.
+
+
+.. _`Boolean LINK_LANGUAGE Generator Expression`:
.. genex:: $<LINK_LANGUAGE:languages>
@@ -509,7 +977,7 @@ Variable Queries
add_library(api_C ...)
add_library(api_CXX ...)
add_library(api INTERFACE)
- target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c>
+ target_link_options(api INTERFACE $<$<LINK_LANGUAGE:C>:-opt_c>
$<$<LINK_LANGUAGE:CXX>:-opt_cxx>)
target_link_libraries(api INTERFACE $<$<LINK_LANGUAGE:C>:api_C>
$<$<LINK_LANGUAGE:CXX>:api_CXX>)
@@ -560,485 +1028,391 @@ Variable Queries
evaluation will give ``C`` as link language, so the second pass will
correctly add target ``libother`` as link dependency.
-String-Valued Generator Expressions
-===================================
-
-These expressions expand to some string.
-For example,
-
-.. code-block:: cmake
-
- include_directories(/usr/include/$<CXX_COMPILER_ID>/)
-
-expands to ``/usr/include/GNU/`` or ``/usr/include/Clang/`` etc, depending on
-the compiler identifier.
-
-String-valued expressions may also be combined with other expressions.
-Here an example for a string-valued expression within a boolean expressions
-within a conditional expression:
-
-.. code-block:: cmake
-
- $<$<VERSION_LESS:$<CXX_COMPILER_VERSION>,4.2.0>:OLD_COMPILER>
-
-expands to ``OLD_COMPILER`` if the
-:variable:`CMAKE_CXX_COMPILER_VERSION <CMAKE_<LANG>_COMPILER_VERSION>` is less
-than 4.2.0.
-
-And here two nested string-valued expressions:
-
-.. code-block:: cmake
-
- -I$<JOIN:$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>, -I>
-
-generates a string of the entries in the :prop_tgt:`INCLUDE_DIRECTORIES` target
-property with each entry preceded by ``-I``.
-
-Expanding on the previous example, if one first wants to check if the
-``INCLUDE_DIRECTORIES`` property is non-empty, then it is advisable to
-introduce a helper variable to keep the code readable:
-
-.. code-block:: cmake
-
- set(prop "$<TARGET_PROPERTY:INCLUDE_DIRECTORIES>") # helper variable
- $<$<BOOL:${prop}>:-I$<JOIN:${prop}, -I>>
-
-The following string-valued generator expressions are available:
-
-Escaped Characters
-------------------
-
-String literals to escape the special meaning a character would otherwise have:
-
-.. genex:: $<ANGLE-R>
-
- A literal ``>``. Used for example to compare strings that contain a ``>``.
-
-.. genex:: $<COMMA>
-
- A literal ``,``. Used for example to compare strings which contain a ``,``.
-
-.. genex:: $<SEMICOLON>
-
- A literal ``;``. Used to prevent list expansion on an argument with ``;``.
-
-.. _`Conditional Generator Expressions`:
-
-Conditional Expressions
------------------------
-
-Conditional generator expressions depend on a boolean condition
-that must be ``0`` or ``1``.
-
-.. genex:: $<condition:true_string>
-
- Evaluates to ``true_string`` if ``condition`` is ``1``.
- Otherwise evaluates to the empty string.
-
-.. genex:: $<IF:condition,true_string,false_string>
-
- .. versionadded:: 3.8
-
- Evaluates to ``true_string`` if ``condition`` is ``1``.
- Otherwise evaluates to ``false_string``.
-
-Typically, the ``condition`` is a :ref:`boolean generator expression
-<Boolean Generator Expressions>`. For instance,
-
-.. code-block:: cmake
-
- $<$<CONFIG:Debug>:DEBUG_MODE>
-
-expands to ``DEBUG_MODE`` when the ``Debug`` configuration is used, and
-otherwise expands to the empty string.
-
-.. _`String Transforming Generator Expressions`:
-
-String Transformations
-----------------------
-
-.. genex:: $<JOIN:list,string>
-
- Joins the list with the content of ``string``.
-
-.. genex:: $<REMOVE_DUPLICATES:list>
-
- .. versionadded:: 3.15
-
- Removes duplicated items in the given ``list``. The relative order of items
- is preserved, but if duplicates are encountered, only the first instance is
- preserved.
-
-.. genex:: $<FILTER:list,INCLUDE|EXCLUDE,regex>
-
- .. versionadded:: 3.15
-
- Includes or removes items from ``list`` that match the regular expression ``regex``.
-
-.. genex:: $<LOWER_CASE:string>
-
- Content of ``string`` converted to lower case.
-
-.. genex:: $<UPPER_CASE:string>
-
- Content of ``string`` converted to upper case.
-
-.. genex:: $<GENEX_EVAL:expr>
-
- .. versionadded:: 3.12
-
- Content of ``expr`` evaluated as a generator expression in the current
- context. This enables consumption of generator expressions whose
- evaluation results itself in generator expressions.
-
-.. genex:: $<TARGET_GENEX_EVAL:tgt,expr>
-
- .. versionadded:: 3.12
-
- Content of ``expr`` evaluated as a generator expression in the context of
- ``tgt`` target. This enables consumption of custom target properties that
- themselves contain generator expressions.
-
- Having the capability to evaluate generator expressions is very useful when
- you want to manage custom properties supporting generator expressions.
- For example:
-
- .. code-block:: cmake
-
- add_library(foo ...)
-
- set_property(TARGET foo PROPERTY
- CUSTOM_KEYS $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>
- )
-
- add_custom_target(printFooKeys
- COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_PROPERTY:foo,CUSTOM_KEYS>
- )
+.. genex:: $<LINK_LANG_AND_ID:language,compiler_ids>
- This naive implementation of the ``printFooKeys`` custom command is wrong
- because ``CUSTOM_KEYS`` target property is not evaluated and the content
- is passed as is (i.e. ``$<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>``).
+ .. versionadded:: 3.18
- To have the expected result (i.e. ``FOO_EXTRA_THINGS`` if config is
- ``Debug``), it is required to evaluate the output of
- ``$<TARGET_PROPERTY:foo,CUSTOM_KEYS>``:
+ ``1`` when the language used for link step matches ``language`` and the
+ CMake's compiler id of the language linker matches any one of the entries
+ in ``compiler_ids``, otherwise ``0``. This expression is a short form for the
+ combination of ``$<LINK_LANGUAGE:language>`` and
+ ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
+ link libraries, link options, link directories and link dependencies of a
+ particular language and linker combination in a target. For example:
.. code-block:: cmake
- add_custom_target(printFooKeys
- COMMAND ${CMAKE_COMMAND} -E
- echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>>
- )
-
-.. _GenEx Path Decomposition:
-
-Path Decomposition
-------------------
-
-The ``$<PATH>`` generator expression offers the same capabilities as the
-:command:`cmake_path` command, for the
-:ref:`Decomposition <Path Decomposition>` options.
-
-For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
-format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
-expression can be used to convert a native path to a cmake-style one.
-
-The ``$<PATH>`` generator expression can also be used for path
-:ref:`Queries <GenEx Path Queries>` and
-:ref:`Transformations <GenEx Path Transformations>`.
-
-.. genex:: $<PATH:GET_*,...>
-
- .. versionadded:: 3.24
-
- The following operations retrieve a different component or group of
- components from a path. See :ref:`Path Structure And Terminology` for the
- meaning of each path component.
-
- ::
-
- $<PATH:GET_ROOT_NAME,path>
- $<PATH:GET_ROOT_DIRECTORY,path>
- $<PATH:GET_ROOT_PATH,path>
- $<PATH:GET_FILENAME,path>
- $<PATH:GET_EXTENSION[,LAST_ONLY],path>
- $<PATH:GET_STEM[,LAST_ONLY],path>
- $<PATH:GET_RELATIVE_PART,path>
- $<PATH:GET_PARENT_PATH,path>
-
- If a requested component is not present in the path, an empty string is
- returned.
-
-.. _GenEx Path Transformations:
-
-Path Transformations
---------------------
+ add_library(libC_Clang ...)
+ add_library(libCXX_Clang ...)
+ add_library(libC_Intel ...)
+ add_library(libCXX_Intel ...)
-The ``$<PATH>`` generator expression offers the same capabilities as the
-:command:`cmake_path` command, for the
-:ref:`Modification <Path Modification>` and
-:ref:`Generation <Path Generation>` options.
+ add_executable(myapp main.c)
+ if (CXX_CONFIG)
+ target_sources(myapp PRIVATE file.cxx)
+ endif()
+ target_link_libraries(myapp
+ PRIVATE $<$<LINK_LANG_AND_ID:CXX,Clang,AppleClang>:libCXX_Clang>
+ $<$<LINK_LANG_AND_ID:C,Clang,AppleClang>:libC_Clang>
+ $<$<LINK_LANG_AND_ID:CXX,Intel>:libCXX_Intel>
+ $<$<LINK_LANG_AND_ID:C,Intel>:libC_Intel>)
-For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
-format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
-expression can be used to convert a native path to a cmake-style one.
+ This specifies the use of different link libraries based on both the
+ compiler id and link language. This example will have target ``libCXX_Clang``
+ as link dependency when ``Clang`` or ``AppleClang`` is the ``CXX``
+ linker, and ``libCXX_Intel`` when ``Intel`` is the ``CXX`` linker.
+ Likewise when the ``C`` linker is ``Clang`` or ``AppleClang``, target
+ ``libC_Clang`` will be added as link dependency and ``libC_Intel`` when
+ ``Intel`` is the ``C`` linker.
-The ``$<PATH>`` generator expression can also be used for path
-:ref:`Queries <GenEx Path Queries>` and
-:ref:`Decomposition <GenEx Path Decomposition>`.
+ See :ref:`the note related to
+ <Constraints LINK_LANGUAGE Generator Expression>`
+ ``$<LINK_LANGUAGE:language>`` for constraints about the usage of this
+ generator expression.
-.. _GenEx PATH-CMAKE_PATH:
+Link Features
+^^^^^^^^^^^^^
-.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path>
+.. genex:: $<LINK_LIBRARY:feature,library-list>
.. versionadded:: 3.24
- Returns ``path``. If ``path`` is a native path, it is converted into a
- cmake-style path with forward-slashes (``/``). On Windows, the long filename
- marker is taken into account.
-
- When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
- <Normalization>` after the conversion.
+ Specify a set of libraries to link to a target, along with a ``feature``
+ which provides details about *how* they should be linked. For example:
-.. genex:: $<PATH:APPEND,path,input,...>
+ .. code-block:: cmake
- .. versionadded:: 3.24
+ add_library(lib1 STATIC ...)
+ add_library(lib2 ...)
+ target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,lib1>")
- Returns all the ``input`` arguments appended to ``path`` using ``/`` as the
- ``directory-separator``. Depending on the ``input``, the value of ``path``
- may be discarded.
+ This specifies that ``lib2`` should link to ``lib1`` and use the
+ ``WHOLE_ARCHIVE`` feature when doing so.
- See :ref:`cmake_path(APPEND) <APPEND>` for more details.
+ Feature names are case-sensitive and may only contain letters, numbers and
+ underscores. Feature names defined in all uppercase are reserved for CMake's
+ own built-in features. The pre-defined built-in library features are:
-.. genex:: $<PATH:REMOVE_FILENAME,path>
+ .. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
- .. versionadded:: 3.24
+ Built-in and custom library features are defined in terms of the following
+ variables:
- Returns ``path`` with filename component (as returned by
- ``$<PATH:GET_FILENAME>``) removed. After removal, any trailing
- ``directory-separator`` is left alone, if present.
+ * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>`
+ * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>`
- See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details.
+ The value used for each of these variables is the value as set at the end of
+ the directory scope in which the target was created. The usage is as follows:
-.. genex:: $<PATH:REPLACE_FILENAME,path,input>
+ 1. If the language-specific
+ :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable
+ is true, the ``feature`` must be defined by the corresponding
+ :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` variable.
+ 2. If no language-specific ``feature`` is supported, then the
+ :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable must be
+ true and the ``feature`` must be defined by the corresponding
+ :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variable.
- .. versionadded:: 3.24
+ The following limitations should be noted:
- Returns ``path`` with the filename component replaced by ``input``. If
- ``path`` has no filename component (i.e. ``$<PATH:HAS_FILENAME>`` returns
- ``0``), ``path`` is unchanged.
+ * The ``library-list`` can specify CMake targets or libraries.
+ Any CMake target of type :ref:`OBJECT <Object Libraries>`
+ or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect
+ of the expression and instead be linked in the standard way.
- See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details.
+ * The ``$<LINK_LIBRARY:...>`` generator expression can only be used to
+ specify link libraries. In practice, this means it can appear in the
+ :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`, and
+ :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be
+ specified in :command:`target_link_libraries` and :command:`link_libraries`
+ commands.
-.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path>
+ * If a ``$<LINK_LIBRARY:...>`` generator expression appears in the
+ :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be
+ included in the imported target generated by a :command:`install(EXPORT)`
+ command. It is the responsibility of the environment consuming this
+ import to define the link feature used by this expression.
- .. versionadded:: 3.24
+ * Each target or library involved in the link step must have at most only
+ one kind of library feature. The absence of a feature is also incompatible
+ with all other features. For example:
- Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` removed, if any.
+ .. code-block:: cmake
- See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details.
+ add_library(lib1 ...)
+ add_library(lib2 ...)
+ add_library(lib3 ...)
-.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path>
+ # lib1 will be associated with feature1
+ target_link_libraries(lib2 PUBLIC "$<LINK_LIBRARY:feature1,lib1>")
- .. versionadded:: 3.24
+ # lib1 is being linked with no feature here. This conflicts with the
+ # use of feature1 in the line above and would result in an error.
+ target_link_libraries(lib3 PRIVATE lib1 lib2)
- Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` replaced by
- ``input``, if any.
+ Where it isn't possible to use the same feature throughout a build for a
+ given target or library, the :prop_tgt:`LINK_LIBRARY_OVERRIDE` and
+ :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties can be
+ used to resolve such incompatibilities.
- See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details.
+ * The ``$<LINK_LIBRARY:...>`` generator expression does not guarantee
+ that the list of specified targets and libraries will be kept grouped
+ together. To manage constructs like ``--start-group`` and ``--end-group``,
+ as supported by the GNU ``ld`` linker, use the :genex:`LINK_GROUP`
+ generator expression instead.
-.. genex:: $<PATH:NORMAL_PATH,path>
+.. genex:: $<LINK_GROUP:feature,library-list>
.. versionadded:: 3.24
- Returns ``path`` normalized according to the steps described in
- :ref:`Normalization`.
-
-.. genex:: $<PATH:RELATIVE_PATH,path,base_directory>
+ Specify a group of libraries to link to a target, along with a ``feature``
+ which defines how that group should be linked. For example:
- .. versionadded:: 3.24
+ .. code-block:: cmake
- Returns ``path``, modified to make it relative to the ``base_directory``
- argument.
+ add_library(lib1 STATIC ...)
+ add_library(lib2 ...)
+ target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:RESCAN,lib1,external>")
- See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more
- details.
+ This specifies that ``lib2`` should link to ``lib1`` and ``external``, and
+ that both of those two libraries should be included on the linker command
+ line according to the definition of the ``RESCAN`` feature.
-.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory>
+ Feature names are case-sensitive and may only contain letters, numbers and
+ underscores. Feature names defined in all uppercase are reserved for CMake's
+ own built-in features. Currently, there is only one pre-defined built-in
+ group feature:
- .. versionadded:: 3.24
+ .. include:: ../variable/LINK_GROUP_PREDEFINED_FEATURES.txt
- Returns ``path`` as absolute. If ``path`` is a relative path
- (``$<PATH:IS_RELATIVE>`` returns ``1``), it is evaluated relative to the
- given base directory specified by ``base_directory`` argument.
+ Built-in and custom group features are defined in terms of the following
+ variables:
- When the ``NORMALIZE`` option is specified, the path is
- :ref:`normalized <Normalization>` after the path computation.
+ * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>`
+ * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
+ * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>`
- See :ref:`cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>` for more details.
+ The value used for each of these variables is the value as set at the end of
+ the directory scope in which the target was created. The usage is as follows:
-Variable Queries
-----------------
+ 1. If the language-specific
+ :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable
+ is true, the ``feature`` must be defined by the corresponding
+ :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` variable.
+ 2. If no language-specific ``feature`` is supported, then the
+ :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable must be
+ true and the ``feature`` must be defined by the corresponding
+ :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` variable.
-.. genex:: $<CONFIG>
+ The ``LINK_GROUP`` generator expression is compatible with the
+ :genex:`LINK_LIBRARY` generator expression. The libraries involved in a
+ group can be specified using the :genex:`LINK_LIBRARY` generator expression.
- Configuration name.
+ Each target or external library involved in the link step is allowed to be
+ part of multiple groups, but only if all the groups involved specify the
+ same ``feature``. Such groups will not be merged on the linker command line,
+ the individual groups will still be preserved. Mixing different group
+ features for the same target or library is forbidden.
-.. genex:: $<CONFIGURATION>
+ .. code-block:: cmake
- Configuration name. Deprecated since CMake 3.0. Use ``CONFIG`` instead.
+ add_library(lib1 ...)
+ add_library(lib2 ...)
+ add_library(lib3 ...)
+ add_library(lib4 ...)
+ add_library(lib5 ...)
-.. genex:: $<PLATFORM_ID>
+ target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
+ target_link_libraries(lib4 PRIVATE "$<LINK_GROUP:feature1,lib1,lib3>")
+ # lib4 will be linked with the groups {lib1,lib2} and {lib1,lib3}.
+ # Both groups specify the same feature, so this is fine.
- The current system's CMake platform id.
- See also the :variable:`CMAKE_SYSTEM_NAME` variable.
+ target_link_libraries(lib5 PRIVATE "$<LINK_GROUP:feature2,lib1,lib3>")
+ # An error will be raised here because both lib1 and lib3 are part of two
+ # groups with different features.
-.. genex:: $<C_COMPILER_ID>
+ When a target or an external library is involved in the link step as part of
+ a group and also as not part of any group, any occurrence of the non-group
+ link item will be replaced by the groups it belongs to.
- The CMake's compiler id of the C compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ .. code-block:: cmake
-.. genex:: $<CXX_COMPILER_ID>
+ add_library(lib1 ...)
+ add_library(lib2 ...)
+ add_library(lib3 ...)
+ add_library(lib4 ...)
- The CMake's compiler id of the CXX compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ target_link_libraries(lib3 PUBLIC lib1)
-.. genex:: $<CUDA_COMPILER_ID>
+ target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>")
+ # lib4 will only be linked with lib3 and the group {lib1,lib2}
- The CMake's compiler id of the CUDA compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ Because ``lib1`` is part of the group defined for ``lib4``, that group then
+ gets applied back to the use of ``lib1`` for ``lib3``. The end result will
+ be as though the linking relationship for ``lib3`` had been specified as:
-.. genex:: $<OBJC_COMPILER_ID>
+ .. code-block:: cmake
- .. versionadded:: 3.16
+ target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
- The CMake's compiler id of the OBJC compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ Be aware that the precedence of the group over the non-group link item can
+ result in circular dependencies between groups. If this occurs, a fatal
+ error is raised because circular dependencies are not allowed for groups.
-.. genex:: $<OBJCXX_COMPILER_ID>
+ .. code-block:: cmake
- .. versionadded:: 3.16
+ add_library(lib1A ...)
+ add_library(lib1B ...)
+ add_library(lib2A ...)
+ add_library(lib2B ...)
+ add_library(lib3 ...)
- The CMake's compiler id of the OBJCXX compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ # Non-group linking relationships, these are non-circular so far
+ target_link_libraries(lib1A PUBLIC lib2A)
+ target_link_libraries(lib2B PUBLIC lib1B)
-.. genex:: $<Fortran_COMPILER_ID>
+ # The addition of these groups creates circular dependencies
+ target_link_libraries(lib3 PRIVATE
+ "$<LINK_GROUP:feat,lib1A,lib1B>"
+ "$<LINK_GROUP:feat,lib2A,lib2B>"
+ )
- The CMake's compiler id of the Fortran compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ Because of the groups defined for ``lib3``, the linking relationships for
+ ``lib1A`` and ``lib2B`` effectively get expanded to the equivalent of:
-.. genex:: $<HIP_COMPILER_ID>
+ .. code-block:: cmake
- .. versionadded:: 3.21
+ target_link_libraries(lib1A PUBLIC "$<LINK_GROUP:feat,lib2A,lib2B>")
+ target_link_libraries(lib2B PUBLIC "$<LINK_GROUP:feat,lib1A,lib1B>")
- The CMake's compiler id of the HIP compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ This creates a circular dependency between groups:
+ ``lib1A --> lib2B --> lib1A``.
-.. genex:: $<ISPC_COMPILER_ID>
+ The following limitations should also be noted:
- .. versionadded:: 3.19
+ * The ``library-list`` can specify CMake targets or libraries.
+ Any CMake target of type :ref:`OBJECT <Object Libraries>`
+ or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect
+ of the expression and instead be linked in the standard way.
- The CMake's compiler id of the ISPC compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_ID` variable.
+ * The ``$<LINK_GROUP:...>`` generator expression can only be used to
+ specify link libraries. In practice, this means it can appear in the
+ :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`,and
+ :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be
+ specified in :command:`target_link_libraries` and :command:`link_libraries`
+ commands.
-.. genex:: $<C_COMPILER_VERSION>
+ * If a ``$<LINK_GROUP:...>`` generator expression appears in the
+ :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be
+ included in the imported target generated by a :command:`install(EXPORT)`
+ command. It is the responsibility of the environment consuming this
+ import to define the link feature used by this expression.
- The version of the C compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+Link Context
+^^^^^^^^^^^^
-.. genex:: $<CXX_COMPILER_VERSION>
+.. genex:: $<LINK_ONLY:...>
- The version of the CXX compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+ .. versionadded:: 3.1
-.. genex:: $<CUDA_COMPILER_VERSION>
+ Content of ``...``, except while collecting :ref:`Target Usage Requirements`,
+ in which case it is the empty string. This is intended for use in an
+ :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
+ via the :command:`target_link_libraries` command, to specify private link
+ dependencies without other usage requirements.
- The version of the CUDA compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+ .. versionadded:: 3.24
+ ``LINK_ONLY`` may also be used in a :prop_tgt:`LINK_LIBRARIES` target
+ property. See policy :policy:`CMP0131`.
-.. genex:: $<OBJC_COMPILER_VERSION>
+.. genex:: $<DEVICE_LINK:list>
- .. versionadded:: 3.16
+ .. versionadded:: 3.18
- The version of the OBJC compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+ Returns the list if it is the device link step, an empty list otherwise.
+ The device link step is controlled by :prop_tgt:`CUDA_SEPARABLE_COMPILATION`
+ and :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` properties and
+ policy :policy:`CMP0105`. This expression can only be used to specify link
+ options.
-.. genex:: $<OBJCXX_COMPILER_VERSION>
+.. genex:: $<HOST_LINK:list>
- .. versionadded:: 3.16
+ .. versionadded:: 3.18
- The version of the OBJCXX compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+ Returns the list if it is the normal link step, an empty list otherwise.
+ This expression is mainly useful when a device link step is also involved
+ (see :genex:`$<DEVICE_LINK:list>` generator expression). This expression can
+ only be used to specify link options.
-.. genex:: $<Fortran_COMPILER_VERSION>
- The version of the Fortran compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+.. _`Target-Dependent Queries`:
-.. genex:: $<HIP_COMPILER_VERSION>
+Target-Dependent Expressions
+----------------------------
- .. versionadded:: 3.21
+These queries refer to a target ``tgt``. Unless otherwise stated, this can
+be any runtime artifact, namely:
- The version of the HIP compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+* An executable target created by :command:`add_executable`.
+* A shared library target (``.so``, ``.dll`` but not their ``.lib`` import
+ library) created by :command:`add_library`.
+* A static library target created by :command:`add_library`.
-.. genex:: $<ISPC_COMPILER_VERSION>
+In the following, the phrase "the ``tgt`` filename" means the name of the
+``tgt`` binary file. This has to be distinguished from the phrase
+"the target name", which is just the string ``tgt``.
- .. versionadded:: 3.19
+.. genex:: $<TARGET_EXISTS:tgt>
- The version of the ISPC compiler used.
- See also the :variable:`CMAKE_<LANG>_COMPILER_VERSION` variable.
+ .. versionadded:: 3.12
-.. genex:: $<COMPILE_LANGUAGE>
+ ``1`` if ``tgt`` exists as a CMake target, else ``0``.
- .. versionadded:: 3.3
+.. genex:: $<TARGET_NAME_IF_EXISTS:tgt>
- The compile language of source files when evaluating compile options.
- See :ref:`the related boolean expression
- <Boolean COMPILE_LANGUAGE Generator Expression>`
- ``$<COMPILE_LANGUAGE:language>``
- for notes about the portability of this generator expression.
+ .. versionadded:: 3.12
-.. genex:: $<LINK_LANGUAGE>
+ The target name ``tgt`` if the target exists, an empty string otherwise.
- .. versionadded:: 3.18
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
- The link language of target when evaluating link options.
- See :ref:`the related boolean expression
- <Boolean LINK_LANGUAGE Generator Expression>` ``$<LINK_LANGUAGE:language>``
- for notes about the portability of this generator expression.
+.. genex:: $<TARGET_NAME:...>
- .. note::
+ Marks ``...`` as being the name of a target. This is required if exporting
+ targets to multiple dependent export sets. The ``...`` must be a literal
+ name of a target, it may not contain generator expressions.
- This generator expression is not supported by the link libraries
- properties to avoid side-effects due to the double evaluation of
- these properties.
+.. genex:: $<TARGET_PROPERTY:tgt,prop>
-.. _`Target-Dependent Queries`:
+ Value of the property ``prop`` on the target ``tgt``.
-Target-Dependent Queries
-------------------------
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
-These queries refer to a target ``tgt``. This can be any runtime artifact,
-namely:
+.. genex:: $<TARGET_PROPERTY:prop>
-* an executable target created by :command:`add_executable`
-* a shared library target (``.so``, ``.dll`` but not their ``.lib`` import library)
- created by :command:`add_library`
-* a static library target created by :command:`add_library`
+ Value of the property ``prop`` on the target for which the expression
+ is being evaluated. Note that for generator expressions in
+ :ref:`Target Usage Requirements` this is the consuming target rather
+ than the target specifying the requirement.
-In the following, "the ``tgt`` filename" means the name of the ``tgt``
-binary file. This has to be distinguished from "the target name",
-which is just the string ``tgt``.
+.. genex:: $<TARGET_OBJECTS:tgt>
-.. genex:: $<TARGET_NAME_IF_EXISTS:tgt>
+ .. versionadded:: 3.1
- .. versionadded:: 3.12
+ List of objects resulting from building ``tgt``. This would typically be
+ used on :ref:`object library <Object Libraries>` targets.
- The target name ``tgt`` if the target exists, an empty string otherwise.
+.. genex:: $<TARGET_POLICY:policy>
- Note that ``tgt`` is not added as a dependency of the target this
- expression is evaluated on.
+ ``1`` if the ``policy`` was ``NEW`` when the 'head' target was created,
+ else ``0``. If the ``policy`` was not set, the warning message for the policy
+ will be emitted. This generator expression only works for a subset of
+ policies.
.. genex:: $<TARGET_FILE:tgt>
@@ -1273,20 +1647,6 @@ which is just the string ``tgt``.
Note that ``tgt`` is not added as a dependency of the target this
expression is evaluated on (see policy :policy:`CMP0112`).
-.. genex:: $<TARGET_PROPERTY:tgt,prop>
-
- Value of the property ``prop`` on the target ``tgt``.
-
- Note that ``tgt`` is not added as a dependency of the target this
- expression is evaluated on.
-
-.. genex:: $<TARGET_PROPERTY:prop>
-
- Value of the property ``prop`` on the target for which the expression
- is being evaluated. Note that for generator expressions in
- :ref:`Target Usage Requirements` this is the consuming target rather
- than the target specifying the requirement.
-
.. genex:: $<TARGET_RUNTIME_DLLS:tgt>
.. versionadded:: 3.21
@@ -1294,8 +1654,8 @@ which is just the string ``tgt``.
List of DLLs that the target depends on at runtime. This is determined by
the locations of all the ``SHARED`` targets in the target's transitive
dependencies. Using this generator expression on targets other than
- executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error. On
- non-DLL platforms, it evaluates to an empty string.
+ executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error.
+ **On non-DLL platforms, this expression always evaluates to an empty string**.
This generator expression can be used to copy all of the DLLs that a target
depends on into its output directory in a ``POST_BUILD`` custom command. For
@@ -1310,7 +1670,7 @@ which is just the string ``tgt``.
add_custom_command(TARGET exe POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_RUNTIME_DLLS:exe> $<TARGET_FILE_DIR:exe>
COMMAND_EXPAND_LISTS
- )
+ )
.. note::
@@ -1321,368 +1681,100 @@ which is just the string ``tgt``.
section for details. Many :ref:`Find Modules` produce imported targets
with the ``UNKNOWN`` type and therefore will be ignored.
-.. genex:: $<INSTALL_PREFIX>
-
- Content of the install prefix when the target is exported via
- :command:`install(EXPORT)`, or when evaluated in the
- :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of
- :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise.
-
-Output-Related Expressions
---------------------------
-
-.. genex:: $<TARGET_NAME:...>
-
- Marks ``...`` as being the name of a target. This is required if exporting
- targets to multiple dependent export sets. The ``...`` must be a literal
- name of a target- it may not contain generator expressions.
-
-.. genex:: $<LINK_ONLY:...>
-
- .. versionadded:: 3.1
-
- Content of ``...``, except while collecting :ref:`Target Usage Requirements`,
- in which case it is the empty string. This is intended for use in an
- :prop_tgt:`INTERFACE_LINK_LIBRARIES` target property, typically populated
- via the :command:`target_link_libraries` command, to specify private link
- dependencies without other usage requirements.
-
- .. versionadded:: 3.24
- ``LINK_ONLY`` may also be used in a :prop_tgt:`LINK_LIBRARIES` target
- property. See policy :policy:`CMP0131`.
-
-.. genex:: $<DEVICE_LINK:list>
-
- .. versionadded:: 3.18
-
- Returns the list if it is the device link step, an empty list otherwise.
- The device link step is controlled by :prop_tgt:`CUDA_SEPARABLE_COMPILATION`
- and :prop_tgt:`CUDA_RESOLVE_DEVICE_SYMBOLS` properties and
- policy :policy:`CMP0105`. This expression can only be used to specify link
- options.
-
-.. genex:: $<HOST_LINK:list>
-
- .. versionadded:: 3.18
-
- Returns the list if it is the normal link step, an empty list otherwise.
- This expression is mainly useful when a device link step is also involved
- (see :genex:`$<DEVICE_LINK:list>` generator expression). This expression can
- only be used to specify link options.
-
-.. genex:: $<LINK_LIBRARY:feature,library-list>
-
- .. versionadded:: 3.24
-
- Specify a set of libraries to link to a target, along with a ``feature``
- which provides details about *how* they should be linked. For example:
-
- .. code-block:: cmake
-
- add_library(lib1 STATIC ...)
- add_library(lib2 ...)
- target_link_libraries(lib2 PRIVATE "$<LINK_LIBRARY:WHOLE_ARCHIVE,lib1>")
-
- This specifies that ``lib2`` should link to ``lib1`` and use the
- ``WHOLE_ARCHIVE`` feature when doing so.
-
- Feature names are case-sensitive and may only contain letters, numbers and
- underscores. Feature names defined in all uppercase are reserved for CMake's
- own built-in features. The pre-defined built-in library features are:
-
- .. include:: ../variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
-
- Built-in and custom library features are defined in terms of the following
- variables:
-
- * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED`
- * :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>`
- * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED`
- * :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>`
-
- The value used for each of these variables is the value as set at the end of
- the directory scope in which the target was created. The usage is as follows:
-
- 1. If the language-specific
- :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable
- is true, the ``feature`` must be defined by the corresponding
- :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` variable.
- 2. If no language-specific ``feature`` is supported, then the
- :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>_SUPPORTED` variable must be
- true and the ``feature`` must be defined by the corresponding
- :variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variable.
-
- The following limitations should be noted:
-
- * The ``library-list`` can specify CMake targets or libraries.
- Any CMake target of type :ref:`OBJECT <Object Libraries>`
- or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect
- of the expression and instead be linked in the standard way.
-
- * The ``$<LINK_LIBRARY:...>`` generator expression can only be used to
- specify link libraries. In practice, this means it can appear in the
- :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`, and
- :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be
- specified in :command:`target_link_libraries` and :command:`link_libraries`
- commands.
-
- * If a ``$<LINK_LIBRARY:...>`` generator expression appears in the
- :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be
- included in the imported target generated by a :command:`install(EXPORT)`
- command. It is the responsibility of the environment consuming this
- import to define the link feature used by this expression.
- * Each target or library involved in the link step must have at most only
- one kind of library feature. The absence of a feature is also incompatible
- with all other features. For example:
+Export And Install Expressions
+------------------------------
- .. code-block:: cmake
-
- add_library(lib1 ...)
- add_library(lib2 ...)
- add_library(lib3 ...)
-
- # lib1 will be associated with feature1
- target_link_libraries(lib2 PUBLIC "$<LINK_LIBRARY:feature1,lib1>")
-
- # lib1 is being linked with no feature here. This conflicts with the
- # use of feature1 in the line above and would result in an error.
- target_link_libraries(lib3 PRIVATE lib1 lib2)
-
- Where it isn't possible to use the same feature throughout a build for a
- given target or library, the :prop_tgt:`LINK_LIBRARY_OVERRIDE` and
- :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties can be
- used to resolve such incompatibilities.
-
- * The ``$<LINK_LIBRARY:...>`` generator expression does not guarantee
- that the list of specified targets and libraries will be kept grouped
- together. To manage constructs like ``--start-group`` and ``--end-group``,
- as supported by the GNU ``ld`` linker, use the :genex:`LINK_GROUP`
- generator expression instead.
-
-.. genex:: $<LINK_GROUP:feature,library-list>
-
- .. versionadded:: 3.24
-
- Specify a group of libraries to link to a target, along with a ``feature``
- which defines how that group should be linked. For example:
-
- .. code-block:: cmake
-
- add_library(lib1 STATIC ...)
- add_library(lib2 ...)
- target_link_libraries(lib2 PRIVATE "$<LINK_GROUP:RESCAN,lib1,external>")
+.. genex:: $<INSTALL_INTERFACE:...>
- This specifies that ``lib2`` should link to ``lib1`` and ``external``, and
- that both of those two libraries should be included on the linker command
- line according to the definition of the ``RESCAN`` feature.
+ Content of ``...`` when the property is exported using
+ :command:`install(EXPORT)`, and empty otherwise.
- Feature names are case-sensitive and may only contain letters, numbers and
- underscores. Feature names defined in all uppercase are reserved for CMake's
- own built-in features. Currently, there is only one pre-defined built-in
- group feature:
-
- .. include:: ../variable/LINK_GROUP_PREDEFINED_FEATURES.txt
+.. genex:: $<BUILD_INTERFACE:...>
- Built-in and custom group features are defined in terms of the following
- variables:
+ Content of ``...`` when the property is exported using :command:`export`, or
+ when the target is used by another target in the same buildsystem. Expands to
+ the empty string otherwise.
- * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
- * :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>`
- * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED`
- * :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>`
+.. genex:: $<INSTALL_PREFIX>
- The value used for each of these variables is the value as set at the end of
- the directory scope in which the target was created. The usage is as follows:
+ Content of the install prefix when the target is exported via
+ :command:`install(EXPORT)`, or when evaluated in the
+ :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of
+ :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise.
- 1. If the language-specific
- :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable
- is true, the ``feature`` must be defined by the corresponding
- :variable:`CMAKE_<LANG>_LINK_GROUP_USING_<FEATURE>` variable.
- 2. If no language-specific ``feature`` is supported, then the
- :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>_SUPPORTED` variable must be
- true and the ``feature`` must be defined by the corresponding
- :variable:`CMAKE_LINK_GROUP_USING_<FEATURE>` variable.
+Multi-level Expression Evaluation
+---------------------------------
- The ``LINK_GROUP`` generator expression is compatible with the
- :genex:`LINK_LIBRARY` generator expression. The libraries involved in a
- group can be specified using the :genex:`LINK_LIBRARY` generator expression.
+.. genex:: $<GENEX_EVAL:expr>
- Each target or external library involved in the link step is allowed to be
- part of multiple groups, but only if all the groups involved specify the
- same ``feature``. Such groups will not be merged on the linker command line,
- the individual groups will still be preserved. Mixing different group
- features for the same target or library is forbidden.
+ .. versionadded:: 3.12
- .. code-block:: cmake
+ Content of ``expr`` evaluated as a generator expression in the current
+ context. This enables consumption of generator expressions whose
+ evaluation results itself in generator expressions.
- add_library(lib1 ...)
- add_library(lib2 ...)
- add_library(lib3 ...)
- add_library(lib4 ...)
- add_library(lib5 ...)
+.. genex:: $<TARGET_GENEX_EVAL:tgt,expr>
- target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
- target_link_libraries(lib4 PRIVATE "$<LINK_GROUP:feature1,lib1,lib3>")
- # lib4 will be linked with the groups {lib1,lib2} and {lib1,lib3}.
- # Both groups specify the same feature, so this is fine.
+ .. versionadded:: 3.12
- target_link_libraries(lib5 PRIVATE "$<LINK_GROUP:feature2,lib1,lib3>")
- # An error will be raised here because both lib1 and lib3 are part of two
- # groups with different features.
+ Content of ``expr`` evaluated as a generator expression in the context of
+ ``tgt`` target. This enables consumption of custom target properties that
+ themselves contain generator expressions.
- When a target or an external library is involved in the link step as part of
- a group and also as not part of any group, any occurrence of the non-group
- link item will be replaced by the groups it belongs to.
+ Having the capability to evaluate generator expressions is very useful when
+ you want to manage custom properties supporting generator expressions.
+ For example:
.. code-block:: cmake
- add_library(lib1 ...)
- add_library(lib2 ...)
- add_library(lib3 ...)
- add_library(lib4 ...)
-
- target_link_libraries(lib3 PUBLIC lib1)
-
- target_link_libraries(lib4 PRIVATE lib3 "$<LINK_GROUP:feature1,lib1,lib2>")
- # lib4 will only be linked with lib3 and the group {lib1,lib2}
+ add_library(foo ...)
- Because ``lib1`` is part of the group defined for ``lib4``, that group then
- gets applied back to the use of ``lib1`` for ``lib3``. The end result will
- be as though the linking relationship for ``lib3`` had been specified as:
+ set_property(TARGET foo PROPERTY
+ CUSTOM_KEYS $<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>
+ )
- .. code-block:: cmake
+ add_custom_target(printFooKeys
+ COMMAND ${CMAKE_COMMAND} -E echo $<TARGET_PROPERTY:foo,CUSTOM_KEYS>
+ )
- target_link_libraries(lib3 PUBLIC "$<LINK_GROUP:feature1,lib1,lib2>")
+ This naive implementation of the ``printFooKeys`` custom command is wrong
+ because ``CUSTOM_KEYS`` target property is not evaluated and the content
+ is passed as is (i.e. ``$<$<CONFIG:DEBUG>:FOO_EXTRA_THINGS>``).
- Be aware that the precedence of the group over the non-group link item can
- result in circular dependencies between groups. If this occurs, a fatal
- error is raised because circular dependencies are not allowed for groups.
+ To have the expected result (i.e. ``FOO_EXTRA_THINGS`` if config is
+ ``Debug``), it is required to evaluate the output of
+ ``$<TARGET_PROPERTY:foo,CUSTOM_KEYS>``:
.. code-block:: cmake
- add_library(lib1A ...)
- add_library(lib1B ...)
- add_library(lib2A ...)
- add_library(lib2B ...)
- add_library(lib3 ...)
-
- # Non-group linking relationships, these are non-circular so far
- target_link_libraries(lib1A PUBLIC lib2A)
- target_link_libraries(lib2B PUBLIC lib1B)
-
- # The addition of these groups creates circular dependencies
- target_link_libraries(lib3 PRIVATE
- "$<LINK_GROUP:feat,lib1A,lib1B>"
- "$<LINK_GROUP:feat,lib2A,lib2B>"
+ add_custom_target(printFooKeys
+ COMMAND ${CMAKE_COMMAND} -E
+ echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>>
)
- Because of the groups defined for ``lib3``, the linking relationships for
- ``lib1A`` and ``lib2B`` effectively get expanded to the equivalent of:
-
- .. code-block:: cmake
-
- target_link_libraries(lib1A PUBLIC "$<LINK_GROUP:feat,lib2A,lib2B>")
- target_link_libraries(lib2B PUBLIC "$<LINK_GROUP:feat,lib1A,lib1B>")
-
- This creates a circular dependency between groups:
- ``lib1A --> lib2B --> lib1A``.
-
- The following limitations should also be noted:
-
- * The ``library-list`` can specify CMake targets or libraries.
- Any CMake target of type :ref:`OBJECT <Object Libraries>`
- or :ref:`INTERFACE <Interface Libraries>` will ignore the feature aspect
- of the expression and instead be linked in the standard way.
-
- * The ``$<LINK_GROUP:...>`` generator expression can only be used to
- specify link libraries. In practice, this means it can appear in the
- :prop_tgt:`LINK_LIBRARIES`, :prop_tgt:`INTERFACE_LINK_LIBRARIES`,and
- :prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` target properties, and be
- specified in :command:`target_link_libraries` and :command:`link_libraries`
- commands.
-
- * If a ``$<LINK_GROUP:...>`` generator expression appears in the
- :prop_tgt:`INTERFACE_LINK_LIBRARIES` property of a target, it will be
- included in the imported target generated by a :command:`install(EXPORT)`
- command. It is the responsibility of the environment consuming this
- import to define the link feature used by this expression.
-
-.. genex:: $<INSTALL_INTERFACE:...>
-
- Content of ``...`` when the property is exported using :command:`install(EXPORT)`,
- and empty otherwise.
-
-.. genex:: $<BUILD_INTERFACE:...>
-
- Content of ``...`` when the property is exported using :command:`export`, or
- when the target is used by another target in the same buildsystem. Expands to
- the empty string otherwise.
-
-.. genex:: $<MAKE_C_IDENTIFIER:...>
-
- Content of ``...`` converted to a C identifier. The conversion follows the
- same behavior as :command:`string(MAKE_C_IDENTIFIER)`.
-
-.. genex:: $<TARGET_OBJECTS:objLib>
-
- .. versionadded:: 3.1
-
- List of objects resulting from build of ``objLib``.
-
-.. genex:: $<SHELL_PATH:...>
-
- .. versionadded:: 3.4
-
- Content of ``...`` converted to shell path style. For example, slashes are
- converted to backslashes in Windows shells and drive letters are converted
- to posix paths in MSYS shells. The ``...`` must be an absolute path.
-
- .. versionadded:: 3.14
- The ``...`` may be a :ref:`semicolon-separated list <CMake Language Lists>`
- of paths, in which case each path is converted individually and a result
- list is generated using the shell path separator (``:`` on POSIX and
- ``;`` on Windows). Be sure to enclose the argument containing this genex
- in double quotes in CMake source code so that ``;`` does not split arguments.
-
-.. genex:: $<OUTPUT_CONFIG:...>
-
- .. versionadded:: 3.20
-
- Only valid in :command:`add_custom_command` and :command:`add_custom_target`
- as the outer-most generator expression in an argument.
- With the :generator:`Ninja Multi-Config` generator, generator expressions
- in ``...`` are evaluated using the custom command's "output config".
- With other generators, the content of ``...`` is evaluated normally.
-
-.. genex:: $<COMMAND_CONFIG:...>
-
- .. versionadded:: 3.20
+Escaped Characters
+------------------
- Only valid in :command:`add_custom_command` and :command:`add_custom_target`
- as the outer-most generator expression in an argument.
- With the :generator:`Ninja Multi-Config` generator, generator expressions
- in ``...`` are evaluated using the custom command's "command config".
- With other generators, the content of ``...`` is evaluated normally.
+These expressions evaluate to specific string literals. Use them in place of
+the actual string literal where you need to prevent them from having their
+special meaning.
-Debugging
-=========
+.. genex:: $<ANGLE-R>
-Since generator expressions are evaluated during generation of the buildsystem,
-and not during processing of ``CMakeLists.txt`` files, it is not possible to
-inspect their result with the :command:`message()` command.
+ A literal ``>``. Used for example to compare strings that contain a ``>``.
-One possible way to generate debug messages is to add a custom target,
+.. genex:: $<COMMA>
-.. code-block:: cmake
+ A literal ``,``. Used for example to compare strings which contain a ``,``.
- add_custom_target(genexdebug COMMAND ${CMAKE_COMMAND} -E echo "$<...>")
+.. genex:: $<SEMICOLON>
-The shell command ``make genexdebug`` (invoked after execution of ``cmake``)
-would then print the result of ``$<...>``.
+ A literal ``;``. Used to prevent list expansion on an argument with ``;``.
-Another way is to write debug messages to a file:
+Deprecated Expressions
+----------------------
-.. code-block:: cmake
+.. genex:: $<CONFIGURATION>
- file(GENERATE OUTPUT filename CONTENT "$<...>")
+ Configuration name. Deprecated since CMake 3.0. Use :genex:`CONFIG` instead.
diff --git a/Help/manual/cmake-modules.7.rst b/Help/manual/cmake-modules.7.rst
index 93beea9f8d..9dd623ace9 100644
--- a/Help/manual/cmake-modules.7.rst
+++ b/Help/manual/cmake-modules.7.rst
@@ -236,6 +236,7 @@ They are normally called through the :command:`find_package` command.
/module/FindRuby
/module/FindSDL
/module/FindSDL_image
+ /module/FindSDL_gfx
/module/FindSDL_mixer
/module/FindSDL_net
/module/FindSDL_sound
diff --git a/Help/module/FindSDL_gfx.rst b/Help/module/FindSDL_gfx.rst
new file mode 100644
index 0000000000..e05d661ea6
--- /dev/null
+++ b/Help/module/FindSDL_gfx.rst
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/FindSDL_gfx.cmake
diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst
index 2240874e0f..24957d08d4 100644
--- a/Help/policy/CMP0097.rst
+++ b/Help/policy/CMP0097.rst
@@ -4,9 +4,11 @@ CMP0097
.. versionadded:: 3.16
:command:`ExternalProject_Add` with ``GIT_SUBMODULES ""`` initializes no
-submodules.
+submodules. The policy also applies to :command:`FetchContent_Declare`,
+which uses the same download and update features as
+:command:`ExternalProject_Add`.
-The module provides a ``GIT_SUBMODULES`` option which controls what submodules
+The commands provide a ``GIT_SUBMODULES`` option which controls what submodules
to initialize and update. Starting with CMake 3.16, explicitly setting
``GIT_SUBMODULES`` to an empty string means no submodules will be initialized
or updated.
diff --git a/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst b/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst
index a9af74d4bd..0749c6fb18 100644
--- a/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst
+++ b/Help/prop_tgt/LINK_LIBRARIES_ONLY_TARGETS.rst
@@ -8,6 +8,8 @@ Enforce that link items that can be target names are actually existing targets.
Set this property to a true value to enable additional checks on the contents
of the :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
target properties, typically populated by :command:`target_link_libraries`.
+Checks are also applied to libraries added to a target through the
+:prop_tgt:`INTERFACE_LINK_LIBRARIES_DIRECT` properties of its dependencies.
CMake will verify that link items that might be target names actually name
existing targets. An item is considered a possible target name if:
diff --git a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst
index 30c02f5b00..da461a79cd 100644
--- a/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst
+++ b/Help/prop_tgt/VERIFY_INTERFACE_HEADER_SETS.rst
@@ -7,13 +7,13 @@ Used to verify that all headers in a target's ``PUBLIC`` and ``INTERFACE``
header sets can be included on their own.
When this property is set to true, and the target is an object library, static
-library, shared library, or executable with exports enabled, and the target
-has one or more ``PUBLIC`` or ``INTERFACE`` header sets, an object library
-target named ``<target_name>_verify_interface_header_sets`` is created. This
-verification target has one source file per header in the ``PUBLIC`` and
-``INTERFACE`` header sets. Each source file only includes its associated
-header file. The verification target links against the original target to get
-all of its usage requirements. The verification target has its
+library, shared library, interface library, or executable with exports enabled,
+and the target has one or more ``PUBLIC`` or ``INTERFACE`` header sets, an
+object library target named ``<target_name>_verify_interface_header_sets`` is
+created. This verification target has one source file per header in the
+``PUBLIC`` and ``INTERFACE`` header sets. Each source file only includes its
+associated header file. The verification target links against the original
+target to get all of its usage requirements. The verification target has its
:prop_tgt:`EXCLUDE_FROM_ALL` and :prop_tgt:`DISABLE_PRECOMPILE_HEADERS`
properties set to true, and its :prop_tgt:`AUTOMOC`, :prop_tgt:`AUTORCC`,
:prop_tgt:`AUTOUIC`, and :prop_tgt:`UNITY_BUILD` properties set to false.
@@ -22,7 +22,13 @@ If the header's :prop_sf:`LANGUAGE` property is set, the value of that property
is used to determine the language with which to compile the header file.
Otherwise, if the target has any C++ sources, the header is compiled as C++.
Otherwise, if the target has any C sources, the header is compiled as C.
-Otherwise, the header file is not compiled.
+Otherwise, if C++ is enabled globally, the header is compiled as C++.
+Otherwise, if C is enabled globally, the header is compiled as C. Otherwise,
+the header file is not compiled.
+
+This property is initialized by the value of the
+:variable:`CMAKE_VERIFY_INTERFACE_HEADER_SETS` variable if it is set when
+a target is created.
If the project wishes to control which header sets are verified by this
-property, you can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`.
+property, it can set :prop_tgt:`INTERFACE_HEADER_SETS_TO_VERIFY`.
diff --git a/Help/release/3.22.rst b/Help/release/3.22.rst
index 5ae3b574fb..eba5d66c39 100644
--- a/Help/release/3.22.rst
+++ b/Help/release/3.22.rst
@@ -170,8 +170,8 @@ Changes made since CMake 3.22.0 include the following.
compatibility. The fix may be restored in a future version of CMake
via a policy.
-3.22.4, 3.22.5
---------------
+3.22.4, 3.22.5, 3.22.6
+----------------------
* These versions made no changes to documented features or interfaces.
Some implementation updates were made to support ecosystem changes
diff --git a/Help/release/3.23.rst b/Help/release/3.23.rst
index 70a6175dab..69898e9574 100644
--- a/Help/release/3.23.rst
+++ b/Help/release/3.23.rst
@@ -309,3 +309,10 @@ Changes made since CMake 3.23.0 include the following.
expected the ``CPACK_PACKAGEMAKER_CHOICES`` variable to be defined.
The old ``CPACK_PACKAGEMAKER_CHOICES`` variable is now also set to the
same content as it was before, but it is formally deprecated.
+
+3.23.3
+------
+
+* This version made no changes to documented features or interfaces.
+ Some implementation updates were made to support ecosystem changes
+ and/or fix regressions.
diff --git a/Help/release/3.24.rst b/Help/release/3.24.rst
index f9e66b64e1..02252e0c59 100644
--- a/Help/release/3.24.rst
+++ b/Help/release/3.24.rst
@@ -220,6 +220,10 @@ Modules
gained a ``NO_IMPLICIT_LINK_TO_MATLAB_LIBRARIES`` option to disable
automatic linking of MATLAB libraries.
+* The :module:`FindVulkan` module now supports components to select which
+ VulkanSDK tool and libraries to find in addition to the Vulkan SDK headers
+ and library.
+
* The :module:`FindZLIB` gained a new ``ZLIB_USE_STATIC_LIBS`` variable to
search only for static libraries.
diff --git a/Help/release/dev/FindVulkan-dxc.rst b/Help/release/dev/FindVulkan-dxc.rst
new file mode 100644
index 0000000000..e22f016f37
--- /dev/null
+++ b/Help/release/dev/FindVulkan-dxc.rst
@@ -0,0 +1,5 @@
+FindVulkan-dxc
+--------------
+
+* The :module:`FindVulkan` module gained support for a DirectX Shader Compiler
+ component, ``dxc``.
diff --git a/Help/release/dev/try_run_split_output.rst b/Help/release/dev/try_run_split_output.rst
new file mode 100644
index 0000000000..98aedd64f6
--- /dev/null
+++ b/Help/release/dev/try_run_split_output.rst
@@ -0,0 +1,6 @@
+try_run_split_output
+--------------------
+
+* The :command:`try_run` command gained ``RUN_OUTPUT_STDOUT_VARIABLE``
+ and ``RUN_OUTPUT_STDERR_VARIABLE`` options to capture stdout and stderr
+ separately from the output of the compiled program.
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index df40c859d4..27070c05a4 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -1189,19 +1189,18 @@ function(FetchContent_Declare contentName)
)
endif()
- set(options "")
+ # Because we are only looking for a subset of the supported keywords, we
+ # cannot check for multi-value arguments with this method. We will have to
+ # handle the URL keyword differently.
set(oneValueArgs
SVN_REPOSITORY
DOWNLOAD_NO_EXTRACT
DOWNLOAD_EXTRACT_TIMESTAMP
- URL
BINARY_DIR
SOURCE_DIR
)
- set(multiValueArgs "")
- cmake_parse_arguments(PARSE_ARGV 1 ARG
- "${options}" "${oneValueArgs}" "${multiValueArgs}")
+ cmake_parse_arguments(PARSE_ARGV 1 ARG "" "${oneValueArgs}" "")
string(TOLOWER ${contentName} contentNameLower)
@@ -1230,31 +1229,45 @@ function(FetchContent_Declare contentName)
# explicitly set the relevant option if not already provided. The condition
# here is essentially an abbreviated version of the logic in
# ExternalProject's _ep_add_download_command() function.
- if(ARG_URL AND
- NOT IS_DIRECTORY "${ARG_URL}" AND
- NOT ARG_DOWNLOAD_NO_EXTRACT AND
+ if(NOT ARG_DOWNLOAD_NO_EXTRACT AND
NOT DEFINED ARG_DOWNLOAD_EXTRACT_TIMESTAMP)
- cmake_policy(GET CMP0135 _FETCHCONTENT_CMP0135
- PARENT_SCOPE # undocumented, do not use outside of CMake
- )
- if(_FETCHCONTENT_CMP0135 STREQUAL "")
- message(AUTHOR_WARNING
- "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy "
- "CMP0135 is not set. The policy's OLD behavior will be used. "
- "When using a URL download, the timestamps of extracted files "
- "should preferably be that of the time of extraction, otherwise "
- "code that depends on the extracted contents might not be "
- "rebuilt if the URL changes. The OLD behavior preserves the "
- "timestamps from the archive instead, but this is usually not "
- "what you want. Update your project to the NEW behavior or "
- "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of "
- "true to avoid this robustness issue."
- )
- set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
- elseif(_FETCHCONTENT_CMP0135 STREQUAL "NEW")
- set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP FALSE)
- else()
- set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
+ list(FIND ARGN URL urlIndex)
+ if(urlIndex GREATER_EQUAL 0)
+ math(EXPR urlIndex "${urlIndex} + 1")
+ list(LENGTH ARGN numArgs)
+ if(urlIndex GREATER_EQUAL numArgs)
+ message(FATAL_ERROR
+ "URL keyword needs to be followed by at least one URL"
+ )
+ endif()
+ # If we have multiple URLs, none of them are allowed to be local paths.
+ # Therefore, we can test just the first URL, and if it is non-local, so
+ # will be the others if there are more.
+ list(GET ARGN ${urlIndex} firstUrl)
+ if(NOT IS_DIRECTORY "${firstUrl}")
+ cmake_policy(GET CMP0135 _FETCHCONTENT_CMP0135
+ PARENT_SCOPE # undocumented, do not use outside of CMake
+ )
+ if(_FETCHCONTENT_CMP0135 STREQUAL "")
+ message(AUTHOR_WARNING
+ "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy "
+ "CMP0135 is not set. The policy's OLD behavior will be used. "
+ "When using a URL download, the timestamps of extracted files "
+ "should preferably be that of the time of extraction, otherwise "
+ "code that depends on the extracted contents might not be "
+ "rebuilt if the URL changes. The OLD behavior preserves the "
+ "timestamps from the archive instead, but this is usually not "
+ "what you want. Update your project to the NEW behavior or "
+ "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of "
+ "true to avoid this robustness issue."
+ )
+ set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
+ elseif(_FETCHCONTENT_CMP0135 STREQUAL "NEW")
+ set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP FALSE)
+ else()
+ set(ARG_DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
+ endif()
+ endif()
endif()
endif()
diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake
index 279e2634c4..acb87dc605 100644
--- a/Modules/FindCURL.cmake
+++ b/Modules/FindCURL.cmake
@@ -82,7 +82,6 @@ find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_CURL QUIET libcurl)
if(PC_CURL_FOUND)
- set(CURL_VERSION_STRING ${PC_CURL_VERSION})
pkg_get_variable(CURL_SUPPORTED_PROTOCOLS libcurl supported_protocols)
pkg_get_variable(CURL_SUPPORTED_FEATURES libcurl supported_features)
endif()
@@ -122,7 +121,7 @@ if(NOT CURL_LIBRARY)
select_library_configurations(CURL)
endif()
-if(CURL_INCLUDE_DIR AND NOT CURL_VERSION_STRING)
+if(CURL_INCLUDE_DIR)
foreach(_curl_version_header curlver.h curl.h)
if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
diff --git a/Modules/FindCoin3D.cmake b/Modules/FindCoin3D.cmake
index 301e70bd6e..5910ad1e47 100644
--- a/Modules/FindCoin3D.cmake
+++ b/Modules/FindCoin3D.cmake
@@ -31,11 +31,11 @@ if (WIN32)
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/include"
)
- find_library(COIN3D_LIBRARY_DEBUG coin2d
+ find_library(COIN3D_LIBRARY_DEBUG NAMES coin2d coin4d
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib"
)
- find_library(COIN3D_LIBRARY_RELEASE coin2
+ find_library(COIN3D_LIBRARY_RELEASE NAMES coin2 coin4
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib"
)
diff --git a/Modules/FindGTest.cmake b/Modules/FindGTest.cmake
index 60bb401032..92334e44e8 100644
--- a/Modules/FindGTest.cmake
+++ b/Modules/FindGTest.cmake
@@ -314,7 +314,7 @@ if(GTest_FOUND)
__gtest_define_backwards_compatible_library_targets()
endif()
-if(GMock_FOUND)
+if(GMock_FOUND AND GTest_FOUND)
if(NOT TARGET GTest::gmock)
__gtest_determine_library_type(GMOCK_LIBRARY)
add_library(GTest::gmock ${GMOCK_LIBRARY_TYPE} IMPORTED)
diff --git a/Modules/FindOpenACC.cmake b/Modules/FindOpenACC.cmake
index cf58f3b807..71bd84ff0e 100644
--- a/Modules/FindOpenACC.cmake
+++ b/Modules/FindOpenACC.cmake
@@ -27,6 +27,13 @@ The module provides :prop_tgt:`IMPORTED` targets:
Variables
^^^^^^^^^
+The module defines the following variables:
+
+``OpenACC_FOUND``
+ .. versionadded:: 3.25
+
+ Variable indicating that OpenACC flags for at least one languages have been found.
+
This module will set the following variables per language in your
project, where ``<lang>`` is one of C, CXX, or Fortran:
@@ -270,6 +277,9 @@ foreach (LANG IN ITEMS C CXX Fortran)
REQUIRED_VARS OpenACC_${LANG}_FLAGS
VERSION_VAR OpenACC_${LANG}_VERSION
)
+ if(OpenACC_${LANG}_FOUND)
+ set(OpenACC_FOUND TRUE)
+ endif()
endif()
endforeach()
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake
index d19624e3a3..844ceb36a4 100644
--- a/Modules/FindOpenMP.cmake
+++ b/Modules/FindOpenMP.cmake
@@ -279,7 +279,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
DOC "Path to the ${_OPENMP_IMPLICIT_LIB_PLAIN} library for OpenMP"
HINTS ${OpenMP_${LANG}_IMPLICIT_LINK_DIRS}
CMAKE_FIND_ROOT_PATH_BOTH
- NO_DEFAULT_PATH
+ NO_PACKAGE_ROOT_PATH
+ NO_CMAKE_PATH
+ NO_CMAKE_ENVIRONMENT_PATH
)
endif()
mark_as_advanced(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY)
@@ -477,10 +479,14 @@ foreach(LANG IN ITEMS C CXX)
if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND"
OR NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
_OPENMP_GET_FLAGS("${LANG}" "${LANG}" OpenMP_${LANG}_FLAGS_WORK OpenMP_${LANG}_LIB_NAMES_WORK)
- set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}"
- CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE)
- set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}"
- CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE)
+ if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND")
+ set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}"
+ CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE)
+ endif()
+ if(NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
+ set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}"
+ CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE)
+ endif()
mark_as_advanced(OpenMP_${LANG}_FLAGS OpenMP_${LANG}_LIB_NAMES)
endif()
endif()
@@ -496,10 +502,14 @@ if(CMAKE_Fortran_COMPILER_LOADED)
set(OpenMP_Fortran_HAVE_OMPLIB_MODULE TRUE CACHE BOOL INTERNAL "")
endif()
- set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
- CACHE STRING "Fortran compiler flags for OpenMP parallelization")
- set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
- CACHE STRING "Fortran compiler libraries for OpenMP parallelization")
+ if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND")
+ set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
+ CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE)
+ endif()
+ if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND")
+ set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
+ CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE)
+ endif()
mark_as_advanced(OpenMP_Fortran_FLAGS OpenMP_Fortran_LIB_NAMES)
endif()
@@ -512,11 +522,14 @@ if(CMAKE_Fortran_COMPILER_LOADED)
set(OpenMP_Fortran_HAVE_OMPLIB_HEADER TRUE CACHE BOOL INTERNAL "")
endif()
- set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
- CACHE STRING "Fortran compiler flags for OpenMP parallelization")
-
- set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES}"
- CACHE STRING "Fortran compiler libraries for OpenMP parallelization")
+ if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND")
+ set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
+ CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE)
+ endif()
+ if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND")
+ set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
+ CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE)
+ endif()
endif()
if(OpenMP_Fortran_HAVE_OMPLIB_MODULE)
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 53519ba65f..7a127e4b40 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -2871,7 +2871,15 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
- if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS
+ set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE)
+ foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module Embed)
+ string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID)
+ if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+ AND NOT "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS)
+ set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE)
+ endif()
+ endforeach()
+ if (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED
AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
# Don't search for include dir if no library was founded
break()
diff --git a/Modules/FindSDL_gfx.cmake b/Modules/FindSDL_gfx.cmake
new file mode 100644
index 0000000000..2dd96e98fd
--- /dev/null
+++ b/Modules/FindSDL_gfx.cmake
@@ -0,0 +1,86 @@
+# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FindSDL_gfx
+-----------
+
+.. versionadded:: 3.25
+
+Locate SDL_gfx library
+
+This module defines:
+
+::
+
+ SDL::SDL_gfx, the name of the target to use with target_*() commands
+ SDL_GFX_LIBRARIES, the name of the library to link against
+ SDL_GFX_INCLUDE_DIRS, where to find the headers
+ SDL_GFX_FOUND, if false, do not try to link against
+ SDL_GFX_VERSION_STRING - human-readable string containing the
+ version of SDL_gfx
+
+``$SDLDIR`` is an environment variable that would correspond to the
+``./configure --prefix=$SDLDIR`` used in building SDL.
+#]=======================================================================]
+
+find_path(SDL_GFX_INCLUDE_DIRS
+ NAMES
+ SDL_framerate.h
+ SDL_gfxBlitFunc.h
+ SDL_gfxPrimitives.h
+ SDL_gfxPrimitives_font.h
+ SDL_imageFilter.h
+ SDL_rotozoom.h
+ HINTS
+ ENV SDLGFXDIR
+ ENV SDLDIR
+ PATH_SUFFIXES SDL
+ # path suffixes to search inside ENV{SDLDIR}
+ include/SDL include/SDL12 include/SDL11 include
+)
+
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(VC_LIB_PATH_SUFFIX lib/x64)
+else()
+ set(VC_LIB_PATH_SUFFIX lib/x86)
+endif()
+
+find_library(SDL_GFX_LIBRARIES
+ NAMES SDL_gfx
+ HINTS
+ ENV SDLGFXDIR
+ ENV SDLDIR
+ PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX}
+)
+
+if(SDL_GFX_INCLUDE_DIRS AND EXISTS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h")
+ file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+[0-9]+$")
+ file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+[0-9]+$")
+ file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+[0-9]+$")
+ string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MAJOR "${SDL_GFX_VERSION_MAJOR_LINE}")
+ string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MINOR "${SDL_GFX_VERSION_MINOR_LINE}")
+ string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_PATCH "${SDL_GFX_VERSION_PATCH_LINE}")
+ set(SDL_GFX_VERSION_STRING ${SDL_GFX_VERSION_MAJOR}.${SDL_GFX_VERSION_MINOR}.${SDL_GFX_VERSION_PATCH})
+ unset(SDL_GFX_VERSION_MAJOR_LINE)
+ unset(SDL_GFX_VERSION_MINOR_LINE)
+ unset(SDL_GFX_VERSION_PATCH_LINE)
+ unset(SDL_GFX_VERSION_MAJOR)
+ unset(SDL_GFX_VERSION_MINOR)
+ unset(SDL_GFX_VERSION_PATCH)
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_gfx
+ REQUIRED_VARS SDL_GFX_LIBRARIES SDL_GFX_INCLUDE_DIRS
+ VERSION_VAR SDL_GFX_VERSION_STRING)
+
+if(SDL_gfx_FOUND)
+ if(NOT TARGET SDL::SDL_gfx)
+ add_library(SDL::SDL_gfx INTERFACE IMPORTED)
+ set_target_properties(SDL::SDL_gfx PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${SDL_GFX_INCLUDE_DIRS}"
+ INTERFACE_LINK_LIBRARIES "${SDL_GFX_LIBRARIES}")
+ endif()
+endif()
diff --git a/Modules/FindSDL_image.cmake b/Modules/FindSDL_image.cmake
index e687b49836..324fef5d29 100644
--- a/Modules/FindSDL_image.cmake
+++ b/Modules/FindSDL_image.cmake
@@ -31,10 +31,6 @@ For backward compatibility the following variables are also set:
$SDLDIR is an environment variable that would correspond to the
./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing. This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
#]=======================================================================]
if(NOT SDL_IMAGE_INCLUDE_DIR AND SDLIMAGE_INCLUDE_DIR)
diff --git a/Modules/FindSDL_mixer.cmake b/Modules/FindSDL_mixer.cmake
index 315400a30e..8ed3cb4ba5 100644
--- a/Modules/FindSDL_mixer.cmake
+++ b/Modules/FindSDL_mixer.cmake
@@ -31,10 +31,6 @@ For backward compatibility the following variables are also set:
$SDLDIR is an environment variable that would correspond to the
./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing. This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
#]=======================================================================]
if(NOT SDL_MIXER_INCLUDE_DIR AND SDLMIXER_INCLUDE_DIR)
diff --git a/Modules/FindSDL_net.cmake b/Modules/FindSDL_net.cmake
index 28cb4d6c21..639e5bd78f 100644
--- a/Modules/FindSDL_net.cmake
+++ b/Modules/FindSDL_net.cmake
@@ -30,10 +30,6 @@ For backward compatibility the following variables are also set:
$SDLDIR is an environment variable that would correspond to the
./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing. This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
#]=======================================================================]
if(NOT SDL_NET_INCLUDE_DIR AND SDLNET_INCLUDE_DIR)
diff --git a/Modules/FindSDL_sound.cmake b/Modules/FindSDL_sound.cmake
index 8d2f9f8fc4..68075229e6 100644
--- a/Modules/FindSDL_sound.cmake
+++ b/Modules/FindSDL_sound.cmake
@@ -54,7 +54,19 @@ Typically, you should not use these variables directly, and you should
use SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other
audio libraries (if needed) to successfully compile on your system.
-Created by Eric Wing. This module is a bit more complicated than the
+Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that
+would correspond to the ./configure --prefix=$SDLDIR used in building
+SDL.
+
+On OSX, this will prefer the Framework version (if found) over others.
+People will have to manually change the cache values of SDL_LIBRARY to
+override this selectionor set the CMake environment CMAKE_INCLUDE_PATH
+to modify the search paths.
+#]=======================================================================]
+
+
+#[[
+This module is a bit more complicated than the
other FindSDL* family modules. The reason is that SDL_sound can be
compiled in a large variety of different ways which are independent of
platform. SDL_sound may dynamically link against other 3rd party
@@ -70,16 +82,7 @@ This module uses a brute force approach to create a test program that
uses SDL_sound, and then tries to build it. If the build fails, it
parses the error output for known symbol names to figure out which
libraries are needed.
-
-Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that
-would correspond to the ./configure --prefix=$SDLDIR used in building
-SDL.
-
-On OSX, this will prefer the Framework version (if found) over others.
-People will have to manually change the cache values of SDL_LIBRARY to
-override this selectionor set the CMake environment CMAKE_INCLUDE_PATH
-to modify the search paths.
-#]=======================================================================]
+#]]
set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags")
mark_as_advanced(SDL_SOUND_EXTRAS)
diff --git a/Modules/FindSDL_ttf.cmake b/Modules/FindSDL_ttf.cmake
index d5721da0a7..d67c08930e 100644
--- a/Modules/FindSDL_ttf.cmake
+++ b/Modules/FindSDL_ttf.cmake
@@ -30,10 +30,6 @@ For backward compatibility the following variables are also set:
$SDLDIR is an environment variable that would correspond to the
./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing. This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
#]=======================================================================]
if(NOT SDL_TTF_INCLUDE_DIR AND SDLTTF_INCLUDE_DIR)
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake
index 78b07c41d7..2f69234c7b 100644
--- a/Modules/FindVulkan.cmake
+++ b/Modules/FindVulkan.cmake
@@ -15,10 +15,34 @@ Optional COMPONENTS
.. versionadded:: 3.24
-This module respects several optional COMPONENTS: ``glslc``,
-``glslangValidator``, ``glslang``, ``shaderc_combined`` and ``SPIRV-Tools``.
-On macOS, an additional component ``MoltenVK`` is available.
-There are corresponding import targets for each of these flags.
+This module respects several optional COMPONENTS.
+There are corresponding imported targets for each of these.
+
+``glslc``
+ The SPIR-V compiler.
+
+``glslangValidator``
+ The ``glslangValidator`` tool.
+
+``glslang``
+ The SPIR-V generator library.
+
+``shaderc_combined``
+ The static library for Vulkan shader compilation.
+
+``SPIRV-Tools``
+ Tools to process SPIR-V modules.
+
+``MoltenVK``
+ On macOS, an additional component ``MoltenVK`` is available.
+
+``dxc``
+ .. versionadded:: 3.25
+
+ The DirectX Shader Compiler.
+
+The ``glslc`` and ``glslangValidator`` components are provided even
+if not explicitly requested (for backward compatibility).
IMPORTED Targets
^^^^^^^^^^^^^^^^
@@ -75,6 +99,16 @@ This module defines :prop_tgt:`IMPORTED` targets if Vulkan has been found:
Defined if SDK has the Vulkan meta-loader (volk).
+``Vulkan::dxc_lib``
+ .. versionadded:: 3.25
+
+ Defined if SDK has the DirectX shader compiler library.
+
+``Vulkan::dxc_exe``
+ .. versionadded:: 3.25
+
+ Defined if SDK has the DirectX shader compiler CLI tool.
+
Result Variables
^^^^^^^^^^^^^^^^
@@ -119,6 +153,17 @@ This module defines the following variables:
True, if the SDK has the volk library.
+``Vulkan_dxc_lib_FOUND``
+ .. versionadded:: 3.25
+
+ True, if the SDK has the DirectX shader compiler library.
+
+``Vulkan_dxc_exe_FOUND``
+ .. versionadded:: 3.25
+
+ True, if the SDK has the DirectX shader compiler CLI tool.
+
+
The module will also defines these cache variables:
``Vulkan_INCLUDE_DIR``
@@ -151,6 +196,16 @@ The module will also defines these cache variables:
Path to the volk library.
+``Vulkan_dxc_LIBRARY``
+ .. versionadded:: 3.25
+
+ Path to the DirectX shader compiler library.
+
+``Vulkan_dxc_EXECUTABLE``
+ .. versionadded:: 3.25
+
+ Path to the DirectX shader compiler CLI tool.
+
Hints
^^^^^
@@ -396,6 +451,20 @@ if(volk IN_LIST Vulkan_FIND_COMPONENTS)
mark_as_advanced(Vulkan_Volk_LIBRARY)
endif()
+if (dxc IN_LIST Vulkan_FIND_COMPONENTS)
+ find_library(Vulkan_dxc_LIBRARY
+ NAMES dxcompiler
+ HINTS
+ ${_Vulkan_hint_library_search_paths})
+ mark_as_advanced(Vulkan_dxc_LIBRARY)
+
+ find_program(Vulkan_dxc_EXECUTABLE
+ NAMES dxc
+ HINTS
+ ${_Vulkan_hint_executable_search_paths})
+ mark_as_advanced(Vulkan_dxc_EXECUTABLE)
+endif()
+
if(Vulkan_GLSLC_EXECUTABLE)
set(Vulkan_glslc_FOUND TRUE)
else()
@@ -408,6 +477,12 @@ else()
set(Vulkan_glslangValidator_FOUND FALSE)
endif()
+if (Vulkan_dxc_EXECUTABLE)
+ set(Vulkan_dxc_exe_FOUND TRUE)
+else()
+ set(Vulkan_dxc_exe_FOUND FALSE)
+endif()
+
function(_Vulkan_set_library_component_found component)
cmake_parse_arguments(PARSE_ARGV 1 _ARG
"NO_WARNING"
@@ -459,6 +534,7 @@ _Vulkan_set_library_component_found(glslang
_Vulkan_set_library_component_found(shaderc_combined)
_Vulkan_set_library_component_found(SPIRV-Tools)
_Vulkan_set_library_component_found(volk)
+_Vulkan_set_library_component_found(dxc)
if(Vulkan_MoltenVK_INCLUDE_DIR AND Vulkan_MoltenVK_LIBRARY)
set(Vulkan_MoltenVK_FOUND TRUE)
@@ -764,6 +840,25 @@ if(Vulkan_FOUND)
IMPORTED_LINK_INTERFACE_LIBRARIES dl)
endif()
endif()
+
+ if (Vulkan_dxc_LIBRARY AND NOT TARGET Vulkan::dxc_lib)
+ add_library(Vulkan::dxc_lib STATIC IMPORTED)
+ set_property(TARGET Vulkan::dxc_lib
+ PROPERTY
+ INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
+ set_property(TARGET Vulkan::dxc_lib APPEND
+ PROPERTY
+ IMPORTED_CONFIGURATIONS Release)
+ set_property(TARGET Vulkan::dxc_lib APPEND
+ PROPERTY
+ IMPORTED_LOCATION_RELEASE "${Vulkan_dxc_LIBRARY}")
+ endif()
+
+ if(Vulkan_dxc_EXECUTABLE AND NOT TARGET Vulkan::dxc_exe)
+ add_executable(Vulkan::dxc_exe IMPORTED)
+ set_property(TARGET Vulkan::dxc_exe PROPERTY IMPORTED_LOCATION "${Vulkan_dxc_EXECUTABLE}")
+ endif()
+
endif()
if(Vulkan_MoltenVK_FOUND)
diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake
index 5a532c7ccb..a9aa8e0220 100644
--- a/Modules/Platform/AIX-GNU.cmake
+++ b/Modules/Platform/AIX-GNU.cmake
@@ -23,11 +23,11 @@ macro(__aix_compiler_gnu lang)
# Construct the export list ourselves to pass only the object files so
# that we export only the symbols actually provided by the sources.
set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
- "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS> <OBJECTS>"
+ "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS> <OBJECTS>"
"<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
)
set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
- "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
+ "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>"
"<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
endmacro()
diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake
index 2a8c159afb..902cbb3aab 100644
--- a/Modules/Platform/AIX-XL.cmake
+++ b/Modules/Platform/AIX-XL.cmake
@@ -29,12 +29,12 @@ macro(__aix_compiler_xl lang)
# Construct the export list ourselves to pass only the object files so
# that we export only the symbols actually provided by the sources.
set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
- "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS>${_OBJECTS}"
+ "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS>${_OBJECTS}"
"<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
)
set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
- "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
+ "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>"
"<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
unset(_OBJECTS)
diff --git a/Modules/Platform/AIX/ExportImportList b/Modules/Platform/AIX/ExportImportList
index 891bce76d9..5e16fcb4c6 100755
--- a/Modules/Platform/AIX/ExportImportList
+++ b/Modules/Platform/AIX/ExportImportList
@@ -5,7 +5,7 @@
# This script is internal to CMake and meant only to be
# invoked by CMake-generated build systems on AIX.
-usage='usage: ExportImportList -o <out-file> [-l <lib>] [-n] [--] <objects>...'
+usage='usage: ExportImportList -o <out-file> -c <compiler> [-l <lib>] [-n] [--] <objects>...'
die() {
echo "$@" 1>&2; exit 1
@@ -15,11 +15,13 @@ die() {
out=''
lib=''
no_objects=''
+compiler=''
while test "$#" != 0; do
case "$1" in
-l) shift; lib="$1" ;;
-o) shift; out="$1" ;;
-n) no_objects='1' ;;
+ -c) shift; compiler="$1" ;;
--) shift; break ;;
-*) die "$usage" ;;
*) break ;;
@@ -27,27 +29,47 @@ while test "$#" != 0; do
shift
done
test -n "$out" || die "$usage"
+# We need the compiler executable to resolve where the ibm-llvm-nm executable is
+test -n "$compiler" || die "$usage"
# Build a temporary file that atomically replaces the output later.
out_tmp="$out.tmp$$"
trap 'rm -f "$out_tmp"' EXIT INT TERM
> "$out_tmp"
+# If IPA was enabled and a compiler from the IBMClang family is used, then
+# the object files contain LLVM bitcode[0] rather than XCOFF objects and so
+# need to be handled differently.
+#
+# [0]: https://www.ibm.com/docs/en/openxl-c-and-cpp-aix/17.1.0?topic=compatibility-link-time-optimization-lto
+NM="$(dirname "$compiler")/../libexec/ibm-llvm-nm"
+
+function IsBitcode {
+ # N4 = first 4 bytes, -tx = output in hexadecimal, -An = don't display offset
+ # cut: trim off the preceding whitespace where the offset would be
+ # 4243code is the hexadecimal magic number for LLVM bitcode
+ [ "$(od -N4 -tx -An $1 | cut -d ' ' -f 2)" == "4243c0de" ];
+}
+
# Collect symbols exported from all object files.
if test -z "$no_objects"; then
for f in "$@"; do
- dump -tov -X 32_64 "$f" |
- awk '
- BEGIN {
- V["EXPORTED"]=" export"
- V["PROTECTED"]=" protected"
- }
- /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / {
- if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) {
- print $NF V[$(NF-1)]
+ if IsBitcode "$f"; then
+ "$NM" "$f" --defined-only --extern-only --just-symbol-name 2>/dev/null
+ else
+ dump -tov -X 32_64 "$f" |
+ awk '
+ BEGIN {
+ V["EXPORTED"]=" export"
+ V["PROTECTED"]=" protected"
+ }
+ /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / {
+ if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) {
+ print $NF V[$(NF-1)]
+ }
}
- }
- '
+ '
+ fi
done >> "$out_tmp"
fi
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 0862ef7a41..5190ab1d51 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 24)
-set(CMake_VERSION_PATCH 20220713)
+set(CMake_VERSION_PATCH 20220803)
#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx
index af365ad729..be952cd26d 100644
--- a/Source/CTest/cmCTestHandlerCommand.cxx
+++ b/Source/CTest/cmCTestHandlerCommand.cxx
@@ -82,14 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
// Process input arguments.
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> parsedKeywords;
- this->Parse(args, &unparsedArguments, /*keywordsMissingValue=*/nullptr,
- &parsedKeywords);
+ this->Parse(args, &unparsedArguments);
this->CheckArguments();
- std::sort(parsedKeywords.begin(), parsedKeywords.end());
- auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end());
- if (it != parsedKeywords.end()) {
+ std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end());
+ auto it = std::adjacent_find(this->ParsedKeywords.begin(),
+ this->ParsedKeywords.end());
+ if (it != this->ParsedKeywords.end()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Called with more than one value for ", *it));
@@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*)
void cmCTestHandlerCommand::BindArguments()
{
+ this->BindParsedKeywords(this->ParsedKeywords);
this->Bind("APPEND"_s, this->Append);
this->Bind("QUIET"_s, this->Quiet);
this->Bind("RETURN_VALUE"_s, this->ReturnValue);
diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h
index 31942c8feb..ed6d9af581 100644
--- a/Source/CTest/cmCTestHandlerCommand.h
+++ b/Source/CTest/cmCTestHandlerCommand.h
@@ -7,6 +7,8 @@
#include <string>
#include <vector>
+#include <cm/string_view>
+
#include "cmArgumentParser.h"
#include "cmCTestCommand.h"
@@ -44,6 +46,7 @@ protected:
virtual void BindArguments();
virtual void CheckArguments();
+ std::vector<cm::string_view> ParsedKeywords;
bool Append = false;
bool Quiet = false;
std::string CaptureCMakeError;
diff --git a/Source/cmArgumentParser.cxx b/Source/cmArgumentParser.cxx
index f0c4cfcbe0..614d00d680 100644
--- a/Source/cmArgumentParser.cxx
+++ b/Source/cmArgumentParser.cxx
@@ -5,10 +5,13 @@
#include <algorithm>
#include "cmArgumentParserTypes.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
namespace ArgumentParser {
-auto ActionMap::Emplace(cm::string_view name, Action action)
+auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action)
-> std::pair<iterator, bool>
{
auto const it =
@@ -21,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action)
: std::make_pair(this->emplace(it, name, std::move(action)), true);
}
-auto ActionMap::Find(cm::string_view name) const -> const_iterator
+auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator
{
auto const it =
std::lower_bound(this->begin(), this->end(), name,
@@ -31,82 +34,168 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator
return (it != this->end() && it->first == name) ? it : this->end();
}
+auto PositionActionMap::Emplace(std::size_t pos, PositionAction action)
+ -> std::pair<iterator, bool>
+{
+ auto const it = std::lower_bound(
+ this->begin(), this->end(), pos,
+ [](value_type const& elem, std::size_t k) { return elem.first < k; });
+ return (it != this->end() && it->first == pos)
+ ? std::make_pair(it, false)
+ : std::make_pair(this->emplace(it, pos, std::move(action)), true);
+}
+
+auto PositionActionMap::Find(std::size_t pos) const -> const_iterator
+{
+ auto const it = std::lower_bound(
+ this->begin(), this->end(), pos,
+ [](value_type const& elem, std::size_t k) { return elem.first < k; });
+ return (it != this->end() && it->first == pos) ? it : this->end();
+}
+
+void Instance::Bind(std::function<Continue(cm::string_view)> f,
+ ExpectAtLeast expect)
+{
+ this->KeywordValueFunc = std::move(f);
+ this->KeywordValuesExpected = expect.Count;
+}
+
void Instance::Bind(bool& val)
{
val = true;
- this->CurrentString = nullptr;
- this->CurrentList = nullptr;
- this->ExpectValue = false;
+ this->Bind(nullptr, ExpectAtLeast{ 0 });
}
void Instance::Bind(std::string& val)
{
- this->CurrentString = &val;
- this->CurrentList = nullptr;
- this->ExpectValue = true;
+ this->Bind(
+ [&val](cm::string_view arg) -> Continue {
+ val = std::string(arg);
+ return Continue::No;
+ },
+ ExpectAtLeast{ 1 });
+}
+
+void Instance::Bind(NonEmpty<std::string>& val)
+{
+ this->Bind(
+ [this, &val](cm::string_view arg) -> Continue {
+ if (arg.empty() && this->ParseResults) {
+ this->ParseResults->AddKeywordError(this->Keyword,
+ " empty string not allowed\n");
+ }
+ val.assign(std::string(arg));
+ return Continue::No;
+ },
+ ExpectAtLeast{ 1 });
}
void Instance::Bind(Maybe<std::string>& val)
{
- this->CurrentString = &val;
- this->CurrentList = nullptr;
- this->ExpectValue = false;
+ this->Bind(
+ [&val](cm::string_view arg) -> Continue {
+ static_cast<std::string&>(val) = std::string(arg);
+ return Continue::No;
+ },
+ ExpectAtLeast{ 0 });
}
void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val)
{
- this->CurrentString = nullptr;
- this->CurrentList = &val;
- this->ExpectValue = false;
+ this->Bind(
+ [&val](cm::string_view arg) -> Continue {
+ val.emplace_back(arg);
+ return Continue::Yes;
+ },
+ ExpectAtLeast{ 0 });
}
void Instance::Bind(NonEmpty<std::vector<std::string>>& val)
{
- this->CurrentString = nullptr;
- this->CurrentList = &val;
- this->ExpectValue = true;
+ this->Bind(
+ [&val](cm::string_view arg) -> Continue {
+ val.emplace_back(arg);
+ return Continue::Yes;
+ },
+ ExpectAtLeast{ 1 });
}
-void Instance::Bind(std::vector<std::vector<std::string>>& val)
+void Instance::Bind(std::vector<std::vector<std::string>>& multiVal)
{
- this->CurrentString = nullptr;
- this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back());
- this->ExpectValue = false;
+ multiVal.emplace_back();
+ std::vector<std::string>& val = multiVal.back();
+ this->Bind(
+ [&val](cm::string_view arg) -> Continue {
+ val.emplace_back(arg);
+ return Continue::Yes;
+ },
+ ExpectAtLeast{ 0 });
}
-void Instance::Consume(cm::string_view arg, void* result,
- std::vector<std::string>* unparsedArguments,
- std::vector<cm::string_view>* keywordsMissingValue,
- std::vector<cm::string_view>* parsedKeywords)
+void Instance::Consume(std::size_t pos, cm::string_view arg)
{
- auto const it = this->Bindings.Find(arg);
- if (it != this->Bindings.end()) {
- if (parsedKeywords != nullptr) {
- parsedKeywords->emplace_back(it->first);
+ auto const it = this->Bindings.Keywords.Find(arg);
+ if (it != this->Bindings.Keywords.end()) {
+ this->FinishKeyword();
+ this->Keyword = it->first;
+ this->KeywordValuesSeen = 0;
+ if (this->Bindings.ParsedKeyword) {
+ this->Bindings.ParsedKeyword(*this, it->first);
}
- it->second(*this, result);
- if (this->ExpectValue && keywordsMissingValue != nullptr) {
- keywordsMissingValue->emplace_back(it->first);
+ it->second(*this);
+ return;
+ }
+
+ if (this->KeywordValueFunc) {
+ switch (this->KeywordValueFunc(arg)) {
+ case Continue::Yes:
+ break;
+ case Continue::No:
+ this->KeywordValueFunc = nullptr;
+ break;
}
+ ++this->KeywordValuesSeen;
+ return;
+ }
+
+ auto const pit = this->Bindings.Positions.Find(pos);
+ if (pit != this->Bindings.Positions.end()) {
+ pit->second(*this, pos, arg);
return;
}
- if (this->CurrentString != nullptr) {
- this->CurrentString->assign(std::string(arg));
- this->CurrentString = nullptr;
- this->CurrentList = nullptr;
- } else if (this->CurrentList != nullptr) {
- this->CurrentList->emplace_back(arg);
- } else if (unparsedArguments != nullptr) {
- unparsedArguments->emplace_back(arg);
+ if (this->UnparsedArguments != nullptr) {
+ this->UnparsedArguments->emplace_back(arg);
}
+}
- if (this->ExpectValue) {
- if (keywordsMissingValue != nullptr) {
- keywordsMissingValue->pop_back();
+void Instance::FinishKeyword()
+{
+ if (this->Keyword.empty()) {
+ return;
+ }
+ if (this->KeywordValuesSeen < this->KeywordValuesExpected) {
+ if (this->ParseResults != nullptr) {
+ this->ParseResults->AddKeywordError(this->Keyword,
+ " missing required value\n");
+ }
+ if (this->Bindings.KeywordMissingValue) {
+ this->Bindings.KeywordMissingValue(*this, this->Keyword);
}
- this->ExpectValue = false;
}
}
+bool ParseResult::MaybeReportError(cmMakefile& mf) const
+{
+ if (*this) {
+ return false;
+ }
+ std::string e;
+ for (auto const& ke : this->KeywordErrors) {
+ e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second);
+ }
+ mf.IssueMessage(MessageType::FATAL_ERROR, e);
+ return true;
+}
+
} // namespace ArgumentParser
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
index 26148d9aa2..ac788724a8 100644
--- a/Source/cmArgumentParser.h
+++ b/Source/cmArgumentParser.h
@@ -5,40 +5,173 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <cassert>
+#include <cstddef>
#include <functional>
+#include <map>
#include <string>
#include <utility>
#include <vector>
#include <cm/optional>
#include <cm/string_view>
+#include <cm/type_traits>
#include <cmext/string_view>
#include "cmArgumentParserTypes.h" // IWYU pragma: keep
+template <typename Result>
+class cmArgumentParser; // IWYU pragma: keep
+
+class cmMakefile;
+
namespace ArgumentParser {
+class ParseResult
+{
+ std::map<cm::string_view, std::string> KeywordErrors;
+
+public:
+ explicit operator bool() const { return this->KeywordErrors.empty(); }
+
+ void AddKeywordError(cm::string_view key, cm::string_view text)
+
+ {
+ this->KeywordErrors[key] += text;
+ }
+
+ std::map<cm::string_view, std::string> const& GetKeywordErrors() const
+ {
+ return this->KeywordErrors;
+ }
+
+ bool MaybeReportError(cmMakefile& mf) const;
+};
+
+template <typename Result>
+typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
+ ParseResult*>::type
+AsParseResultPtr(Result& result)
+{
+ return &result;
+}
+
+template <typename Result>
+typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
+ ParseResult*>::type
+AsParseResultPtr(Result&)
+{
+ return nullptr;
+}
+
+enum class Continue
+{
+ No,
+ Yes,
+};
+
+struct ExpectAtLeast
+{
+ std::size_t Count = 0;
+
+ ExpectAtLeast(std::size_t count)
+ : Count(count)
+ {
+ }
+};
+
class Instance;
-using Action = std::function<void(Instance&, void*)>;
+using KeywordAction = std::function<void(Instance&)>;
+using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
+using PositionAction =
+ std::function<void(Instance&, std::size_t, cm::string_view)>;
-// using ActionMap = cm::flat_map<cm::string_view, Action>;
-class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
+// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
+class KeywordActionMap
+ : public std::vector<std::pair<cm::string_view, KeywordAction>>
{
public:
- std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
+ std::pair<iterator, bool> Emplace(cm::string_view name,
+ KeywordAction action);
const_iterator Find(cm::string_view name) const;
};
+// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>;
+class PositionActionMap
+ : public std::vector<std::pair<std::size_t, PositionAction>>
+{
+public:
+ std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action);
+ const_iterator Find(std::size_t pos) const;
+};
+
+class ActionMap
+{
+public:
+ KeywordActionMap Keywords;
+ KeywordNameAction KeywordMissingValue;
+ KeywordNameAction ParsedKeyword;
+ PositionActionMap Positions;
+};
+
+class Base
+{
+public:
+ using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
+ using Continue = ArgumentParser::Continue;
+ using Instance = ArgumentParser::Instance;
+ using ParseResult = ArgumentParser::ParseResult;
+
+ ArgumentParser::ActionMap Bindings;
+
+ bool MaybeBind(cm::string_view name, KeywordAction action)
+ {
+ return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
+ }
+
+ void Bind(cm::string_view name, KeywordAction action)
+ {
+ bool const inserted = this->MaybeBind(name, std::move(action));
+ assert(inserted);
+ static_cast<void>(inserted);
+ }
+
+ void BindParsedKeyword(KeywordNameAction action)
+ {
+ assert(!this->Bindings.ParsedKeyword);
+ this->Bindings.ParsedKeyword = std::move(action);
+ }
+
+ void BindKeywordMissingValue(KeywordNameAction action)
+ {
+ assert(!this->Bindings.KeywordMissingValue);
+ this->Bindings.KeywordMissingValue = std::move(action);
+ }
+
+ void Bind(std::size_t pos, PositionAction action)
+ {
+ bool const inserted =
+ this->Bindings.Positions.Emplace(pos, std::move(action)).second;
+ assert(inserted);
+ static_cast<void>(inserted);
+ }
+};
+
class Instance
{
public:
- Instance(ActionMap const& bindings)
+ Instance(ActionMap const& bindings, ParseResult* parseResult,
+ std::vector<std::string>* unparsedArguments, void* result = nullptr)
: Bindings(bindings)
+ , ParseResults(parseResult)
+ , UnparsedArguments(unparsedArguments)
+ , Result(result)
{
}
+ void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
void Bind(bool& val);
void Bind(std::string& val);
+ void Bind(NonEmpty<std::string>& val);
void Bind(Maybe<std::string>& val);
void Bind(MaybeEmpty<std::vector<std::string>>& val);
void Bind(NonEmpty<std::vector<std::string>>& val);
@@ -54,22 +187,37 @@ public:
this->Bind(*optVal);
}
- void Consume(cm::string_view arg, void* result,
- std::vector<std::string>* unparsedArguments,
- std::vector<cm::string_view>* keywordsMissingValue,
- std::vector<cm::string_view>* parsedKeywords);
+ template <typename Range>
+ void Parse(Range const& args, std::size_t pos = 0)
+ {
+ for (cm::string_view arg : args) {
+ this->Consume(pos++, arg);
+ }
+ this->FinishKeyword();
+ }
private:
ActionMap const& Bindings;
- std::string* CurrentString = nullptr;
- std::vector<std::string>* CurrentList = nullptr;
- bool ExpectValue = false;
+ ParseResult* ParseResults = nullptr;
+ std::vector<std::string>* UnparsedArguments = nullptr;
+ void* Result = nullptr;
+
+ cm::string_view Keyword;
+ std::size_t KeywordValuesSeen = 0;
+ std::size_t KeywordValuesExpected = 0;
+ std::function<Continue(cm::string_view)> KeywordValueFunc;
+
+ void Consume(std::size_t pos, cm::string_view arg);
+ void FinishKeyword();
+
+ template <typename Result>
+ friend class ::cmArgumentParser;
};
} // namespace ArgumentParser
template <typename Result>
-class cmArgumentParser
+class cmArgumentParser : private ArgumentParser::Base
{
public:
// I *think* this function could be made `constexpr` when the code is
@@ -77,81 +225,194 @@ public:
template <typename T>
cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
{
- bool const inserted =
- this->Bindings
- .Emplace(name,
- [member](ArgumentParser::Instance& instance, void* result) {
- instance.Bind(static_cast<Result*>(result)->*member);
- })
- .second;
- assert(inserted), (void)inserted;
+ this->Base::Bind(name, [member](Instance& instance) {
+ instance.Bind(static_cast<Result*>(instance.Result)->*member);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(cm::static_string_view name,
+ Continue (Result::*member)(cm::string_view),
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [member, expect](Instance& instance) {
+ Result* result = static_cast<Result*>(instance.Result);
+ instance.Bind(
+ [result, member](cm::string_view arg) -> Continue {
+ return (result->*member)(arg);
+ },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(cm::static_string_view name,
+ Continue (Result::*member)(cm::string_view,
+ cm::string_view),
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [member, expect](Instance& instance) {
+ Result* result = static_cast<Result*>(instance.Result);
+ cm::string_view keyword = instance.Keyword;
+ instance.Bind(
+ [result, member, keyword](cm::string_view arg) -> Continue {
+ return (result->*member)(keyword, arg);
+ },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(cm::static_string_view name,
+ std::function<Continue(Result&, cm::string_view)> f,
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [f, expect](Instance& instance) {
+ Result* result = static_cast<Result*>(instance.Result);
+ instance.Bind(
+ [result, &f](cm::string_view arg) -> Continue {
+ return f(*result, arg);
+ },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(
+ cm::static_string_view name,
+ std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [f, expect](Instance& instance) {
+ Result* result = static_cast<Result*>(instance.Result);
+ cm::string_view keyword = instance.Keyword;
+ instance.Bind(
+ [result, keyword, &f](cm::string_view arg) -> Continue {
+ return f(*result, keyword, arg);
+ },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(std::size_t position,
+ cm::optional<std::string> Result::*member)
+ {
+ this->Base::Bind(
+ position,
+ [member](Instance& instance, std::size_t, cm::string_view arg) {
+ Result* result = static_cast<Result*>(instance.Result);
+ result->*member = arg;
+ });
+ return *this;
+ }
+
+ cmArgumentParser& BindParsedKeywords(
+ std::vector<cm::string_view> Result::*member)
+ {
+ this->Base::BindParsedKeyword(
+ [member](Instance& instance, cm::string_view arg) {
+ (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
+ });
return *this;
}
template <typename Range>
- void Parse(Result& result, Range const& args,
+ bool Parse(Result& result, Range const& args,
std::vector<std::string>* unparsedArguments,
- std::vector<cm::string_view>* keywordsMissingValue = nullptr,
- std::vector<cm::string_view>* parsedKeywords = nullptr) const
+ std::size_t pos = 0) const
{
- ArgumentParser::Instance instance(this->Bindings);
- for (cm::string_view arg : args) {
- instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue,
- parsedKeywords);
- }
+ using ArgumentParser::AsParseResultPtr;
+ ParseResult* parseResultPtr = AsParseResultPtr(result);
+ Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
+ &result);
+ instance.Parse(args, pos);
+ return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
}
template <typename Range>
Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
- std::vector<cm::string_view>* keywordsMissingValue = nullptr,
- std::vector<cm::string_view>* parsedKeywords = nullptr) const
+ std::size_t pos = 0) const
{
Result result;
- this->Parse(result, args, unparsedArguments, keywordsMissingValue,
- parsedKeywords);
+ this->Parse(result, args, unparsedArguments, pos);
return result;
}
-
-private:
- ArgumentParser::ActionMap Bindings;
};
template <>
-class cmArgumentParser<void>
+class cmArgumentParser<void> : private ArgumentParser::Base
{
public:
template <typename T>
cmArgumentParser& Bind(cm::static_string_view name, T& ref)
{
- bool const inserted = this->Bind(cm::string_view(name), ref);
- assert(inserted), (void)inserted;
+ this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(cm::static_string_view name,
+ std::function<Continue(cm::string_view)> f,
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [f, expect](Instance& instance) {
+ instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(
+ cm::static_string_view name,
+ std::function<Continue(cm::string_view, cm::string_view)> f,
+ ExpectAtLeast expect = { 1 })
+ {
+ this->Base::Bind(name, [f, expect](Instance& instance) {
+ cm::string_view keyword = instance.Keyword;
+ instance.Bind(
+ [keyword, &f](cm::string_view arg) -> Continue {
+ return f(keyword, arg);
+ },
+ expect);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
+ {
+ this->Base::Bind(position,
+ [&ref](Instance&, std::size_t, cm::string_view arg) {
+ ref = std::string(arg);
+ });
+ return *this;
+ }
+
+ cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
+ {
+ this->Base::BindParsedKeyword(
+ [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
return *this;
}
template <typename Range>
- void Parse(Range const& args, std::vector<std::string>* unparsedArguments,
- std::vector<cm::string_view>* keywordsMissingValue = nullptr,
- std::vector<cm::string_view>* parsedKeywords = nullptr) const
+ ParseResult Parse(Range const& args,
+ std::vector<std::string>* unparsedArguments,
+ std::size_t pos = 0) const
{
- ArgumentParser::Instance instance(this->Bindings);
- for (cm::string_view arg : args) {
- instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue,
- parsedKeywords);
- }
+ ParseResult parseResult;
+ Instance instance(this->Bindings, &parseResult, unparsedArguments);
+ instance.Parse(args, pos);
+ return parseResult;
}
protected:
+ using Base::Instance;
+ using Base::BindKeywordMissingValue;
+
template <typename T>
bool Bind(cm::string_view name, T& ref)
{
- return this->Bindings
- .Emplace(name,
- [&ref](ArgumentParser::Instance& instance, void*) {
- instance.Bind(ref);
- })
- .second;
+ return this->MaybeBind(name,
+ [&ref](Instance& instance) { instance.Bind(ref); });
}
-
-private:
- ArgumentParser::ActionMap Bindings;
};
diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h
index 9afa5c7166..7daae09744 100644
--- a/Source/cmArgumentParserTypes.h
+++ b/Source/cmArgumentParserTypes.h
@@ -4,21 +4,66 @@
#include "cmConfigure.h" // IWYU pragma: keep
+#if defined(__SUNPRO_CC)
+
+# include <string>
+# include <vector>
+
+namespace ArgumentParser {
+
+template <typename T>
+struct Maybe;
+template <>
+struct Maybe<std::string> : public std::string
+{
+ using std::string::basic_string;
+};
+
+template <typename T>
+struct MaybeEmpty;
+template <typename T>
+struct MaybeEmpty<std::vector<T>> : public std::vector<T>
+{
+ using std::vector<T>::vector;
+};
+
+template <typename T>
+struct NonEmpty;
+template <typename T>
+struct NonEmpty<std::vector<T>> : public std::vector<T>
+{
+ using std::vector<T>::vector;
+};
+template <>
+struct NonEmpty<std::string> : public std::string
+{
+ using std::string::basic_string;
+};
+
+} // namespace ArgumentParser
+
+#else
+
namespace ArgumentParser {
template <typename T>
struct Maybe : public T
{
+ using T::T;
};
template <typename T>
struct MaybeEmpty : public T
{
+ using T::T;
};
template <typename T>
struct NonEmpty : public T
{
+ using T::T;
};
} // namespace ArgumentParser
+
+#endif
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 033dd6de0a..58129a06b6 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
}
std::string const& key = *args.begin();
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
std::string ValueName;
bool ValueNames = false;
@@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
.Bind("SEPARATOR"_s, &Arguments::Separator)
.Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable);
std::vector<std::string> invalidArgs;
- std::vector<cm::string_view> keywordsMissingValue;
- Arguments const arguments =
- parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue);
+ Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs);
if (!invalidArgs.empty()) {
status.SetError(cmStrCat("given invalid argument(s) \"",
cmJoin(invalidArgs, ", "_s), "\"."));
return false;
}
- if (!keywordsMissingValue.empty()) {
- status.SetError(cmStrCat("missing expected value for argument(s) \"",
- cmJoin(keywordsMissingValue, ", "_s), "\"."));
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if ((!arguments.ValueName.empty() &&
(arguments.ValueNames || arguments.SubKeys)) ||
diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx
index adcffa9f68..7755082236 100644
--- a/Source/cmCMakePathCommand.cxx
+++ b/Source/cmCMakePathCommand.cxx
@@ -2,7 +2,6 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCMakePathCommand.h"
-#include <algorithm>
#include <functional>
#include <iomanip>
#include <map>
@@ -11,10 +10,12 @@
#include <utility>
#include <vector>
+#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
#include "cmCMakePath.h"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
@@ -43,15 +44,12 @@ public:
}
template <int Advance = 2>
- Result Parse(std::vector<std::string> const& args,
- std::vector<cm::string_view>* keywordsMissingValue = nullptr,
- std::vector<cm::string_view>* parsedKeywords = nullptr) const
+ Result Parse(std::vector<std::string> const& args) const
{
this->Inputs.clear();
return this->cmArgumentParser<Result>::Parse(
- cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue,
- parsedKeywords);
+ cmMakeRange(args).advance(Advance), &this->Inputs);
}
const std::vector<std::string>& GetInputs() const { return this->Inputs; }
@@ -82,52 +80,14 @@ public:
template <int Advance = 2>
Result Parse(std::vector<std::string> const& args) const
{
- this->KeywordsMissingValue.clear();
- this->ParsedKeywords.clear();
-
return this->CMakePathArgumentParser<Result>::template Parse<Advance>(
- args, &this->KeywordsMissingValue, &this->ParsedKeywords);
- }
-
- const std::vector<cm::string_view>& GetKeywordsMissingValue() const
- {
- return this->KeywordsMissingValue;
- }
- const std::vector<cm::string_view>& GetParsedKeywords() const
- {
- return this->ParsedKeywords;
- }
-
- bool checkOutputVariable(const Result& arguments,
- cmExecutionStatus& status) const
- {
- if (std::find(this->GetKeywordsMissingValue().begin(),
- this->GetKeywordsMissingValue().end(),
- "OUTPUT_VARIABLE"_s) !=
- this->GetKeywordsMissingValue().end()) {
- status.SetError("OUTPUT_VARIABLE requires an argument.");
- return false;
- }
-
- if (std::find(this->GetParsedKeywords().begin(),
- this->GetParsedKeywords().end(),
- "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() &&
- arguments.Output.empty()) {
- status.SetError("Invalid name for output variable.");
- return false;
- }
-
- return true;
+ args);
}
-
-private:
- mutable std::vector<cm::string_view> KeywordsMissingValue;
- mutable std::vector<cm::string_view> ParsedKeywords;
};
-struct OutputVariable
+struct OutputVariable : public ArgumentParser::ParseResult
{
- std::string Output;
+ cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
};
// Usable when OUTPUT_VARIABLE is the only option
class OutputVariableParser
@@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
const auto arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1]));
@@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
}
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args,
const auto arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
std::string inputPath;
@@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args,
}
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
const auto arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!parser.GetInputs().empty()) {
@@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
path.RemoveFileName();
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
const auto arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (parser.GetInputs().size() > 1) {
@@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
parser.GetInputs().empty() ? "" : parser.GetInputs().front());
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
- std::string Output;
+ cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
bool LastOnly = false;
};
@@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
Arguments const arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!parser.GetInputs().empty()) {
@@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
}
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
- std::string Output;
+ cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
bool LastOnly = false;
};
@@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
Arguments const arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (parser.GetInputs().size() > 1) {
@@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
}
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args,
const auto arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!parser.GetInputs().empty()) {
@@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args,
auto path = cmCMakePath(inputPath).Normal();
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
@@ -523,10 +483,10 @@ bool HandleTransformPathCommand(
const std::string& base)>& transform,
bool normalizeOption = false)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
- std::string Output;
- std::string BaseDirectory;
+ cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
+ cm::optional<std::string> BaseDirectory;
bool Normalize = false;
};
@@ -538,8 +498,8 @@ bool HandleTransformPathCommand(
Arguments arguments = parser.Parse(args);
- if (!parser.checkOutputVariable(arguments, status)) {
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!parser.GetInputs().empty()) {
@@ -547,17 +507,11 @@ bool HandleTransformPathCommand(
return false;
}
- if (std::find(parser.GetKeywordsMissingValue().begin(),
- parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) !=
- parser.GetKeywordsMissingValue().end()) {
- status.SetError("BASE_DIRECTORY requires an argument.");
- return false;
- }
-
- if (std::find(parser.GetParsedKeywords().begin(),
- parser.GetParsedKeywords().end(),
- "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) {
- arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
+ std::string baseDirectory;
+ if (arguments.BaseDirectory) {
+ baseDirectory = *arguments.BaseDirectory;
+ } else {
+ baseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
}
std::string inputPath;
@@ -565,13 +519,13 @@ bool HandleTransformPathCommand(
return false;
}
- auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory);
+ auto path = transform(cmCMakePath(inputPath), baseDirectory);
if (arguments.Normalize) {
path = path.Normal();
}
status.GetMakefile().AddDefinition(
- arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+ arguments.Output ? *arguments.Output : args[1], path.String());
return true;
}
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index c6296f9f42..5e616b30d0 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -20,7 +20,6 @@
#include "cmCMakeMinimumRequired.h"
#include "cmCMakePathCommand.h"
#include "cmCMakePolicyCommand.h"
-#include "cmCommand.h"
#include "cmConfigureFileCommand.h"
#include "cmContinueCommand.h"
#include "cmCreateTestSourceList.h"
@@ -264,9 +263,8 @@ void GetProjectCommands(cmState* state)
cmTargetLinkLibrariesCommand);
state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand);
state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand);
- state->AddBuiltinCommand("try_compile",
- cm::make_unique<cmTryCompileCommand>());
- state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>());
+ state->AddBuiltinCommand("try_compile", cmTryCompileCommand);
+ state->AddBuiltinCommand("try_run", cmTryRunCommand);
state->AddBuiltinCommand("target_precompile_headers",
cmTargetPrecompileHeadersCommand);
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 45afdd1650..d90b574a72 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -238,7 +238,7 @@ std::set<std::string> const ghs_platform_vars{
int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
bool isTryRun)
{
- this->BinaryDirectory = argv[1];
+ std::string const& resultVar = argv[0];
this->OutputFile.clear();
// which signature were we called with ?
this->SrcFileSignature = true;
@@ -264,7 +264,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
}
}
- std::string sourceDirectory = argv[2];
+ std::string sourceDirectory;
std::string projectName;
std::string targetName;
std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0]
@@ -283,11 +283,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
std::vector<std::string> linkOptions;
std::string libsToLink = " ";
bool useOldLinkLibs = true;
- char targetNameBuf[64];
bool didOutputVariable = false;
bool didCopyFile = false;
bool didCopyFileError = false;
- bool useSources = argv[2] == "SOURCES";
+ bool useSources = false;
std::vector<std::string> sources;
enum Doing
@@ -303,9 +302,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
DoingSources,
DoingCMakeInternal
};
- Doing doing = useSources ? DoingSources : DoingNone;
- for (size_t i = 3; i < argv.size(); ++i) {
- if (argv[i] == "CMAKE_FLAGS") {
+ Doing doing = DoingNone;
+ for (size_t i = 1; i < argv.size(); ++i) {
+ if (argv[i] == "SOURCES") {
+ useSources = true;
+ doing = DoingSources;
+ } else if (argv[i] == "CMAKE_FLAGS") {
doing = DoingCMakeFlags;
} else if (argv[i] == "COMPILE_DEFINITIONS") {
doing = DoingCompileDefinitions;
@@ -379,6 +381,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
} else if (doing == DoingCMakeInternal) {
cmakeInternal = argv[i];
doing = DoingNone;
+ } else if (i == 1) {
+ this->BinaryDirectory = argv[i];
+ } else if (i == 2) {
+ sourceDirectory = argv[i];
} else if (i == 3) {
this->SrcFileSignature = false;
projectName = argv[i];
@@ -391,6 +397,37 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
}
}
+ if (!this->BinaryDirectory.empty()) {
+ if (!cmSystemTools::FileIsFullPath(this->BinaryDirectory)) {
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("<bindir> is not an absolute path:\n '",
+ this->BinaryDirectory, "'"));
+ // Do not try to clean up the ill-specified directory.
+ this->BinaryDirectory.clear();
+ return -1;
+ }
+ // compute the binary dir when TRY_COMPILE is called with a src file
+ // signature
+ if (this->SrcFileSignature) {
+ this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
+ }
+ } else {
+ this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+ "No <bindir> specified.");
+ return -1;
+ }
+
+ if (this->SrcFileSignature) {
+ projectName = "CMAKE_TRY_COMPILE";
+ /* Use a random file name to avoid rapid creation and deletion
+ of the same executable name (some filesystems fail on that). */
+ char targetNameBuf[64];
+ snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
+ cmSystemTools::RandomSeed() & 0xFFFFF);
+ targetName = targetNameBuf;
+ }
+
if (didCopyFile && copyFile.empty()) {
this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
"COPY_FILE must be followed by a file path");
@@ -425,6 +462,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
return -1;
}
+ // only valid for srcfile signatures
if (!this->SrcFileSignature) {
if (!cState.Validate(this->Makefile)) {
return -1;
@@ -444,14 +482,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
if (!objcxxState.Validate(this->Makefile)) {
return -1;
}
- }
- // compute the binary dir when TRY_COMPILE is called with a src file
- // signature
- if (this->SrcFileSignature) {
- this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
- } else {
- // only valid for srcfile signatures
if (!compileDefs.empty()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
@@ -711,12 +742,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
cmJoin(compileDefs, "]==] [==[").c_str());
}
- /* Use a random file name to avoid rapid creation and deletion
- of the same executable name (some filesystems fail on that). */
- snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
- cmSystemTools::RandomSeed() & 0xFFFFF);
- targetName = targetNameBuf;
-
if (!targets.empty()) {
std::string fname = "/" + std::string(targetName) + "Targets.cmake";
cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
@@ -873,7 +898,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
libsToLink.c_str());
}
fclose(fout);
- projectName = "CMAKE_TRY_COMPILE";
}
// Forward a set of variables to the inner project cache.
@@ -996,7 +1020,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
}
// set the result var to the return value to indicate success or failure
- this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"),
+ this->Makefile->AddCacheDefinition(resultVar, (res == 0 ? "TRUE" : "FALSE"),
"Result of TRY_COMPILE",
cmStateEnums::INTERNAL);
diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h
index 594fd7f9f5..9d43899110 100644
--- a/Source/cmCoreTryCompile.h
+++ b/Source/cmCoreTryCompile.h
@@ -7,19 +7,24 @@
#include <string>
#include <vector>
-#include "cmCommand.h"
#include "cmStateTypes.h"
+class cmMakefile;
+
/** \class cmCoreTryCompile
* \brief Base class for cmTryCompileCommand and cmTryRunCommand
*
* cmCoreTryCompile implements the functionality to build a program.
* It is the base class for cmTryCompileCommand and cmTryRunCommand.
*/
-class cmCoreTryCompile : public cmCommand
+class cmCoreTryCompile
{
public:
-protected:
+ cmCoreTryCompile(cmMakefile* mf)
+ : Makefile(mf)
+ {
+ }
+
/**
* This is the core code for try compile. It is here so that other
* commands, such as TryRun can access the same logic without
@@ -46,4 +51,5 @@ protected:
std::string OutputFile;
std::string FindErrorMessage;
bool SrcFileSignature = false;
+ cmMakefile* Makefile;
};
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index af56e2dc04..7fbd826906 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
return false;
}
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
std::vector<std::vector<std::string>> Commands;
std::string OutputVariable;
@@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
.Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> keywordsMissingValue;
- Arguments const arguments =
- parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
+ Arguments const arguments = parser.Parse(args, &unparsedArguments);
- if (!keywordsMissingValue.empty()) {
- status.SetError(cmStrCat(" called with no value for ",
- keywordsMissingValue.front(), "."));
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!unparsedArguments.empty()) {
status.SetError(" given unknown argument \"" + unparsedArguments.front() +
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index d2aa63cfaa..330d9916c0 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -954,42 +954,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args,
{
// Evaluate arguments.
std::string file;
- std::string oldRPath;
- std::string newRPath;
+ cm::optional<std::string> oldRPath;
+ cm::optional<std::string> newRPath;
bool removeEnvironmentRPath = false;
cmArgumentParser<void> parser;
std::vector<std::string> unknownArgs;
- std::vector<cm::string_view> missingArgs;
- std::vector<cm::string_view> parsedArgs;
parser.Bind("FILE"_s, file)
.Bind("OLD_RPATH"_s, oldRPath)
.Bind("NEW_RPATH"_s, newRPath)
.Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath);
- parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
- &parsedArgs);
+ ArgumentParser::ParseResult parseResult =
+ parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(
cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front()));
return false;
}
- if (!missingArgs.empty()) {
- status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(),
- "\" argument not given value."));
- return false;
+ if (parseResult.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (file.empty()) {
status.SetError("RPATH_CHANGE not given FILE option.");
return false;
}
- if (oldRPath.empty() &&
- std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") ==
- parsedArgs.end()) {
+ if (!oldRPath) {
status.SetError("RPATH_CHANGE not given OLD_RPATH option.");
return false;
}
- if (newRPath.empty() &&
- std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") ==
- parsedArgs.end()) {
+ if (!newRPath) {
status.SetError("RPATH_CHANGE not given NEW_RPATH option.");
return false;
}
@@ -1003,17 +995,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args,
std::string emsg;
bool changed;
- if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath,
+ if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath,
removeEnvironmentRPath, &emsg, &changed)) {
status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n ",
- newRPath, "\nto the file:\n ", file, "\n",
+ *newRPath, "\nto the file:\n ", file, "\n",
emsg));
success = false;
}
if (success) {
if (changed) {
std::string message =
- cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"');
+ cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
status.GetMakefile().DisplayStatus(message, -1);
}
ft.Store(file);
@@ -1026,31 +1018,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args,
{
// Evaluate arguments.
std::string file;
- std::string newRPath;
+ cm::optional<std::string> newRPath;
cmArgumentParser<void> parser;
std::vector<std::string> unknownArgs;
- std::vector<cm::string_view> missingArgs;
- std::vector<cm::string_view> parsedArgs;
parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath);
- parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
- &parsedArgs);
+ ArgumentParser::ParseResult parseResult =
+ parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"",
unknownArgs.front(), "\"."));
return false;
}
- if (!missingArgs.empty()) {
- status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(),
- "\" argument not given value."));
- return false;
+ if (parseResult.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (file.empty()) {
status.SetError("RPATH_SET not given FILE option.");
return false;
}
- if (newRPath.empty() &&
- std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") ==
- parsedArgs.end()) {
+ if (!newRPath) {
status.SetError("RPATH_SET not given NEW_RPATH option.");
return false;
}
@@ -1064,16 +1050,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args,
std::string emsg;
bool changed;
- if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) {
+ if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) {
status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ",
- newRPath, "\nto the file:\n ", file, "\n",
+ *newRPath, "\nto the file:\n ", file, "\n",
emsg));
success = false;
}
if (success) {
if (changed) {
std::string message =
- cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"');
+ cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
status.GetMakefile().DisplayStatus(message, -1);
}
ft.Store(file);
@@ -1088,18 +1074,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
std::string file;
cmArgumentParser<void> parser;
std::vector<std::string> unknownArgs;
- std::vector<cm::string_view> missingArgs;
parser.Bind("FILE"_s, file);
- parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs);
+ ArgumentParser::ParseResult parseResult =
+ parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(
cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front()));
return false;
}
- if (!missingArgs.empty()) {
- status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(),
- "\" argument not given value."));
- return false;
+ if (parseResult.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (file.empty()) {
status.SetError("RPATH_REMOVE not given FILE option.");
@@ -1136,31 +1120,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args,
{
// Evaluate arguments.
std::string file;
- std::string rpath;
+ cm::optional<std::string> rpath;
cmArgumentParser<void> parser;
std::vector<std::string> unknownArgs;
- std::vector<cm::string_view> missingArgs;
- std::vector<cm::string_view> parsedArgs;
parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath);
- parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
- &parsedArgs);
+ ArgumentParser::ParseResult parseResult =
+ parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(
cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front()));
return false;
}
- if (!missingArgs.empty()) {
- status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(),
- "\" argument not given value."));
- return false;
+ if (parseResult.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (file.empty()) {
status.SetError("RPATH_CHECK not given FILE option.");
return false;
}
- if (rpath.empty() &&
- std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") ==
- parsedArgs.end()) {
+ if (!rpath) {
status.SetError("RPATH_CHECK not given RPATH option.");
return false;
}
@@ -1169,7 +1147,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args,
// delete it. This is used during installation to re-install a file
// if its RPath will change.
if (cmSystemTools::FileExists(file, true) &&
- !cmSystemTools::CheckRPath(file, rpath)) {
+ !cmSystemTools::CheckRPath(file, *rpath)) {
cmSystemTools::RemoveFile(file);
}
@@ -1252,7 +1230,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
return false;
}
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
cm::optional<std::string> BaseDirectory;
bool ExpandTilde = false;
@@ -1263,17 +1241,15 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
.Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> keywordsMissingValue;
- auto arguments = parser.Parse(cmMakeRange(args).advance(3),
- &unparsedArguments, &keywordsMissingValue);
+ auto arguments =
+ parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments);
if (!unparsedArguments.empty()) {
status.SetError("REAL_PATH called with unexpected arguments");
return false;
}
- if (!keywordsMissingValue.empty()) {
- status.SetError("BASE_DIRECTORY requires a value");
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!arguments.BaseDirectory) {
@@ -2495,7 +2471,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
return false;
}
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
cm::optional<std::string> Output;
cm::optional<std::string> Input;
@@ -2506,6 +2482,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
bool NoSourcePermissions = false;
bool UseSourcePermissions = false;
ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
+ std::vector<cm::string_view> ParsedKeywords;
};
static auto const parser =
@@ -2518,20 +2495,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
.Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
.Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
.Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
- .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle);
+ .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle)
+ .BindParsedKeywords(&Arguments::ParsedKeywords);
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> keywordsMissingValues;
- std::vector<cm::string_view> parsedKeywords;
Arguments const arguments =
- parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments,
- &keywordsMissingValues, &parsedKeywords);
+ parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
- if (!keywordsMissingValues.empty()) {
- status.SetError(
- cmStrCat("GENERATE keywords missing values:\n ",
- cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
- return false;
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
}
if (!unparsedArguments.empty()) {
@@ -2539,7 +2511,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
return false;
}
- if (!arguments.Output || parsedKeywords[0] != "OUTPUT"_s) {
+ if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) {
status.SetError("GENERATE requires OUTPUT as first option.");
return false;
}
@@ -2549,8 +2521,8 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
status.SetError("GENERATE requires INPUT or CONTENT option.");
return false;
}
- const bool inputIsContent = parsedKeywords[1] == "CONTENT"_s;
- if (!inputIsContent && parsedKeywords[1] == "INPUT") {
+ const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s;
+ if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") {
status.SetError("Unknown argument to GENERATE subcommand.");
}
std::string const& input =
@@ -3045,7 +3017,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
"\n ]])");
}
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
std::string ResolvedDependenciesVar;
std::string UnresolvedDependenciesVar;
@@ -3088,10 +3060,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
.Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict);
std::vector<std::string> unrecognizedArguments;
- std::vector<cm::string_view> keywordsMissingValues;
auto parsedArgs =
- parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
- &keywordsMissingValues);
+ parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
auto argIt = unrecognizedArguments.begin();
if (argIt != unrecognizedArguments.end()) {
status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3099,12 +3069,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
return false;
}
- if (!keywordsMissingValues.empty()) {
- status.SetError(
- cmStrCat("Keywords missing values:\n ",
- cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
+ if (parsedArgs.MaybeReportError(status.GetMakefile())) {
cmSystemTools::SetFatalErrorOccurred();
- return false;
+ return true;
}
cmRuntimeDependencyArchive archive(
@@ -3204,7 +3171,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
bool HandleConfigureCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
cm::optional<std::string> Output;
cm::optional<std::string> Content;
@@ -3223,10 +3190,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
.Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
std::vector<std::string> unrecognizedArguments;
- std::vector<cm::string_view> keywordsMissingValues;
auto parsedArgs =
- parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
- &keywordsMissingValues);
+ parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
auto argIt = unrecognizedArguments.begin();
if (argIt != unrecognizedArguments.end()) {
@@ -3236,12 +3201,9 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
return false;
}
- if (!keywordsMissingValues.empty()) {
- status.SetError(
- cmStrCat("CONFIGURE keywords missing values:\n ",
- cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
+ if (parsedArgs.MaybeReportError(status.GetMakefile())) {
cmSystemTools::SetFatalErrorOccurred();
- return false;
+ return true;
}
if (!parsedArgs.Output) {
@@ -3336,7 +3298,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
std::string Output;
std::string Format;
@@ -3362,10 +3324,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
.Bind("PATHS"_s, &Arguments::Paths);
std::vector<std::string> unrecognizedArguments;
- std::vector<cm::string_view> keywordsMissingValues;
auto parsedArgs =
- parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
- &keywordsMissingValues);
+ parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
auto argIt = unrecognizedArguments.begin();
if (argIt != unrecognizedArguments.end()) {
status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3373,12 +3333,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
return false;
}
- if (!keywordsMissingValues.empty()) {
- status.SetError(
- cmStrCat("Keywords missing values:\n ",
- cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
+ if (parsedArgs.MaybeReportError(status.GetMakefile())) {
cmSystemTools::SetFatalErrorOccurred();
- return false;
+ return true;
}
const char* knownFormats[] = {
@@ -3467,7 +3424,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
std::string Input;
bool Verbose = false;
@@ -3486,10 +3443,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
.Bind("TOUCH"_s, &Arguments::Touch);
std::vector<std::string> unrecognizedArguments;
- std::vector<cm::string_view> keywordsMissingValues;
auto parsedArgs =
- parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
- &keywordsMissingValues);
+ parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
auto argIt = unrecognizedArguments.begin();
if (argIt != unrecognizedArguments.end()) {
status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3497,12 +3452,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
return false;
}
- if (!keywordsMissingValues.empty()) {
- status.SetError(
- cmStrCat("Keywords missing values:\n ",
- cmJoin(cmMakeRange(keywordsMissingValues), "\n ")));
+ if (parsedArgs.MaybeReportError(status.GetMakefile())) {
cmSystemTools::SetFatalErrorOccurred();
- return false;
+ return true;
}
std::string inFile = parsedArgs.Input;
@@ -3557,10 +3509,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
return true;
}
-bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions,
- mode_t& perms, cmExecutionStatus& status)
+bool ValidateAndConvertPermissions(
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const&
+ permissions,
+ mode_t& perms, cmExecutionStatus& status)
{
- for (const auto& i : permissions) {
+ if (!permissions) {
+ return true;
+ }
+ for (const auto& i : *permissions) {
if (!cmFSPermissions::stringToModeT(i, perms)) {
status.SetError(i + " is an invalid permission specifier");
cmSystemTools::SetFatalErrorOccurred();
@@ -3592,11 +3549,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
globber.SetRecurse(recurse);
globber.SetRecurseListDirs(recurse);
- struct Arguments
+ struct Arguments : public ArgumentParser::ParseResult
{
- ArgumentParser::NonEmpty<std::vector<std::string>> Permissions;
- ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
- ArgumentParser::NonEmpty<std::vector<std::string>> DirectoryPermissions;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+ Permissions;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+ FilePermissions;
+ cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+ DirectoryPermissions;
};
static auto const parser =
@@ -3606,21 +3566,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
.Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
std::vector<std::string> pathEntries;
- std::vector<cm::string_view> keywordsMissingValues;
- Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1),
- &pathEntries, &keywordsMissingValues);
+ Arguments parsedArgs =
+ parser.Parse(cmMakeRange(args).advance(1), &pathEntries);
// check validity of arguments
- if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() &&
- parsedArgs.DirectoryPermissions.empty()) // no permissions given
+ if (!parsedArgs.Permissions && !parsedArgs.FilePermissions &&
+ !parsedArgs.DirectoryPermissions) // no permissions given
{
status.SetError("No permissions given");
cmSystemTools::SetFatalErrorOccurred();
return false;
}
- if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() &&
- !parsedArgs.DirectoryPermissions.empty()) // all keywords are used
+ if (parsedArgs.Permissions && parsedArgs.FilePermissions &&
+ parsedArgs.DirectoryPermissions) // all keywords are used
{
status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or "
"DIRECTORY_PERMISSIONS from the invocation");
@@ -3628,12 +3587,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
return false;
}
- if (!keywordsMissingValues.empty()) {
- for (const auto& i : keywordsMissingValues) {
- status.SetError(cmStrCat(i, " is not given any arguments"));
- cmSystemTools::SetFatalErrorOccurred();
- }
- return false;
+ if (parsedArgs.MaybeReportError(status.GetMakefile())) {
+ cmSystemTools::SetFatalErrorOccurred();
+ return true;
}
// validate permissions
@@ -3677,7 +3633,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
if (cmSystemTools::FileExists(i, true)) {
bool success = true;
const mode_t& filePermissions =
- parsedArgs.FilePermissions.empty() ? perms : fperms;
+ parsedArgs.FilePermissions ? fperms : perms;
if (filePermissions) {
success = SetPermissions(i, filePermissions, status);
}
@@ -3689,7 +3645,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
else if (cmSystemTools::FileIsDirectory(i)) {
bool success = true;
const mode_t& directoryPermissions =
- parsedArgs.DirectoryPermissions.empty() ? perms : dperms;
+ parsedArgs.DirectoryPermissions ? dperms : perms;
if (directoryPermissions) {
success = SetPermissions(i, directoryPermissions, status);
}
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 4ad9124f74..f260ec7431 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -5,7 +5,6 @@
#include <algorithm>
#include <cassert>
#include <cstdio>
-#include <cstring>
#include <deque>
#include <functional>
#include <iterator>
@@ -43,8 +42,403 @@
# include <StorageDefs.h>
#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# include <windows.h>
+// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx
+# if !defined(KEY_WOW64_32KEY)
+# define KEY_WOW64_32KEY 0x0200
+# endif
+# if !defined(KEY_WOW64_64KEY)
+# define KEY_WOW64_64KEY 0x0100
+# endif
+#endif
+
class cmExecutionStatus;
-class cmFileList;
+
+namespace {
+
+template <template <typename> class Op>
+struct StrverscmpOp
+{
+ bool operator()(const std::string& lhs, const std::string& rhs) const
+ {
+ return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0);
+ }
+};
+
+std::size_t collectPathsForDebug(std::string& buffer,
+ cmSearchPath const& searchPath,
+ std::size_t const startIndex = 0)
+{
+ const auto& paths = searchPath.GetPaths();
+ if (paths.empty()) {
+ buffer += " none\n";
+ return 0;
+ }
+ for (auto i = startIndex; i < paths.size(); i++) {
+ buffer += " " + paths[i].Path + "\n";
+ }
+ return paths.size();
+}
+
+#if !(defined(_WIN32) && !defined(__CYGWIN__))
+class cmFindPackageCommandHoldFile
+{
+ const char* File;
+
+public:
+ cmFindPackageCommandHoldFile(const char* const f)
+ : File(f)
+ {
+ }
+ ~cmFindPackageCommandHoldFile()
+ {
+ if (this->File) {
+ cmSystemTools::RemoveFile(this->File);
+ }
+ }
+ cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete;
+ cmFindPackageCommandHoldFile& operator=(
+ const cmFindPackageCommandHoldFile&) = delete;
+ void Release() { this->File = nullptr; }
+};
+#endif
+
+bool isDirentryToIgnore(const char* const fname)
+{
+ assert(fname != nullptr);
+ assert(fname[0] != 0);
+ return fname[0] == '.' &&
+ (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0));
+}
+
+class cmAppendPathSegmentGenerator
+{
+public:
+ cmAppendPathSegmentGenerator(cm::string_view dirName)
+ : DirName{ dirName }
+ {
+ }
+
+ std::string GetNextCandidate(const std::string& parent)
+ {
+ if (this->NeedReset) {
+ return {};
+ }
+ this->NeedReset = true;
+ return cmStrCat(parent, '/', this->DirName);
+ }
+
+ void Reset() { this->NeedReset = false; }
+
+private:
+ const cm::string_view DirName;
+ bool NeedReset = false;
+};
+
+class cmEnumPathSegmentsGenerator
+{
+public:
+ cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init)
+ : Names{ init }
+ , Current{ this->Names.get().cbegin() }
+ {
+ }
+
+ std::string GetNextCandidate(const std::string& parent)
+ {
+ if (this->Current != this->Names.get().cend()) {
+ return cmStrCat(parent, '/', *this->Current++);
+ }
+ return {};
+ }
+
+ void Reset() { this->Current = this->Names.get().cbegin(); }
+
+private:
+ std::reference_wrapper<const std::vector<cm::string_view>> Names;
+ std::vector<cm::string_view>::const_iterator Current;
+};
+
+class cmCaseInsensitiveDirectoryListGenerator
+{
+public:
+ cmCaseInsensitiveDirectoryListGenerator(cm::string_view name)
+ : DirectoryLister{}
+ , DirName{ name }
+ {
+ }
+
+ std::string GetNextCandidate(const std::string& parent)
+ {
+ if (!this->Loaded) {
+ this->CurrentIdx = 0ul;
+ this->Loaded = true;
+ if (!this->DirectoryLister.Load(parent)) {
+ return {};
+ }
+ }
+
+ while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) {
+ const char* const fname =
+ this->DirectoryLister.GetFile(this->CurrentIdx++);
+ if (isDirentryToIgnore(fname)) {
+ continue;
+ }
+ if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) {
+ auto candidate = cmStrCat(parent, '/', fname);
+ if (cmSystemTools::FileIsDirectory(candidate)) {
+ return candidate;
+ }
+ }
+ }
+ return {};
+ }
+
+ void Reset() { this->Loaded = false; }
+
+private:
+ cmsys::Directory DirectoryLister;
+ const cm::string_view DirName;
+ unsigned long CurrentIdx = 0ul;
+ bool Loaded = false;
+};
+
+class cmDirectoryListGenerator
+{
+public:
+ cmDirectoryListGenerator(std::vector<std::string> const& names)
+ : Names{ names }
+ , Matches{}
+ , Current{ this->Matches.cbegin() }
+ {
+ }
+ virtual ~cmDirectoryListGenerator() = default;
+
+ std::string GetNextCandidate(const std::string& parent)
+ {
+ // Construct a list of matches if not yet
+ if (this->Matches.empty()) {
+ cmsys::Directory directoryLister;
+ // ALERT `Directory::Load()` keeps only names
+ // internally and LOST entry type from `dirent`.
+ // So, `Directory::FileIsDirectory` gonna use
+ // `SystemTools::FileIsDirectory()` and waste a syscall.
+ // TODO Need to enhance the `Directory` class.
+ directoryLister.Load(parent);
+
+ // ATTENTION Is it guaranteed that first two entries are
+ // `.` and `..`?
+ // TODO If so, just start with index 2 and drop the
+ // `isDirentryToIgnore(i)` condition to check.
+ for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) {
+ const char* const fname = directoryLister.GetFile(i);
+ if (isDirentryToIgnore(fname)) {
+ continue;
+ }
+
+ for (const auto& n : this->Names.get()) {
+ // NOTE Customization point for `cmMacProjectDirectoryListGenerator`
+ const auto name = this->TransformNameBeforeCmp(n);
+ // Skip entries that don't match and non-directories.
+ // ATTENTION BTW, original code also didn't check if it's a symlink
+ // to a directory!
+ const auto equal =
+ (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0);
+ if (equal && directoryLister.FileIsDirectory(i)) {
+ this->Matches.emplace_back(fname);
+ }
+ }
+ }
+ // NOTE Customization point for `cmProjectDirectoryListGenerator`
+ this->OnMatchesLoaded();
+
+ this->Current = this->Matches.cbegin();
+ }
+
+ if (this->Current != this->Matches.cend()) {
+ auto candidate = cmStrCat(parent, '/', *this->Current++);
+ return candidate;
+ }
+
+ return {};
+ }
+
+ void Reset()
+ {
+ this->Matches.clear();
+ this->Current = this->Matches.cbegin();
+ }
+
+protected:
+ virtual void OnMatchesLoaded() {}
+ virtual std::string TransformNameBeforeCmp(std::string same) { return same; }
+
+ std::reference_wrapper<const std::vector<std::string>> Names;
+ std::vector<std::string> Matches;
+ std::vector<std::string>::const_iterator Current;
+};
+
+class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator
+{
+public:
+ cmProjectDirectoryListGenerator(std::vector<std::string> const& names,
+ cmFindPackageCommand::SortOrderType so,
+ cmFindPackageCommand::SortDirectionType sd)
+ : cmDirectoryListGenerator{ names }
+ , SortOrder{ so }
+ , SortDirection{ sd }
+ {
+ }
+
+ void OnMatchesLoaded() override
+ {
+ // check if there is a specific sorting order to perform
+ if (this->SortOrder != cmFindPackageCommand::None) {
+ cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(),
+ this->SortOrder, this->SortDirection);
+ }
+ }
+
+private:
+ // sort parameters
+ const cmFindPackageCommand::SortOrderType SortOrder;
+ const cmFindPackageCommand::SortDirectionType SortDirection;
+};
+
+class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator
+{
+public:
+ cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names,
+ cm::string_view ext)
+ : cmDirectoryListGenerator{ names }
+ , Extension{ ext }
+ {
+ }
+
+ std::string TransformNameBeforeCmp(std::string name) override
+ {
+ return cmStrCat(name, this->Extension);
+ }
+
+private:
+ const cm::string_view Extension;
+};
+
+class cmFileListGeneratorGlob
+{
+public:
+ cmFileListGeneratorGlob(cm::string_view pattern)
+ : Pattern(pattern)
+ , Files{}
+ , Current{}
+ {
+ }
+
+ std::string GetNextCandidate(const std::string& parent)
+ {
+ if (this->Files.empty()) {
+ // Glob the set of matching files.
+ std::string expr = cmStrCat(parent, this->Pattern);
+ cmsys::Glob g;
+ if (!g.FindFiles(expr)) {
+ return {};
+ }
+ this->Files = g.GetFiles();
+ this->Current = this->Files.cbegin();
+ }
+
+ // Skip non-directories
+ for (; this->Current != this->Files.cend() &&
+ !cmSystemTools::FileIsDirectory(*this->Current);
+ ++this->Current) {
+ }
+
+ return (this->Current != this->Files.cend()) ? *this->Current++
+ : std::string{};
+ }
+
+ void Reset()
+ {
+ this->Files.clear();
+ this->Current = this->Files.cbegin();
+ }
+
+private:
+ cm::string_view Pattern;
+ std::vector<std::string> Files;
+ std::vector<std::string>::const_iterator Current;
+};
+
+#if defined(__LCC__)
+# define CM_LCC_DIAG_SUPPRESS_1222
+# pragma diag_suppress 1222 // invalid error number (3288, but works anyway)
+# define CM_LCC_DIAG_SUPPRESS_3288
+# pragma diag_suppress 3288 // parameter was declared but never referenced
+#endif
+
+void ResetGenerator()
+{
+}
+
+template <typename Generator>
+void ResetGenerator(Generator&& generator)
+{
+ std::forward<Generator&&>(generator).Reset();
+}
+
+template <typename Generator, typename... Generators>
+void ResetGenerator(Generator&& generator, Generators&&... generators)
+{
+ ResetGenerator(std::forward<Generator&&>(generator));
+ ResetGenerator(std::forward<Generators&&>(generators)...);
+}
+
+template <typename CallbackFn>
+bool TryGeneratedPaths(CallbackFn&& filesCollector,
+ const std::string& fullPath)
+{
+ assert(!fullPath.empty() && fullPath.back() != '/');
+ return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/');
+}
+
+template <typename CallbackFn, typename Generator, typename... Rest>
+bool TryGeneratedPaths(CallbackFn&& filesCollector,
+ const std::string& startPath, Generator&& gen,
+ Rest&&... tail)
+{
+ ResetGenerator(std::forward<Generator&&>(gen));
+ for (auto path = gen.GetNextCandidate(startPath); !path.empty();
+ path = gen.GetNextCandidate(startPath)) {
+ ResetGenerator(std::forward<Rest&&>(tail)...);
+ if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path,
+ std::forward<Rest&&>(tail)...)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef CM_LCC_DIAG_SUPPRESS_3288
+# undef CM_LCC_DIAG_SUPPRESS_3288
+# pragma diag_default 3288
+#endif
+
+#ifdef CM_LCC_DIAG_SUPPRESS_1222
+# undef CM_LCC_DIAG_SUPPRESS_1222
+# pragma diag_default 1222
+#endif
+
+// Parse the version number and store the results that were
+// successfully parsed.
+int parseVersion(const std::string& version, unsigned int& major,
+ unsigned int& minor, unsigned int& patch, unsigned int& tweak)
+{
+ return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch,
+ &tweak);
+}
+
+} // anonymous namespace
cmFindPackageCommand::PathLabel
cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT");
@@ -60,25 +454,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED(
const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED(
"EXCLUDE");
-struct StrverscmpGreater
-{
- bool operator()(const std::string& lhs, const std::string& rhs) const
- {
- return cmSystemTools::strverscmp(lhs, rhs) > 0;
- }
-};
-
-struct StrverscmpLesser
-{
- bool operator()(const std::string& lhs, const std::string& rhs) const
- {
- return cmSystemTools::strverscmp(lhs, rhs) < 0;
- }
-};
-
void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin,
std::vector<std::string>::iterator end,
- SortOrderType order, SortDirectionType dir)
+ SortOrderType const order,
+ SortDirectionType const dir)
{
if (order == Name_order) {
if (dir == Dec) {
@@ -86,14 +465,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin,
} else {
std::sort(begin, end);
}
- } else if (order == Natural)
- // natural order uses letters and numbers (contiguous numbers digit are
- // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10
- {
+ } else if (order == Natural) {
+ // natural order uses letters and numbers (contiguous numbers digit are
+ // compared such that e.g. 000 00 < 01 < 010 < 09 < 0 < 1 < 9 < 10
if (dir == Dec) {
- std::sort(begin, end, StrverscmpGreater());
+ std::sort(begin, end, StrverscmpOp<std::greater>());
} else {
- std::sort(begin, end, StrverscmpLesser());
+ std::sort(begin, end, StrverscmpOp<std::less>());
}
}
// else do not sort
@@ -113,11 +491,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status)
void cmFindPackageCommand::AppendSearchPathGroups()
{
- std::vector<cmFindCommon::PathLabel>* labels;
-
// Update the All group with new paths. Note that package redirection must
// take precedence over everything else, so it has to be first in the array.
- labels = &this->PathGroupLabelMap[PathGroup::All];
+ std::vector<cmFindCommon::PathLabel>* const labels =
+ &this->PathGroupLabelMap[PathGroup::All];
labels->insert(labels->begin(), PathLabel::PackageRedirect);
labels->insert(
std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem),
@@ -147,15 +524,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
// Lookup required version of CMake.
- if (cmValue rv =
+ if (cmValue const rv =
this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
unsigned int v[3] = { 0, 0, 0 };
- sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
+ std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]);
}
// Lookup target architecture, if any.
- if (cmValue arch =
+ if (cmValue const arch =
this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
this->LibraryArchitecture = *arch;
}
@@ -184,7 +561,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
// Check if User Package Registry should be disabled
// The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has
// priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
- if (cmValue def =
+ if (cmValue const def =
this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) {
this->NoUserRegistry = !cmIsOn(*def);
} else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
@@ -194,7 +571,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
// Check if System Package Registry should be disabled
// The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has
// priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
- if (cmValue def = this->Makefile->GetDefinition(
+ if (cmValue const def = this->Makefile->GetDefinition(
"CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) {
this->NoSystemRegistry = !cmIsOn(*def);
} else if (this->Makefile->IsOn(
@@ -208,7 +585,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
// Check if Sorting should be enabled
- if (cmValue so =
+ if (cmValue const so =
this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {
if (*so == "NAME") {
@@ -219,7 +596,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
this->SortOrder = None;
}
}
- if (cmValue sd =
+ if (cmValue const sd =
this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) {
this->SortDirection = (*sd == "ASC") ? Asc : Dec;
}
@@ -265,9 +642,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
cmsys::RegularExpression versionRegex(
R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V");
bool haveVersion = false;
- std::set<unsigned int> configArgs;
- std::set<unsigned int> moduleArgs;
- for (unsigned int i = 1; i < args.size(); ++i) {
+ std::vector<std::size_t> configArgs;
+ std::vector<std::size_t> moduleArgs;
+ for (std::size_t i = 1u; i < args.size(); ++i) {
if (args[i] == "QUIET") {
this->Quiet = true;
doing = DoingNone;
@@ -281,17 +658,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
this->GlobalScope = true;
doing = DoingNone;
} else if (args[i] == "MODULE") {
- moduleArgs.insert(i);
+ moduleArgs.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "CONFIG") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "NO_MODULE") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
} else if (args[i] == "REQUIRED") {
this->Required = true;
@@ -301,36 +678,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
} else if (args[i] == "OPTIONAL_COMPONENTS") {
doing = DoingOptionalComponents;
} else if (args[i] == "NAMES") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNames;
} else if (args[i] == "PATHS") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingPaths;
} else if (args[i] == "HINTS") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingHints;
} else if (args[i] == "PATH_SUFFIXES") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingPathSuffixes;
} else if (args[i] == "CONFIGS") {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingConfigs;
} else if (args[i] == "NO_POLICY_SCOPE") {
this->PolicyScope = false;
doing = DoingNone;
} else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") {
this->NoUserRegistry = true;
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
} else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") {
this->NoSystemRegistry = true;
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (args[i] == "NO_CMAKE_BUILDS_PATH") {
// Ignore legacy option.
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
} else if (args[i] == "REGISTRY_VIEW") {
if (++i == args.size()) {
@@ -347,7 +724,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return false;
}
} else if (this->CheckCommonArgument(args[i])) {
- configArgs.insert(i);
+ configArgs.push_back(i);
doing = DoingNone;
} else if ((doing == DoingComponents) ||
(doing == DoingOptionalComponents)) {
@@ -361,8 +738,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
requiredComponents.insert(args[i]);
}
- std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
- componentVarDefs.emplace_back(req_var, isRequired);
+ componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i],
+ isRequired);
// Append to the list of required components.
components += components_sep;
@@ -420,11 +797,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
if (!this->UseFindModules && !this->UseConfigFiles) {
std::ostringstream e;
e << "given options exclusive to Module mode:\n";
- for (unsigned int si : moduleArgs) {
+ for (auto si : moduleArgs) {
e << " " << args[si] << "\n";
}
e << "and options exclusive to Config mode:\n";
- for (unsigned int si : configArgs) {
+ for (auto si : configArgs) {
e << " " << args[si] << "\n";
}
e << "The options are incompatible.";
@@ -443,20 +820,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
if (this->VersionComplete.empty() || components.empty()) {
// Check whether we are recursing inside "Find<name>.cmake" within
// another find_package(<name>) call.
- std::string mod = cmStrCat(this->Name, "_FIND_MODULE");
+ std::string const mod = cmStrCat(this->Name, "_FIND_MODULE");
if (this->Makefile->IsOn(mod)) {
if (this->VersionComplete.empty()) {
// Get version information from the outer call if necessary.
// Requested version string.
- std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE");
+ std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE");
this->VersionComplete = this->Makefile->GetSafeDefinition(ver);
// Whether an exact version is required.
- std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
+ std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
this->VersionExact = this->Makefile->IsOn(exact);
}
if (components.empty()) {
- std::string components_var = this->Name + "_FIND_COMPONENTS";
+ std::string const components_var = this->Name + "_FIND_COMPONENTS";
components = this->Makefile->GetSafeDefinition(components_var);
}
}
@@ -497,15 +874,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
return false;
}
- // Parse the version number and store the results that were
- // successfully parsed.
- auto parseVersion = [](const std::string& version, unsigned int& major,
- unsigned int& minor, unsigned int& patch,
- unsigned int& tweak) -> unsigned int {
- return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch,
- &tweak);
- };
-
if (!this->Version.empty()) {
this->VersionCount =
parseVersion(this->Version, this->VersionMajor, this->VersionMinor,
@@ -533,7 +901,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
- std::string disableFindPackageVar =
+ std::string const disableFindPackageVar =
cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name);
if (this->Makefile->IsOn(disableFindPackageVar)) {
if (this->Required) {
@@ -557,8 +925,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
// A dependency provider (if set) gets first look before other methods.
// We do this before modifying the package root path stack because a
// provider might use methods that ignore that.
- cmState* state = this->Makefile->GetState();
- cmState::Command providerCommand = state->GetDependencyProviderCommand(
+ cmState* const state = this->Makefile->GetState();
+ cmState::Command const providerCommand = state->GetDependencyProviderCommand(
cmDependencyProvider::Method::FindPackage);
if (bypassProvider) {
if (this->DebugMode && providerCommand) {
@@ -725,11 +1093,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
"package configuration file provided by "
<< this->Name << " (" << this->Name << "Config.cmake or "
<< cmSystemTools::LowerCase(this->Name)
- << "-config.cmake). "
- "Otherwise make Find"
- << this->Name
- << ".cmake available in "
- "CMAKE_MODULE_PATH.";
+ << "-config.cmake). Otherwise make Find" << this->Name
+ << ".cmake available in CMAKE_MODULE_PATH.";
}
aw << "\n"
"(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this "
@@ -813,9 +1178,9 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode()
void cmFindPackageCommand::SetVersionVariables(
const std::function<void(const std::string&, cm::string_view)>&
addDefinition,
- const std::string& prefix, const std::string& version, unsigned int count,
- unsigned int major, unsigned int minor, unsigned int patch,
- unsigned int tweak)
+ const std::string& prefix, const std::string& version,
+ const unsigned int count, const unsigned int major, const unsigned int minor,
+ const unsigned int patch, const unsigned int tweak)
{
addDefinition(prefix, version);
@@ -910,7 +1275,7 @@ void cmFindPackageCommand::SetModuleVariables(
}
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
- cm::string_view value)
+ const cm::string_view value)
{
if (cmValue old = this->Makefile->GetDefinition(var)) {
this->OriginalDefs[var].exists = true;
@@ -954,7 +1319,7 @@ bool cmFindPackageCommand::FindModule(bool& found)
if (!mfile.empty()) {
if (system) {
- auto it = this->DeprecatedFindModules.find(this->Name);
+ auto const it = this->DeprecatedFindModules.find(this->Name);
if (it != this->DeprecatedFindModules.end()) {
cmPolicies::PolicyStatus status =
this->Makefile->GetPolicyStatus(it->second);
@@ -978,13 +1343,13 @@ bool cmFindPackageCommand::FindModule(bool& found)
// Load the module we found, and set "<name>_FIND_MODULE" to true
// while inside it.
found = true;
- std::string var = cmStrCat(this->Name, "_FIND_MODULE");
+ std::string const var = cmStrCat(this->Name, "_FIND_MODULE");
this->Makefile->AddDefinition(var, "1");
bool result = this->ReadListFile(mfile, DoPolicyScope);
this->Makefile->RemoveDefinition(var);
if (this->DebugMode) {
- std::string foundVar = cmStrCat(this->Name, "_FOUND");
+ std::string const foundVar = cmStrCat(this->Name, "_FOUND");
if (this->Makefile->IsDefinitionSet(foundVar) &&
!this->Makefile->IsOn(foundVar)) {
@@ -999,7 +1364,7 @@ bool cmFindPackageCommand::FindModule(bool& found)
}
bool cmFindPackageCommand::HandlePackageMode(
- HandlePackageModeType handlePackageModeType)
+ const HandlePackageModeType handlePackageModeType)
{
this->ConsideredConfigs.clear();
@@ -1042,8 +1407,9 @@ bool cmFindPackageCommand::HandlePackageMode(
}
}
- std::string foundVar = cmStrCat(this->Name, "_FOUND");
- std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
+ std::string const foundVar = cmStrCat(this->Name, "_FOUND");
+ std::string const notFoundMessageVar =
+ cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
std::string notFoundMessage;
// If the directory for the config file was found, try to read the file.
@@ -1123,8 +1489,9 @@ bool cmFindPackageCommand::HandlePackageMode(
<< (this->VersionExact ? "exactly matches" : "is compatible with")
<< " requested version "
<< (this->VersionRange.empty() ? "" : "range ") << "\""
- << this->VersionComplete << "\".\n"
- << "The following configuration files were considered but not "
+ << this->VersionComplete
+ << "\".\n"
+ "The following configuration files were considered but not "
"accepted:\n";
for (ConfigFileInfo const& info :
@@ -1172,8 +1539,9 @@ bool cmFindPackageCommand::HandlePackageMode(
"package or SDK, be sure it has been installed.";
} else // if(!this->UseFindModules && !this->UseConfigFiles)
{
- e << "No \"Find" << this->Name << ".cmake\" found in "
- << "CMAKE_MODULE_PATH.";
+ e << "No \"Find" << this->Name
+ << ".cmake\" found in "
+ "CMAKE_MODULE_PATH.";
aw
<< "Find" << this->Name
@@ -1217,16 +1585,16 @@ bool cmFindPackageCommand::HandlePackageMode(
this->Makefile->AddDefinition(foundVar, found ? "1" : "0");
// Set a variable naming the configuration file that was found.
- std::string fileVar = cmStrCat(this->Name, "_CONFIG");
+ std::string const fileVar = cmStrCat(this->Name, "_CONFIG");
if (found) {
this->Makefile->AddDefinition(fileVar, this->FileFound);
} else {
this->Makefile->RemoveDefinition(fileVar);
}
- std::string consideredConfigsVar =
+ std::string const consideredConfigsVar =
cmStrCat(this->Name, "_CONSIDERED_CONFIGS");
- std::string consideredVersionsVar =
+ std::string const consideredVersionsVar =
cmStrCat(this->Name, "_CONSIDERED_VERSIONS");
std::string consideredConfigFiles;
@@ -1312,7 +1680,7 @@ bool cmFindPackageCommand::FindConfig()
void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value)
{
- std::string help =
+ std::string const help =
cmStrCat("The directory containing a CMake configuration file for ",
this->Name, '.');
this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(),
@@ -1351,7 +1719,7 @@ bool cmFindPackageCommand::FindAppBundleConfig()
}
bool cmFindPackageCommand::ReadListFile(const std::string& f,
- PolicyScopeRule psr)
+ const PolicyScopeRule psr)
{
const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
@@ -1362,12 +1730,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f,
if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
return true;
}
- std::string e = cmStrCat("Error reading CMake code from \"", f, "\".");
+ std::string const e = cmStrCat("Error reading CMake code from \"", f, "\".");
this->SetError(e);
return false;
}
-void cmFindPackageCommand::AppendToFoundProperty(bool found)
+void cmFindPackageCommand::AppendToFoundProperty(const bool found)
{
std::vector<std::string> foundContents;
cmValue foundProp =
@@ -1410,27 +1778,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found)
void cmFindPackageCommand::AppendSuccessInformation()
{
{
- std::string transitivePropName =
+ std::string const transitivePropName =
cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY");
this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False");
}
- std::string found = cmStrCat(this->Name, "_FOUND");
- std::string upperFound = cmSystemTools::UpperCase(found);
+ std::string const found = cmStrCat(this->Name, "_FOUND");
+ std::string const upperFound = cmSystemTools::UpperCase(found);
- bool upperResult = this->Makefile->IsOn(upperFound);
- bool result = this->Makefile->IsOn(found);
- bool packageFound = (result || upperResult);
+ bool const upperResult = this->Makefile->IsOn(upperFound);
+ bool const result = this->Makefile->IsOn(found);
+ bool const packageFound = (result || upperResult);
this->AppendToFoundProperty(packageFound);
// Record whether the find was quiet or not, so this can be used
// e.g. in FeatureSummary.cmake
- std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET");
+ std::string const quietInfoPropName =
+ cmStrCat("_CMAKE_", this->Name, "_QUIET");
this->Makefile->GetState()->SetGlobalProperty(
quietInfoPropName, this->Quiet ? "TRUE" : "FALSE");
// set a global property to record the required version of this package
- std::string versionInfoPropName =
+ std::string const versionInfoPropName =
cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION");
std::string versionInfo;
if (!this->VersionRange.empty()) {
@@ -1442,28 +1811,13 @@ void cmFindPackageCommand::AppendSuccessInformation()
this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName,
versionInfo.c_str());
if (this->Required) {
- std::string requiredInfoPropName =
+ std::string const requiredInfoPropName =
cmStrCat("_CMAKE_", this->Name, "_TYPE");
this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName,
"REQUIRED");
}
}
-inline std::size_t collectPathsForDebug(std::string& buffer,
- cmSearchPath const& searchPath,
- std::size_t startIndex = 0)
-{
- const auto& paths = searchPath.GetPaths();
- if (paths.empty()) {
- buffer += " none\n";
- return 0;
- }
- for (std::size_t i = startIndex; i < paths.size(); i++) {
- buffer += " " + paths[i].Path + "\n";
- }
- return paths.size();
-}
-
void cmFindPackageCommand::ComputePrefixes()
{
this->FillPrefixesPackageRedirect();
@@ -1674,14 +2028,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry()
}
#if defined(_WIN32) && !defined(__CYGWIN__)
-# include <windows.h>
-// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx
-# if !defined(KEY_WOW64_32KEY)
-# define KEY_WOW64_32KEY 0x0200
-# endif
-# if !defined(KEY_WOW64_64KEY)
-# define KEY_WOW64_64KEY 0x0100
-# endif
void cmFindPackageCommand::LoadPackageRegistryWinUser()
{
// HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views.
@@ -1704,7 +2050,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem()
}
}
-void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view,
+void cmFindPackageCommand::LoadPackageRegistryWin(const bool user,
+ const unsigned int view,
cmSearchPath& outPaths)
{
std::wstring key = L"Software\\Kitware\\CMake\\Packages\\";
@@ -1756,28 +2103,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view,
RegCloseKey(hKey);
}
}
-#else
-class cmFindPackageCommandHoldFile
-{
- const char* File;
-
-public:
- cmFindPackageCommandHoldFile(const char* f)
- : File(f)
- {
- }
- ~cmFindPackageCommandHoldFile()
- {
- if (this->File) {
- cmSystemTools::RemoveFile(this->File);
- }
- }
- cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete;
- cmFindPackageCommandHoldFile& operator=(
- const cmFindPackageCommandHoldFile&) = delete;
- void Release() { this->File = nullptr; }
-};
+#else
void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir,
cmSearchPath& outPaths)
{
@@ -1877,7 +2204,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable()
std::vector<std::string> expanded = cmExpandedList(*prefix_paths);
long count = 0;
for (const auto& path : expanded) {
- bool to_add =
+ bool const to_add =
!(path == install_path_to_remove && ++count == install_prefix_count);
if (to_add) {
paths.AddPath(path);
@@ -1941,7 +2268,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir)
std::string d = dir;
if (!s.empty()) {
d += s;
- d += "/";
+ d += '/';
}
if (this->CheckDirectory(d)) {
return true;
@@ -1955,7 +2282,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir)
assert(!dir.empty() && dir.back() == '/');
// Look for the file in this directory.
- std::string d = dir.substr(0, dir.size() - 1);
+ std::string const d = dir.substr(0, dir.size() - 1);
if (this->FindConfigFile(d, this->FileFound)) {
// Remove duplicate slashes.
cmSystemTools::ConvertToUnixSlashes(this->FileFound);
@@ -2028,8 +2355,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
std::string& result_version)
{
// The version file will be loaded in an isolated scope.
- cmMakefile::ScopePushPop varScope(this->Makefile);
- cmMakefile::PolicyPushPop polScope(this->Makefile);
+ cmMakefile::ScopePushPop const varScope(this->Makefile);
+ cmMakefile::PolicyPushPop const polScope(this->Makefile);
static_cast<void>(varScope);
static_cast<void>(polScope);
@@ -2076,7 +2403,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
if (this->ReadListFile(version_file, NoPolicyScope)) {
// Check the output variables.
bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT");
- bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE");
+ bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE");
if (!okay && !this->VersionExact) {
okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE");
}
@@ -2096,8 +2423,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
unsigned int parsed_patch;
unsigned int parsed_tweak;
this->VersionFoundCount =
- sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major,
- &parsed_minor, &parsed_patch, &parsed_tweak);
+ parseVersion(this->VersionFound, parsed_major, parsed_minor,
+ parsed_patch, parsed_tweak);
switch (this->VersionFoundCount) {
case 4:
this->VersionFoundTweak = parsed_tweak;
@@ -2129,7 +2456,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
void cmFindPackageCommand::StoreVersionFound()
{
// Store the whole version string.
- std::string ver = cmStrCat(this->Name, "_VERSION");
+ std::string const ver = cmStrCat(this->Name, "_VERSION");
auto addDefinition = [this](const std::string& variable,
cm::string_view value) {
this->Makefile->AddDefinition(variable, value);
@@ -2145,357 +2472,6 @@ void cmFindPackageCommand::StoreVersionFound()
}
}
-class cmFileListGeneratorBase
-{
-public:
- virtual ~cmFileListGeneratorBase() = default;
-
-protected:
- bool Consider(std::string const& fullPath, cmFileList& listing);
-
-private:
- bool Search(cmFileList&);
- virtual bool Search(std::string const& parent, cmFileList&) = 0;
- virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0;
- friend class cmFileList;
- cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next);
- std::unique_ptr<cmFileListGeneratorBase> Next;
-};
-
-class cmFileList
-{
-public:
- virtual ~cmFileList() = default;
- cmFileList& operator/(cmFileListGeneratorBase const& rhs)
- {
- if (this->Last) {
- this->Last = this->Last->SetNext(rhs);
- } else {
- this->First = rhs.Clone();
- this->Last = this->First.get();
- }
- return *this;
- }
- bool Search()
- {
- if (this->First) {
- return this->First->Search(*this);
- }
- return false;
- }
-
-private:
- virtual bool Visit(std::string const& fullPath) = 0;
- friend class cmFileListGeneratorBase;
- std::unique_ptr<cmFileListGeneratorBase> First;
- cmFileListGeneratorBase* Last = nullptr;
-};
-
-class cmFindPackageFileList : public cmFileList
-{
-public:
- cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true)
- : FPC(fpc)
- , UseSuffixes(use_suffixes)
- {
- }
-
-private:
- bool Visit(std::string const& fullPath) override
- {
- if (this->UseSuffixes) {
- return this->FPC->SearchDirectory(fullPath);
- }
- return this->FPC->CheckDirectory(fullPath);
- }
- cmFindPackageCommand* FPC;
- bool UseSuffixes;
-};
-
-bool cmFileListGeneratorBase::Search(cmFileList& listing)
-{
- return this->Search("", listing);
-}
-
-cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext(
- cmFileListGeneratorBase const& next)
-{
- this->Next = next.Clone();
- return this->Next.get();
-}
-
-bool cmFileListGeneratorBase::Consider(std::string const& fullPath,
- cmFileList& listing)
-{
- if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) {
- return false;
- }
- if (this->Next) {
- return this->Next->Search(fullPath + "/", listing);
- }
- return listing.Visit(fullPath + "/");
-}
-
-class cmFileListGeneratorFixed : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorFixed(std::string str)
- : String(std::move(str))
- {
- }
- cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r)
- : String(r.String)
- {
- }
-
-private:
- std::string String;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- std::string fullPath = parent + this->String;
- return this->Consider(fullPath, lister);
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- std::unique_ptr<cmFileListGeneratorBase> g(
- new cmFileListGeneratorFixed(*this));
- return g;
- }
-};
-
-class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorEnumerate(std::vector<std::string> const& v)
- : Vector(v)
- {
- }
- cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r)
- : Vector(r.Vector)
- {
- }
-
-private:
- std::vector<std::string> const& Vector;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- for (std::string const& i : this->Vector) {
- if (this->Consider(parent + i, lister)) {
- return true;
- }
- }
- return false;
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- std::unique_ptr<cmFileListGeneratorBase> g(
- new cmFileListGeneratorEnumerate(*this));
- return g;
- }
-};
-
-class cmFileListGeneratorProject : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorProject(std::vector<std::string> const& names,
- cmFindPackageCommand::SortOrderType so,
- cmFindPackageCommand::SortDirectionType sd)
- : Names(names)
- {
- this->SetSort(so, sd);
- }
- cmFileListGeneratorProject(cmFileListGeneratorProject const& r)
- : Names(r.Names)
- {
- this->SetSort(r.SortOrder, r.SortDirection);
- }
-
- void SetSort(cmFindPackageCommand::SortOrderType o,
- cmFindPackageCommand::SortDirectionType d)
- {
- this->SortOrder = o;
- this->SortDirection = d;
- }
-
-protected:
- // sort parameters
- cmFindPackageCommand::SortOrderType SortOrder;
- cmFindPackageCommand::SortDirectionType SortDirection;
-
-private:
- std::vector<std::string> const& Names;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- // Construct a list of matches.
- std::vector<std::string> matches;
- cmsys::Directory d;
- d.Load(parent);
- for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
- const char* fname = d.GetFile(i);
- if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
- continue;
- }
- for (std::string const& n : this->Names) {
- if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) {
- matches.emplace_back(fname);
- }
- }
- }
-
- // before testing the matches check if there is a specific sorting order to
- // perform
- if (this->SortOrder != cmFindPackageCommand::None) {
- cmFindPackageCommand::Sort(matches.begin(), matches.end(),
- this->SortOrder, this->SortDirection);
- }
-
- for (std::string const& i : matches) {
- if (this->Consider(parent + i, lister)) {
- return true;
- }
- }
- return false;
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- std::unique_ptr<cmFileListGeneratorBase> g(
- new cmFileListGeneratorProject(*this));
- return g;
- }
-};
-
-class cmFileListGeneratorMacProject : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorMacProject(std::vector<std::string> const& names,
- const char* ext)
- : Names(names)
- , Extension(ext)
- {
- }
- cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r)
- : Names(r.Names)
- , Extension(r.Extension)
- {
- }
-
-private:
- std::vector<std::string> const& Names;
- std::string Extension;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- // Construct a list of matches.
- std::vector<std::string> matches;
- cmsys::Directory d;
- d.Load(parent);
- for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
- const char* fname = d.GetFile(i);
- if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
- continue;
- }
- for (std::string name : this->Names) {
- name += this->Extension;
- if (cmsysString_strcasecmp(fname, name.c_str()) == 0) {
- matches.emplace_back(fname);
- }
- }
- }
-
- for (std::string const& i : matches) {
- if (this->Consider(parent + i, lister)) {
- return true;
- }
- }
- return false;
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- std::unique_ptr<cmFileListGeneratorBase> g(
- new cmFileListGeneratorMacProject(*this));
- return g;
- }
-};
-
-class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorCaseInsensitive(std::string str)
- : String(std::move(str))
- {
- }
- cmFileListGeneratorCaseInsensitive(
- cmFileListGeneratorCaseInsensitive const& r)
- : String(r.String)
- {
- }
-
-private:
- std::string String;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- // Look for matching files.
- std::vector<std::string> matches;
- cmsys::Directory d;
- d.Load(parent);
- for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
- const char* fname = d.GetFile(i);
- if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
- continue;
- }
- if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) {
- if (this->Consider(parent + fname, lister)) {
- return true;
- }
- }
- }
- return false;
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- std::unique_ptr<cmFileListGeneratorBase> g(
- new cmFileListGeneratorCaseInsensitive(*this));
- return g;
- }
-};
-
-class cmFileListGeneratorGlob : public cmFileListGeneratorBase
-{
-public:
- cmFileListGeneratorGlob(std::string str)
- : Pattern(std::move(str))
- {
- }
- cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r)
- : Pattern(r.Pattern)
- {
- }
-
-private:
- std::string Pattern;
- bool Search(std::string const& parent, cmFileList& lister) override
- {
- // Glob the set of matching files.
- std::string expr = cmStrCat(parent, this->Pattern);
- cmsys::Glob g;
- if (!g.FindFiles(expr)) {
- return false;
- }
- std::vector<std::string> const& files = g.GetFiles();
-
- // Look for directories among the matches.
- for (std::string const& f : files) {
- if (this->Consider(f, lister)) {
- return true;
- }
- }
- return false;
- }
- std::unique_ptr<cmFileListGeneratorBase> Clone() const override
- {
- return cm::make_unique<cmFileListGeneratorGlob>(*this);
- }
-};
-
bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
{
assert(!prefix_in.empty() && prefix_in.back() == '/');
@@ -2515,148 +2491,95 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
return false;
}
- // PREFIX/ (useful on windows or in build trees)
+ // PREFIX/ (useful on windows or in build trees)
if (this->SearchDirectory(prefix_in)) {
return true;
}
// Strip the trailing slash because the path generator is about to
// add one.
- std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+ std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
- // PREFIX/(cmake|CMake)/ (useful on windows or in build trees)
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
+ auto searchFn = [this](const std::string& fullPath) -> bool {
+ return this->SearchDirectory(fullPath);
+ };
+
+ auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
+ auto firstPkgDirGen =
+ cmProjectDirectoryListGenerator{ this->Names, this->SortOrder,
+ this->SortDirection };
+
+ // PREFIX/(cmake|CMake)/ (useful on windows or in build trees)
+ if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) {
+ return true;
}
- // PREFIX/(Foo|foo|FOO).*/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection);
- if (lister.Search()) {
- return true;
- }
+ // PREFIX/(Foo|foo|FOO).*/
+ if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) {
+ return true;
}
- // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
+ // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/
+ if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) {
+ return true;
}
// Construct list of common install locations (lib and share).
- std::vector<std::string> common;
+ std::vector<cm::string_view> common;
+ std::string libArch;
if (!this->LibraryArchitecture.empty()) {
- common.push_back("lib/" + this->LibraryArchitecture);
+ libArch = "lib/" + this->LibraryArchitecture;
+ common.emplace_back(libArch);
}
if (this->UseLib32Paths) {
- common.emplace_back("lib32");
+ common.emplace_back("lib32"_s);
}
if (this->UseLib64Paths) {
- common.emplace_back("lib64");
+ common.emplace_back("lib64"_s);
}
if (this->UseLibx32Paths) {
- common.emplace_back("libx32");
+ common.emplace_back("libx32"_s);
}
- common.emplace_back("lib");
- common.emplace_back("share");
+ common.emplace_back("lib"_s);
+ common.emplace_back("share"_s);
- // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorFixed("cmake") /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection);
- if (lister.Search()) {
- return true;
- }
+ auto cmnGen = cmEnumPathSegmentsGenerator{ common };
+ auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s };
+
+ // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
+ if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) {
+ return true;
}
- // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection);
- if (lister.Search()) {
- return true;
- }
+ // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
+ if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) {
+ return true;
}
- // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
+ // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
+ if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) {
+ return true;
}
+ auto secondPkgDirGen =
+ cmProjectDirectoryListGenerator{ this->Names, this->SortOrder,
+ this->SortDirection };
+
// PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorFixed("cmake") /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection);
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen,
+ secondPkgDirGen)) {
+ return true;
}
// PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection);
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
+ secondPkgDirGen)) {
+ return true;
}
// PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorEnumerate(common) /
- cmFileListGeneratorProject(this->Names, this->SortOrder,
- this->SortDirection) /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
- }
-
- return false;
+ return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
+ secondPkgDirGen, iCMakeGen);
}
bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
@@ -2665,56 +2588,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
// Strip the trailing slash because the path generator is about to
// add one.
- std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+ std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
+
+ auto searchFn = [this](const std::string& fullPath) -> bool {
+ return this->SearchDirectory(fullPath);
+ };
+
+ auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
+ auto fwGen =
+ cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s };
+ auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s };
+ auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s };
+ auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s };
// <prefix>/Foo.framework/Resources/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".framework") /
- cmFileListGeneratorFixed("Resources");
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) {
+ return true;
}
+
// <prefix>/Foo.framework/Resources/CMake/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".framework") /
- cmFileListGeneratorFixed("Resources") /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) {
+ return true;
}
// <prefix>/Foo.framework/Versions/*/Resources/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".framework") /
- cmFileListGeneratorFixed("Versions") /
- cmFileListGeneratorGlob("*/Resources");
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) {
+ return true;
}
// <prefix>/Foo.framework/Versions/*/Resources/CMake/
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".framework") /
- cmFileListGeneratorFixed("Versions") /
- cmFileListGeneratorGlob("*/Resources") /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
- }
-
- return false;
+ return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen);
}
bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
@@ -2723,32 +2626,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
// Strip the trailing slash because the path generator is about to
// add one.
- std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+ std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
+
+ auto searchFn = [this](const std::string& fullPath) -> bool {
+ return this->SearchDirectory(fullPath);
+ };
+
+ auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s };
+ auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s };
// <prefix>/Foo.app/Contents/Resources
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".app") /
- cmFileListGeneratorFixed("Contents/Resources");
- if (lister.Search()) {
- return true;
- }
+ if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) {
+ return true;
}
// <prefix>/Foo.app/Contents/Resources/CMake
- {
- cmFindPackageFileList lister(this);
- lister / cmFileListGeneratorFixed(prefix) /
- cmFileListGeneratorMacProject(this->Names, ".app") /
- cmFileListGeneratorFixed("Contents/Resources") /
- cmFileListGeneratorCaseInsensitive("cmake");
- if (lister.Search()) {
- return true;
- }
- }
-
- return false;
+ return TryGeneratedPaths(
+ searchFn, prefix, appGen, crGen,
+ cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s });
}
// TODO: Debug cmsys::Glob double slash problem.
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 80fd8f8cae..28e00a109d 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -153,8 +153,6 @@ private:
bool SearchFrameworkPrefix(std::string const& prefix_in);
bool SearchAppBundlePrefix(std::string const& prefix_in);
- friend class cmFindPackageFileList;
-
struct OriginalDef
{
bool exists;
diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx
index b529b8f04c..6212bbde2b 100644
--- a/Source/cmGeneratedFileStream.cxx
+++ b/Source/cmGeneratedFileStream.cxx
@@ -124,10 +124,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
void cmGeneratedFileStreamBase::Open(std::string const& name)
{
// Save the original name of the file.
- this->Name = name;
+ this->Name = cmSystemTools::CollapseFullPath(name);
// Create the name of the temporary file.
- this->TempName = name;
+ this->TempName = this->Name;
#if defined(__VMS)
this->TempName += "_";
#else
@@ -231,7 +231,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
void cmGeneratedFileStream::SetName(const std::string& fname)
{
- this->Name = fname;
+ this->Name = cmSystemTools::CollapseFullPath(fname);
}
void cmGeneratedFileStream::SetTempExt(std::string const& ext)
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index dace0556b6..d59ac2b3a0 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -16,6 +16,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -8660,6 +8661,11 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
verifyTarget->SetProperty("AUTOUIC", "OFF");
verifyTarget->SetProperty("DISABLE_PRECOMPILE_HEADERS", "ON");
verifyTarget->SetProperty("UNITY_BUILD", "OFF");
+ cm::optional<std::map<std::string, cmValue>>
+ perConfigCompileDefinitions;
+ verifyTarget->FinalizeTargetCompileInfo(
+ this->Makefile->GetCompileDefinitionsEntries(),
+ perConfigCompileDefinitions);
}
if (fileCgesContextSensitive) {
@@ -8706,6 +8712,12 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
languages->insert("C");
}
}
+
+ if (languages->empty()) {
+ std::vector<std::string> languagesVector;
+ this->GlobalGenerator->GetEnabledLanguages(languagesVector);
+ languages->insert(languagesVector.begin(), languagesVector.end());
+ }
}
if (languages->count("CXX")) {
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 4cb541a752..edc4118499 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -14,6 +14,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
@@ -49,6 +50,7 @@
#include "cmState.h"
#include "cmStateDirectory.h"
#include "cmStateTypes.h"
+#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmVersion.h"
#include "cmWorkingDirectory.h"
@@ -62,10 +64,6 @@
# include "cmQtAutoGenGlobalInitializer.h"
#endif
-#if defined(_MSC_VER) && _MSC_VER >= 1800
-# define KWSYS_WINDOWS_DEPRECATED_GetVersionEx
-#endif
-
const std::string kCMAKE_PLATFORM_INFO_INITIALIZED =
"CMAKE_PLATFORM_INFO_INITIALIZED";
@@ -616,34 +614,12 @@ void cmGlobalGenerator::EnableLanguage(
// what platform we are running on
if (!mf->GetDefinition("CMAKE_SYSTEM")) {
#if defined(_WIN32) && !defined(__CYGWIN__)
- /* Windows version number data. */
- OSVERSIONINFOEXW osviex;
- ZeroMemory(&osviex, sizeof(osviex));
- osviex.dwOSVersionInfoSize = sizeof(osviex);
-
-# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
-# pragma warning(push)
-# ifdef __INTEL_COMPILER
-# pragma warning(disable : 1478)
-# elif defined __clang__
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wdeprecated-declarations"
-# else
-# pragma warning(disable : 4996)
-# endif
-# endif
- GetVersionExW((OSVERSIONINFOW*)&osviex);
-# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersionEx
-# ifdef __clang__
-# pragma clang diagnostic pop
-# else
-# pragma warning(pop)
-# endif
-# endif
+ cmSystemTools::WindowsVersion windowsVersion =
+ cmSystemTools::GetWindowsVersion();
std::ostringstream windowsVersionString;
- windowsVersionString << osviex.dwMajorVersion << "."
- << osviex.dwMinorVersion << "."
- << osviex.dwBuildNumber;
+ windowsVersionString << windowsVersion.dwMajorVersion << "."
+ << windowsVersion.dwMinorVersion << "."
+ << windowsVersion.dwBuildNumber;
mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str());
#endif
// Read the DetermineSystem file
@@ -1399,7 +1375,9 @@ void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes)
this->CheckTargetProperties();
}
this->CreateGeneratorTargets(targetTypes);
- this->ComputeBuildFileGenerators();
+ if (targetTypes == TargetTypes::AllTargets) {
+ this->ComputeBuildFileGenerators();
+ }
}
void cmGlobalGenerator::CreateImportedGenerationObjects(
@@ -1846,39 +1824,14 @@ void cmGlobalGenerator::FinalizeTargetCompileInfo()
// Construct per-target generator information.
for (const auto& mf : this->Makefiles) {
- const cmBTStringRange noconfig_compile_definitions =
+ const cmBTStringRange noConfigCompileDefinitions =
mf->GetCompileDefinitionsEntries();
+ cm::optional<std::map<std::string, cmValue>> perConfigCompileDefinitions;
for (auto& target : mf->GetTargets()) {
cmTarget* t = &target.second;
- if (t->GetType() == cmStateEnums::GLOBAL_TARGET) {
- continue;
- }
-
- t->AppendBuildInterfaceIncludes();
-
- if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
- continue;
- }
-
- for (auto const& def : noconfig_compile_definitions) {
- t->InsertCompileDefinition(def);
- }
-
- cmPolicies::PolicyStatus polSt =
- mf->GetPolicyStatus(cmPolicies::CMP0043);
- if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
- std::vector<std::string> configs =
- mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
-
- for (std::string const& c : configs) {
- std::string defPropName =
- cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
- if (cmValue val = mf->GetProperty(defPropName)) {
- t->AppendProperty(defPropName, *val);
- }
- }
- }
+ t->FinalizeTargetCompileInfo(noConfigCompileDefinitions,
+ perConfigCompileDefinitions);
}
// The standard include directories for each language
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 155efde70d..29eeb5a917 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -1286,14 +1286,6 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand(
return makeCommands;
}
-bool cmGlobalVisualStudio10Generator::IsInSolution(
- const cmGeneratorTarget* gt) const
-{
- return gt->IsInBuildSystem() &&
- !(this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
- gt->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET);
-}
-
bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
{
if (this->DefaultPlatformToolset == "v100") {
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index 2203f7110d..4977a845d0 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -118,8 +118,6 @@ public:
return this->WindowsTargetPlatformVersion;
}
- bool IsInSolution(const cmGeneratorTarget* gt) const override;
-
/** Return true if building for WindowsCE */
bool TargetsWindowsCE() const override { return this->SystemIsWindowsCE; }
diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx
index ce943a2c97..758ce83813 100644
--- a/Source/cmGlobalVisualStudio71Generator.cxx
+++ b/Source/cmGlobalVisualStudio71Generator.cxx
@@ -180,7 +180,7 @@ void cmGlobalVisualStudio71Generator::WriteExternalProject(
fout << "\tProjectSection(ProjectDependencies) = postProject\n";
for (BT<std::pair<std::string, bool>> const& it : depends) {
std::string const& dep = it.Value.first;
- if (!dep.empty()) {
+ if (this->IsDepInSolution(dep)) {
fout << "\t\t{" << this->GetGUID(dep) << "} = {" << this->GetGUID(dep)
<< "}\n";
}
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index cddaaa4339..c6af20ad64 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -843,6 +843,12 @@ bool cmGlobalVisualStudioGenerator::IsInSolution(
return gt->IsInBuildSystem();
}
+bool cmGlobalVisualStudioGenerator::IsDepInSolution(
+ const std::string& targetName) const
+{
+ return !targetName.empty();
+}
+
bool cmGlobalVisualStudioGenerator::TargetCompare::operator()(
cmGeneratorTarget const* l, cmGeneratorTarget const* r) const
{
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 4f5f1006e7..f45b4d49a2 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -101,6 +101,9 @@ public:
// return true if target should be included in solution.
virtual bool IsInSolution(const cmGeneratorTarget* gt) const;
+ // return true if project dependency should be included in solution.
+ virtual bool IsDepInSolution(const std::string& targetName) const;
+
/** Get the top-level registry key for this VS version. */
std::string GetRegistryBase();
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index b72fc4e80f..7e36881935 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -25,53 +25,100 @@
#include "cmVSSetupHelper.h"
#include "cmake.h"
-#if defined(_M_ARM64)
-# define HOST_PLATFORM_NAME "ARM64"
-# define HOST_TOOLS_ARCH(v) \
- (v >= cmGlobalVisualStudioGenerator::VSVersion::VS17) ? "ARM64" : ""
-#elif defined(_M_ARM)
-# define HOST_PLATFORM_NAME "ARM"
-# define HOST_TOOLS_ARCH(v) ""
-#elif defined(_M_IA64)
-# define HOST_PLATFORM_NAME "Itanium"
-# define HOST_TOOLS_ARCH(v) ""
-#elif defined(_WIN64)
-# define HOST_PLATFORM_NAME "x64"
-# define HOST_TOOLS_ARCH(v) "x64"
-#else
+#ifndef IMAGE_FILE_MACHINE_ARM64
+# define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian
+#endif
+
static bool VSIsWow64()
{
BOOL isWow64 = false;
return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
}
+
+static bool VSIsArm64Host()
+{
+ typedef BOOL(WINAPI * CM_ISWOW64PROCESS2)(
+ HANDLE hProcess, USHORT * pProcessMachine, USHORT * pNativeMachine);
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+# define CM_VS_GCC_DIAGNOSTIC_PUSHED
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
+ static const CM_ISWOW64PROCESS2 s_IsWow64Process2Impl =
+ (CM_ISWOW64PROCESS2)GetProcAddress(
+ GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"),
+ "IsWow64Process2");
+#ifdef CM_VS_GCC_DIAGNOSTIC_PUSHED
+# pragma GCC diagnostic pop
+# undef CM_VS_GCC_DIAGNOSTIC_PUSHED
+#endif
+
+ USHORT processMachine, nativeMachine;
+
+ return s_IsWow64Process2Impl != nullptr &&
+ s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine,
+ &nativeMachine) &&
+ nativeMachine == IMAGE_FILE_MACHINE_ARM64;
+}
+
+static bool VSHasDotNETFrameworkArm64()
+{
+ std::string dotNetArm64;
+ return cmSystemTools::ReadRegistryValue(
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64",
+ dotNetArm64, cmSystemTools::KeyWOW64_64);
+}
+
+static bool VSIsWindows11OrGreater()
+{
+ cmSystemTools::WindowsVersion const windowsVersion =
+ cmSystemTools::GetWindowsVersion();
+ return (windowsVersion.dwMajorVersion > 10 ||
+ (windowsVersion.dwMajorVersion == 10 &&
+ windowsVersion.dwMinorVersion > 0) ||
+ (windowsVersion.dwMajorVersion == 10 &&
+ windowsVersion.dwMinorVersion == 0 &&
+ windowsVersion.dwBuildNumber >= 22000));
+}
static std::string VSHostPlatformName()
{
-#ifdef HOST_PLATFORM_NAME
- return HOST_PLATFORM_NAME;
-#else
- if (VSIsWow64()) {
+ if (VSIsArm64Host()) {
+ return "ARM64";
+ } else if (VSIsWow64()) {
return "x64";
} else {
+#if defined(_M_ARM)
+ return "ARM";
+#elif defined(_M_IA64)
+ return "Itanium";
+#elif defined(_WIN64)
+ return "x64";
+#else
return "Win32";
- }
#endif
+ }
}
static std::string VSHostArchitecture(
cmGlobalVisualStudioGenerator::VSVersion v)
{
- static_cast<void>(v);
-#ifdef HOST_TOOLS_ARCH
- return HOST_TOOLS_ARCH(v);
-#else
- if (VSIsWow64()) {
+ if (VSIsArm64Host()) {
+ return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : "";
+ } else if (VSIsWow64()) {
return "x64";
} else {
+#if defined(_M_ARM)
+ return "";
+#elif defined(_M_IA64)
+ return "";
+#elif defined(_WIN64)
+ return "x64";
+#else
return "x86";
- }
#endif
+ }
}
static unsigned int VSVersionToMajor(
@@ -899,17 +946,24 @@ std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
std::string vs;
if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
-#if defined(_M_ARM64)
- std::string msbuild_arm64 =
- vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe";
- if (cmSystemTools::FileExists(msbuild_arm64)) {
- return msbuild_arm64;
- }
-#endif
-
- msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
- if (cmSystemTools::FileExists(msbuild)) {
- return msbuild;
+ if (VSIsArm64Host()) {
+ if (VSHasDotNETFrameworkArm64()) {
+ msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe";
+ if (cmSystemTools::FileExists(msbuild)) {
+ return msbuild;
+ }
+ }
+ if (VSIsWindows11OrGreater()) {
+ msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
+ if (cmSystemTools::FileExists(msbuild)) {
+ return msbuild;
+ }
+ }
+ } else {
+ msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
+ if (cmSystemTools::FileExists(msbuild)) {
+ return msbuild;
+ }
}
}
msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index b1044a8c36..82adca83ca 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -2160,11 +2160,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
// These generic args also contain the runtime dependency set
std::string runtimeDependencySetArg;
std::vector<std::string> runtimeDependencyArgVector;
- std::vector<cm::string_view> parsedArgs;
cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg);
- genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr,
- &parsedArgs);
+ genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector);
bool success = genericArgs.Finalize();
cmInstallCommandArguments libraryArgs(helper.DefaultComponentName);
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 86ae45db95..a4080d80df 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -1343,9 +1343,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
}
}
- this->ExportObjectCompileCommand(
- language, sourceFilePath, objectDir, objectFileName, objectFileDir,
- vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
+ if (firstForConfig) {
+ this->ExportObjectCompileCommand(
+ language, sourceFilePath, objectDir, objectFileName, objectFileDir,
+ vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
+ }
objBuild.Outputs.push_back(objectFileName);
if (firstForConfig) {
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 31538b6f5b..7e195664e2 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -48,6 +48,12 @@ using options_set = std::set<cm::string_view>;
struct UserArgumentParser : public cmArgumentParser<void>
{
+ void BindKeywordsMissingValue(std::vector<cm::string_view>& ref)
+ {
+ this->cmArgumentParser<void>::BindKeywordMissingValue(
+ [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
+ }
+
template <typename T, typename H>
void Bind(std::vector<std::string> const& names,
std::map<std::string, T>& ref, H duplicateKey)
@@ -211,8 +217,9 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args,
}
std::vector<cm::string_view> keywordsMissingValues;
+ parser.BindKeywordsMissingValue(keywordsMissingValues);
- parser.Parse(list, &unparsed, &keywordsMissingValues);
+ parser.Parse(list, &unparsed);
PassParsedArguments(
prefix, status.GetMakefile(), options, singleValArgs, multiValArgs,
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 1514a8aced..40f3ab58d1 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -909,7 +909,6 @@ bool cmQtAutoGenInitializer::InitScanFiles()
// The reason is that their file names might be discovered from source files
// at generation time.
if (this->MocOrUicEnabled()) {
- std::unordered_set<std::string> addedFiles;
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
@@ -951,28 +950,25 @@ bool cmQtAutoGenInitializer::InitScanFiles()
cmExpandedList(uicOpts));
}
- auto uiHeaderFileName = cmStrCat(
- "ui_"_s, cmSystemTools::GetFilenameWithoutLastExtension(fullPath),
- ".h"_s);
+ auto uiHeaderRelativePath = cmSystemTools::RelativePath(
+ this->LocalGen->GetCurrentSourceDirectory(),
+ cmSystemTools::GetFilenamePath(fullPath));
- // .ui files with the same base name will conflict. Yield an error.
- {
- auto insertResult = addedFiles.insert(uiHeaderFileName);
- if (!insertResult.second) {
- this->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmStrCat("More than one .ui file with the name "_s,
- cmSystemTools::GetFilenameName(fullPath),
- " was found in the sources for target "_s,
- this->GenTarget->GetName(), "."));
- }
+ // 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),
- cmStrCat("/"_s, uiHeaderFileName));
+ uiHeaderFilePath);
this->Uic.UiHeaders.emplace_back(
std::make_pair(uiHeader, uiHeaderGenex));
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 3de45bc7d7..672cdc79b8 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -107,6 +107,10 @@
# include <sys/utsname.h>
#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+# define CM_WINDOWS_DEPRECATED_GetVersionEx
+#endif
+
namespace {
cmSystemTools::InterruptCallback s_InterruptCallback;
@@ -904,6 +908,40 @@ cmSystemTools::WindowsFileRetry cmSystemTools::GetWindowsDirectoryRetry()
InitWindowsDirectoryRetry().Retry;
return retry;
}
+
+cmSystemTools::WindowsVersion cmSystemTools::GetWindowsVersion()
+{
+ /* Windows version number data. */
+ OSVERSIONINFOEXW osviex;
+ ZeroMemory(&osviex, sizeof(osviex));
+ osviex.dwOSVersionInfoSize = sizeof(osviex);
+
+# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
+# pragma warning(push)
+# ifdef __INTEL_COMPILER
+# pragma warning(disable : 1478)
+# elif defined __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wdeprecated-declarations"
+# else
+# pragma warning(disable : 4996)
+# endif
+# endif
+ GetVersionExW((OSVERSIONINFOW*)&osviex);
+# ifdef CM_WINDOWS_DEPRECATED_GetVersionEx
+# ifdef __clang__
+# pragma clang diagnostic pop
+# else
+# pragma warning(pop)
+# endif
+# endif
+
+ WindowsVersion result;
+ result.dwMajorVersion = osviex.dwMajorVersion;
+ result.dwMinorVersion = osviex.dwMinorVersion;
+ result.dwBuildNumber = osviex.dwBuildNumber;
+ return result;
+}
#endif
std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
@@ -3319,22 +3357,12 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
uv_fs_t req;
int flags = 0;
#if defined(_WIN32)
- bool const isDir = cmsys::SystemTools::FileIsDirectory(origName);
- if (isDir) {
- flags |= UV_FS_SYMLINK_JUNCTION;
+ if (cmsys::SystemTools::FileIsDirectory(origName)) {
+ flags |= UV_FS_SYMLINK_DIR;
}
#endif
int err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
flags, nullptr);
-#if defined(_WIN32)
- if (err && uv_fs_get_system_error(&req) == ERROR_NOT_SUPPORTED && isDir) {
- // Try fallback to symlink for network (requires additional permissions).
- flags ^= UV_FS_SYMLINK_JUNCTION | UV_FS_SYMLINK_DIR;
- err = uv_fs_symlink(nullptr, &req, origName.c_str(), newName.c_str(),
- flags, nullptr);
- }
-#endif
-
cmsys::Status status;
if (err) {
#if defined(_WIN32)
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 5f7a5ecbb0..ec650f7431 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -509,6 +509,14 @@ public:
};
static WindowsFileRetry GetWindowsFileRetry();
static WindowsFileRetry GetWindowsDirectoryRetry();
+
+ struct WindowsVersion
+ {
+ unsigned int dwMajorVersion;
+ unsigned int dwMinorVersion;
+ unsigned int dwBuildNumber;
+ };
+ static WindowsVersion GetWindowsVersion();
#endif
/** Get the real path for a given path, removing all symlinks.
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index c76e2cb02a..eafea059e1 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -1935,6 +1935,51 @@ void cmTarget::AppendBuildInterfaceIncludes()
}
}
+void cmTarget::FinalizeTargetCompileInfo(
+ const cmBTStringRange& noConfigCompileDefinitions,
+ cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions)
+{
+ if (this->GetType() == cmStateEnums::GLOBAL_TARGET) {
+ return;
+ }
+
+ this->AppendBuildInterfaceIncludes();
+
+ if (this->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ return;
+ }
+
+ for (auto const& def : noConfigCompileDefinitions) {
+ this->InsertCompileDefinition(def);
+ }
+
+ auto* mf = this->GetMakefile();
+ cmPolicies::PolicyStatus polSt = mf->GetPolicyStatus(cmPolicies::CMP0043);
+ if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
+ if (perConfigCompileDefinitions) {
+ for (auto const& it : *perConfigCompileDefinitions) {
+ if (cmValue val = it.second) {
+ this->AppendProperty(it.first, *val);
+ }
+ }
+ } else {
+ perConfigCompileDefinitions.emplace();
+ std::vector<std::string> configs =
+ mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+
+ for (std::string const& c : configs) {
+ std::string defPropName =
+ cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
+ cmValue val = mf->GetProperty(defPropName);
+ (*perConfigCompileDefinitions)[defPropName] = val;
+ if (val) {
+ this->AppendProperty(defPropName, *val);
+ }
+ }
+ }
+ }
+}
+
void cmTarget::InsertInclude(BT<std::string> const& entry, bool before)
{
auto position = before ? this->impl->IncludeDirectoriesEntries.begin()
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 94d66885a7..3d0a06be0f 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -5,12 +5,15 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <iosfwd>
+#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
+#include <cm/optional>
+
#include "cmAlgorithms.h"
#include "cmFileSet.h"
#include "cmPolicies.h"
@@ -233,6 +236,9 @@ public:
void InsertPrecompileHeader(BT<std::string> const& entry);
void AppendBuildInterfaceIncludes();
+ void FinalizeTargetCompileInfo(
+ const cmBTStringRange& noConfigCompileDefinitions,
+ cm::optional<std::map<std::string, cmValue>>& perConfigCompileDefinitions);
std::string GetDebugGeneratorExpressions(const std::string& value,
cmTargetLinkLibraryType llt) const;
diff --git a/Source/cmTryCompileCommand.cxx b/Source/cmTryCompileCommand.cxx
index 130c2288a8..7514a23769 100644
--- a/Source/cmTryCompileCommand.cxx
+++ b/Source/cmTryCompileCommand.cxx
@@ -2,34 +2,35 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmTryCompileCommand.h"
+#include "cmCoreTryCompile.h"
+#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmake.h"
-class cmExecutionStatus;
-
-// cmTryCompileCommand
-bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv,
- cmExecutionStatus&)
+bool cmTryCompileCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
{
- if (argv.size() < 3) {
+ if (args.size() < 3) {
return false;
}
- if (this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
- cmake::FIND_PACKAGE_MODE) {
- this->Makefile->IssueMessage(
+ cmMakefile& mf = status.GetMakefile();
+
+ if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) {
+ mf.IssueMessage(
MessageType::FATAL_ERROR,
"The try_compile() command is not supported in --find-package mode.");
return false;
}
- this->TryCompileCode(argv, false);
+ cmCoreTryCompile tc(&mf);
+ tc.TryCompileCode(args, false);
// if They specified clean then we clean up what we can
- if (this->SrcFileSignature) {
- if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
- this->CleanupFiles(this->BinaryDirectory);
+ if (tc.SrcFileSignature) {
+ if (!mf.GetCMakeInstance()->GetDebugTryCompile()) {
+ tc.CleanupFiles(tc.BinaryDirectory);
}
}
return true;
diff --git a/Source/cmTryCompileCommand.h b/Source/cmTryCompileCommand.h
index d8cc16e59a..6a3430b6b6 100644
--- a/Source/cmTryCompileCommand.h
+++ b/Source/cmTryCompileCommand.h
@@ -7,33 +7,7 @@
#include <string>
#include <vector>
-#include <cm/memory>
-
-#include "cmCommand.h"
-#include "cmCoreTryCompile.h"
-
class cmExecutionStatus;
-/** \class cmTryCompileCommand
- * \brief Specifies where to install some files
- *
- * cmTryCompileCommand is used to test if source code can be compiled
- */
-class cmTryCompileCommand : public cmCoreTryCompile
-{
-public:
- /**
- * This is a virtual constructor for the command.
- */
- std::unique_ptr<cmCommand> Clone() override
- {
- return cm::make_unique<cmTryCompileCommand>();
- }
-
- /**
- * This is called when the command is first encountered in
- * the CMakeLists.txt file.
- */
- bool InitialPass(std::vector<std::string> const& args,
- cmExecutionStatus& status) override;
-};
+bool cmTryCompileCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status);
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx
index c82ac6429d..98cacdc65a 100644
--- a/Source/cmTryRunCommand.cxx
+++ b/Source/cmTryRunCommand.cxx
@@ -6,7 +6,9 @@
#include "cmsys/FStream.hxx"
+#include "cmCoreTryCompile.h"
#include "cmDuration.h"
+#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmRange.h"
@@ -17,24 +19,40 @@
#include "cmValue.h"
#include "cmake.h"
-class cmExecutionStatus;
+namespace {
-// cmTryRunCommand
-bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
- cmExecutionStatus&)
+class TryRunCommandImpl : public cmCoreTryCompile
{
- if (argv.size() < 4) {
- return false;
- }
-
- if (this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
- cmake::FIND_PACKAGE_MODE) {
- this->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- "The try_run() command is not supported in --find-package mode.");
- return false;
+public:
+ TryRunCommandImpl(cmMakefile* mf)
+ : cmCoreTryCompile(mf)
+ {
}
+ bool TryRunCode(std::vector<std::string> const& args);
+
+ void RunExecutable(const std::string& runArgs,
+ std::string* runOutputContents,
+ std::string* runOutputStdOutContents,
+ std::string* runOutputStdErrContents);
+ void DoNotRunExecutable(const std::string& runArgs,
+ const std::string& srcFile,
+ std::string* runOutputContents,
+ std::string* runOutputStdOutContents,
+ std::string* runOutputStdErrContents);
+
+ std::string CompileResultVariable;
+ std::string RunResultVariable;
+ std::string OutputVariable;
+ std::string RunOutputVariable;
+ std::string RunOutputStdOutVariable;
+ std::string RunOutputStdErrVariable;
+ std::string CompileOutputVariable;
+ std::string WorkingDirectory;
+};
+
+bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv)
+{
// build an arg list for TryCompile and extract the runArgs,
std::vector<std::string> tryCompile;
@@ -42,6 +60,8 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
this->RunResultVariable.clear();
this->OutputVariable.clear();
this->RunOutputVariable.clear();
+ this->RunOutputStdOutVariable.clear();
+ this->RunOutputStdErrVariable.clear();
this->CompileOutputVariable.clear();
std::string runArgs;
@@ -76,6 +96,22 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
}
i++;
this->RunOutputVariable = argv[i];
+ } else if (argv[i] == "RUN_OUTPUT_STDOUT_VARIABLE") {
+ if (argv.size() <= (i + 1)) {
+ cmSystemTools::Error(
+ "RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable");
+ return false;
+ }
+ i++;
+ this->RunOutputStdOutVariable = argv[i];
+ } else if (argv[i] == "RUN_OUTPUT_STDERR_VARIABLE") {
+ if (argv.size() <= (i + 1)) {
+ cmSystemTools::Error(
+ "RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable");
+ return false;
+ }
+ i++;
+ this->RunOutputStdErrVariable = argv[i];
} else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") {
if (argv.size() <= (i + 1)) {
cmSystemTools::Error(
@@ -102,11 +138,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
// using OUTPUT_VARIABLE makes crosscompiling harder
if (!this->OutputVariable.empty() &&
(!this->RunOutputVariable.empty() ||
- !this->CompileOutputVariable.empty())) {
+ !this->CompileOutputVariable.empty() ||
+ !this->RunOutputStdOutVariable.empty() ||
+ !this->RunOutputStdErrVariable.empty())) {
cmSystemTools::Error(
"You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
- "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
- "RUN_OUTPUT_VARIABLE.");
+ ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or "
+ "RUN_OUTPUT_STDERR_VARIABLE. "
+ "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, "
+ "RUN_OUTPUT_STDOUT_VARIABLE "
+ "and/or RUN_OUTPUT_STDERR_VARIABLE.");
+ return false;
+ }
+
+ if ((!this->RunOutputStdOutVariable.empty() ||
+ !RunOutputStdErrVariable.empty()) &&
+ !this->RunOutputVariable.empty()) {
+ cmSystemTools::Error(
+ "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or "
+ "RUN_OUTPUT_STDERR_VARIABLE together "
+ "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or "
+ "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE.");
return false;
}
@@ -119,6 +171,7 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
}
bool captureRunOutput = false;
+ bool captureRunOutputStdOutErr = false;
if (!this->OutputVariable.empty()) {
captureRunOutput = true;
tryCompile.emplace_back("OUTPUT_VARIABLE");
@@ -128,7 +181,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
tryCompile.emplace_back("OUTPUT_VARIABLE");
tryCompile.push_back(this->CompileOutputVariable);
}
- if (!this->RunOutputVariable.empty()) {
+ if (!this->RunOutputStdOutVariable.empty() ||
+ !RunOutputStdErrVariable.empty()) {
+ captureRunOutputStdOutErr = true;
+ } else if (!this->RunOutputVariable.empty()) {
captureRunOutput = true;
}
@@ -145,12 +201,27 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
} else {
// "run" it and capture the output
std::string runOutputContents;
+ std::string runOutputStdOutContents;
+ std::string runOutputStdErrContents;
if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") &&
!this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) {
this->DoNotRunExecutable(
- runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr);
+ runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty()
+ ? &runOutputStdOutContents
+ : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty()
+ ? &runOutputStdErrContents
+ : nullptr);
} else {
- this->RunExecutable(runArgs, &runOutputContents);
+ this->RunExecutable(
+ runArgs, captureRunOutput ? &runOutputContents : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdOutVariable.empty()
+ ? &runOutputStdOutContents
+ : nullptr,
+ captureRunOutputStdOutErr && !RunOutputStdErrVariable.empty()
+ ? &runOutputStdErrContents
+ : nullptr);
}
// now put the output into the variables
@@ -158,6 +229,14 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
this->Makefile->AddDefinition(this->RunOutputVariable,
runOutputContents);
}
+ if (!this->RunOutputStdOutVariable.empty()) {
+ this->Makefile->AddDefinition(this->RunOutputStdOutVariable,
+ runOutputStdOutContents);
+ }
+ if (!this->RunOutputStdErrVariable.empty()) {
+ this->Makefile->AddDefinition(this->RunOutputStdErrVariable,
+ runOutputStdErrContents);
+ }
if (!this->OutputVariable.empty()) {
// if the TryCompileCore saved output in this outputVariable then
@@ -179,8 +258,9 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
return true;
}
-void cmTryRunCommand::RunExecutable(const std::string& runArgs,
- std::string* out)
+void TryRunCommandImpl::RunExecutable(const std::string& runArgs,
+ std::string* out, std::string* stdOut,
+ std::string* stdErr)
{
int retVal = -1;
@@ -204,7 +284,8 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
finalCommand += runArgs;
}
bool worked = cmSystemTools::RunSingleCommand(
- finalCommand, out, out, &retVal,
+ finalCommand, stdOut || stdErr ? stdOut : out,
+ stdOut || stdErr ? stdErr : out, &retVal,
this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(),
cmSystemTools::OUTPUT_NONE, cmDuration::zero());
// set the run var
@@ -225,9 +306,11 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
executable, two cache variables are created which will hold the results
the executable would have produced.
*/
-void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
- const std::string& srcFile,
- std::string* out)
+void TryRunCommandImpl::DoNotRunExecutable(const std::string& runArgs,
+ const std::string& srcFile,
+ std::string* out,
+ std::string* stdOut,
+ std::string* stdErr)
{
// copy the executable out of the CMakeFiles/ directory, so it is not
// removed at the end of try_run() and the user can run it manually
@@ -246,6 +329,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
std::string internalRunOutputName =
this->RunResultVariable + "__TRYRUN_OUTPUT";
+ std::string internalRunOutputStdOutName =
+ this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT";
+ std::string internalRunOutputStdErrName =
+ this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR";
bool error = false;
if (!this->Makefile->GetDefinition(this->RunResultVariable)) {
@@ -269,7 +356,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
}
// is the output from the executable used ?
- if (out) {
+ if (stdOut || stdErr) {
+ if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) {
+ // if the variables doesn't exist, create it with a helpful error text
+ // and mark it as advanced
+ std::string comment = cmStrCat(
+ "Output of try_run(), contains the text, which the executable "
+ "would have printed on stdout on its target platform.\n",
+ detailsString);
+
+ this->Makefile->AddCacheDefinition(
+ internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND",
+ comment.c_str(), cmStateEnums::STRING);
+ cmState* state = this->Makefile->GetState();
+ cmValue existing =
+ state->GetCacheEntryValue(internalRunOutputStdOutName);
+ if (existing) {
+ state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED",
+ "1");
+ }
+
+ error = true;
+ }
+
+ if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) {
+ // if the variables doesn't exist, create it with a helpful error text
+ // and mark it as advanced
+ std::string comment = cmStrCat(
+ "Output of try_run(), contains the text, which the executable "
+ "would have printed on stderr on its target platform.\n",
+ detailsString);
+
+ this->Makefile->AddCacheDefinition(
+ internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND",
+ comment.c_str(), cmStateEnums::STRING);
+ cmState* state = this->Makefile->GetState();
+ cmValue existing =
+ state->GetCacheEntryValue(internalRunOutputStdErrName);
+ if (existing) {
+ state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED",
+ "1");
+ }
+
+ error = true;
+ }
+ } else if (out) {
if (!this->Makefile->GetDefinition(internalRunOutputName)) {
// if the variables doesn't exist, create it with a helpful error text
// and mark it as advanced
@@ -317,7 +448,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
" to\n"
" the exit code (in many cases 0 for success), otherwise "
"enter \"FAILED_TO_RUN\".\n");
- if (out) {
+ if (stdOut || stdErr) {
+ if (stdOut) {
+ comment += internalRunOutputStdOutName;
+ comment +=
+ "\n contains the text the executable "
+ "would have printed on stdout.\n"
+ " If the executable would not have been able to run, set ";
+ comment += internalRunOutputStdOutName;
+ comment += " empty.\n"
+ " Otherwise check if the output is evaluated by the "
+ "calling CMake code. If so,\n"
+ " check what the source file would have printed when "
+ "called with the given arguments.\n";
+ }
+ if (stdErr) {
+ comment += internalRunOutputStdErrName;
+ comment +=
+ "\n contains the text the executable "
+ "would have printed on stderr.\n"
+ " If the executable would not have been able to run, set ";
+ comment += internalRunOutputStdErrName;
+ comment += " empty.\n"
+ " Otherwise check if the output is evaluated by the "
+ "calling CMake code. If so,\n"
+ " check what the source file would have printed when "
+ "called with the given arguments.\n";
+ }
+ } else if (out) {
comment += internalRunOutputName;
comment +=
"\n contains the text the executable "
@@ -330,6 +488,7 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
" check what the source file would have printed when "
"called with the given arguments.\n";
}
+
comment += "The ";
comment += this->CompileResultVariable;
comment += " variable holds the build result for this try_run().\n\n"
@@ -370,7 +529,35 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
return;
}
- if (out) {
+ if (stdOut || stdErr) {
+ if (stdOut) {
+ (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName);
+ }
+ if (stdErr) {
+ (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName);
+ }
+ } else if (out) {
(*out) = *this->Makefile->GetDefinition(internalRunOutputName);
}
}
+}
+
+bool cmTryRunCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.size() < 4) {
+ return false;
+ }
+
+ cmMakefile& mf = status.GetMakefile();
+
+ if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) {
+ mf.IssueMessage(
+ MessageType::FATAL_ERROR,
+ "The try_run() command is not supported in --find-package mode.");
+ return false;
+ }
+
+ TryRunCommandImpl tr(&mf);
+ return tr.TryRunCode(args);
+}
diff --git a/Source/cmTryRunCommand.h b/Source/cmTryRunCommand.h
index d45acd85ec..38e36380e4 100644
--- a/Source/cmTryRunCommand.h
+++ b/Source/cmTryRunCommand.h
@@ -7,47 +7,7 @@
#include <string>
#include <vector>
-#include <cm/memory>
-
-#include "cmCommand.h"
-#include "cmCoreTryCompile.h"
-
class cmExecutionStatus;
-/** \class cmTryRunCommand
- * \brief Specifies where to install some files
- *
- * cmTryRunCommand is used to test if source code can be compiled
- */
-class cmTryRunCommand : public cmCoreTryCompile
-{
-public:
- /**
- * This is a virtual constructor for the command.
- */
- std::unique_ptr<cmCommand> Clone() override
- {
- return cm::make_unique<cmTryRunCommand>();
- }
-
- /**
- * This is called when the command is first encountered in
- * the CMakeLists.txt file.
- */
- bool InitialPass(std::vector<std::string> const& args,
- cmExecutionStatus& status) override;
-
-private:
- void RunExecutable(const std::string& runArgs,
- std::string* runOutputContents);
- void DoNotRunExecutable(const std::string& runArgs,
- const std::string& srcFile,
- std::string* runOutputContents);
-
- std::string CompileResultVariable;
- std::string RunResultVariable;
- std::string OutputVariable;
- std::string RunOutputVariable;
- std::string CompileOutputVariable;
- std::string WorkingDirectory;
-};
+bool cmTryRunCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status);
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 020691d059..4c1fa0107f 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -50,24 +50,6 @@
#include "cmValue.h"
#include "cmVisualStudioGeneratorOptions.h"
-namespace {
-std::string getProjectFileExtension(VsProjectType projectType)
-{
- switch (projectType) {
- case VsProjectType::csproj:
- return ".csproj";
- case VsProjectType::proj:
- return ".proj";
- case VsProjectType::vcxproj:
- return ".vcxproj";
- // Valid inputs shouldn't reach here. This default is needed so that all
- // paths return value (C4715).
- default:
- return "";
- }
-}
-}
-
struct cmIDEFlagTable;
static void ConvertToWindowsSlash(std::string& s);
@@ -253,6 +235,31 @@ static bool cmVS10IsTargetsFile(std::string const& path)
return cmSystemTools::Strucmp(ext.c_str(), ".targets") == 0;
}
+static VsProjectType computeProjectType(cmGeneratorTarget const* t)
+{
+ if (t->IsCSharpOnly()) {
+ return VsProjectType::csproj;
+ }
+ return VsProjectType::vcxproj;
+}
+
+static std::string computeProjectFileExtension(VsProjectType projectType)
+{
+ switch (projectType) {
+ case VsProjectType::csproj:
+ return ".csproj";
+ case VsProjectType::proj:
+ return ".proj";
+ default:
+ return ".vcxproj";
+ }
+}
+
+static std::string computeProjectFileExtension(cmGeneratorTarget const* t)
+{
+ return computeProjectFileExtension(computeProjectType(t));
+}
+
cmVisualStudio10TargetGenerator::cmVisualStudio10TargetGenerator(
cmGeneratorTarget* target, cmGlobalVisualStudio10Generator* gg)
: GeneratorTarget(target)
@@ -359,10 +366,10 @@ void cmVisualStudio10TargetGenerator::Generate()
"by the generator"));
}
- this->ProjectType = this->ComputeProjectType(this->GeneratorTarget);
+ this->ProjectType = computeProjectType(this->GeneratorTarget);
this->Managed = this->ProjectType == VsProjectType::csproj;
const std::string ProjectFileExtension =
- getProjectFileExtension(this->ProjectType);
+ computeProjectFileExtension(this->ProjectType);
if (this->ProjectType == VsProjectType::csproj &&
this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
@@ -423,12 +430,10 @@ void cmVisualStudio10TargetGenerator::Generate()
char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
BuildFileStream.write(magic, 3);
- if (this->ProjectType == VsProjectType::proj) {
- this->WriteZeroCheckProj(BuildFileStream);
- } else if (this->ProjectType == VsProjectType::csproj &&
- this->GeneratorTarget->IsDotNetSdkTarget() &&
- this->GlobalGenerator->GetVersion() >=
- cmGlobalVisualStudioGenerator::VSVersion::VS16) {
+ if (this->ProjectType == VsProjectType::csproj &&
+ this->GeneratorTarget->IsDotNetSdkTarget() &&
+ this->GlobalGenerator->GetVersion() >=
+ cmGlobalVisualStudioGenerator::VSVersion::VS16) {
this->WriteSdkStyleProjectFile(BuildFileStream);
} else {
this->WriteClassicMsBuildProjectFile(BuildFileStream);
@@ -977,45 +982,6 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
this->WriteProjectReferences(e0);
}
-void cmVisualStudio10TargetGenerator::WriteZeroCheckProj(
- cmGeneratedFileStream& BuildFileStream)
-{
- // ZERO_CHECK.proj is an XML file without any imports or targets. This is a
- // ProjectReference for other targets and therefore, it needs to follow the
- // ProjectReference protocol as documented here:
- // https://github.com/dotnet/msbuild/blob/main/documentation/ProjectReference-Protocol.md
- //
- // We implement MSBuild target Build from WriteCustomCommand which calls
- // WriteZeroCheckBuildTarget after setting up the command generator. MSBuild
- // target Clean is a no-op as we do all the work for ZERO_CHECK on Build.
- // MSBuild target GetTargetPath is needed and is no-op.
- // MSBuild targets GetNativeManifest and GetCopyToOutputDirectoryItems are
- // needed for MSBuild versions below 15.7 and are no-op. MSBuild target
- // BeforeBuild is needed for supporting GLOBs.
- BuildFileStream << "<?xml version=\"1.0\" encoding=\""
- << this->GlobalGenerator->Encoding() << "\"?>";
- {
- Elem e0(BuildFileStream, "Project");
- e0.Attribute("DefaultTargets", "Build");
- e0.Attribute("ToolsVersion", this->GlobalGenerator->GetToolsVersion());
- e0.Attribute("xmlns",
- "http://schemas.microsoft.com/developer/msbuild/2003");
-
- this->WriteCustomCommands(e0);
-
- for (const char* targetName :
- { "Clean", "GetTargetPath", "GetNativeManifest",
- "GetCopyToOutputDirectoryItems" }) {
- {
- Elem e1(e0, "Target");
- e1.Attribute("Name", targetName);
- }
- }
-
- this->WriteZeroCheckBeforeBuildTarget(e0);
- }
-}
-
void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1)
{
e1.Attribute("Label", "Globals");
@@ -1557,6 +1523,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues(
this->ASanEnabledConfigurations.end()) {
e1.Element("EnableAsan", "true");
}
+ if (this->FuzzerEnabledConfigurations.find(config) !=
+ this->FuzzerEnabledConfigurations.end()) {
+ e1.Element("EnableFuzzer", "true");
+ }
{
auto s = this->SpectreMitigation.find(config);
if (s != this->SpectreMitigation.end()) {
@@ -1735,16 +1705,11 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule(
}
}
}
- if (this->ProjectType == VsProjectType::proj) {
- this->WriteZeroCheckBuildTarget(e0, command, source);
- return;
- }
-
cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
std::unique_ptr<Elem> spe1;
std::unique_ptr<Elem> spe2;
- if (this->ProjectType == VsProjectType::vcxproj) {
+ if (this->ProjectType != VsProjectType::csproj) {
spe1 = cm::make_unique<Elem>(e0, "ItemGroup");
spe2 = cm::make_unique<Elem>(*spe1, "CustomBuild");
this->WriteSource(*spe2, source);
@@ -1942,7 +1907,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups()
// Write out group file
std::string path = cmStrCat(
this->LocalGenerator->GetCurrentBinaryDirectory(), '/', this->Name,
- this->ComputeProjectFileExtension(this->GeneratorTarget), ".filters");
+ computeProjectFileExtension(this->GeneratorTarget), ".filters");
cmGeneratedFileStream fout(path);
fout.SetCopyIfDifferent(true);
char magic[] = { char(0xEF), char(0xBB), char(0xBF) };
@@ -2930,7 +2895,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
Elem& e0)
{
cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType();
- if (ttype > cmStateEnums::GLOBAL_TARGET) {
+ if (ttype > cmStateEnums::INTERFACE_LIBRARY) {
return;
}
if (this->ProjectType == VsProjectType::csproj) {
@@ -3097,134 +3062,6 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental(
}
}
-void cmVisualStudio10TargetGenerator::WriteZeroCheckBuildTarget(
- cmVisualStudio10TargetGenerator::Elem& e0, const cmCustomCommand& command,
- const cmSourceFile* source)
-{
- cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
-
- Elem e1(e0, "Target");
- e1.Attribute("Name", "Build");
-
- std::string noConfig{};
- cmCustomCommandGenerator ccg{ command, noConfig, lg, true };
- std::string comment = lg->ConstructComment(ccg);
- comment = cmVS10EscapeComment(comment);
- std::string script = lg->ConstructScript(ccg);
- bool symbolic = false;
- // input files for custom command
- std::stringstream additional_inputs;
- {
- const char* sep = "";
- if (this->ProjectType == VsProjectType::proj) {
- // List explicitly the path to primary input.
- std::string sourceFullPath = source->GetFullPath();
- ConvertToWindowsSlash(sourceFullPath);
- additional_inputs << sourceFullPath;
- sep = ";";
- }
-
- // Avoid listing an input more than once.
- std::set<std::string> unique_inputs;
- // The source is either implicitly an input or has been added above.
- unique_inputs.insert(source->GetFullPath());
-
- for (std::string const& d : ccg.GetDepends()) {
- std::string dep;
- if (lg->GetRealDependency(d, noConfig, dep)) {
- if (!unique_inputs.insert(dep).second) {
- // already listed
- continue;
- }
- ConvertToWindowsSlash(dep);
- additional_inputs << sep << dep;
- sep = ";";
- if (!symbolic) {
- if (cmSourceFile* sf = this->Makefile->GetSource(
- dep, cmSourceFileLocationKind::Known)) {
- symbolic = sf->GetPropertyAsBool("SYMBOLIC");
- }
- }
- }
- }
- }
- // output files for custom command
- std::stringstream outputs;
- {
- const char* sep = "";
- for (std::string const& o : ccg.GetOutputs()) {
- std::string out = o;
- ConvertToWindowsSlash(out);
- outputs << sep << out;
- sep = ";";
- if (!symbolic) {
- if (cmSourceFile* sf =
- this->Makefile->GetSource(o, cmSourceFileLocationKind::Known)) {
- symbolic = sf->GetPropertyAsBool("SYMBOLIC");
- }
- }
- }
- }
- script += lg->FinishConstructScript(this->ProjectType);
-
- e1.Attribute("Inputs", cmVS10EscapeAttr(additional_inputs.str()));
- e1.Attribute("Outputs", cmVS10EscapeAttr(outputs.str()));
-
- e1.SetHasElements();
-
- if (!comment.empty()) {
- Elem(e1, "Message").Attribute("Text", comment);
- }
- Elem(e1, "Exec").Attribute("Command", script);
-}
-
-void cmVisualStudio10TargetGenerator::WriteZeroCheckBeforeBuildTarget(
- cmVisualStudio10TargetGenerator::Elem& e0)
-{
- const auto& commands = this->GeneratorTarget->GetPreBuildCommands();
- if (commands.empty()) {
- return;
- }
-
- {
- Elem e1(e0, "Target");
- e1.Attribute("Name", "BeforeBuild");
- e1.Attribute("BeforeTargets", "Build");
-
- cmLocalVisualStudio7Generator* lg = this->LocalGenerator;
- std::string script;
- const char* pre = "";
- std::string comment;
- for (cmCustomCommand const& cc : commands) {
- cmCustomCommandGenerator ccg(cc, std::string{}, lg);
- if (!ccg.HasOnlyEmptyCommandLines()) {
- comment += pre;
- comment += lg->ConstructComment(ccg);
- script += pre;
- pre = "\n";
- script += lg->ConstructScript(ccg);
- }
- }
-
- if (script.empty()) {
- return;
- }
-
- script += lg->FinishConstructScript(this->ProjectType);
- comment = cmVS10EscapeComment(comment);
- std::string strippedComment = comment;
- strippedComment.erase(
- std::remove(strippedComment.begin(), strippedComment.end(), '\t'),
- strippedComment.end());
-
- e1.SetHasElements();
- if (!comment.empty() && !strippedComment.empty()) {
- Elem(e1, "Message").Attribute("Text", comment);
- }
- Elem(e1, "Exec").Attribute("Command", script);
- }
-}
-
std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes(
std::string const& config, std::string const& lang) const
{
@@ -3312,10 +3149,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
}
// Check if ASan is enabled.
- if (flags.find("/fsanitize=address") != std::string::npos) {
+ if (flags.find("/fsanitize=address") != std::string::npos ||
+ flags.find("-fsanitize=address") != std::string::npos) {
this->ASanEnabledConfigurations.insert(configName);
}
+ // Check if (lib)Fuzzer is enabled.
+ if (flags.find("/fsanitize=fuzzer") != std::string::npos ||
+ flags.find("-fsanitize=fuzzer") != std::string::npos) {
+ this->FuzzerEnabledConfigurations.insert(configName);
+ }
+
// Precompile Headers
std::string pchHeader =
this->GeneratorTarget->GetPchHeader(configName, linkLanguage);
@@ -3357,7 +3201,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
// anymore, because cmGeneratorTarget may not be aware that the
// target uses C++/CLI.
if (flags.find("/clr") != std::string::npos ||
- defineFlags.find("/clr") != std::string::npos) {
+ flags.find("-clr") != std::string::npos ||
+ defineFlags.find("/clr") != std::string::npos ||
+ defineFlags.find("-clr") != std::string::npos) {
if (configName == this->Configurations[0]) {
std::string message = "For the target \"" +
this->GeneratorTarget->GetName() +
@@ -4746,7 +4592,7 @@ void cmVisualStudio10TargetGenerator::WriteProjectReferences(Elem& e0)
path = *p;
} else {
path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(),
- this->ComputeProjectFileExtension(dt));
+ computeProjectFileExtension(dt));
}
ConvertToWindowsSlash(path);
Elem e2(e1, "ProjectReference");
@@ -5600,26 +5446,6 @@ std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath(
return path;
}
-std::string cmVisualStudio10TargetGenerator::ComputeProjectFileExtension(
- cmGeneratorTarget const* t) const
-{
- return getProjectFileExtension(this->ComputeProjectType(t));
-}
-
-VsProjectType cmVisualStudio10TargetGenerator::ComputeProjectType(
- cmGeneratorTarget const* t) const
-{
- if (this->GlobalGenerator->GetVersion() >=
- cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
- t->GetName() == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
- return VsProjectType::proj;
- }
- if (t->IsCSharpOnly()) {
- return VsProjectType::csproj;
- }
- return VsProjectType::vcxproj;
-}
-
void cmVisualStudio10TargetGenerator::WriteStdOutEncodingUtf8(Elem& e1)
{
if (this->GlobalGenerator->IsUtf8EncodingSupported()) {
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 7a0b8da728..17dcecdb13 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -231,6 +231,7 @@ private:
bool TargetCompileAsWinRT;
std::set<std::string> IPOEnabledConfigurations;
std::set<std::string> ASanEnabledConfigurations;
+ std::set<std::string> FuzzerEnabledConfigurations;
std::map<std::string, std::string> SpectreMitigation;
cmGlobalVisualStudio10Generator* const GlobalGenerator;
cmLocalVisualStudio10Generator* const LocalGenerator;
@@ -264,13 +265,6 @@ private:
void WriteClassicMsBuildProjectFile(cmGeneratedFileStream& BuildFileStream);
void WriteSdkStyleProjectFile(cmGeneratedFileStream& BuildFileStream);
- void WriteZeroCheckProj(cmGeneratedFileStream& BuildFileStream);
- void WriteZeroCheckBuildTarget(cmVisualStudio10TargetGenerator::Elem& e0,
- const cmCustomCommand& command,
- const cmSourceFile* source);
- void WriteZeroCheckBeforeBuildTarget(
- cmVisualStudio10TargetGenerator::Elem& e0);
-
void WriteCommonPropertyGroupGlobals(
cmVisualStudio10TargetGenerator::Elem& e1);
@@ -282,7 +276,4 @@ private:
void ParseSettingsProperty(const std::string& settingsPropertyValue,
ConfigToSettings& toolSettings);
std::string GetCMakeFilePath(const char* name) const;
-
- std::string ComputeProjectFileExtension(cmGeneratorTarget const* t) const;
- VsProjectType ComputeProjectType(cmGeneratorTarget const* t) const;
};
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index efb2520683..12d42b230c 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -406,9 +406,6 @@ void cmake::PrintPresetEnvironment()
// Parse the args
bool cmake::SetCacheArgs(const std::vector<std::string>& args)
{
- auto findPackageMode = false;
- auto seenScriptOption = false;
-
auto DefineLambda = [](std::string const& entry, cmake* state) -> bool {
std::string var;
std::string value;
@@ -499,10 +496,10 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
GetProjectCommandsInScriptMode(state->GetState());
// Documented behavior of CMAKE{,_CURRENT}_{SOURCE,BINARY}_DIR is to be
// set to $PWD for -P mode.
+ state->SetWorkingMode(SCRIPT_MODE);
state->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
state->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
state->ReadListFile(args, path);
- seenScriptOption = true;
return true;
};
@@ -566,12 +563,12 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
"No install directory specified for --install-prefix",
CommandArgument::Values::One, PrefixLambda },
CommandArgument{ "--find-package", CommandArgument::Values::Zero,
- CommandArgument::setToTrue(findPackageMode) },
+ IgnoreAndTrueLambda },
};
for (decltype(args.size()) i = 1; i < args.size(); ++i) {
std::string const& arg = args[i];
- if (arg == "--" && seenScriptOption) {
+ if (arg == "--" && this->GetWorkingMode() == SCRIPT_MODE) {
// Stop processing CMake args and avoid possible errors
// when arbitrary args are given to CMake script.
break;
@@ -586,7 +583,7 @@ bool cmake::SetCacheArgs(const std::vector<std::string>& args)
}
}
- if (findPackageMode) {
+ if (this->GetWorkingMode() == FIND_PACKAGE_MODE) {
return this->FindPackage(args);
}
@@ -791,7 +788,6 @@ void cmake::SetArgs(const std::vector<std::string>& args)
bool haveToolset = false;
bool havePlatform = false;
bool haveBArg = false;
- bool scriptMode = false;
std::string possibleUnknownArg;
std::string extraProvidedPath;
#if !defined(CMAKE_BOOTSTRAP)
@@ -874,7 +870,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
CommandArgument{ "-P", "-P must be followed by a file name.",
CommandArgument::Values::One,
CommandArgument::RequiresSeparator::No,
- CommandArgument::setToTrue(scriptMode) },
+ IgnoreAndTrueLambda },
CommandArgument{ "-D", "-D must be followed with VAR=VALUE.",
CommandArgument::Values::One,
CommandArgument::RequiresSeparator::No,
@@ -1166,7 +1162,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
// iterate each argument
std::string const& arg = args[i];
- if (scriptMode && arg == "--") {
+ if (this->GetWorkingMode() == SCRIPT_MODE && arg == "--") {
// Stop processing CMake args and avoid possible errors
// when arbitrary args are given to CMake script.
break;
@@ -1212,12 +1208,12 @@ void cmake::SetArgs(const std::vector<std::string>& args)
}
}
- if (!extraProvidedPath.empty() && !scriptMode) {
+ if (!extraProvidedPath.empty() && this->GetWorkingMode() == NORMAL_MODE) {
this->IssueMessage(MessageType::WARNING,
cmStrCat("Ignoring extra path from command line:\n \"",
extraProvidedPath, "\""));
}
- if (!possibleUnknownArg.empty() && !scriptMode) {
+ if (!possibleUnknownArg.empty() && this->GetWorkingMode() != SCRIPT_MODE) {
cmSystemTools::Error(cmStrCat("Unknown argument ", possibleUnknownArg));
cmSystemTools::Error("Run 'cmake --help' for all supported options.");
exit(1);
@@ -1830,7 +1826,8 @@ void cmake::SetHomeDirectoryViaCommandLine(std::string const& path)
}
auto prev_path = this->GetHomeDirectory();
- if (prev_path != path && !prev_path.empty()) {
+ if (prev_path != path && !prev_path.empty() &&
+ this->GetWorkingMode() == NORMAL_MODE) {
this->IssueMessage(MessageType::WARNING,
cmStrCat("Ignoring extra path from command line:\n \"",
prev_path, "\""));
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 97c275efc2..f931e9dbcd 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -207,7 +207,7 @@ int do_cmake(int ac, char const* const* av)
#ifndef CMAKE_BOOTSTRAP
cmDocumentation doc;
doc.addCMakeStandardDocSections();
- if (doc.CheckOptions(ac, av)) {
+ if (doc.CheckOptions(ac, av, "--")) {
// Construct and print requested documentation.
cmake hcm(cmake::RoleInternal, cmState::Unknown);
hcm.SetHomeDirectory("");
diff --git a/Source/cmcldeps.cxx b/Source/cmcldeps.cxx
index 8921aa0ca1..69eb19e2f0 100644
--- a/Source/cmcldeps.cxx
+++ b/Source/cmcldeps.cxx
@@ -273,6 +273,7 @@ int main()
std::string clrest = rest;
// rc: /fo x.dir\x.rc.res -> cl: /out:x.dir\x.rc.res.dep.obj
clrest = replace(clrest, "/fo ", "/out:");
+ clrest = replace(clrest, "-fo ", "-out:");
clrest = replace(clrest, objfile, objfile + ".dep.obj ");
cl = "\"" + cl + "\" /P /DRC_INVOKED /TC ";
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 9ab39f1ebe..b1f1bcfe3c 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -2242,13 +2242,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
// Parse the link command to extract information we need.
for (; arg != argEnd; ++arg) {
if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 ||
- cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) {
+ cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 ||
+ cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 ||
+ cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) {
this->Incremental = true;
- } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) {
+ } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 ||
+ cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) {
this->LinkGeneratesManifest = false;
- } else if (cmHasLiteralPrefix(*arg, "/Fe")) {
+ } else if (cmHasLiteralPrefix(*arg, "/Fe") ||
+ cmHasLiteralPrefix(*arg, "-Fe")) {
this->TargetFile = arg->substr(3);
- } else if (cmHasLiteralPrefix(*arg, "/out:")) {
+ } else if (cmHasLiteralPrefix(*arg, "/out:") ||
+ cmHasLiteralPrefix(*arg, "-out:")) {
this->TargetFile = arg->substr(5);
}
}
diff --git a/Source/kwsys/Directory.cxx b/Source/kwsys/Directory.cxx
index d520c14404..f239576efe 100644
--- a/Source/kwsys/Directory.cxx
+++ b/Source/kwsys/Directory.cxx
@@ -43,12 +43,12 @@ public:
{
std::string Name;
#if defined(_WIN32) && !defined(__CYGWIN__)
- _wfinddata_t FindData;
+ WIN32_FIND_DATAW FindData;
#endif
FileData(std::string name
#if defined(_WIN32) && !defined(__CYGWIN__)
,
- _wfinddata_t data
+ WIN32_FIND_DATAW data
#endif
)
: Name(std::move(name))
@@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const
bool Directory::FileIsDirectory(std::size_t i) const
{
#if defined(_WIN32) && !defined(__CYGWIN__)
- _wfinddata_t const& data = this->Internal->Files[i].FindData;
- return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ auto const& data = this->Internal->Files[i].FindData;
+ return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
std::string const& path = this->GetFilePath(i);
return kwsys::SystemTools::FileIsDirectory(path);
@@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const
{
std::string const& path = this->GetFilePath(i);
#if defined(_WIN32) && !defined(__CYGWIN__)
- _wfinddata_t const& data = this->Internal->Files[i].FindData;
+ auto const& data = this->Internal->Files[i].FindData;
return kwsys::SystemTools::FileIsSymlinkWithAttr(
- Encoding::ToWindowsExtendedPath(path), data.attrib);
+ Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes);
#else
return kwsys::SystemTools::FileIsSymlink(path);
#endif
@@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE {
Status Directory::Load(std::string const& name, std::string* errorMessage)
{
this->Clear();
- intptr_t srchHandle;
+ HANDLE srchHandle;
char* buf;
size_t bufLength;
size_t n = name.size();
@@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
snprintf(buf, bufLength, "%s/*", name.c_str());
}
}
- struct _wfinddata_t data; // data of current file
+ WIN32_FIND_DATAW data; // data of current file
// Now put them into the file array
srchHandle =
- _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
+ FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
delete[] buf;
- if (srchHandle == -1) {
+ if (srchHandle == INVALID_HANDLE_VALUE) {
Status status = Status::POSIX_errno();
if (errorMessage) {
*errorMessage = status.GetString();
@@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
// Loop through names
do {
- this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data);
- } while (_wfindnext(srchHandle, &data) != -1);
+ this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName),
+ data);
+ } while (FindNextFileW(srchHandle, &data));
this->Internal->Path = name;
- if (_findclose(srchHandle) == -1) {
+ if (!FindClose(srchHandle)) {
Status status = Status::POSIX_errno();
if (errorMessage) {
*errorMessage = status.GetString();
@@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
std::string* errorMessage)
{
- intptr_t srchHandle;
+ HANDLE srchHandle;
char* buf;
size_t bufLength;
size_t n = name.size();
@@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
buf = new char[n + 2 + 1];
snprintf(buf, bufLength, "%s/*", name.c_str());
}
- struct _wfinddata_t data; // data of current file
+ WIN32_FIND_DATAW data; // data of current file
// Now put them into the file array
- srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data);
+ srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data);
delete[] buf;
- if (srchHandle == -1) {
+ if (srchHandle == INVALID_HANDLE_VALUE) {
if (errorMessage) {
if (unsigned int errorId = GetLastError()) {
LPSTR message = nullptr;
@@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
unsigned long count = 0;
do {
count++;
- } while (_wfindnext(srchHandle, &data) != -1);
- _findclose(srchHandle);
+ } while (FindNextFileW(srchHandle, &data));
+ FindClose(srchHandle);
return count;
}
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 89a3cc789f..a20901cb29 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -536,9 +536,11 @@ public:
StringMap TranslationMap;
#endif
#ifdef _WIN32
- static std::string GetCasePathName(std::string const& pathIn);
+ static std::string GetCasePathName(std::string const& pathIn,
+ bool const cache);
static std::string GetActualCaseForPathCached(std::string const& path);
static const char* GetEnvBuffered(const char* key);
+ std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap;
std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap;
std::map<std::string, std::string> EnvMap;
#endif
@@ -571,7 +573,8 @@ public:
static SystemToolsStatic* SystemToolsStatics;
#ifdef _WIN32
-std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
+std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn,
+ bool const cache)
{
std::string casePath;
@@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
} else {
std::string test_str = casePath;
test_str += path_components[idx];
- WIN32_FIND_DATAW findData;
- HANDLE hFind =
- ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
- if (INVALID_HANDLE_VALUE != hFind) {
- path_components[idx] = Encoding::ToNarrow(findData.cFileName);
- ::FindClose(hFind);
- } else {
- converting = false;
+
+ bool found_in_cache = false;
+ if (cache) {
+ auto const it = SystemToolsStatics->FindFileMap.find(test_str);
+ if (it != SystemToolsStatics->FindFileMap.end()) {
+ path_components[idx] = it->second;
+ found_in_cache = true;
+ }
+ }
+
+ if (!found_in_cache) {
+ WIN32_FIND_DATAW findData;
+ HANDLE hFind =
+ ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
+ if (INVALID_HANDLE_VALUE != hFind) {
+ auto case_file_name = Encoding::ToNarrow(findData.cFileName);
+ if (cache) {
+ SystemToolsStatics->FindFileMap.emplace(test_str,
+ case_file_name);
+ }
+ path_components[idx] = std::move(case_file_name);
+ ::FindClose(hFind);
+ } else {
+ converting = false;
+ }
}
}
}
@@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p)
{
- // Check to see if actual case has already been called
- // for this path, and the result is stored in the PathCaseMap
- auto& pcm = SystemToolsStatics->PathCaseMap;
- {
- auto itr = pcm.find(p);
- if (itr != pcm.end()) {
- return itr->second;
- }
- }
- std::string casePath = SystemToolsStatic::GetCasePathName(p);
- if (casePath.size() <= MAX_PATH) {
- pcm[p] = casePath;
+ std::string casePath;
+
+ auto it = SystemToolsStatics->PathCaseMap.find(p);
+ if (it != SystemToolsStatics->PathCaseMap.end()) {
+ casePath = it->second;
+ } else {
+ casePath = SystemToolsStatic::GetCasePathName(p, true);
+ SystemToolsStatics->PathCaseMap.emplace(p, casePath);
}
+
return casePath;
}
#endif
@@ -3677,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local,
std::string SystemTools::GetActualCaseForPath(const std::string& p)
{
#ifdef _WIN32
- return SystemToolsStatic::GetCasePathName(p);
+ return SystemToolsStatic::GetCasePathName(p, false);
#else
return p;
#endif
diff --git a/Templates/MSBuild/FlagTables/v10_CSharp.json b/Templates/MSBuild/FlagTables/v10_CSharp.json
index 9f21d9a27b..53418416c1 100644
--- a/Templates/MSBuild/FlagTables/v10_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v10_CSharp.json
@@ -201,6 +201,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v11_CSharp.json b/Templates/MSBuild/FlagTables/v11_CSharp.json
index 9f21d9a27b..53418416c1 100644
--- a/Templates/MSBuild/FlagTables/v11_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v11_CSharp.json
@@ -201,6 +201,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v12_CSharp.json b/Templates/MSBuild/FlagTables/v12_CSharp.json
index 9f21d9a27b..53418416c1 100644
--- a/Templates/MSBuild/FlagTables/v12_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v12_CSharp.json
@@ -201,6 +201,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v140_CSharp.json b/Templates/MSBuild/FlagTables/v140_CSharp.json
index 9f21d9a27b..53418416c1 100644
--- a/Templates/MSBuild/FlagTables/v140_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v140_CSharp.json
@@ -201,6 +201,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v141_CSharp.json b/Templates/MSBuild/FlagTables/v141_CSharp.json
index 9f21d9a27b..53418416c1 100644
--- a/Templates/MSBuild/FlagTables/v141_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v141_CSharp.json
@@ -201,6 +201,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v142_CSharp.json b/Templates/MSBuild/FlagTables/v142_CSharp.json
index ae1bd47bf6..9ea8f4baae 100644
--- a/Templates/MSBuild/FlagTables/v142_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v142_CSharp.json
@@ -208,6 +208,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Templates/MSBuild/FlagTables/v143_CSharp.json b/Templates/MSBuild/FlagTables/v143_CSharp.json
index ae1bd47bf6..9ea8f4baae 100644
--- a/Templates/MSBuild/FlagTables/v143_CSharp.json
+++ b/Templates/MSBuild/FlagTables/v143_CSharp.json
@@ -208,6 +208,13 @@
"flags": []
},
{
+ "name": "DebugType",
+ "switch": "debug:embedded",
+ "comment": "",
+ "value": "embedded",
+ "flags": []
+ },
+ {
"name": "Optimize",
"switch": "optimize",
"comment": "",
diff --git a/Tests/CMakeLib/testArgumentParser.cxx b/Tests/CMakeLib/testArgumentParser.cxx
index ecfc5fcfe8..58c041859e 100644
--- a/Tests/CMakeLib/testArgumentParser.cxx
+++ b/Tests/CMakeLib/testArgumentParser.cxx
@@ -1,9 +1,12 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
+#include <functional>
#include <initializer_list>
#include <iostream>
+#include <map>
#include <string>
+#include <utility>
#include <vector>
#include <cm/optional>
@@ -15,7 +18,7 @@
namespace {
-struct Result
+struct Result : public ArgumentParser::ParseResult
{
bool Option1 = false;
bool Option2 = false;
@@ -24,6 +27,8 @@ struct Result
cm::optional<std::string> String2;
cm::optional<std::string> String3;
ArgumentParser::Maybe<std::string> String4;
+ ArgumentParser::NonEmpty<std::string> String5;
+ ArgumentParser::NonEmpty<std::string> String6;
ArgumentParser::NonEmpty<std::vector<std::string>> List1;
ArgumentParser::NonEmpty<std::vector<std::string>> List2;
@@ -36,16 +41,71 @@ struct Result
std::vector<std::vector<std::string>> Multi2;
cm::optional<std::vector<std::vector<std::string>>> Multi3;
cm::optional<std::vector<std::vector<std::string>>> Multi4;
+
+ cm::optional<std::string> Pos1;
+
+ bool Func0_ = false;
+ ArgumentParser::Continue Func0(cm::string_view)
+ {
+ Func0_ = true;
+ return ArgumentParser::Continue::No;
+ }
+
+ std::string Func1_;
+ ArgumentParser::Continue Func1(cm::string_view arg)
+ {
+ Func1_ = std::string(arg);
+ return ArgumentParser::Continue::No;
+ }
+
+ std::map<std::string, std::vector<std::string>> Func2_;
+ ArgumentParser::Continue Func2(cm::string_view key, cm::string_view arg)
+ {
+ Func2_[std::string(key)].emplace_back(arg);
+ return key == "FUNC_2b" ? ArgumentParser::Continue::Yes
+ : ArgumentParser::Continue::No;
+ }
+
+ std::vector<std::string> Func3_;
+ ArgumentParser::Continue Func3(cm::string_view arg)
+ {
+ Func3_.emplace_back(arg);
+ return ArgumentParser::Continue::Yes;
+ }
+
+ std::map<std::string, std::vector<std::string>> Func4_;
+ ArgumentParser::Continue Func4(cm::string_view key, cm::string_view arg)
+ {
+ Func4_[std::string(key)].emplace_back(arg);
+ return key == "FUNC_4b" ? ArgumentParser::Continue::Yes
+ : ArgumentParser::Continue::No;
+ }
+
+ ArgumentParser::Maybe<std::string> UnboundMaybe{ 'u', 'n', 'b', 'o',
+ 'u', 'n', 'd' };
+ ArgumentParser::MaybeEmpty<std::vector<std::string>> UnboundMaybeEmpty{
+ 1, "unbound"
+ };
+ ArgumentParser::NonEmpty<std::vector<std::string>> UnboundNonEmpty{
+ 1, "unbound"
+ };
+ ArgumentParser::NonEmpty<std::string> UnboundNonEmptyStr{ 'u', 'n', 'b', 'o',
+ 'u', 'n', 'd' };
+
+ std::vector<cm::string_view> ParsedKeywords;
};
std::initializer_list<cm::string_view> const args = {
/* clang-format off */
"OPTION_1", // option
// "OPTION_2", // option that is not present
+ "pos1", // position index 1
"STRING_1", // string arg missing value
"STRING_2", "foo", "bar", // string arg + unparsed value, presence captured
// "STRING_3", // string arg that is not present
"STRING_4", // string arg allowed to be missing value
+ "STRING_5", "foo", // string arg that is not empty
+ "STRING_6", "", // string arg that is empty
"LIST_1", // list arg missing values
"LIST_2", "foo", "bar", // list arg with 2 elems
"LIST_3", "bar", // list arg ...
@@ -57,18 +117,63 @@ std::initializer_list<cm::string_view> const args = {
"MULTI_3", "foo", "bar", // multi list with first list with two elems
"MULTI_3", "bar", "foo", // multi list with second list with two elems
// "MULTI_4", // multi list arg that is not present
+ "FUNC_0", // callback arg missing value
+ "FUNC_1", "foo", "ign1", // callback with one arg + unparsed value
+ "FUNC_2a", "foo", "ign2", // callback with keyword-dependent arg count
+ "FUNC_2b", "bar", "zot", // callback with keyword-dependent arg count
+ "FUNC_3", "foo", "bar", // callback with list arg ...
+ "FUNC_4a", "foo", "ign4", // callback with keyword-dependent arg count
+ "FUNC_4b", "bar", "zot", // callback with keyword-dependent arg count
/* clang-format on */
};
bool verifyResult(Result const& result,
- std::vector<std::string> const& unparsedArguments,
- std::vector<cm::string_view> const& keywordsMissingValue)
+ std::vector<std::string> const& unparsedArguments)
{
static std::vector<std::string> const foobar = { "foo", "bar" };
static std::vector<std::string> const barfoo = { "bar", "foo" };
- static std::vector<cm::string_view> const missing = { "STRING_1"_s,
- "LIST_1"_s,
- "LIST_4"_s };
+ static std::vector<std::string> const unbound = { "unbound" };
+ static std::vector<cm::string_view> const parsedKeywords = {
+ /* clang-format off */
+ "OPTION_1",
+ "STRING_1",
+ "STRING_2",
+ "STRING_4",
+ "STRING_5",
+ "STRING_6",
+ "LIST_1",
+ "LIST_2",
+ "LIST_3",
+ "LIST_3",
+ "LIST_4",
+ "LIST_6",
+ "MULTI_2",
+ "MULTI_3",
+ "MULTI_3",
+ "FUNC_0",
+ "FUNC_1",
+ "FUNC_2a",
+ "FUNC_2b",
+ "FUNC_3",
+ "FUNC_4a",
+ "FUNC_4b",
+ /* clang-format on */
+ };
+ static std::map<std::string, std::vector<std::string>> const func2map = {
+ { "FUNC_2a", { "foo" } }, { "FUNC_2b", { "bar", "zot" } }
+ };
+ static std::map<std::string, std::vector<std::string>> const func4map = {
+ { "FUNC_4a", { "foo" } }, { "FUNC_4b", { "bar", "zot" } }
+ };
+ static std::map<cm::string_view, std::string> const keywordErrors = {
+ { "STRING_1"_s, " missing required value\n" },
+ { "STRING_6"_s, " empty string not allowed\n" },
+ { "LIST_1"_s, " missing required value\n" },
+ { "LIST_4"_s, " missing required value\n" },
+ { "FUNC_0"_s, " missing required value\n" }
+ };
+ static std::vector<std::string> const unparsed = { "bar", "ign1", "ign2",
+ "ign4" };
#define ASSERT_TRUE(x) \
do { \
@@ -78,6 +183,8 @@ bool verifyResult(Result const& result,
} \
} while (false)
+ ASSERT_TRUE(!result);
+
ASSERT_TRUE(result.Option1);
ASSERT_TRUE(!result.Option2);
@@ -86,6 +193,8 @@ bool verifyResult(Result const& result,
ASSERT_TRUE(*result.String2 == "foo");
ASSERT_TRUE(!result.String3);
ASSERT_TRUE(result.String4.empty());
+ ASSERT_TRUE(result.String5 == "foo");
+ ASSERT_TRUE(result.String6.empty());
ASSERT_TRUE(result.List1.empty());
ASSERT_TRUE(result.List2 == foobar);
@@ -106,9 +215,29 @@ bool verifyResult(Result const& result,
ASSERT_TRUE((*result.Multi3)[1] == barfoo);
ASSERT_TRUE(!result.Multi4);
- ASSERT_TRUE(unparsedArguments.size() == 1);
- ASSERT_TRUE(unparsedArguments[0] == "bar");
- ASSERT_TRUE(keywordsMissingValue == missing);
+ ASSERT_TRUE(result.Pos1 == "pos1");
+
+ ASSERT_TRUE(result.Func0_ == false);
+ ASSERT_TRUE(result.Func1_ == "foo");
+ ASSERT_TRUE(result.Func2_ == func2map);
+ ASSERT_TRUE(result.Func3_ == foobar);
+ ASSERT_TRUE(result.Func4_ == func4map);
+
+ ASSERT_TRUE(unparsedArguments == unparsed);
+
+ ASSERT_TRUE(result.UnboundMaybe == "unbound");
+ ASSERT_TRUE(result.UnboundMaybeEmpty == unbound);
+ ASSERT_TRUE(result.UnboundNonEmpty == unbound);
+ ASSERT_TRUE(result.UnboundNonEmptyStr == "unbound");
+
+ ASSERT_TRUE(result.ParsedKeywords == parsedKeywords);
+
+ ASSERT_TRUE(result.GetKeywordErrors().size() == keywordErrors.size());
+ for (auto const& ke : result.GetKeywordErrors()) {
+ auto const ki = keywordErrors.find(ke.first);
+ ASSERT_TRUE(ki != keywordErrors.end());
+ ASSERT_TRUE(ke.second == ki->second);
+ }
return true;
}
@@ -117,57 +246,112 @@ bool testArgumentParserDynamic()
{
Result result;
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> keywordsMissingValue;
-
- cmArgumentParser<void>{}
- .Bind("OPTION_1"_s, result.Option1)
- .Bind("OPTION_2"_s, result.Option2)
- .Bind("STRING_1"_s, result.String1)
- .Bind("STRING_2"_s, result.String2)
- .Bind("STRING_3"_s, result.String3)
- .Bind("STRING_4"_s, result.String4)
- .Bind("LIST_1"_s, result.List1)
- .Bind("LIST_2"_s, result.List2)
- .Bind("LIST_3"_s, result.List3)
- .Bind("LIST_4"_s, result.List4)
- .Bind("LIST_5"_s, result.List5)
- .Bind("LIST_6"_s, result.List6)
- .Bind("MULTI_1"_s, result.Multi1)
- .Bind("MULTI_2"_s, result.Multi2)
- .Bind("MULTI_3"_s, result.Multi3)
- .Bind("MULTI_4"_s, result.Multi4)
- .Parse(args, &unparsedArguments, &keywordsMissingValue);
-
- return verifyResult(result, unparsedArguments, keywordsMissingValue);
+
+ std::function<ArgumentParser::Continue(cm::string_view, cm::string_view)>
+ func4 = [&result](cm::string_view key,
+ cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func4(key, arg);
+ };
+
+ static_cast<ArgumentParser::ParseResult&>(result) =
+ cmArgumentParser<void>{}
+ .Bind("OPTION_1"_s, result.Option1)
+ .Bind("OPTION_2"_s, result.Option2)
+ .Bind("STRING_1"_s, result.String1)
+ .Bind("STRING_2"_s, result.String2)
+ .Bind("STRING_3"_s, result.String3)
+ .Bind("STRING_4"_s, result.String4)
+ .Bind("STRING_5"_s, result.String5)
+ .Bind("STRING_6"_s, result.String6)
+ .Bind("LIST_1"_s, result.List1)
+ .Bind("LIST_2"_s, result.List2)
+ .Bind("LIST_3"_s, result.List3)
+ .Bind("LIST_4"_s, result.List4)
+ .Bind("LIST_5"_s, result.List5)
+ .Bind("LIST_6"_s, result.List6)
+ .Bind("MULTI_1"_s, result.Multi1)
+ .Bind("MULTI_2"_s, result.Multi2)
+ .Bind("MULTI_3"_s, result.Multi3)
+ .Bind("MULTI_4"_s, result.Multi4)
+ .Bind(1, result.Pos1)
+ .Bind("FUNC_0"_s,
+ [&result](cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func0(arg);
+ })
+ .Bind("FUNC_1"_s,
+ [&result](cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func1(arg);
+ })
+ .Bind("FUNC_2a"_s,
+ [&result](cm::string_view key, cm::string_view arg)
+ -> ArgumentParser::Continue { return result.Func2(key, arg); })
+ .Bind("FUNC_2b"_s,
+ [&result](cm::string_view key, cm::string_view arg)
+ -> ArgumentParser::Continue { return result.Func2(key, arg); })
+ .Bind("FUNC_3"_s,
+ [&result](cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func3(arg);
+ })
+ .Bind("FUNC_4a"_s, func4)
+ .Bind("FUNC_4b"_s, func4)
+ .BindParsedKeywords(result.ParsedKeywords)
+ .Parse(args, &unparsedArguments);
+
+ return verifyResult(result, unparsedArguments);
}
+static auto const parserStaticFunc4 =
+ [](Result& result, cm::string_view key,
+ cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func4(key, arg);
+};
+static auto const parserStatic = //
+ cmArgumentParser<Result>{}
+ .Bind("OPTION_1"_s, &Result::Option1)
+ .Bind("OPTION_2"_s, &Result::Option2)
+ .Bind("STRING_1"_s, &Result::String1)
+ .Bind("STRING_2"_s, &Result::String2)
+ .Bind("STRING_3"_s, &Result::String3)
+ .Bind("STRING_4"_s, &Result::String4)
+ .Bind("STRING_5"_s, &Result::String5)
+ .Bind("STRING_6"_s, &Result::String6)
+ .Bind("LIST_1"_s, &Result::List1)
+ .Bind("LIST_2"_s, &Result::List2)
+ .Bind("LIST_3"_s, &Result::List3)
+ .Bind("LIST_4"_s, &Result::List4)
+ .Bind("LIST_5"_s, &Result::List5)
+ .Bind("LIST_6"_s, &Result::List6)
+ .Bind("MULTI_1"_s, &Result::Multi1)
+ .Bind("MULTI_2"_s, &Result::Multi2)
+ .Bind("MULTI_3"_s, &Result::Multi3)
+ .Bind("MULTI_4"_s, &Result::Multi4)
+ .Bind(1, &Result::Pos1)
+ .Bind("FUNC_0"_s, &Result::Func0)
+ .Bind("FUNC_1"_s, &Result::Func1)
+ .Bind("FUNC_2a"_s, &Result::Func2)
+ .Bind("FUNC_2b"_s, &Result::Func2)
+ .Bind("FUNC_3"_s,
+ [](Result& result, cm::string_view arg) -> ArgumentParser::Continue {
+ return result.Func3(arg);
+ })
+ .Bind("FUNC_4a"_s, parserStaticFunc4)
+ .Bind("FUNC_4b"_s, parserStaticFunc4)
+ .BindParsedKeywords(&Result::ParsedKeywords)
+ /* keep semicolon on own line */;
+
bool testArgumentParserStatic()
{
- static auto const parser = //
- cmArgumentParser<Result>{}
- .Bind("OPTION_1"_s, &Result::Option1)
- .Bind("OPTION_2"_s, &Result::Option2)
- .Bind("STRING_1"_s, &Result::String1)
- .Bind("STRING_2"_s, &Result::String2)
- .Bind("STRING_3"_s, &Result::String3)
- .Bind("STRING_4"_s, &Result::String4)
- .Bind("LIST_1"_s, &Result::List1)
- .Bind("LIST_2"_s, &Result::List2)
- .Bind("LIST_3"_s, &Result::List3)
- .Bind("LIST_4"_s, &Result::List4)
- .Bind("LIST_5"_s, &Result::List5)
- .Bind("LIST_6"_s, &Result::List6)
- .Bind("MULTI_1"_s, &Result::Multi1)
- .Bind("MULTI_2"_s, &Result::Multi2)
- .Bind("MULTI_3"_s, &Result::Multi3)
- .Bind("MULTI_4"_s, &Result::Multi4);
-
std::vector<std::string> unparsedArguments;
- std::vector<cm::string_view> keywordsMissingValue;
- Result const result =
- parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
+ Result const result = parserStatic.Parse(args, &unparsedArguments);
+ return verifyResult(result, unparsedArguments);
+}
- return verifyResult(result, unparsedArguments, keywordsMissingValue);
+bool testArgumentParserStaticBool()
+{
+ std::vector<std::string> unparsedArguments;
+ Result result;
+ ASSERT_TRUE(parserStatic.Parse(result, args, &unparsedArguments) == false);
+ return verifyResult(result, unparsedArguments);
}
} // namespace
@@ -184,5 +368,10 @@ int testArgumentParser(int /*unused*/, char* /*unused*/ [])
return -1;
}
+ if (!testArgumentParserStaticBool()) {
+ std::cout << "While executing testArgumentParserStaticBool().\n";
+ return -1;
+ }
+
return 0;
}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index f98e7e9c45..ab1fba0cb1 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1747,7 +1747,7 @@ if(BUILD_TESTING)
if(NOT CMake_TEST_EXTERNAL_CMAKE)
foreach(STP RANGE 2 12)
- if (STP EQUAL 6)
+ if (STP EQUAL 7)
set(pass_regex ".*using log and exp")
else()
set(pass_regex "The square root of 25 is 5")
diff --git a/Tests/FindVulkan/Test/CMakeLists.txt b/Tests/FindVulkan/Test/CMakeLists.txt
index 727e1ce838..dfcfc156a7 100644
--- a/Tests/FindVulkan/Test/CMakeLists.txt
+++ b/Tests/FindVulkan/Test/CMakeLists.txt
@@ -8,7 +8,9 @@ set(components
shaderc_combined
SPIRV-Tools
volk
+ dxc
)
+
if(APPLE)
list(APPEND components MoltenVK)
endif()
@@ -80,6 +82,10 @@ add_executable(test_tgt_volk main-volk.cxx)
target_link_libraries(test_tgt_volk Vulkan::volk)
add_test(NAME test_tgt_volk COMMAND test_tgt_volk)
+add_executable(test_tgt_dxc_lib main-dxc_lib.cxx)
+target_link_libraries(test_tgt_dxc_lib Vulkan::dxc_lib)
+add_test(NAME test_tgt_dxc_lib COMMAND test_tgt_dxc_lib)
+
if(Vulkan_GLSLC_EXECUTABLE)
add_test(NAME test_glslc
COMMAND ${CMAKE_COMMAND}
@@ -97,3 +103,12 @@ if(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE)
-P "${CMAKE_CURRENT_LIST_DIR}/Run-glslangValidator.cmake"
)
endif()
+
+if(Vulkan_dxc_EXECUTABLE)
+ add_test(NAME test_dxc_exe
+ COMMAND ${CMAKE_COMMAND}
+ "-DVULKAN_DXC_EXECUTABLE=${Vulkan_dxc_EXECUTABLE}"
+ "-DVULKAN_DXC_EXECUTABLE_TARGET=$<TARGET_FILE:Vulkan::dxc_exe>"
+ -P "${CMAKE_CURRENT_LIST_DIR}/Run-dxc_exe.cmake"
+ )
+endif()
diff --git a/Tests/FindVulkan/Test/Run-dxc_exe.cmake b/Tests/FindVulkan/Test/Run-dxc_exe.cmake
new file mode 100644
index 0000000000..0d38855eea
--- /dev/null
+++ b/Tests/FindVulkan/Test/Run-dxc_exe.cmake
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.12)
+
+function(run_dxc_exe exe exe_display)
+ execute_process(COMMAND ${exe} --help
+ OUTPUT_VARIABLE output
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ RESULT_VARIABLE result
+ )
+
+ if(NOT result EQUAL 0)
+ message(SEND_ERROR "Result of ${exe_display} --help is ${result}, should be 0")
+ endif()
+
+ if(NOT output MATCHES "^OVERVIEW: HLSL Compiler for ")
+ message(SEND_ERROR "Output of ${exe_display} --help is \"${output}\", should begin with \"OVERVIEW: HLSL Compiler for \"")
+ endif()
+endfunction()
+
+run_dxc_exe("${VULKAN_DXC_EXECUTABLE}" "\${VULKAN_DXC_EXECUTABLE}")
+run_dxc_exe("${VULKAN_DXC_EXECUTABLE_TARGET}" "Vulkan::dxc_exe")
diff --git a/Tests/FindVulkan/Test/main-dxc_lib.cxx b/Tests/FindVulkan/Test/main-dxc_lib.cxx
new file mode 100644
index 0000000000..6ccb0dea00
--- /dev/null
+++ b/Tests/FindVulkan/Test/main-dxc_lib.cxx
@@ -0,0 +1,23 @@
+#include <cstdio>
+
+#include "dxc/dxcapi.h"
+#include "printf.h"
+
+int main()
+{
+ IDxcCompiler3* compiler;
+ DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler));
+
+ assert(compiler);
+
+ IDxcVersionInfo* version;
+ compiler->QueryInterface(&version);
+
+ uint32_t major, minor;
+ version->GetVersion(&major, &minor);
+ printf("DirectX Shader Compiler: %u.%u\n", major, minor);
+ version->Release();
+ compiler->Release();
+
+ return 0;
+}
diff --git a/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt b/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt
index c94c19da33..cde76f72a1 100644
--- a/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt
+++ b/Tests/RunCMake/CommandLine/P_arbitrary_args-stdout.txt
@@ -1,4 +1,4 @@
-^-- CMAKE_ARGC='8'
+^-- CMAKE_ARGC='9'
-- CMAKE_ARGV1='-P'
-- CMAKE_ARGV2='[^']*/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake'
-- CMAKE_ARGV3='--'
@@ -6,4 +6,5 @@
-- CMAKE_ARGV5='-S'
-- CMAKE_ARGV6='-B'
-- CMAKE_ARGV7='--fresh'
--- CMAKE_ARGV8=''$
+-- CMAKE_ARGV8='--version'
+-- CMAKE_ARGV9=''$
diff --git a/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake b/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake
index 8dca990109..7d79af5bc9 100644
--- a/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake
+++ b/Tests/RunCMake/CommandLine/P_arbitrary_args.cmake
@@ -7,3 +7,4 @@ message(STATUS "CMAKE_ARGV5='${CMAKE_ARGV5}'")
message(STATUS "CMAKE_ARGV6='${CMAKE_ARGV6}'")
message(STATUS "CMAKE_ARGV7='${CMAKE_ARGV7}'")
message(STATUS "CMAKE_ARGV8='${CMAKE_ARGV8}'")
+message(STATUS "CMAKE_ARGV9='${CMAKE_ARGV9}'")
diff --git a/Tests/RunCMake/CommandLine/P_args-stdout.txt b/Tests/RunCMake/CommandLine/P_args-stdout.txt
new file mode 100644
index 0000000000..f9d039fe8f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/P_args-stdout.txt
@@ -0,0 +1,6 @@
+^-- CMAKE_ARGC='5'
+-- CMAKE_ARGV1='-P'
+-- CMAKE_ARGV2='[^']*/Tests/RunCMake/CommandLine/P_args.cmake'
+-- CMAKE_ARGV3='relative/path'
+-- CMAKE_ARGV4='[^']*/Tests/RunCMake/CommandLine'
+-- CMAKE_ARGV5=''$
diff --git a/Tests/RunCMake/CommandLine/P_args.cmake b/Tests/RunCMake/CommandLine/P_args.cmake
new file mode 100644
index 0000000000..6c4fa4c704
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/P_args.cmake
@@ -0,0 +1,6 @@
+message(STATUS "CMAKE_ARGC='${CMAKE_ARGC}'")
+message(STATUS "CMAKE_ARGV1='${CMAKE_ARGV1}'")
+message(STATUS "CMAKE_ARGV2='${CMAKE_ARGV2}'")
+message(STATUS "CMAKE_ARGV3='${CMAKE_ARGV3}'")
+message(STATUS "CMAKE_ARGV4='${CMAKE_ARGV4}'")
+message(STATUS "CMAKE_ARGV5='${CMAKE_ARGV5}'")
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 80a7c69c76..39d7858c23 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -52,7 +52,8 @@ run_cmake_command(G_no-arg ${CMAKE_COMMAND} -B DummyBuildDir -G)
run_cmake_command(G_bad-arg ${CMAKE_COMMAND} -B DummyBuildDir -G NoSuchGenerator)
run_cmake_command(P_no-arg ${CMAKE_COMMAND} -P)
run_cmake_command(P_no-file ${CMAKE_COMMAND} -P nosuchscriptfile.cmake)
-run_cmake_command(P_arbitrary_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_arbitrary_args.cmake" -- -DFOO -S -B --fresh)
+run_cmake_command(P_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_args.cmake" relative/path "${RunCMake_SOURCE_DIR}")
+run_cmake_command(P_arbitrary_args ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_arbitrary_args.cmake" -- -DFOO -S -B --fresh --version)
run_cmake_command(P_fresh ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/P_fresh.cmake" --fresh)
run_cmake_command(build-no-dir
diff --git a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt
index ecfe401b8f..fd026f9297 100644
--- a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt
+++ b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt
@@ -1,9 +1,19 @@
^CMake Error at create-missing-args.cmake:[0-9]+ \(file\):
- file Keywords missing values:
+ Error after keyword "COMPRESSION":
+
+ missing required value
+
+ Error after keyword "COMPRESSION_LEVEL":
+
+ missing required value
+
+ Error after keyword "FORMAT":
+
+ missing required value
+
+ Error after keyword "OUTPUT":
+
+ missing required value
- OUTPUT
- FORMAT
- COMPRESSION
- COMPRESSION_LEVEL
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt
index 96c779f581..0c93ecef2e 100644
--- a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt
+++ b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt
@@ -1,7 +1,11 @@
^CMake Error at extract-missing-args.cmake:[0-9]+ \(file\):
- file Keywords missing values:
+ Error after keyword "DESTINATION":
+
+ missing required value
+
+ Error after keyword "INPUT":
+
+ missing required value
- INPUT
- DESTINATION
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt
index dd6a2e7a51..72292f9b50 100644
--- a/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt
+++ b/Tests/RunCMake/File_Configure/BadArgContent-stderr.txt
@@ -1,6 +1,7 @@
CMake Error at BadArgContent.cmake:[0-9]+ \(file\):
- file CONFIGURE keywords missing values:
+ Error after keyword "CONTENT":
+
+ missing required value
- CONTENT
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt b/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt
index e1bf7b41bc..d793f48308 100644
--- a/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt
+++ b/Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt
@@ -1,6 +1,7 @@
CMake Error at BadArgOutput.cmake:[0-9]+ \(file\):
- file CONFIGURE keywords missing values:
+ Error after keyword "OUTPUT":
+
+ missing required value
- OUTPUT
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt
index 4278bceb8e..708e6bec43 100644
--- a/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt
+++ b/Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt
@@ -1,6 +1,7 @@
CMake Error at EmptyCondition1.cmake:2 \(file\):
- file GENERATE keywords missing values:
+ Error after keyword "CONDITION":
+
+ missing required value
- CONDITION
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
index 4059bf8277..b1b7f80a1e 100644
--- a/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
+++ b/Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
@@ -1,6 +1,7 @@
CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\):
- file GENERATE keywords missing values:
+ Error after keyword "NEWLINE_STYLE":
+
+ missing required value
- NEWLINE_STYLE
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
new file mode 100644
index 0000000000..a1ae6ac99d
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
@@ -0,0 +1,28 @@
+set(expected_compile_commands
+[==[^\[
+{
+ "directory": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)",
+ "command": "[^
+]*Debug[^
+]*",
+ "file": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)"
+},
+{
+ "directory": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)",
+ "command": "[^
+]*Release[^
+]*",
+ "file": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)"
+}
+]$]==])
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" actual_compile_commands)
+if(NOT actual_compile_commands MATCHES "${expected_compile_commands}")
+ string(REPLACE "\n" "\n " expected_compile_commands_formatted "${expected_compile_commands}")
+ string(REPLACE "\n" "\n " actual_compile_commands_formatted "${actual_compile_commands}")
+ string(APPEND RunCMake_TEST_FAILED "Expected compile_commands.json to match:\n ${expected_compile_commands_formatted}\nActual compile_commands.json:\n ${actual_compile_commands_formatted}\n")
+endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake
new file mode 100644
index 0000000000..fc44d5ad84
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+
+add_executable(exe main.c)
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index 738bc6c5d2..c040e8f1ee 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -453,6 +453,11 @@ run_cmake_command(NoUnusedVariables ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR}
"-DCMAKE_DEFAULT_BUILD_TYPE=Debug"
"-DCMAKE_DEFAULT_CONFIGS=all"
)
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_CROSS_CONFIGS=all;-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")
+run_cmake(CompileCommands)
+unset(RunCMake_TEST_OPTIONS)
# CudaSimple uses separable compilation, which is currently only supported on NVCC.
if(CMake_TEST_CUDA)
diff --git a/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake b/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake
index bcdc10105d..1701a36aa0 100644
--- a/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake
+++ b/Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake
@@ -5,6 +5,7 @@ if(NOT EXISTS "${vcProjectFile}")
endif()
set(found_iface_h 0)
+set(found_int_dir 0)
file(STRINGS "${vcProjectFile}" lines)
foreach(line IN LISTS lines)
if(line MATCHES "<([A-Za-z0-9_]+) +Include=.*iface\\.h")
@@ -19,7 +20,15 @@ foreach(line IN LISTS lines)
endif()
set(found_iface_h 1)
endif()
+ if(line MATCHES "^ *<IntDir [^<>]+>[^<>]+</IntDir>")
+ set(found_int_dir 1)
+ endif()
endforeach()
if(NOT found_iface_h)
set(RunCMake_TEST_FAILED "iface.h not referenced in\n ${vcProjectFile}")
+ return()
+endif()
+if(NOT found_int_dir)
+ set(RunCMake_TEST_FAILED "No references to IntDir in\n ${vcProjectFile}")
+ return()
endif()
diff --git a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake
index f022a43c47..9f9a1e70bd 100644
--- a/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VerifyHeaderSets/RunCMakeTest.cmake
@@ -40,6 +40,7 @@ endif()
run_cmake_build(VerifyHeaderSets lang_test_c_verify_interface_header_sets)
run_cmake_build(VerifyHeaderSets lang_test_cxx_verify_interface_header_sets)
+run_cmake_build(VerifyHeaderSets interface_lang_test_cxx_verify_interface_header_sets)
run_cmake_build(VerifyHeaderSets list_verify_interface_header_sets)
set(RunCMake_TEST_OPTIONS -DCMAKE_VERIFY_INTERFACE_HEADER_SETS=ON)
diff --git a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake
index 82ed935a62..f260609b0f 100644
--- a/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake
+++ b/Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets.cmake
@@ -1,5 +1,7 @@
enable_language(C CXX)
+add_compile_definitions(TEST_ADD_COMPILE_DEFINITIONS)
+
set_property(SOURCE a.h PROPERTY LANGUAGE C)
set_property(SOURCE dir/c.h PROPERTY LANGUAGE C)
set_property(SOURCE dir/cxx.h PROPERTY LANGUAGE CXX)
@@ -59,6 +61,10 @@ add_library(lang_test_cxx STATIC lib.c lib.cxx)
target_compile_definitions(lang_test_cxx INTERFACE EXPECT_CXX)
target_sources(lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
+add_library(interface_lang_test_cxx INTERFACE)
+target_compile_definitions(interface_lang_test_cxx INTERFACE EXPECT_CXX)
+target_sources(interface_lang_test_cxx INTERFACE FILE_SET HEADERS FILES lang_test.h)
+
set_property(SOURCE error.h PROPERTY LANGUAGE C)
add_library(list STATIC lib.c)
diff --git a/Tests/RunCMake/VerifyHeaderSets/a.h b/Tests/RunCMake/VerifyHeaderSets/a.h
index 8b1718216a..898da4953f 100644
--- a/Tests/RunCMake/VerifyHeaderSets/a.h
+++ b/Tests/RunCMake/VerifyHeaderSets/a.h
@@ -2,4 +2,8 @@
# error "TEST_A_H defined"
#endif
+#ifndef TEST_ADD_COMPILE_DEFINITIONS
+# error "TEST_ADD_COMPILE_DEFINITIONS not defined"
+#endif
+
extern void a_h(void);
diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt
index 6a430f1813..ea1566d9ab 100644
--- a/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt
+++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt
@@ -1,5 +1,7 @@
CMake Error at Registry_BadQuery2.cmake:[0-9]+ \(cmake_host_system_information\):
- cmake_host_system_information missing expected value for argument\(s\)
- "VALUE".
+ Error after keyword "VALUE":
+
+ missing required value
+
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt
index 5eda4ffb70..f8c96d8ffd 100644
--- a/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt
+++ b/Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt
@@ -1,5 +1,7 @@
CMake Error at Registry_BadView1.cmake:[0-9]+ \(cmake_host_system_information\):
- cmake_host_system_information missing expected value for argument\(s\)
- "VIEW".
+ Error after keyword "VIEW":
+
+ missing required value
+
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt b/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt
new file mode 100644
index 0000000000..ad7d134f02
--- /dev/null
+++ b/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
+ Error after keyword "BASE_DIRECTORY":
+
+ missing required value
diff --git a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt
new file mode 100644
index 0000000000..f1b52cc4a5
--- /dev/null
+++ b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at .+/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
+ Error after keyword "OUTPUT_VARIABLE":
+
+ empty string not allowed
diff --git a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt
index e1d6592cf3..63289ef85f 100644
--- a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt
+++ b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt
@@ -1,2 +1,4 @@
CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
- cmake_path OUTPUT_VARIABLE requires an argument.
+ Error after keyword "OUTPUT_VARIABLE":
+
+ missing required value
diff --git a/Tests/RunCMake/cmake_path/RunCMakeTest.cmake b/Tests/RunCMake/cmake_path/RunCMakeTest.cmake
index 991f46b098..1742b0662b 100644
--- a/Tests/RunCMake/cmake_path/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_path/RunCMakeTest.cmake
@@ -74,6 +74,14 @@ foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME
endforeach()
+## BASE_DIRECTORY without argument
+set (RunCMake-stderr-file "BASE_DIRECTORY-no-arg-stderr.txt")
+
+foreach (command IN ITEMS RELATIVE_PATH ABSOLUTE_PATH)
+ run_cmake_command (${command}-OUTPUT_VARIABLE-no-arg "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path BASE_DIRECTORY" -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake")
+endforeach()
+
+
## Invalid output variable
set (RunCMake-stderr-file "invalid-output-var-stderr.txt")
@@ -106,6 +114,9 @@ foreach (command IN ITEMS NATIVE_PATH
run_cmake_command (${command}-invalid-output "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path ${extra_args}" -DCHECK_INVALID_OUTPUT=ON -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake")
endforeach()
+# OUTPUT_VARIABLE empty name
+set (RunCMake-stderr-file "OUTPUT_VARIABLE-empty-stderr.txt")
+
foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME
REMOVE_EXTENSION REPLACE_EXTENSION NORMAL_PATH
RELATIVE_PATH ABSOLUTE_PATH)
diff --git a/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt b/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt
index e27f1e6d25..62cad521fc 100644
--- a/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt
+++ b/Tests/RunCMake/execute_process/EchoCommand3-stderr.txt
@@ -1,2 +1,4 @@
CMake Error at .*EchoCommand.cmake:.*\(execute_process\):
- execute_process called with no value for COMMAND_ECHO.
+ Error after keyword "COMMAND_ECHO":
+
+ missing required value
diff --git a/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt b/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt
index 1a695790fc..7f85654c6a 100644
--- a/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt
+++ b/Tests/RunCMake/execute_process/EncodingMissing-stderr.txt
@@ -1,4 +1,7 @@
^CMake Error at EncodingMissing.cmake:[0-9]+ \(execute_process\):
- execute_process called with no value for ENCODING.
+ Error after keyword "ENCODING":
+
+ missing required value
+
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/export/RunCMakeTest.cmake b/Tests/RunCMake/export/RunCMakeTest.cmake
index 0e6020f0f4..ee00b273cf 100644
--- a/Tests/RunCMake/export/RunCMakeTest.cmake
+++ b/Tests/RunCMake/export/RunCMakeTest.cmake
@@ -18,3 +18,4 @@ run_cmake(DependOnDoubleExport)
run_cmake(UnknownExport)
run_cmake(NamelinkOnlyExport)
run_cmake(SeparateNamelinkExport)
+run_cmake(TryCompileExport)
diff --git a/Tests/RunCMake/export/TryCompileExport.cmake b/Tests/RunCMake/export/TryCompileExport.cmake
new file mode 100644
index 0000000000..5ad7c6ee0b
--- /dev/null
+++ b/Tests/RunCMake/export/TryCompileExport.cmake
@@ -0,0 +1,9 @@
+enable_language(CXX)
+
+add_library(interface INTERFACE)
+install(TARGETS interface EXPORT export)
+export(EXPORT export)
+
+add_library(imported IMPORTED INTERFACE)
+
+try_compile(tc "${CMAKE_CURRENT_BINARY_DIR}/tc" "${CMAKE_CURRENT_SOURCE_DIR}/empty.cpp" LINK_LIBRARIES imported)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt
deleted file mode 100644
index b22387bbd1..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CMake Error at CHMOD-all-perms\.cmake:[0-9]+ \(file\):
- file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS
- from the invocation
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake
deleted file mode 100644
index b49583d5c3..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
- FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt
deleted file mode 100644
index 8d09e35104..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at CHMOD-invalid-path\.cmake:[0-9]+ \(file\):
- file does not exist:
-
- .*/chmod-tests/I_dont_exist
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake
deleted file mode 100644
index 36915c15bd..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/I_dont_exist PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt
deleted file mode 100644
index 84ba2a299f..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-invalid-perms\.cmake:[0-9]+ \(file\):
- file INVALID_PERMISSION is an invalid permission specifier
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake
deleted file mode 100644
index 22cab0b14b..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS INVALID_PERMISSION)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt
deleted file mode 100644
index 2c248f8bf2..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-no-keyword\.cmake:[0-9]+ \(file\):
- file No permissions given
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake
deleted file mode 100644
index 8b62106513..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt
deleted file mode 100644
index a18609f2c9..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-no-perms\.cmake:[0-9]+ \(file\):
- file No permissions given
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake
deleted file mode 100644
index 9fbd3599df..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake
deleted file mode 100644
index 87e3e5766f..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake
deleted file mode 100644
index d9226b835f..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
- FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt
deleted file mode 100644
index 1c87a593f6..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at CHMOD-write-only\.cmake:[0-9]+ \(file\):
- file failed to open for reading \(Permission denied\):
-
- .*/chmod-tests/a
-Call Stack \(most recent call first\):
- CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake
deleted file mode 100644
index 1289efc103..0000000000
--- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a "CONTENT")
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_WRITE)
-file(READ ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a content)
diff --git a/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake b/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
index 18deb89517..e6b11694de 100644
--- a/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
@@ -1,12 +1,14 @@
include(RunCMake)
-run_cmake(CHMOD-no-perms)
-run_cmake(CHMOD-no-keyword)
-run_cmake(CHMOD-all-perms)
-run_cmake(CHMOD-invalid-perms)
-run_cmake(CHMOD-invalid-path)
-run_cmake(CHMOD-ok)
-run_cmake(CHMOD-override)
+run_cmake_script(no-perms)
+run_cmake_script(missing-perms)
+run_cmake_script(missing-file-perms)
+run_cmake_script(missing-dir-perms)
+run_cmake_script(all-perms)
+run_cmake_script(invalid-perms)
+run_cmake_script(invalid-path)
+run_cmake_script(ok)
+run_cmake_script(override)
if(UNIX)
execute_process(COMMAND id -u $ENV{USER}
@@ -15,5 +17,5 @@ if(UNIX)
endif()
if(NOT WIN32 AND NOT MSYS AND NOT "${uid}" STREQUAL "0")
- run_cmake(CHMOD-write-only)
+ run_cmake_script(write-only)
endif()
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt b/Tests/RunCMake/file-CHMOD/all-perms-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt
+++ b/Tests/RunCMake/file-CHMOD/all-perms-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt
new file mode 100644
index 0000000000..6932a1f0b8
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at [^
+]*/all-perms\.cmake:[0-9]+ \(file\):
+ file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS
+ from the invocation$
diff --git a/Tests/RunCMake/file-CHMOD/all-perms.cmake b/Tests/RunCMake/file-CHMOD/all-perms.cmake
new file mode 100644
index 0000000000..5ff81b85fd
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/all-perms.cmake
@@ -0,0 +1,3 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ
+ FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt b/Tests/RunCMake/file-CHMOD/invalid-path-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt
+++ b/Tests/RunCMake/file-CHMOD/invalid-path-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt
new file mode 100644
index 0000000000..eb5fb31638
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/invalid-path\.cmake:[0-9]+ \(file\):
+ file does not exist:
+
+ [^
+]*/Tests/RunCMake/file-CHMOD/invalid-path-build/I_dont_exist$
diff --git a/Tests/RunCMake/file-CHMOD/invalid-path.cmake b/Tests/RunCMake/file-CHMOD/invalid-path.cmake
new file mode 100644
index 0000000000..e8b0313c4a
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/invalid-path.cmake
@@ -0,0 +1 @@
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/I_dont_exist PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt
+++ b/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt
new file mode 100644
index 0000000000..daab22e4eb
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/invalid-perms\.cmake:[0-9]+ \(file\):
+ file INVALID_PERMISSION is an invalid permission specifier$
diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/invalid-perms.cmake
new file mode 100644
index 0000000000..42129b9dfd
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/invalid-perms.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS INVALID_PERMISSION)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt
+++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt
new file mode 100644
index 0000000000..c05bb2dd77
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-dir-perms.cmake:[0-9]+ \(file\):
+ Error after keyword "DIRECTORY_PERMISSIONS":
+
+ missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake
new file mode 100644
index 0000000000..9d0fdade8b
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt
+++ b/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt
new file mode 100644
index 0000000000..c3f4f0f17f
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-file-perms.cmake:[0-9]+ \(file\):
+ Error after keyword "FILE_PERMISSIONS":
+
+ missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake
new file mode 100644
index 0000000000..bcf35aa194
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ FILE_PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt b/Tests/RunCMake/file-CHMOD/missing-perms-result.txt
index d00491fd7e..d00491fd7e 100644
--- a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt
+++ b/Tests/RunCMake/file-CHMOD/missing-perms-result.txt
diff --git a/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt
new file mode 100644
index 0000000000..1508b130cc
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-perms.cmake:[0-9]+ \(file\):
+ Error after keyword "PERMISSIONS":
+
+ missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-perms.cmake
new file mode 100644
index 0000000000..da9f182f68
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/missing-perms.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a FILE_PERMISSIONS OWNER_READ PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/no-perms-result.txt b/Tests/RunCMake/file-CHMOD/no-perms-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/no-perms-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt
new file mode 100644
index 0000000000..4c5a13983e
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/no-perms\.cmake:[0-9]+ \(file\):
+ file No permissions given$
diff --git a/Tests/RunCMake/file-CHMOD/no-perms.cmake b/Tests/RunCMake/file-CHMOD/no-perms.cmake
new file mode 100644
index 0000000000..602cfc20f8
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/no-perms.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a)
diff --git a/Tests/RunCMake/file-CHMOD/ok.cmake b/Tests/RunCMake/file-CHMOD/ok.cmake
new file mode 100644
index 0000000000..7c74d27761
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/ok.cmake
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/override.cmake b/Tests/RunCMake/file-CHMOD/override.cmake
new file mode 100644
index 0000000000..67e5a23cae
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/override.cmake
@@ -0,0 +1,3 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ
+ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
diff --git a/Tests/RunCMake/file-CHMOD/write-only-result.txt b/Tests/RunCMake/file-CHMOD/write-only-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/write-only-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/write-only-stderr.txt
new file mode 100644
index 0000000000..169a09266c
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/write-only-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/write-only\.cmake:[0-9]+ \(file\):
+ file failed to open for reading \(Permission denied\):
+
+ [^
+]*/Tests/RunCMake/file-CHMOD/write-only-build/a$
diff --git a/Tests/RunCMake/file-CHMOD/write-only.cmake b/Tests/RunCMake/file-CHMOD/write-only.cmake
new file mode 100644
index 0000000000..aa9d8038b9
--- /dev/null
+++ b/Tests/RunCMake/file-CHMOD/write-only.cmake
@@ -0,0 +1,3 @@
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/a "CONTENT")
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_WRITE)
+file(READ ${CMAKE_CURRENT_BINARY_DIR}/a content)
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt
index c6ad3d090d..39f307d2e7 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt
@@ -13,11 +13,21 @@ Call Stack \(most recent call first\):
This warning is for project developers\. Use -Wno-dev to suppress it\.
CMake Error at badargs2\.cmake:[0-9]+ \(file\):
- file Keywords missing values:
+ Error after keyword "BUNDLE_EXECUTABLE":
+
+ missing required value
+
+ Error after keyword "CONFLICTING_DEPENDENCIES_PREFIX":
+
+ missing required value
+
+ Error after keyword "RESOLVED_DEPENDENCIES_VAR":
+
+ missing required value
+
+ Error after keyword "UNRESOLVED_DEPENDENCIES_VAR":
+
+ missing required value
- RESOLVED_DEPENDENCIES_VAR
- UNRESOLVED_DEPENDENCIES_VAR
- CONFLICTING_DEPENDENCIES_PREFIX
- BUNDLE_EXECUTABLE
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt
index 7c58aeb4aa..54a3e5a729 100644
--- a/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt
+++ b/Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt
@@ -1,2 +1,7 @@
-CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\):
- file BASE_DIRECTORY requires a value
+^CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\):
+ Error after keyword "BASE_DIRECTORY":
+
+ missing required value
+
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index 32e54d5027..fa41fc1807 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -55,6 +55,22 @@ run_cmake(REGISTRY_VIEW-no-view)
run_cmake(REGISTRY_VIEW-wrong-view)
run_cmake(REGISTRY_VIEW-propagated)
+file(
+ GLOB SearchPaths_TEST_CASE_LIST
+ LIST_DIRECTORIES TRUE
+ "${RunCMake_SOURCE_DIR}/SearchPaths/*"
+ )
+foreach(TestCasePrefix IN LISTS SearchPaths_TEST_CASE_LIST)
+ if(IS_DIRECTORY "${TestCasePrefix}")
+ cmake_path(GET TestCasePrefix FILENAME TestSuffix)
+ run_cmake_with_options(
+ SearchPaths_${TestSuffix}
+ "-DSearchPaths_ROOT=${TestCasePrefix}"
+ "--debug-find-pkg=SearchPaths"
+ )
+ endif()
+endforeach()
+
if(UNIX
AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS
)
diff --git a/Tests/RunCMake/find_package/SearchPaths.cmake b/Tests/RunCMake/find_package/SearchPaths.cmake
new file mode 100644
index 0000000000..a5a10fcedb
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0074 NEW)
+find_package(SearchPaths REQUIRED CONFIG)
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt
new file mode 100644
index 0000000000..fae13ef3e4
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt
@@ -0,0 +1,10 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt
new file mode 100644
index 0000000000..eba88c94ac
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt
new file mode 100644
index 0000000000..cebc169a27
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt
new file mode 100644
index 0000000000..4e5d3f662b
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt
new file mode 100644
index 0000000000..f29054582b
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt
new file mode 100644
index 0000000000..32b9fcb7fb
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt
new file mode 100644
index 0000000000..7bf5cf8b6d
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt
new file mode 100644
index 0000000000..3592f72e85
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt
new file mode 100644
index 0000000000..b196b7a7bc
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt
new file mode 100644
index 0000000000..17e039961d
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt
new file mode 100644
index 0000000000..ee01a50c54
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt
new file mode 100644
index 0000000000..d6abd2ff64
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt
new file mode 100644
index 0000000000..b578b2bab3
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt
@@ -0,0 +1,14 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt
new file mode 100644
index 0000000000..2f3f18aa2a
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt
new file mode 100644
index 0000000000..3eef00243c
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt
new file mode 100644
index 0000000000..c962f8bce7
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt
@@ -0,0 +1,12 @@
+ find_package considered the following locations for SearchPaths's Config
+ module:
+
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/SearchPathsConfig\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/searchpaths-config\.cmake
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+ The file was found at
+
+ .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake
new file mode 100644
index 0000000000..d831313952
--- /dev/null
+++ b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/include_external_msproject/Program.cs b/Tests/RunCMake/include_external_msproject/Program.cs
new file mode 100644
index 0000000000..5ed58c866f
--- /dev/null
+++ b/Tests/RunCMake/include_external_msproject/Program.cs
@@ -0,0 +1,9 @@
+namespace ConsoleApp
+{
+ internal class Program
+ {
+ static void Main(string[] args)
+ {
+ }
+ }
+}
diff --git a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
index cb0eb18799..4fbf147ef2 100644
--- a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
@@ -10,3 +10,14 @@ if(RunCMake_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
run_cmake(SkipGetTargetFrameworkProperties)
run_cmake(VSCSharpReference)
endif()
+
+if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])")
+ function(run_VSCSharpOnlyProject)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VSCSharpOnlyProject-build)
+ run_cmake(VSCSharpOnlyProject)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(build_flags /restore)
+ run_cmake_command(VSCSharpOnlyProject-build ${CMAKE_COMMAND} --build . -- ${build_flags})
+ endfunction()
+ run_VSCSharpOnlyProject()
+endif()
diff --git a/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake b/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake
new file mode 100644
index 0000000000..e7e0b99abf
--- /dev/null
+++ b/Tests/RunCMake/include_external_msproject/VSCSharpOnlyProject.cmake
@@ -0,0 +1,9 @@
+project(VSCSharpOnlyProject)
+
+file(COPY
+ ${CMAKE_CURRENT_SOURCE_DIR}/Program.cs
+ ${CMAKE_CURRENT_SOURCE_DIR}/consoleapp.csproj
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+
+include_external_msproject(
+ test "${CMAKE_CURRENT_BINARY_DIR}/consoleapp.csproj")
diff --git a/Tests/RunCMake/include_external_msproject/consoleapp.csproj b/Tests/RunCMake/include_external_msproject/consoleapp.csproj
new file mode 100644
index 0000000000..2894848f91
--- /dev/null
+++ b/Tests/RunCMake/include_external_msproject/consoleapp.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net472</TargetFramework>
+ <RootNamespace>ConsoleApp</RootNamespace>
+ <AssemblyName>ConsoleApp</AssemblyName>
+ <PlatformTarget>x64</PlatformTarget>
+ <EnableDefaultItems>false</EnableDefaultItems>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ </ItemGroup>
+</Project>
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-result.txt b/Tests/RunCMake/try_compile/BinDirEmpty-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirEmpty-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt
new file mode 100644
index 0000000000..b1f5ae3b48
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_compile\):
+ No <bindir> specified.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty.cmake b/Tests/RunCMake/try_compile/BinDirEmpty.cmake
new file mode 100644
index 0000000000..7bea43d853
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirEmpty.cmake
@@ -0,0 +1 @@
+try_compile(resultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_compile/BinDirRelative-result.txt b/Tests/RunCMake/try_compile/BinDirRelative-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirRelative-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt b/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt
new file mode 100644
index 0000000000..a7b6302d0c
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_compile\):
+ <bindir> is not an absolute path:
+
+ 'bin_dir_relative'
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/BinDirRelative.cmake b/Tests/RunCMake/try_compile/BinDirRelative.cmake
new file mode 100644
index 0000000000..8deda11eec
--- /dev/null
+++ b/Tests/RunCMake/try_compile/BinDirRelative.cmake
@@ -0,0 +1 @@
+try_compile(resultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_compile/EmptyListArgs.cmake b/Tests/RunCMake/try_compile/EmptyListArgs.cmake
new file mode 100644
index 0000000000..eed7ee4b6e
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EmptyListArgs.cmake
@@ -0,0 +1,8 @@
+enable_language(C)
+
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ CMAKE_FLAGS # no values
+ COMPILE_DEFINITIONS # no values
+ LINK_LIBRARIES # no values
+ LINK_OPTIONS # no values
+ )
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt
new file mode 100644
index 0000000000..b1344bd43d
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\):
+ COPY_FILE must be followed by a file path
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\):
+ COPY_FILE_ERROR must be followed by a variable name
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs.cmake b/Tests/RunCMake/try_compile/EmptyValueArgs.cmake
new file mode 100644
index 0000000000..f564abcc6c
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EmptyValueArgs.cmake
@@ -0,0 +1,4 @@
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ COPY_FILE "")
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ COPY_FILE "x" COPY_FILE_ERROR "")
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
index eca7bf485d..d377cce24b 100644
--- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -15,6 +15,10 @@ run_cmake(BadSources1)
run_cmake(BadSources2)
run_cmake(NonSourceCopyFile)
run_cmake(NonSourceCompileDefinitions)
+run_cmake(BinDirEmpty)
+run_cmake(BinDirRelative)
+run_cmake(EmptyValueArgs)
+run_cmake(EmptyListArgs)
run_cmake(EnvConfig)
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-result.txt b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt
new file mode 100644
index 0000000000..5d7e5e9127
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error: RUN_OUTPUT_STDERR_VARIABLE specified but there is no variable
+CMake Error at BadStdErrVariable.cmake:1 \(try_run\):
+ try_run unknown error.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_run/BadStdErrVariable.cmake b/Tests/RunCMake/try_run/BadStdErrVariable.cmake
new file mode 100644
index 0000000000..88c2a72641
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdErrVariable.cmake
@@ -0,0 +1,5 @@
+try_run(RUN_RESULT COMPILE_RESULT
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+ RUN_OUTPUT_STDERR_VARIABLE
+ )
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-result.txt b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt
new file mode 100644
index 0000000000..df606582a9
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable-stderr.txt
@@ -0,0 +1,5 @@
+CMake Error: RUN_OUTPUT_STDOUT_VARIABLE specified but there is no variable
+CMake Error at BadStdOutVariable.cmake:1 \(try_run\):
+ try_run unknown error.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_run/BadStdOutVariable.cmake b/Tests/RunCMake/try_run/BadStdOutVariable.cmake
new file mode 100644
index 0000000000..691e8813e5
--- /dev/null
+++ b/Tests/RunCMake/try_run/BadStdOutVariable.cmake
@@ -0,0 +1,5 @@
+try_run(RUN_RESULT COMPILE_RESULT
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+ RUN_OUTPUT_STDOUT_VARIABLE
+ )
diff --git a/Tests/RunCMake/try_run/BinDirEmpty-result.txt b/Tests/RunCMake/try_run/BinDirEmpty-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirEmpty-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt
new file mode 100644
index 0000000000..def1c22ea9
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_run\):
+ No <bindir> specified.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/BinDirEmpty.cmake b/Tests/RunCMake/try_run/BinDirEmpty.cmake
new file mode 100644
index 0000000000..d4b7ee3921
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirEmpty.cmake
@@ -0,0 +1 @@
+try_run(runResultVar compileResultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_run/BinDirRelative-result.txt b/Tests/RunCMake/try_run/BinDirRelative-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirRelative-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BinDirRelative-stderr.txt b/Tests/RunCMake/try_run/BinDirRelative-stderr.txt
new file mode 100644
index 0000000000..54d4e8666b
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirRelative-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_run\):
+ <bindir> is not an absolute path:
+
+ 'bin_dir_relative'
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/BinDirRelative.cmake b/Tests/RunCMake/try_run/BinDirRelative.cmake
new file mode 100644
index 0000000000..a277403ecc
--- /dev/null
+++ b/Tests/RunCMake/try_run/BinDirRelative.cmake
@@ -0,0 +1 @@
+try_run(runResultVar compileResultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_run/RunCMakeTest.cmake b/Tests/RunCMake/try_run/RunCMakeTest.cmake
index d74add05ca..76c85dd7b2 100644
--- a/Tests/RunCMake/try_run/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_run/RunCMakeTest.cmake
@@ -1,6 +1,8 @@
include(RunCMake)
run_cmake(BadLinkLibraries)
+run_cmake(BinDirEmpty)
+run_cmake(BinDirRelative)
if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
CMAKE_C_COMPILER_ID MATCHES "^(MSVC|GNU|LCC|Clang|AppleClang)$")
@@ -10,3 +12,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
endif()
run_cmake(WorkingDirArg)
+
+run_cmake(BadStdOutVariable)
+run_cmake(BadStdErrVariable)
diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt
index 000fd2cbc9..7c6f970e52 100644
--- a/Tests/TryCompile/CMakeLists.txt
+++ b/Tests/TryCompile/CMakeLists.txt
@@ -259,11 +259,32 @@ endif()
if("${COMPILE_OUTPUT}" MATCHES "hello world")
message(SEND_ERROR " COMPILE_OUT contains the run output: \"${COMPILE_OUTPUT}\"")
endif()
-# check the run output, it should stdout
+# check the run output, it should contain stdout
if(NOT "${RUN_OUTPUT}" MATCHES "hello world")
message(SEND_ERROR " RUN_OUTPUT didn't contain \"hello world\": \"${RUN_OUTPUT}\"")
endif()
+# try to run a file and parse stdout and stderr separately
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+ ${TryCompile_BINARY_DIR}
+ ${TryCompile_SOURCE_DIR}/stdout_and_stderr.c
+ COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
+ RUN_OUTPUT_STDOUT_VARIABLE RUN_OUTPUT_STDOUT
+ RUN_OUTPUT_STDERR_VARIABLE RUN_OUTPUT_STDERR)
+
+if(NOT SHOULD_COMPILE)
+ message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}")
+endif()
+
+# check the run stdout output
+if(NOT "${RUN_OUTPUT_STDOUT}" MATCHES "hello world")
+ message(SEND_ERROR " RUN_OUTPUT_STDOUT didn't contain \"hello world\": \"${RUN_OUTPUT_STDOUT}\"")
+endif()
+# check the run stderr output
+if(NOT "${RUN_OUTPUT_STDERR}" MATCHES "error")
+ message(SEND_ERROR " RUN_OUTPUT_STDERR didn't contain \"error\": \"${RUN_OUTPUT_STDERR}\"")
+endif()
+
#######################################################################
#
# also test that the CHECK_C_SOURCE_COMPILES, CHECK_CXX_SOURCE_COMPILES
diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c
new file mode 100644
index 0000000000..84ded1f799
--- /dev/null
+++ b/Tests/TryCompile/stdout_and_stderr.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main()
+{
+ fputs("error\n", stderr);
+ puts("hello world\n");
+ return 0;
+}
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index b80fc22885..b0ed911bb0 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -91,7 +91,7 @@
{ symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<cmGlobalNinjaGenerator::TargetAlias &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__decay_and_strip<__gnu_cxx::__normal_iterator<const cmCTestTestHandler::cmCTestTestProperties *, std::vector<cmCTestTestHandler::cmCTestTestProperties, std::allocator<cmCTestTestHandler::cmCTestTestProperties> > > &>::__type", private, "\"cmConfigure.h\"", public ] },
- { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] },
+ { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__success_type<std::chrono::duration<double, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::__success_type<std::chrono::duration<long, std::ratio<1, 1000000000> > >::type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
diff --git a/Utilities/Release/WiX/CustomAction/CMakeLists.txt b/Utilities/Release/WiX/CustomAction/CMakeLists.txt
index 9d89dd89aa..dd07f9f24c 100644
--- a/Utilities/Release/WiX/CustomAction/CMakeLists.txt
+++ b/Utilities/Release/WiX/CustomAction/CMakeLists.txt
@@ -7,6 +7,10 @@ if(MSVC)
"CMAKE_CXX_FLAGS_${CONFIG}"
"${CMAKE_CXX_FLAGS_${CONFIG}}"
)
+ string(REPLACE "-MD" "-MT"
+ "CMAKE_CXX_FLAGS_${CONFIG}"
+ "${CMAKE_CXX_FLAGS_${CONFIG}}"
+ )
endforeach()
endif()
endif()
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index f842270bc5..b8cf412ebf 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -1465,6 +1465,8 @@ if(MSVC)
add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
if(CMAKE_C_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+ elseif(CMAKE_C_FLAGS MATCHES "-W[0-4]")
+ string(REGEX REPLACE "-W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
endif()