summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.codespellrc1
-rw-r--r--.gitignore37
-rw-r--r--.gitlab-ci.yml57
-rw-r--r--.gitlab/artifacts.yml9
-rwxr-xr-x.gitlab/ci/borland-env.ps13
-rwxr-xr-x.gitlab/ci/clang.ps110
-rw-r--r--.gitlab/ci/configure_debian10_aarch64_ninja.cmake17
-rw-r--r--.gitlab/ci/configure_debian10_ninja.cmake17
-rw-r--r--.gitlab/ci/configure_fedora37_makefiles.cmake14
-rw-r--r--.gitlab/ci/configure_macos_arm64_ninja.cmake1
-rw-r--r--.gitlab/ci/configure_macos_x86_64_makefiles.cmake1
-rw-r--r--.gitlab/ci/configure_macos_x86_64_ninja.cmake1
-rw-r--r--.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake2
-rw-r--r--.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake2
-rw-r--r--.gitlab/ci/configure_sphinx.cmake3
-rw-r--r--.gitlab/ci/configure_windows_clang_ninja.cmake4
-rw-r--r--.gitlab/ci/configure_windows_vs_common.cmake1
-rw-r--r--.gitlab/ci/configure_windows_vs_common_ninja.cmake1
-rw-r--r--.gitlab/ci/docker/debian10-aarch64/deps_packages.lst4
-rw-r--r--.gitlab/ci/docker/debian10/deps_packages.lst4
-rw-r--r--.gitlab/ci/docker/fedora37/deps_packages.lst4
-rw-r--r--.gitlab/ci/download_python3.cmake41
-rw-r--r--.gitlab/ci/env_fedora37_makefiles.cmake1
-rw-r--r--.gitlab/ci/env_fedora37_makefiles.sh5
-rw-r--r--.gitlab/ci/env_macos_x86_64_ninja_ub.cmake1
-rw-r--r--.gitlab/ci/env_macos_x86_64_xcode_ub.cmake1
-rwxr-xr-x.gitlab/ci/env_windows_borland5.5.ps11
-rwxr-xr-x.gitlab/ci/env_windows_borland5.8.ps11
-rwxr-xr-x.gitlab/ci/env_windows_msvc_v71_nmake.ps12
-rwxr-xr-x.gitlab/ci/env_windows_openwatcom1.9.ps11
-rwxr-xr-x.gitlab/ci/env_windows_vs2022_x64_jom.ps14
-rwxr-xr-x.gitlab/ci/env_windows_vs2022_x64_nmake.ps11
-rwxr-xr-x.gitlab/ci/openwatcom-env.ps17
-rwxr-xr-x.gitlab/ci/python-env.ps12
-rwxr-xr-x.gitlab/ci/python.ps130
-rw-r--r--.gitlab/issue_templates/Default.md9
-rw-r--r--.gitlab/os-linux.yml6
-rw-r--r--.gitlab/os-macos.yml15
-rw-r--r--.gitlab/os-windows.yml68
-rw-r--r--Auxiliary/cmake-mode.el4
-rw-r--r--Auxiliary/vim/syntax/cmake.vim9
-rw-r--r--CMakeLists.txt2
-rw-r--r--CompileFlags.cmake15
-rw-r--r--Help/command/FIND_XXX.txt49
-rw-r--r--Help/command/UNSET_NOTE.txt9
-rw-r--r--Help/command/add_compile_options.rst15
-rw-r--r--Help/command/add_custom_command.rst21
-rw-r--r--Help/command/add_link_options.rst4
-rw-r--r--Help/command/add_test.rst65
-rw-r--r--Help/command/cmake_language.rst319
-rw-r--r--Help/command/file.rst1862
-rw-r--r--Help/command/find_package.rst42
-rw-r--r--Help/command/get_filename_component.rst6
-rw-r--r--Help/command/if.rst392
-rw-r--r--Help/command/install.rst18
-rw-r--r--Help/command/list.rst364
-rw-r--r--Help/command/math.rst3
-rw-r--r--Help/command/separate_arguments.rst2
-rw-r--r--Help/command/set.rst172
-rw-r--r--Help/command/set_property.rst6
-rw-r--r--Help/command/string.rst588
-rw-r--r--Help/command/target_compile_options.rst13
-rw-r--r--Help/command/target_link_options.rst4
-rw-r--r--Help/command/unset.rst11
-rw-r--r--Help/cpack_gen/nuget.rst4
-rw-r--r--Help/cpack_gen/wix.rst4
-rw-r--r--Help/dev/documentation.rst215
-rw-r--r--Help/envvar/CMAKE_MAXIMUM_RECURSION_DEPTH.rst10
-rw-r--r--Help/envvar/PackageName_ROOT.rst14
-rw-r--r--Help/generator/CodeBlocks.rst6
-rw-r--r--Help/generator/CodeLite.rst6
-rw-r--r--Help/generator/Eclipse CDT4.rst6
-rw-r--r--Help/generator/Kate.rst9
-rw-r--r--Help/generator/Sublime Text 2.rst6
-rw-r--r--Help/generator/Visual Studio 9 2008.rst10
-rw-r--r--Help/guide/ide-integration/index.rst2
-rw-r--r--Help/guide/tutorial/A Basic Starting Point.rst9
-rw-r--r--Help/guide/tutorial/Adding Generator Expressions.rst49
-rw-r--r--Help/guide/tutorial/Adding System Introspection.rst5
-rw-r--r--Help/guide/tutorial/Adding Usage Requirements for a Library.rst4
-rw-r--r--Help/guide/tutorial/Adding a Custom Command and Generated File.rst50
-rw-r--r--Help/guide/tutorial/Adding a Library.rst241
-rw-r--r--Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake10
-rw-r--r--Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx1
-rw-r--r--Help/guide/tutorial/Selecting Static or Shared Libraries.rst45
-rw-r--r--Help/guide/tutorial/Step10/CMakeLists.txt13
-rw-r--r--Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt52
-rw-r--r--Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake10
-rw-r--r--Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx1
-rw-r--r--Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h4
-rw-r--r--Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx4
-rw-r--r--Help/guide/tutorial/Step10/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step10/tutorial.cxx15
-rw-r--r--Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake10
-rw-r--r--Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx1
-rw-r--r--Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake10
-rw-r--r--Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx1
-rw-r--r--Help/guide/tutorial/Step2/CMakeLists.txt16
-rw-r--r--Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt15
-rw-r--r--Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx15
-rw-r--r--Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx6
-rw-r--r--Help/guide/tutorial/Step2/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step2/TutorialConfig.h.in2
-rw-r--r--Help/guide/tutorial/Step2/tutorial.cxx9
-rw-r--r--Help/guide/tutorial/Step3/CMakeLists.txt12
-rw-r--r--Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt15
-rw-r--r--Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step3/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step3/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step3/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step4/CMakeLists.txt18
-rw-r--r--Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt22
-rw-r--r--Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step4/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step4/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step4/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step5/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt23
-rw-r--r--Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step5/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step5/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step5/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step6/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt25
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step6/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step6/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step6/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step7/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt58
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx9
-rw-r--r--Help/guide/tutorial/Step7/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step7/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step7/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step8/CMakeLists.txt12
-rw-r--r--Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt73
-rw-r--r--Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step8/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step8/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step8/tutorial.cxx13
-rw-r--r--Help/guide/tutorial/Step9/CMakeLists.txt11
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt53
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake10
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx19
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h6
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx8
-rw-r--r--Help/guide/tutorial/Step9/MathFunctions/mysqrt.h7
-rw-r--r--Help/guide/tutorial/Step9/TutorialConfig.h.in1
-rw-r--r--Help/guide/tutorial/Step9/tutorial.cxx13
-rw-r--r--Help/manual/ccmake.1.rst1
-rw-r--r--Help/manual/cmake-buildsystem.7.rst6
-rw-r--r--Help/manual/cmake-env-variables.7.rst1
-rw-r--r--Help/manual/cmake-generator-expressions.7.rst319
-rw-r--r--Help/manual/cmake-generators.7.rst6
-rw-r--r--Help/manual/cmake-gui.1.rst10
-rw-r--r--Help/manual/cmake-policies.7.rst13
-rw-r--r--Help/manual/cmake-presets.7.rst49
-rw-r--r--Help/manual/cmake-properties.7.rst5
-rw-r--r--Help/manual/cmake-toolchains.7.rst11
-rw-r--r--Help/manual/cmake-variables.7.rst10
-rw-r--r--Help/manual/cmake.1.rst51
-rw-r--r--Help/manual/ctest.1.rst5
-rw-r--r--Help/manual/presets/schema.json107
-rw-r--r--Help/module/CMAKE_REQUIRED_DEFINITIONS.txt4
-rw-r--r--Help/module/CMAKE_REQUIRED_FLAGS.txt6
-rw-r--r--Help/module/CMAKE_REQUIRED_INCLUDES.txt4
-rw-r--r--Help/module/CMAKE_REQUIRED_LIBRARIES.txt5
-rw-r--r--Help/module/CMAKE_REQUIRED_LINK_OPTIONS.txt5
-rw-r--r--Help/module/CMAKE_REQUIRED_QUIET.txt5
-rw-r--r--Help/policy/CMP0144.rst26
-rw-r--r--Help/policy/CMP0145.rst30
-rw-r--r--Help/policy/CMP0146.rst29
-rw-r--r--Help/policy/CMP0147.rst24
-rw-r--r--Help/policy/CMP0148.rst29
-rw-r--r--Help/policy/CMP0149.rst47
-rw-r--r--Help/prop_test/WILL_FAIL.rst5
-rw-r--r--Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst3
-rw-r--r--Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst3
-rw-r--r--Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst8
-rw-r--r--Help/prop_tgt/COMPILE_OPTIONS.rst4
-rw-r--r--Help/prop_tgt/CUDA_CUBIN_COMPILATION.rst14
-rw-r--r--Help/prop_tgt/CUDA_FATBIN_COMPILATION.rst14
-rw-r--r--Help/prop_tgt/CUDA_OPTIX_COMPILATION.rst14
-rw-r--r--Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst17
-rw-r--r--Help/prop_tgt/ENABLE_EXPORTS.rst29
-rw-r--r--Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst27
-rw-r--r--Help/prop_tgt/IMPORTED_IMPLIB.rst23
-rw-r--r--Help/prop_tgt/IMPORTED_LOCATION.rst2
-rw-r--r--Help/prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES.rst89
-rw-r--r--Help/prop_tgt/LANG_CLANG_TIDY.rst5
-rw-r--r--Help/prop_tgt/LANG_CPPCHECK.rst5
-rw-r--r--Help/prop_tgt/LANG_CPPLINT.rst5
-rw-r--r--Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst5
-rw-r--r--Help/prop_tgt/LANG_LINKER_LAUNCHER.rst5
-rw-r--r--Help/prop_tgt/MACOS_IMPORT_FILES.txt12
-rw-r--r--Help/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst11
-rw-r--r--Help/release/3.11.rst2
-rw-r--r--Help/release/dev/0-sample-topic.rst7
-rw-r--r--Help/release/dev/Apple-tbd-files-management.rst6
-rw-r--r--Help/release/dev/FindCUDA-remove.rst6
-rw-r--r--Help/release/dev/FindCUDAToolkit-target-for-cudla.rst4
-rw-r--r--Help/release/dev/FindOpenGL-gles.rst5
-rw-r--r--Help/release/dev/FindPython-Windows-ARM.rst5
-rw-r--r--Help/release/dev/FindwxWidgets-imported-target.rst4
-rw-r--r--Help/release/dev/PATH-genex-support-list.rst5
-rw-r--r--Help/release/dev/autogen-exe-vars.rst7
-rw-r--r--Help/release/dev/automoc-macro-names.rst5
-rw-r--r--Help/release/dev/cuda-support-new-compile-modes.rst14
-rw-r--r--Help/release/dev/cxx-module-extensions.rst5
-rw-r--r--Help/release/dev/deprecate-extra-generators.rst5
-rw-r--r--Help/release/dev/deprecate-policy-old.rst7
-rw-r--r--Help/release/dev/dll-name-soversion.rst7
-rw-r--r--Help/release/dev/file-GET_RUNTIME_DEPENDENCIES-windows-casing.rst7
-rw-r--r--Help/release/dev/find_package-PACKAGENAME_ROOT.rst7
-rw-r--r--Help/release/dev/install-prefix-genex-install-code-script.rst5
-rw-r--r--Help/release/dev/lang-linker-launcher-genex.rst5
-rw-r--r--Help/release/dev/lint-genex.rst7
-rw-r--r--Help/release/dev/ninja-custom-command-depends.rst11
-rw-r--r--Help/release/dev/remove-FindPythonInterp-FindPythonLibs.rst6
-rw-r--r--Help/release/dev/remove-dart-modules.rst5
-rw-r--r--Help/release/dev/vs-BuildInParallel.rst5
-rw-r--r--Help/release/dev/vs-sdk-selection.rst7
-rw-r--r--Help/release/dev/vs-windows-min-version.rst6
-rw-r--r--Help/release/dev/vs9-deprecate.rst5
-rw-r--r--Help/release/index.rst2
-rw-r--r--Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst11
-rw-r--r--Help/variable/CMAKE_AUTOMOC_EXECUTABLE.rst10
-rw-r--r--Help/variable/CMAKE_AUTORCC_EXECUTABLE.rst10
-rw-r--r--Help/variable/CMAKE_AUTOUIC_EXECUTABLE.rst10
-rw-r--r--Help/variable/CMAKE_CROSSCOMPILING.rst10
-rw-r--r--Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst14
-rw-r--r--Help/variable/CMAKE_EDIT_COMMAND.rst3
-rw-r--r--Help/variable/CMAKE_ENABLE_EXPORTS.rst4
-rw-r--r--Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst12
-rw-r--r--Help/variable/CMAKE_EXTRA_GENERATOR.rst6
-rw-r--r--Help/variable/CMAKE_GENERATOR_PLATFORM.rst42
-rw-r--r--Help/variable/CMAKE_KATE_FILES_MODE.rst20
-rw-r--r--Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst11
-rw-r--r--Help/variable/CMAKE_LANG_FLAGS.rst18
-rw-r--r--Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst17
-rw-r--r--Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst2
-rw-r--r--Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst37
-rw-r--r--Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst10
-rw-r--r--Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst12
-rw-r--r--Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst29
-rw-r--r--Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst4
-rw-r--r--Help/variable/PackageName_ROOT.rst8
-rw-r--r--Modules/CMakeASMCompiler.cmake.in1
-rw-r--r--Modules/CMakeCCompiler.cmake.in1
-rw-r--r--Modules/CMakeCUDAInformation.cmake1
-rw-r--r--Modules/CMakeCXXCompiler.cmake.in3
-rw-r--r--Modules/CMakeDetermineCompilerId.cmake5
-rw-r--r--Modules/CMakeDetermineSystem.cmake7
-rw-r--r--Modules/CMakeFindBinUtils.cmake27
-rw-r--r--Modules/CMakeFindKate.cmake8
-rw-r--r--Modules/CMakeFortranCompiler.cmake.in3
-rw-r--r--Modules/CMakeHIPCompiler.cmake.in1
-rw-r--r--Modules/CMakeOBJCCompiler.cmake.in1
-rw-r--r--Modules/CMakeOBJCXXCompiler.cmake.in1
-rw-r--r--Modules/CMakeSwiftInformation.cmake4
-rw-r--r--Modules/CPackIFW.cmake2
-rw-r--r--Modules/CTestTargets.cmake2
-rw-r--r--Modules/CheckCCompilerFlag.cmake35
-rw-r--r--Modules/CheckCSourceCompiles.cmake62
-rw-r--r--Modules/CheckCSourceRuns.cmake62
-rw-r--r--Modules/CheckCXXCompilerFlag.cmake6
-rw-r--r--Modules/CheckCXXSourceCompiles.cmake62
-rw-r--r--Modules/CheckCXXSourceRuns.cmake62
-rw-r--r--Modules/CheckCXXSymbolExists.cmake27
-rw-r--r--Modules/CheckCompilerFlag.cmake34
-rw-r--r--Modules/CheckFortranCompilerFlag.cmake35
-rw-r--r--Modules/CheckFortranSourceCompiles.cmake60
-rw-r--r--Modules/CheckFortranSourceRuns.cmake58
-rw-r--r--Modules/CheckFunctionExists.cmake27
-rw-r--r--Modules/CheckIncludeFile.cmake27
-rw-r--r--Modules/CheckIncludeFileCXX.cmake27
-rw-r--r--Modules/CheckIncludeFiles.cmake27
-rw-r--r--Modules/CheckLanguage.cmake4
-rw-r--r--Modules/CheckLibraryExists.cmake22
-rw-r--r--Modules/CheckOBJCCompilerFlag.cmake35
-rw-r--r--Modules/CheckOBJCSourceCompiles.cmake58
-rw-r--r--Modules/CheckOBJCSourceRuns.cmake58
-rw-r--r--Modules/CheckOBJCXXCompilerFlag.cmake35
-rw-r--r--Modules/CheckOBJCXXSourceCompiles.cmake58
-rw-r--r--Modules/CheckOBJCXXSourceRuns.cmake58
-rw-r--r--Modules/CheckPrototypeDefinition.cmake26
-rw-r--r--Modules/CheckSourceCompiles.cmake50
-rw-r--r--Modules/CheckSourceRuns.cmake58
-rw-r--r--Modules/CheckStructHasMember.cmake32
-rw-r--r--Modules/CheckSymbolExists.cmake31
-rw-r--r--Modules/CheckTypeSize.cmake30
-rw-r--r--Modules/CheckVariableExists.cmake22
-rw-r--r--Modules/Compiler/IAR-ASM.cmake84
-rw-r--r--Modules/Compiler/IAR.cmake8
-rw-r--r--Modules/Compiler/IBMClang.cmake5
-rw-r--r--Modules/Compiler/LCC-Fortran.cmake7
-rw-r--r--Modules/Compiler/NVHPC-Fortran.cmake1
-rw-r--r--Modules/Compiler/NVIDIA-CUDA.cmake5
-rw-r--r--Modules/Dart.cmake19
-rw-r--r--Modules/ExternalProject.cmake8
-rw-r--r--Modules/ExternalProject/download.cmake.in2
-rw-r--r--Modules/FetchContent.cmake8
-rw-r--r--Modules/FetchContent/CMakeLists.cmake.in18
-rw-r--r--Modules/FindBLAS.cmake8
-rw-r--r--Modules/FindCUDA.cmake25
-rw-r--r--Modules/FindCUDAToolkit.cmake30
-rw-r--r--Modules/FindDart.cmake8
-rw-r--r--Modules/FindEXPAT.cmake100
-rw-r--r--Modules/FindHDF5.cmake70
-rw-r--r--Modules/FindLAPACK.cmake10
-rw-r--r--Modules/FindMatlab.cmake147
-rw-r--r--Modules/FindOpenAL.cmake22
-rw-r--r--Modules/FindOpenCL.cmake14
-rw-r--r--Modules/FindOpenGL.cmake189
-rw-r--r--Modules/FindOpenSSL.cmake4
-rw-r--r--Modules/FindPNG.cmake26
-rw-r--r--Modules/FindPython/Support.cmake110
-rw-r--r--Modules/FindPythonInterp.cmake13
-rw-r--r--Modules/FindPythonLibs.cmake13
-rw-r--r--Modules/FindVulkan.cmake17
-rw-r--r--Modules/FindX11.cmake51
-rw-r--r--Modules/FindXCTest.cmake2
-rw-r--r--Modules/FindwxWidgets.cmake21
-rw-r--r--Modules/FortranCInterface/CMakeLists.txt10
-rw-r--r--Modules/GenerateExportHeader.cmake20
-rw-r--r--Modules/GetPrerequisites.cmake2
-rw-r--r--Modules/Internal/CheckFlagCommonConfig.cmake5
-rw-r--r--Modules/Internal/CheckLinkerFlag.cmake4
-rw-r--r--Modules/Internal/CheckSourceCompiles.cmake4
-rw-r--r--Modules/Internal/CheckSourceRuns.cmake4
-rw-r--r--Modules/Internal/HeaderpadWorkaround.cmake4
-rw-r--r--Modules/MatlabTestsRedirect.cmake4
-rw-r--r--Modules/Platform/Darwin.cmake5
-rw-r--r--Modules/Platform/Linux-LCC-Fortran.cmake6
-rw-r--r--Modules/Platform/Windows-Apple-Swift.cmake2
-rw-r--r--Modules/UseJava.cmake2
-rw-r--r--Source/CMakeLists.txt19
-rw-r--r--Source/CMakeVersion.cmake2
-rw-r--r--Source/CPack/cmCPackFreeBSDGenerator.cxx4
-rw-r--r--Source/CPack/cpack.cxx7
-rw-r--r--Source/CTest/cmCTestBuildAndTestHandler.cxx10
-rw-r--r--Source/CTest/cmCTestBuildCommand.cxx4
-rw-r--r--Source/CTest/cmCTestCurl.cxx2
-rw-r--r--Source/CTest/cmCTestResourceSpec.cxx148
-rw-r--r--Source/CTest/cmCTestResourceSpec.h59
-rw-r--r--Source/CTest/cmCTestTestHandler.cxx8
-rw-r--r--Source/Checks/cm_cxx_features.cmake2
-rw-r--r--Source/Modules/CMakeBuildUtilities.cmake1
-rw-r--r--Source/QtDialog/AddCacheEntry.cxx3
-rw-r--r--Source/QtDialog/CMakeLists.txt1
-rw-r--r--Source/QtDialog/CMakeSetup.cxx11
-rw-r--r--Source/QtDialog/CMakeSetupDialog.cxx19
-rw-r--r--Source/QtDialog/CMakeSetupDialog.h3
-rw-r--r--Source/QtDialog/FirstConfigure.cxx7
-rw-r--r--Source/QtDialog/QCMake.cxx12
-rw-r--r--Source/QtDialog/QCMake.h7
-rw-r--r--Source/QtDialog/QCMakeCacheView.cxx17
-rw-r--r--Source/QtDialog/QCMakePresetItemModel.cxx17
-rw-r--r--Source/QtDialog/QCMakeSizeType.h12
-rw-r--r--Source/bindexplib.cxx9
-rw-r--r--Source/cmAddCustomCommandCommand.cxx9
-rw-r--r--Source/cmAlgorithms.h2
-rw-r--r--Source/cmBinUtilsWindowsPELinker.cxx58
-rw-r--r--Source/cmCMakePresetErrors.h245
-rw-r--r--Source/cmCMakePresetsGraph.cxx379
-rw-r--r--Source/cmCMakePresetsGraph.h105
-rw-r--r--Source/cmCMakePresetsGraphInternal.h69
-rw-r--r--Source/cmCMakePresetsGraphReadJSON.cxx436
-rw-r--r--Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx45
-rw-r--r--Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx161
-rw-r--r--Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx37
-rw-r--r--Source/cmCMakePresetsGraphReadJSONTestPresets.cxx161
-rw-r--r--Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx53
-rw-r--r--Source/cmCTest.cxx9
-rw-r--r--Source/cmCommonTargetGenerator.cxx21
-rw-r--r--Source/cmComputeLinkInformation.cxx120
-rw-r--r--Source/cmConfigure.cmake.h.in2
-rw-r--r--Source/cmCoreTryCompile.cxx6
-rw-r--r--Source/cmCurl.cxx6
-rw-r--r--Source/cmCustomCommand.cxx29
-rw-r--r--Source/cmCustomCommand.h26
-rw-r--r--Source/cmEvaluatedTargetProperty.cxx111
-rw-r--r--Source/cmEvaluatedTargetProperty.h80
-rw-r--r--Source/cmExportBuildFileGenerator.cxx5
-rw-r--r--Source/cmExportFileGenerator.cxx7
-rw-r--r--Source/cmExportInstallFileGenerator.cxx18
-rw-r--r--Source/cmExportTryCompileFileGenerator.cxx2
-rw-r--r--Source/cmExtraCodeBlocksGenerator.cxx2
-rw-r--r--Source/cmExtraCodeLiteGenerator.cxx2
-rw-r--r--Source/cmExtraEclipseCDT4Generator.cxx3
-rw-r--r--Source/cmExtraKateGenerator.cxx132
-rw-r--r--Source/cmExtraKateGenerator.h2
-rw-r--r--Source/cmExtraSublimeTextGenerator.cxx3
-rw-r--r--Source/cmFileAPICodemodel.cxx4
-rw-r--r--Source/cmFileCommand.cxx7
-rw-r--r--Source/cmFileLockResult.cxx4
-rw-r--r--Source/cmFileLockWin32.cxx4
-rw-r--r--Source/cmFindPackageCommand.cxx201
-rw-r--r--Source/cmFindPackageCommand.h8
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.cxx9
-rw-r--r--Source/cmGeneratorExpressionDAGChecker.h1
-rw-r--r--Source/cmGeneratorExpressionNode.cxx691
-rw-r--r--Source/cmGeneratorTarget.cxx493
-rw-r--r--Source/cmGeneratorTarget.h47
-rw-r--r--Source/cmGlobalGenerator.cxx71
-rw-r--r--Source/cmGlobalGenerator.h23
-rw-r--r--Source/cmGlobalGhsMultiGenerator.cxx3
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx51
-rw-r--r--Source/cmGlobalNinjaGenerator.h13
-rw-r--r--Source/cmGlobalVisualStudio10Generator.cxx24
-rw-r--r--Source/cmGlobalVisualStudio10Generator.h5
-rw-r--r--Source/cmGlobalVisualStudio14Generator.cxx125
-rw-r--r--Source/cmGlobalVisualStudio14Generator.h15
-rw-r--r--Source/cmGlobalVisualStudio7Generator.cxx20
-rw-r--r--Source/cmGlobalVisualStudio8Generator.cxx95
-rw-r--r--Source/cmGlobalVisualStudio8Generator.h8
-rw-r--r--Source/cmGlobalVisualStudio9Generator.cxx2
-rw-r--r--Source/cmGlobalVisualStudioGenerator.cxx19
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.cxx10
-rw-r--r--Source/cmGlobalVisualStudioVersionedGenerator.h3
-rw-r--r--Source/cmGlobalXCodeGenerator.cxx36
-rw-r--r--Source/cmIncludeCommand.cxx5
-rw-r--r--Source/cmInstallCommand.cxx89
-rw-r--r--Source/cmInstallRuntimeDependencySetGenerator.cxx3
-rw-r--r--Source/cmInstallScriptGenerator.cxx10
-rw-r--r--Source/cmInstallTargetGenerator.cxx218
-rw-r--r--Source/cmInstallTargetGenerator.h8
-rw-r--r--Source/cmJSONHelpers.h352
-rw-r--r--Source/cmJSONState.cxx163
-rw-r--r--Source/cmJSONState.h73
-rw-r--r--Source/cmList.cxx998
-rw-r--r--Source/cmList.h1198
-rw-r--r--Source/cmListCommand.cxx1152
-rw-r--r--Source/cmLocalGenerator.cxx35
-rw-r--r--Source/cmLocalGenerator.h4
-rw-r--r--Source/cmLocalNinjaGenerator.cxx64
-rw-r--r--Source/cmLocalUnixMakefileGenerator3.cxx6
-rw-r--r--Source/cmLocalVisualStudio7Generator.cxx3
-rw-r--r--Source/cmMakefile.cxx145
-rw-r--r--Source/cmMakefile.h23
-rw-r--r--Source/cmMakefileExecutableTargetGenerator.cxx23
-rw-r--r--Source/cmMakefileLibraryTargetGenerator.cxx124
-rw-r--r--Source/cmMakefileTargetGenerator.cxx106
-rw-r--r--Source/cmMakefileTargetGenerator.h6
-rw-r--r--Source/cmNinjaNormalTargetGenerator.cxx116
-rw-r--r--Source/cmNinjaNormalTargetGenerator.h1
-rw-r--r--Source/cmNinjaTargetGenerator.cxx182
-rw-r--r--Source/cmNinjaTargetGenerator.h15
-rw-r--r--Source/cmNinjaTypes.h3
-rw-r--r--Source/cmOSXBundleGenerator.cxx14
-rw-r--r--Source/cmOSXBundleGenerator.h11
-rw-r--r--Source/cmPolicies.cxx5
-rw-r--r--Source/cmPolicies.h22
-rw-r--r--Source/cmProcessOutput.cxx10
-rw-r--r--Source/cmQtAutoGenGlobalInitializer.cxx7
-rw-r--r--Source/cmQtAutoGenGlobalInitializer.h6
-rw-r--r--Source/cmQtAutoGenInitializer.cxx53
-rw-r--r--Source/cmRST.cxx66
-rw-r--r--Source/cmRST.h36
-rw-r--r--Source/cmSourceFile.cxx8
-rw-r--r--Source/cmSourceFile.h4
-rw-r--r--Source/cmState.h12
-rw-r--r--Source/cmStateTypes.h11
-rw-r--r--Source/cmSystemTools.cxx104
-rw-r--r--Source/cmSystemTools.h3
-rw-r--r--Source/cmTarget.cxx944
-rw-r--r--Source/cmTarget.h17
-rw-r--r--Source/cmVisualStudio10TargetGenerator.cxx68
-rw-r--r--Source/cmVisualStudio10TargetGenerator.h1
-rw-r--r--Source/cm_codecvt.cxx4
-rw-r--r--Source/cmake.cxx134
-rw-r--r--Source/cmake.h10
-rw-r--r--Source/cmcmd.cxx66
-rw-r--r--Source/kwsys/CMakeLists.txt24
-rw-r--r--Source/kwsys/CommandLineArguments.cxx13
-rw-r--r--Source/kwsys/SharedForward.h.in873
-rw-r--r--Source/kwsys/String.hxx.in57
-rw-r--r--Source/kwsys/SystemTools.cxx29
-rw-r--r--Source/kwsys/SystemTools.hxx.in5
-rw-r--r--Source/kwsys/kwsysPlatformTests.cmake63
-rw-r--r--Source/kwsys/testDynamicLoader.cxx4
-rw-r--r--Source/kwsys/testSharedForward.c.in31
-rw-r--r--Tests/Architecture/CMakeLists.txt2
-rw-r--r--Tests/ArgumentExpansion/CMakeLists.txt2
-rw-r--r--Tests/BundleGeneratorTest/CMakeLists.txt2
-rw-r--r--Tests/BundleUtilities/CMakeLists.txt2
-rw-r--r--Tests/CFBundleTest/CMakeLists.txt2
-rw-r--r--Tests/CMakeCommands/add_compile_options/CMakeLists.txt2
-rw-r--r--Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt2
-rw-r--r--Tests/CMakeCommands/target_compile_options/CMakeLists.txt2
-rw-r--r--Tests/CMakeCommands/target_include_directories/CMakeLists.txt2
-rw-r--r--Tests/CMakeCommands/target_link_libraries/CMakeLists.txt2
-rw-r--r--Tests/CMakeLib/CMakeLists.txt1
-rw-r--r--Tests/CMakeLib/testCTestResourceAllocator.cxx16
-rw-r--r--Tests/CMakeLib/testCTestResourceSpec.cxx115
-rw-r--r--Tests/CMakeLib/testJSONHelpers.cxx203
-rw-r--r--Tests/CMakeLib/testList.cxx995
-rw-r--r--Tests/CMakeLib/testRST.expect28
-rw-r--r--Tests/CMakeLib/testRST.rst28
-rw-r--r--Tests/CMakeLib/testSystemTools.cxx47
-rw-r--r--Tests/CMakeLib/testVisualStudioSlnParser.cxx1
-rw-r--r--Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file16
-rw-r--r--Tests/CMakeLists.txt40
-rw-r--r--Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CheckLanguage/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/TargetScope/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/find_library/CMakeLists.txt2
-rw-r--r--Tests/CMakeOnly/find_path/CMakeLists.txt2
-rw-r--r--Tests/CMakeTests/EndStuffTestScript.cmake2
-rw-r--r--Tests/COnly/CMakeLists.txt8
-rw-r--r--Tests/CPackComponents/CMakeLists.txt2
-rw-r--r--Tests/CPackTestAllGenerators/CMakeLists.txt2
-rw-r--r--Tests/CPackWiXGenerator/CMakeLists.txt2
-rw-r--r--Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt2
-rw-r--r--Tests/CTestCoverageCollectGCOV/test.cmake.in2
-rw-r--r--Tests/CTestLimitDashJ/CMakeLists.txt2
-rw-r--r--Tests/CTestTest/SmallAndFast/CMakeLists.txt2
-rw-r--r--Tests/CTestTest2/test.cmake.in2
-rw-r--r--Tests/CTestTestBadExe/test.cmake.in2
-rw-r--r--Tests/CTestTestBadGenerator/test.cmake.in2
-rw-r--r--Tests/CTestTestChecksum/test.cmake.in2
-rw-r--r--Tests/CTestTestCostSerial/test.cmake.in2
-rw-r--r--Tests/CTestTestCrash/CMakeLists.txt2
-rw-r--r--Tests/CTestTestCrash/test.cmake.in2
-rw-r--r--Tests/CTestTestCycle/CMakeLists.txt2
-rw-r--r--Tests/CTestTestCycle/test.cmake.in2
-rw-r--r--Tests/CTestTestDepends/CMakeLists.txt2
-rw-r--r--Tests/CTestTestDepends/test.cmake.in2
-rw-r--r--Tests/CTestTestEmptyBinaryDirectory/test.cmake.in2
-rw-r--r--Tests/CTestTestFailure/CMakeLists.txt2
-rw-r--r--Tests/CTestTestFailure/testNoBuild.cmake.in2
-rw-r--r--Tests/CTestTestFailure/testNoExe.cmake.in2
-rw-r--r--Tests/CTestTestFdSetSize/test.cmake.in2
-rw-r--r--Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt2
-rw-r--r--Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt2
-rw-r--r--Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt2
-rw-r--r--Tests/CTestTestLaunchers/test.cmake.in2
-rw-r--r--Tests/CTestTestMissingDependsExe/CMakeLists.txt2
-rw-r--r--Tests/CTestTestParallel/CMakeLists.txt2
-rw-r--r--Tests/CTestTestParallel/test.cmake.in2
-rw-r--r--Tests/CTestTestResourceLock/CMakeLists.txt2
-rw-r--r--Tests/CTestTestResourceLock/test.cmake.in2
-rw-r--r--Tests/CTestTestScheduler/CMakeLists.txt2
-rw-r--r--Tests/CTestTestScheduler/test.cmake.in2
-rw-r--r--Tests/CTestTestSerialInDepends/CMakeLists.txt2
-rw-r--r--Tests/CTestTestSerialOrder/CMakeLists.txt2
-rw-r--r--Tests/CTestTestSkipReturnCode/CMakeLists.txt2
-rw-r--r--Tests/CTestTestSkipReturnCode/test.cmake.in2
-rw-r--r--Tests/CTestTestStopTime/CMakeLists.txt2
-rw-r--r--Tests/CTestTestStopTime/GetDate.cmake2
-rw-r--r--Tests/CTestTestStopTime/test.cmake.in2
-rw-r--r--Tests/CTestTestSubdir/CMakeLists.txt2
-rw-r--r--Tests/CTestTestSubdir/test.cmake.in2
-rw-r--r--Tests/CTestTestTimeout/test.cmake.in2
-rw-r--r--Tests/CTestTestUpload/CMakeLists.txt2
-rw-r--r--Tests/CTestTestUpload/test.cmake.in2
-rw-r--r--Tests/CTestTestVerboseOutput/CMakeLists.txt2
-rw-r--r--Tests/CTestTestVerboseOutput/test.cmake.in2
-rw-r--r--Tests/CTestTestZeroTimeout/CMakeLists.txt2
-rw-r--r--Tests/CTestTestZeroTimeout/test.cmake.in2
-rw-r--r--Tests/CheckCompilerRelatedVariables/CMakeLists.txt2
-rw-r--r--Tests/CheckFortran.cmake2
-rw-r--r--Tests/CompileDefinitions/CMakeLists.txt2
-rw-r--r--Tests/Contracts/Trilinos/CMakeLists.txt2
-rw-r--r--Tests/Contracts/VTK/CMakeLists.txt2
-rw-r--r--Tests/CudaOnly/CMakeLists.txt3
-rw-r--r--Tests/CudaOnly/CUBIN/CMakeLists.txt29
-rw-r--r--Tests/CudaOnly/CUBIN/kernelA.cu7
-rw-r--r--Tests/CudaOnly/CUBIN/kernelB.cu7
-rw-r--r--Tests/CudaOnly/CUBIN/kernelC.cu7
-rw-r--r--Tests/CudaOnly/CUBIN/main.cu58
-rw-r--r--Tests/CudaOnly/CUBIN/main_no_native_archs.cu4
-rw-r--r--Tests/CudaOnly/Fatbin/CMakeLists.txt25
-rw-r--r--Tests/CudaOnly/Fatbin/main.cu58
-rw-r--r--Tests/CudaOnly/OptixIR/CMakeLists.txt33
-rw-r--r--Tests/CudaOnly/OptixIR/main.cu53
-rw-r--r--Tests/CudaOnly/RuntimeControls/verify_runtime.cmake2
-rw-r--r--Tests/CustomCommand/CMakeLists.txt2
-rw-r--r--Tests/CustomCommandByproducts/External/CMakeLists.txt2
-rw-r--r--Tests/CustomCommandWorkingDirectory/CMakeLists.txt2
-rw-r--r--Tests/CxxDialect/CMakeLists.txt3
-rw-r--r--Tests/CxxOnly/CMakeLists.txt2
-rw-r--r--Tests/Dependency/CMakeLists.txt2
-rw-r--r--Tests/EmptyDepends/CMakeLists.txt2
-rw-r--r--Tests/EnforceConfig.cmake.in5
-rw-r--r--Tests/ExportImport/Import/try_compile/CMakeLists.txt2
-rw-r--r--Tests/ExternalProject/CMakeLists.txt2
-rw-r--r--Tests/ExternalProject/Example/CMakeLists.txt2
-rw-r--r--Tests/ExternalProjectLocal/CMakeLists.txt2
-rw-r--r--Tests/ExternalProjectUpdate/CMakeLists.txt2
-rw-r--r--Tests/FindBLAS/Test/CMakeLists.txt2
-rw-r--r--Tests/FindBZip2/Test/CMakeLists.txt2
-rw-r--r--Tests/FindBoost/Test/CMakeLists.txt2
-rw-r--r--Tests/FindBoost/TestFail/CMakeLists.txt2
-rw-r--r--Tests/FindBoost/TestHeaders/CMakeLists.txt2
-rw-r--r--Tests/FindDevIL/Test/CMakeLists.txt2
-rw-r--r--Tests/FindGIF/Test/CMakeLists.txt2
-rw-r--r--Tests/FindGLEW/Test/CMakeLists.txt2
-rw-r--r--Tests/FindGSL/rng/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/atk/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/atkmm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/cairo/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/cairomm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gdk/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gdkmm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gio/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/giomm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/glib/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/glibmm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gmodule/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gobject/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gthread/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gtk/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/gtkmm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/pango/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/pangocairo/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/pangoft2/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/pangomm/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/pangoxft/CMakeLists.txt2
-rw-r--r--Tests/FindGTK2/sigc++/CMakeLists.txt2
-rw-r--r--Tests/FindGTest/Test/CMakeLists.txt2
-rw-r--r--Tests/FindGnuTLS/Test/CMakeLists.txt2
-rw-r--r--Tests/FindHDF5/CMakeLists.txt87
-rw-r--r--Tests/FindHDF5/Test/CMakeLists.txt106
-rw-r--r--Tests/FindHDF5/Test/test_C.c12
-rw-r--r--Tests/FindHDF5/Test/test_CXX.cxx14
-rw-r--r--Tests/FindHDF5/Test/test_Fortran.f906
-rw-r--r--Tests/FindICU/Test/CMakeLists.txt2
-rw-r--r--Tests/FindImageMagick/Test/CMakeLists.txt2
-rw-r--r--Tests/FindJPEG/Test/CMakeLists.txt2
-rw-r--r--Tests/FindJsonCpp/Test/CMakeLists.txt2
-rw-r--r--Tests/FindLAPACK/Test/CMakeLists.txt2
-rw-r--r--Tests/FindLibLZMA/Test/CMakeLists.txt2
-rw-r--r--Tests/FindLibXml2/Test/CMakeLists.txt2
-rw-r--r--Tests/FindLibXslt/Test/CMakeLists.txt2
-rw-r--r--Tests/FindMPI/CMakeLists.txt3
-rw-r--r--Tests/FindMatlab/basic_checks/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/components_checks/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/failure_reports/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/r2018a_check/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/targets_checks/CMakeLists.txt2
-rw-r--r--Tests/FindMatlab/versions_checks/CMakeLists.txt2
-rw-r--r--Tests/FindODBC/Test/CMakeLists.txt2
-rw-r--r--Tests/FindOpenAL/Test/CMakeLists.txt6
-rw-r--r--Tests/FindOpenAL/Test/main.cxx9
-rw-r--r--Tests/FindOpenCL/Test/CMakeLists.txt2
-rw-r--r--Tests/FindOpenGL/Test/CMakeLists.txt129
-rw-r--r--Tests/FindOpenGL/Test/main_gles2.c17
-rw-r--r--Tests/FindOpenGL/Test/main_gles3.c17
-rw-r--r--Tests/FindOpenSP/Test/CMakeLists.txt2
-rw-r--r--Tests/FindOpenSSL/rand/CMakeLists.txt2
-rw-r--r--Tests/FindPNG/Test/CMakeLists.txt2
-rw-r--r--Tests/FindPatch/CMakeLists.txt1
-rw-r--r--Tests/FindPython/ArtifactsInteractive/CMakeLists.txt2
-rw-r--r--Tests/FindPython/CustomFailureMessage/CMakeLists.txt2
-rw-r--r--Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt2
-rw-r--r--Tests/FindPython/DifferentComponents/CMakeLists.txt2
-rw-r--r--Tests/FindPython/ExactVersion/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Implementation/CMakeLists.txt2
-rw-r--r--Tests/FindPython/IronPython/CMakeLists.txt2
-rw-r--r--Tests/FindPython/IronPython2/CMakeLists.txt2
-rw-r--r--Tests/FindPython/MultiplePackages/CMakeLists.txt2
-rw-r--r--Tests/FindPython/NumPy/CMakeLists.txt2
-rw-r--r--Tests/FindPython/NumPyOnly/CMakeLists.txt2
-rw-r--r--Tests/FindPython/PyPy/CMakeLists.txt2
-rw-r--r--Tests/FindPython/PyPy2/CMakeLists.txt2
-rw-r--r--Tests/FindPython/PyPy3/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python2/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python2Embedded/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python2Fail/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python2Module/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python2SABIModule/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python3/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python3Embedded/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python3Fail/CMakeLists.txt2
-rw-r--r--Tests/FindPython/Python3Module/CMakeLists.txt2
-rw-r--r--Tests/FindPython/RequiredArtifacts/CMakeLists.txt2
-rw-r--r--Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt2
-rw-r--r--Tests/FindPython/SOABI/CMakeLists.txt2
-rw-r--r--Tests/FindPython/VirtualEnv/CMakeLists.txt2
-rw-r--r--Tests/FindPython/VirtualEnvConda/CMakeLists.txt2
-rw-r--r--Tests/FindSDL/Test/CMakeLists.txt2
-rw-r--r--Tests/FindSQLite3/Test/CMakeLists.txt2
-rw-r--r--Tests/FindTIFF/Test/CMakeLists.txt2
-rw-r--r--Tests/FindThreads/C-only/CMakeLists.txt2
-rw-r--r--Tests/FindThreads/CXX-only/CMakeLists.txt2
-rw-r--r--Tests/FindVulkan/Test/CMakeLists.txt2
-rw-r--r--Tests/FindX11/Test/CMakeLists.txt6
-rw-r--r--Tests/FindX11/Test/main.c55
-rw-r--r--Tests/FindXalanC/Test/CMakeLists.txt2
-rw-r--r--Tests/FindXercesC/Test/CMakeLists.txt2
-rw-r--r--Tests/FindwxWidgets/CMakeLists.txt10
-rw-r--r--Tests/FindwxWidgets/Test/CMakeLists.txt17
-rw-r--r--Tests/FindwxWidgets/Test/main.cpp7
-rw-r--r--Tests/Fortran/CMakeLists.txt2
-rw-r--r--Tests/FortranC/CMakeLists.txt2
-rw-r--r--Tests/FortranOnly/CMakeLists.txt19
-rw-r--r--Tests/Framework/CMakeLists.txt2
-rw-r--r--Tests/FunctionTest/CMakeLists.txt2
-rw-r--r--Tests/GeneratorExpression/CMakeLists.txt16
-rw-r--r--Tests/GeneratorExpression/check-part3.cmake1
-rw-r--r--Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt2
-rw-r--r--Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt2
-rw-r--r--Tests/InterfaceLibrary/CMakeLists.txt2
-rw-r--r--Tests/JCTest/CMakeLists.txt2
-rw-r--r--Tests/Java/CMakeLists.txt2
-rw-r--r--Tests/JavaJavah/CMakeLists.txt2
-rw-r--r--Tests/JavaNativeHeaders/CMakeLists.txt2
-rw-r--r--Tests/LinkDirectory/CMakeLists.txt2
-rw-r--r--Tests/LinkDirectory/External/CMakeLists.txt2
-rw-r--r--Tests/LinkFlags/CMakeLists.txt7
-rw-r--r--Tests/LoadCommand/CMakeCommands/CMakeLists.txt2
-rw-r--r--Tests/LoadCommand/CMakeLists.txt3
-rw-r--r--Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt2
-rw-r--r--Tests/LoadCommandOneConfig/CMakeLists.txt3
-rw-r--r--Tests/MFC/CMakeLists.txt2
-rw-r--r--Tests/MFC/CMakeLists.txt.in2
-rw-r--r--Tests/MFC/try_compile/CMakeLists.txt2
-rw-r--r--Tests/MSManifest/Subdir/CMakeLists.txt5
-rw-r--r--Tests/MSManifest/Subdir2/CMakeLists.txt10
-rw-r--r--Tests/MacRuntimePath/A/CMakeLists.txt2
-rw-r--r--Tests/MacRuntimePath/B/CMakeLists.txt2
-rw-r--r--Tests/MakeClean/CMakeLists.txt2
-rw-r--r--Tests/MissingSourceFile/CMakeLists.txt2
-rw-r--r--Tests/ModuleDefinition/CMakeLists.txt2
-rw-r--r--Tests/NewlineArgs/CMakeLists.txt2
-rw-r--r--Tests/ObjectLibrary/CMakeLists.txt2
-rw-r--r--Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt2
-rw-r--r--Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt2
-rw-r--r--Tests/OutDir/CMakeLists.txt2
-rw-r--r--Tests/PDBDirectoryAndName/CMakeLists.txt3
-rw-r--r--Tests/Plugin/CMakeLists.txt3
-rw-r--r--Tests/Plugin/PluginTest/CMakeLists.txt2
-rw-r--r--Tests/PositionIndependentTargets/CMakeLists.txt2
-rw-r--r--Tests/Preprocess/CMakeLists.txt4
-rw-r--r--Tests/Qt4And5Automoc/CMakeLists.txt2
-rw-r--r--Tests/Qt4Targets/CMakeLists.txt2
-rw-r--r--Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt2
-rw-r--r--Tests/QtAutogen/GlobalAutogenExecutable/CMakeLists.txt30
-rw-r--r--Tests/QtAutogen/GlobalAutogenExecutable/main.cpp4
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/CMakeLists.txt61
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/CheckAutogenJson.cmake27
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/CheckExportTargets.cmake45
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/dummy.cpp4
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/shared_lib.cpp6
-rw-r--r--Tests/QtAutogen/MocInterfaceMacroNames/static_lib.cpp3
-rw-r--r--Tests/QtAutogen/Tests.cmake2
-rw-r--r--Tests/QtAutomocNoQt/CMakeLists.txt2
-rw-r--r--Tests/ReturnTest/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/AddRunCMakeTest.cmake10
-rw-r--r--Tests/RunCMake/AppleSilicon/RunCMakeTest.cmake8
-rw-r--r--Tests/RunCMake/AppleSilicon/default-target-arm64-build-check.cmake5
-rw-r--r--Tests/RunCMake/AppleSilicon/default-target-arm64.cmake5
-rw-r--r--Tests/RunCMake/AppleSilicon/default-target-x86_64-build-check.cmake5
-rw-r--r--Tests/RunCMake/AppleSilicon/default-target-x86_64.cmake5
-rw-r--r--Tests/RunCMake/AppleTextStubs/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/AppleTextStubs/Framework-export.cmake12
-rw-r--r--Tests/RunCMake/AppleTextStubs/Framework-import.cmake62
-rw-r--r--Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake1
-rw-r--r--Tests/RunCMake/AppleTextStubs/Framework.cmake59
-rw-r--r--Tests/RunCMake/AppleTextStubs/Library-export.cmake12
-rw-r--r--Tests/RunCMake/AppleTextStubs/Library-import.cmake54
-rw-r--r--Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake1
-rw-r--r--Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake52
-rw-r--r--Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake1
-rw-r--r--Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake96
-rw-r--r--Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake58
-rw-r--r--Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake1
-rw-r--r--Tests/RunCMake/AppleTextStubs/Simple.cmake41
-rw-r--r--Tests/RunCMake/AppleTextStubs/foo-config.cmake.in1
-rw-r--r--Tests/RunCMake/AppleTextStubs/foo.c5
-rw-r--r--Tests/RunCMake/AppleTextStubs/main.c7
-rw-r--r--Tests/RunCMake/AutoExportDll/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/AutoExportDll/hello.cxx9
-rw-r--r--Tests/RunCMake/AutoExportDll/hello.h17
-rw-r--r--Tests/RunCMake/AutoExportDll/say.cxx3
-rw-r--r--Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt1
-rw-r--r--Tests/RunCMake/Autogen/MocPredefs-check.cxx60
-rw-r--r--Tests/RunCMake/Autogen/MocPredefs-prefix.cmake3
-rw-r--r--Tests/RunCMake/Autogen/MocPredefs.cmake39
-rw-r--r--Tests/RunCMake/Autogen/MocPredefs.cxx3
-rw-r--r--Tests/RunCMake/Autogen/RunCMakeTest.cmake7
-rw-r--r--Tests/RunCMake/BuildDepends/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/BundleUtilities/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/Byproducts/CleanByproducts.cmake4
-rw-r--r--Tests/RunCMake/CMP0004/CMP0004-NEW.cmake3
-rw-r--r--Tests/RunCMake/CMP0004/CMP0004-OLD.cmake3
-rw-r--r--Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake3
-rw-r--r--Tests/RunCMake/CMP0004/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt11
-rw-r--r--Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt9
-rw-r--r--Tests/RunCMake/CMP0019/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt9
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt9
-rw-r--r--Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0022/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0026/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0037/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0038/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0039/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0040/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0041/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0042/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0043/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0045/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0046/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0049/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0050/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0051/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0053/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0054/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0055/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0057/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0060/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/CMP0060/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0064/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0065/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/CMP0081/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CMP0102/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CMP0106/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt14
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt14
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt8
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt14
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt10
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt6
-rw-r--r--Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt5
-rw-r--r--Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt5
-rw-r--r--Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt2
-rw-r--r--Tests/RunCMake/CMP0139/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CMakeLists.txt19
-rw-r--r--Tests/RunCMake/CMakePresets/Comment-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/DuplicatePresets-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt8
-rw-r--r--Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake (renamed from Tests/RunCMake/CMP0004/CMP0004-WARN-stderr.txt)0
-rw-r--r--Tests/RunCMake/CMakePresets/ExtraPresetField-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/ExtraRootField-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/ExtraVariableField-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/FileDirFuture-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/FuturePresetInstallDirField-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/FuturePresetToolchainField-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/HighVersion-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/HostSystemNameFuture-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt6
-rw-r--r--Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt7
-rw-r--r--Tests/RunCMake/CMakePresets/IncludeV3-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/IncludeV4V3-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidPresetGenerator-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidPresetName-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidPresetVendor-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidPresets-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidRoot-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidToolsetStrategy-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidVariableValue-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidVariables-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidVendor-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/InvalidVersion-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt10
-rw-r--r--Tests/RunCMake/CMakePresets/LowVersion-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/MinimumRequiredInvalid-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/MinimumRequiredMajor-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/MinimumRequiredMinor-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/MinimumRequiredPatch-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/NoCMakePresets-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/NoPresetName-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/NoTrace.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/NoVariableValue-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/NoVersion-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/PathListSepFuture-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/PresetNotObject-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/RunCMakeTest.cmake13
-rw-r--r--Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/Trace-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/Trace.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/Trace.json.in69
-rw-r--r--Tests/RunCMake/CMakePresets/TraceAll.cmake4
-rw-r--r--Tests/RunCMake/CMakePresets/TraceExpand-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/TraceExpand.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/TraceFormatHuman-stderr.txt4
-rw-r--r--Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/TraceFormatJSON-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/TraceNotSupported-result.txt1
-rw-r--r--Tests/RunCMake/CMakePresets/TraceNotSupported-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/TraceNotSupported.json.in13
-rw-r--r--Tests/RunCMake/CMakePresets/TraceRedirect.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/TraceSource.cmake0
-rw-r--r--Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/UnknownToolsetStrategy-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/UserDuplicateCross-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/UserDuplicateInUser-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresets/VariableNotObject-stderr.txt5
-rw-r--r--Tests/RunCMake/CMakePresets/validate_schema.py6
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable-build-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-build-badConfigurePreset-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-configure-default-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable-test-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-configure-default-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-test-badConfigurePreset-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt3
-rw-r--r--Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt3
-rw-r--r--Tests/RunCMake/CPack/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/CPack/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/CPackConfig/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CPackInstallProperties/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CPackSymlinks/testcpacksym.tarbin20480 -> 10240 bytes
-rw-r--r--Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt1
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt6
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake2
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake7
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt18
-rw-r--r--Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake7
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt1
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt6
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake2
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake7
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake7
-rw-r--r--Tests/RunCMake/CTest/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CTest/RunCMakeTest.cmake7
-rw-r--r--Tests/RunCMake/CTestCommandLine/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CTestCommandLine/test.cmake.in2
-rw-r--r--Tests/RunCMake/CTestResourceAllocation/ctresalloc.cxx3
-rw-r--r--Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in2
-rw-r--r--Tests/RunCMake/CacheNewline/CacheNewline.cmake4
-rw-r--r--Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/CheckIPOSupported/CMakeLists.txt4
-rw-r--r--Tests/RunCMake/CheckModules/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/ClangTidy/CXX.cmake2
-rw-r--r--Tests/RunCMake/CommandLine/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake0
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt5
-rw-r--r--Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake0
-rw-r--r--Tests/RunCMake/CommandLine/E_time-stdout.txt2
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake9
-rw-r--r--Tests/RunCMake/CommandLine/trace-expand-stderr.txt2
-rw-r--r--Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt2
-rw-r--r--Tests/RunCMake/CommandLine/trace-stderr.txt2
-rw-r--r--Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake4
-rw-r--r--Tests/RunCMake/CommandLine/trace-try_compile.cmake4
-rw-r--r--Tests/RunCMake/CommandLineTar/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompatibleInterface/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompatibleInterface/DebugProperties.cmake5
-rw-r--r--Tests/RunCMake/CompileDefinitions/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompileFeatures/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompilerArgs/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompilerChange/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt1
-rw-r--r--Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/Configure/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Cppcheck/CXX.cmake2
-rw-r--r--Tests/RunCMake/Cpplint/CXX.cmake2
-rw-r--r--Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/ExcludeFromAll/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ExternalData/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt10
-rw-r--r--Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake2
-rw-r--r--Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt10
-rw-r--r--Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake2
-rw-r--r--Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt13
-rw-r--r--Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt10
-rw-r--r--Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake2
-rw-r--r--Tests/RunCMake/ExtraGenerators/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake19
-rw-r--r--Tests/RunCMake/ExtraGenerators/Simple-stderr.txt8
-rw-r--r--Tests/RunCMake/ExtraGenerators/Simple.cmake4
-rw-r--r--Tests/RunCMake/FPHSA/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/FeatureSummary/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake2
-rw-r--r--Tests/RunCMake/FetchContent/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/File_Archive/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/File_Generate/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt10
-rw-r--r--Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW.cmake2
-rw-r--r--Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt13
-rw-r--r--Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD.cmake2
-rw-r--r--Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake3
-rw-r--r--Tests/RunCMake/FindLua/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/FindMatlab/CMakeLists.txt4
-rw-r--r--Tests/RunCMake/FindMatlab/MatlabTest1.cmake5
-rw-r--r--Tests/RunCMake/FindMatlab/MatlabTest2.cmake4
-rw-r--r--Tests/RunCMake/FindMatlab/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/FindOpenSSL/version-exact.cmake2
-rw-r--r--Tests/RunCMake/FindOpenSSL/version-range.cmake2
-rw-r--r--Tests/RunCMake/FindOpenSSL/version.cmake2
-rw-r--r--Tests/RunCMake/FindPkgConfig/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake4
-rw-r--r--Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake4
-rw-r--r--Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake2
-rw-r--r--Tests/RunCMake/FindSWIG/version-exact.cmake2
-rw-r--r--Tests/RunCMake/FindSWIG/version-range.cmake2
-rw-r--r--Tests/RunCMake/FindSWIG/version.cmake2
-rw-r--r--Tests/RunCMake/Framework/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Framework/FrameworkConsumption.cmake12
-rw-r--r--Tests/RunCMake/Framework/FrameworkLayout.cmake1
-rw-r--r--Tests/RunCMake/Framework/consumer.c9
-rw-r--r--Tests/RunCMake/GNUInstallDirs/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in24
-rw-r--r--Tests/RunCMake/GenEx-PATH/APPEND.cmake.in35
-rw-r--r--Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in42
-rw-r--r--Tests/RunCMake/GenEx-PATH/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in123
-rw-r--r--Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in14
-rw-r--r--Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in14
-rw-r--r--Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in36
-rw-r--r--Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in36
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake21
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake47
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake44
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake9
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt9
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake9
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt8
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake8
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake32
-rw-r--r--Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c0
-rw-r--r--Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake3
-rw-r--r--Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake1
-rw-r--r--Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt7
-rw-r--r--Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake15
-rw-r--r--Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake15
-rw-r--r--Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake13
-rw-r--r--Tests/RunCMake/GenerateExportHeader/GEH.cmake4
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h42
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h42
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h2
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h2
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h2
-rw-r--r--Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h2
-rw-r--r--Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt11
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt11
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt11
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt11
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt19
-rw-r--r--Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake1
-rw-r--r--Tests/RunCMake/GeneratorPlatform/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake83
-rw-r--r--Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake9
-rw-r--r--Tests/RunCMake/GeneratorPlatform/VersionExists.cmake5
-rw-r--r--Tests/RunCMake/GeneratorToolset/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt11
-rw-r--r--Tests/RunCMake/IfacePaths/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt11
-rw-r--r--Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/IncludeWhatYouUse/CXX.cmake2
-rw-r--r--Tests/RunCMake/InterfaceLibrary/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/InterfaceLibrary/genex_link.cmake5
-rw-r--r--Tests/RunCMake/InterfaceLibrary/global-interface-stderr.txt6
-rw-r--r--Tests/RunCMake/InterfaceLibrary/invalid_name.cmake2
-rw-r--r--Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake2
-rw-r--r--Tests/RunCMake/Languages/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt11
-rw-r--r--Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt11
-rw-r--r--Tests/RunCMake/LinkItemValidation/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/LinkStatic/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt2
-rw-r--r--Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake3
-rw-r--r--Tests/RunCMake/Make/CMP0113-OLD-stderr.txt10
-rw-r--r--Tests/RunCMake/Make/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake44
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-stderr.txt34
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-default-script-stderr.txt8
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-default-stderr.txt6
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-env-script-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-env-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-stderr.txt7
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-script-stderr.txt8
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-stderr.txt6
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-env-script-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-env-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/function-invalid-env-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-env-script-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-env-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/include-invalid-env-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-env-script-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-env-stderr.txt21
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-stderr.txt5
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-stderr.txt22
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-env-stderr.txt22
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-result.txt1
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-stderr.txt6
-rw-r--r--Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-stderr.txt6
-rw-r--r--Tests/RunCMake/MultiLint/CXX.cmake8
-rw-r--r--Tests/RunCMake/Ninja/AssumedSources.cmake3
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt13
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake1
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt13
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake1
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt8
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake1
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt11
-rw-r--r--Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake1
-rw-r--r--Tests/RunCMake/Ninja/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Ninja/CommandConcat.cmake2
-rw-r--r--Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake36
-rw-r--r--Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake3
-rw-r--r--Tests/RunCMake/Ninja/Executable.cmake3
-rw-r--r--Tests/RunCMake/Ninja/LooseObjectDepends.cmake3
-rw-r--r--Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake3
-rw-r--r--Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake3
-rw-r--r--Tests/RunCMake/Ninja/RunCMakeTest.cmake32
-rw-r--r--Tests/RunCMake/Ninja/SharedLib.cmake3
-rw-r--r--Tests/RunCMake/Ninja/StaticLib.cmake3
-rw-r--r--Tests/RunCMake/Ninja/SubDirPrefix.cmake3
-rw-r--r--Tests/RunCMake/Ninja/TwoLibs.cmake3
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt1
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake1
-rw-r--r--Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake9
-rw-r--r--Tests/RunCMake/ObjectLibrary/MissingSource-stderr.txt6
-rw-r--r--Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake3
-rw-r--r--Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake3
-rw-r--r--Tests/RunCMake/PolicyScope/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/PositionIndependentCode/CMakeLists.txt3
-rw-r--r--Tests/RunCMake/README.rst7
-rw-r--r--Tests/RunCMake/RunCMake.cmake22
-rw-r--r--Tests/RunCMake/Swift/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt2
-rw-r--r--Tests/RunCMake/Swift/SwiftMultiArch.cmake4
-rw-r--r--Tests/RunCMake/SymlinkTrees/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Syntax/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/Syntax/String1-stderr.txt4
-rw-r--r--Tests/RunCMake/Syntax/String1.cmake4
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt4
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace0.cmake1
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt4
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace1.cmake1
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt4
-rw-r--r--Tests/RunCMake/Syntax/UnterminatedBrace2.cmake1
-rw-r--r--Tests/RunCMake/TargetArtifacts/CMakeLists.txt (renamed from Tests/RunCMake/ArtifactOutputDirs/CMakeLists.txt)0
-rw-r--r--Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt2
-rw-r--r--Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake18
-rw-r--r--Tests/RunCMake/TargetArtifacts/OutputDirs.cmake (renamed from Tests/RunCMake/ArtifactOutputDirs/ArtifactOutputDirs.cmake)0
-rw-r--r--Tests/RunCMake/TargetArtifacts/RunCMakeTest.cmake (renamed from Tests/RunCMake/ArtifactOutputDirs/RunCMakeTest.cmake)12
-rw-r--r--Tests/RunCMake/TargetArtifacts/check.cmake (renamed from Tests/RunCMake/ArtifactOutputDirs/check.cmake)0
-rw-r--r--Tests/RunCMake/TargetArtifacts/dll.c6
-rw-r--r--Tests/RunCMake/TargetArtifacts/lib.c (renamed from Tests/RunCMake/ArtifactOutputDirs/lib.c)0
-rw-r--r--Tests/RunCMake/TargetArtifacts/main.c (renamed from Tests/RunCMake/ArtifactOutputDirs/main.c)0
-rw-r--r--Tests/RunCMake/TargetProperties/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt8
-rw-r--r--Tests/RunCMake/ToolchainFile/SetCrossCompiling-toolchain.cmake1
-rw-r--r--Tests/RunCMake/ToolchainFile/SetCrossCompiling.cmake0
-rw-r--r--Tests/RunCMake/VS10Project/CustomCommandParallel-check.cmake52
-rw-r--r--Tests/RunCMake/VS10Project/CustomCommandParallel.cmake5
-rw-r--r--Tests/RunCMake/VS10Project/RunCMakeTest.cmake3
-rw-r--r--Tests/RunCMake/VSSolution/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake4
-rw-r--r--Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/XcodeProject/DeploymentTarget.cmake3
-rw-r--r--Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeBundles.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake4
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake1
-rw-r--r--Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake4
-rw-r--r--Tests/RunCMake/add_custom_command/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_custom_command/WorkingDirectory.cmake2
-rw-r--r--Tests/RunCMake/add_custom_target/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_dependencies/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake3
-rw-r--r--Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake3
-rw-r--r--Tests/RunCMake/add_executable/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_library/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake2
-rw-r--r--Tests/RunCMake/add_subdirectory/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt11
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt10
-rw-r--r--Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt10
-rw-r--r--Tests/RunCMake/alias_targets/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/alias_targets/invalid-name.cmake2
-rw-r--r--Tests/RunCMake/build_command/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt26
-rw-r--r--Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt26
-rw-r--r--Tests/RunCMake/cmake_minimum_required/Before3_5.cmake (renamed from Tests/RunCMake/cmake_minimum_required/Before2812.cmake)0
-rw-r--r--Tests/RunCMake/cmake_minimum_required/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt10
-rw-r--r--Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake2
-rw-r--r--Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/cmake_path/call-cmake_path.cmake3
-rw-r--r--Tests/RunCMake/configure_file/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/continue/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt9
-rw-r--r--Tests/RunCMake/ctest_build/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_build/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/ctest_build/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_cmake_error/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_configure/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_configure/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_coverage/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_coverage/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_memcheck/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_start/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_start/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_submit/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_test/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_test/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_update/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_update/test.cmake.in2
-rw-r--r--Tests/RunCMake/ctest_upload/CMakeLists.txt.in2
-rw-r--r--Tests/RunCMake/ctest_upload/test.cmake.in2
-rw-r--r--Tests/RunCMake/export/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake4
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake14
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt7
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt4
-rw-r--r--Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake5
-rw-r--r--Tests/RunCMake/file/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/file/REAL_PATH.cmake16
-rw-r--r--Tests/RunCMake/find_dependency/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/find_file/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/find_library/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt45
-rw-r--r--Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt45
-rw-r--r--Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt45
-rw-r--r--Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt45
-rw-r--r--Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt63
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt45
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt68
-rw-r--r--Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0144-common.cmake78
-rw-r--r--Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt4
-rw-r--r--Tests/RunCMake/find_package/CMP0145-NEW.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0145-OLD.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/find_package/CMP0145-WARN.cmake6
-rw-r--r--Tests/RunCMake/find_package/CMP0146-NEW-stderr.txt4
-rw-r--r--Tests/RunCMake/find_package/CMP0146-NEW.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0146-OLD.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/find_package/CMP0146-WARN.cmake6
-rw-r--r--Tests/RunCMake/find_package/CMP0147-NEW-result.txt1
-rw-r--r--Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt7
-rw-r--r--Tests/RunCMake/find_package/CMP0147-NEW.cmake2
-rw-r--r--Tests/RunCMake/find_package/CMP0147-OLD.cmake2
-rw-r--r--Tests/RunCMake/find_package/CMP0147-WARN.cmake2
-rw-r--r--Tests/RunCMake/find_package/CMP0147-common.cmake3
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Interp-NEW-stderr.txt4
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Interp-NEW.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Interp-OLD.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Interp-WARN.cmake6
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Libs-NEW-stderr.txt4
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Libs-NEW.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Libs-OLD.cmake7
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/find_package/CMP0148-Libs-WARN.cmake6
-rw-r--r--Tests/RunCMake/find_package/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/find_package/RunCMakeTest.cmake28
-rw-r--r--Tests/RunCMake/find_path/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt10
-rw-r--r--Tests/RunCMake/find_program/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/get_filename_component/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/get_property/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/if/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/include/CMP0024-NEW-stderr.txt11
-rw-r--r--Tests/RunCMake/include/CMP0024-NEW.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0024-WARN-stderr.txt26
-rw-r--r--Tests/RunCMake/include/CMP0024-WARN.cmake1
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-name-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-name-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-name.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-path-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-path-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0146-NEW-path.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0146-OLD.cmake7
-rw-r--r--Tests/RunCMake/include/CMP0146-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/include/CMP0146-WARN.cmake7
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-name-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-name-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-name.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-path-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-path-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-NEW-path.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-OLD.cmake7
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/include/CMP0148-Interp-WARN.cmake7
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-name-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-name-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-name.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-path-result.txt1
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-path-stderr.txt6
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-NEW-path.cmake2
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-OLD.cmake7
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt8
-rw-r--r--Tests/RunCMake/include/CMP0148-Libs-WARN.cmake7
-rw-r--r--Tests/RunCMake/include/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/include/EmptyString-stderr.txt6
-rw-r--r--Tests/RunCMake/include/EmptyStringOptional-stderr.txt6
-rw-r--r--Tests/RunCMake/include/ExportExportInclude-stderr.txt7
-rw-r--r--Tests/RunCMake/include/IncludeIsDirectory-stderr.txt7
-rw-r--r--Tests/RunCMake/include/IncludeMalformed-stderr.txt17
-rw-r--r--Tests/RunCMake/include/RunCMakeTest.cmake14
-rw-r--r--Tests/RunCMake/include_directories/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/include_external_msproject/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/install/CMP0062-OLD-stderr.txt15
-rw-r--r--Tests/RunCMake/install/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/install/RunCMakeTest.cmake10
-rw-r--r--Tests/RunCMake/install/SCRIPT-all-check.cmake2
-rw-r--r--Tests/RunCMake/install/SCRIPT.cmake6
-rw-r--r--Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt4
-rw-r--r--Tests/RunCMake/install/TARGETS-Defaults-stderr.txt4
-rw-r--r--Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake1
-rw-r--r--Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake1
-rw-r--r--Tests/RunCMake/install/TARGETS-NAMELINK-No-Tweak.cmake20
-rw-r--r--Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt5
-rw-r--r--Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt5
-rw-r--r--Tests/RunCMake/install/TARGETS-RPATH.cmake3
-rw-r--r--Tests/RunCMake/install/empty3.cmake1
-rw-r--r--Tests/RunCMake/install/empty4.cmake1
-rw-r--r--Tests/RunCMake/list/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt12
-rw-r--r--Tests/RunCMake/math/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/message/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/message/warnmessage-rootdir.cmake2
-rw-r--r--Tests/RunCMake/no_install_prefix/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/project/CMP0048-NEW-stderr.txt6
-rw-r--r--Tests/RunCMake/project/CMP0048-OLD-stderr.txt11
-rw-r--r--Tests/RunCMake/project/CMakeLists.txt6
-rw-r--r--Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt13
-rw-r--r--Tests/RunCMake/separate_arguments/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/set/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/set/ParentPulling.cmake3
-rw-r--r--Tests/RunCMake/set/ParentPullingRecursive.cmake3
-rw-r--r--Tests/RunCMake/set_property/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/string/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/string/RegexClear.cmake3
-rw-r--r--Tests/RunCMake/string/RegexMultiMatchClear.cmake3
-rw-r--r--Tests/RunCMake/target_compile_features/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake3
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake3
-rw-r--r--Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake3
-rw-r--r--Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake2
-rw-r--r--Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake2
-rw-r--r--Tests/RunCMake/target_link_libraries/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake1
-rw-r--r--Tests/RunCMake/target_sources/CMP0076-WARN.cmake2
-rw-r--r--Tests/RunCMake/target_sources/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/target_sources/FileSetDirect-result.txt1
-rw-r--r--Tests/RunCMake/target_sources/FileSetDirect-stderr.txt12
-rw-r--r--Tests/RunCMake/target_sources/FileSetDirect.cmake3
-rw-r--r--Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt2
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt1
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt12
-rw-r--r--Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake4
-rw-r--r--Tests/RunCMake/target_sources/MissingSource-result.txt1
-rw-r--r--Tests/RunCMake/target_sources/MissingSource-stderr.txt6
-rw-r--r--Tests/RunCMake/target_sources/MissingSource.cmake3
-rw-r--r--Tests/RunCMake/target_sources/OriginDebug-stderr.txt8
-rw-r--r--Tests/RunCMake/target_sources/OriginDebug.cmake5
-rw-r--r--Tests/RunCMake/target_sources/RunCMakeTest.cmake3
-rw-r--r--Tests/RunCMake/try_compile/CMP0056.cmake2
-rw-r--r--Tests/RunCMake/try_compile/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/try_run/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/variable_watch/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/while/CMakeLists.txt2
-rw-r--r--Tests/StagingPrefix/Consumer/CMakeLists.txt2
-rw-r--r--Tests/StagingPrefix/Producer/CMakeLists.txt2
-rw-r--r--Tests/StringFileTest/CMakeLists.txt2
-rw-r--r--Tests/SubDirSpaces/CMakeLists.txt2
-rw-r--r--Tests/TargetName/CMakeLists.txt2
-rw-r--r--Tests/TestDriver/CMakeLists.txt2
-rw-r--r--Tests/Testing/CMakeLists.txt8
-rw-r--r--Tests/TestsWorkingDirectory/CMakeLists.txt2
-rw-r--r--Tests/TryCompile/CMakeLists.txt2
-rw-r--r--Tests/TryCompile/Inner/CMakeLists.txt2
-rw-r--r--Tests/VSExternalInclude/CMakeLists.txt2
-rw-r--r--Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt2
-rw-r--r--Tests/VSMidl/CMakeLists.txt2
-rw-r--r--Tests/VSMidl/src/CMakeLists.txt2
-rw-r--r--Tests/VSNASM/CMakeLists.txt2
-rw-r--r--Utilities/ClangTidyModule/.gitignore8
-rw-r--r--Utilities/Doxygen/CMakeLists.txt2
-rw-r--r--Utilities/IWYU/mapping.imp1
-rwxr-xr-xUtilities/Scripts/update-curl.bash2
-rw-r--r--Utilities/Sphinx/CMakeLists.txt2
-rw-r--r--Utilities/Sphinx/cmake.py518
-rw-r--r--Utilities/Sphinx/conf.py.in8
-rwxr-xr-xUtilities/Sphinx/create_identifiers.py8
-rw-r--r--Utilities/Sphinx/static/cmake.css39
-rw-r--r--Utilities/cmThirdPartyChecks.cmake1
-rw-r--r--Utilities/cmcurl/CMake/CMakeConfigurableFile.in2
-rw-r--r--Utilities/cmcurl/CMake/CurlSymbolHiding.cmake2
-rw-r--r--Utilities/cmcurl/CMake/CurlTests.c2
-rw-r--r--Utilities/cmcurl/CMake/FindBearSSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindBrotli.cmake6
-rw-r--r--Utilities/cmcurl/CMake/FindCARES.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindGSS.cmake6
-rw-r--r--Utilities/cmcurl/CMake/FindLibPSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindLibSSH2.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindMSH3.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindMbedTLS.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGHTTP2.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGHTTP3.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindNGTCP2.cmake4
-rw-r--r--Utilities/cmcurl/CMake/FindNSS.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindQUICHE.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindWolfSSL.cmake2
-rw-r--r--Utilities/cmcurl/CMake/FindZstd.cmake2
-rw-r--r--Utilities/cmcurl/CMake/Macros.cmake2
-rw-r--r--Utilities/cmcurl/CMake/OtherTests.cmake2
-rw-r--r--Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake4
-rw-r--r--Utilities/cmcurl/CMake/Utilities.cmake2
-rw-r--r--Utilities/cmcurl/CMake/cmake_uninstall.cmake.in2
-rw-r--r--Utilities/cmcurl/CMake/curl-config.cmake.in2
-rw-r--r--Utilities/cmcurl/CMakeLists.txt188
-rw-r--r--Utilities/cmcurl/COPYING2
-rw-r--r--Utilities/cmcurl/include/curl/curl.h23
-rw-r--r--Utilities/cmcurl/include/curl/curlver.h14
-rw-r--r--Utilities/cmcurl/include/curl/easy.h2
-rw-r--r--Utilities/cmcurl/include/curl/header.h2
-rw-r--r--Utilities/cmcurl/include/curl/mprintf.h2
-rw-r--r--Utilities/cmcurl/include/curl/multi.h2
-rw-r--r--Utilities/cmcurl/include/curl/options.h2
-rw-r--r--Utilities/cmcurl/include/curl/stdcheaders.h2
-rw-r--r--Utilities/cmcurl/include/curl/system.h20
-rw-r--r--Utilities/cmcurl/include/curl/typecheck-gcc.h8
-rw-r--r--Utilities/cmcurl/include/curl/urlapi.h8
-rw-r--r--Utilities/cmcurl/include/curl/websockets.h3
-rw-r--r--Utilities/cmcurl/lib/CMakeLists.txt28
-rw-r--r--Utilities/cmcurl/lib/Makefile.inc26
-rw-r--r--Utilities/cmcurl/lib/altsvc.c2
-rw-r--r--Utilities/cmcurl/lib/altsvc.h2
-rw-r--r--Utilities/cmcurl/lib/amigaos.c2
-rw-r--r--Utilities/cmcurl/lib/amigaos.h2
-rw-r--r--Utilities/cmcurl/lib/arpa_telnet.h2
-rw-r--r--Utilities/cmcurl/lib/asyn-ares.c2
-rw-r--r--Utilities/cmcurl/lib/asyn-thread.c2
-rw-r--r--Utilities/cmcurl/lib/asyn.h2
-rw-r--r--Utilities/cmcurl/lib/base64.c2
-rw-r--r--Utilities/cmcurl/lib/bufref.c2
-rw-r--r--Utilities/cmcurl/lib/bufref.h2
-rw-r--r--Utilities/cmcurl/lib/c-hyper.c24
-rw-r--r--Utilities/cmcurl/lib/c-hyper.h2
-rw-r--r--Utilities/cmcurl/lib/cf-https-connect.c569
-rw-r--r--Utilities/cmcurl/lib/cf-https-connect.h58
-rw-r--r--Utilities/cmcurl/lib/cf-socket.c1921
-rw-r--r--Utilities/cmcurl/lib/cf-socket.h185
-rw-r--r--Utilities/cmcurl/lib/cfilters.c531
-rw-r--r--Utilities/cmcurl/lib/cfilters.h334
-rw-r--r--Utilities/cmcurl/lib/conncache.c18
-rw-r--r--Utilities/cmcurl/lib/conncache.h4
-rw-r--r--Utilities/cmcurl/lib/connect.c2451
-rw-r--r--Utilities/cmcurl/lib/connect.h137
-rw-r--r--Utilities/cmcurl/lib/content_encoding.c54
-rw-r--r--Utilities/cmcurl/lib/content_encoding.h5
-rw-r--r--Utilities/cmcurl/lib/cookie.c262
-rw-r--r--Utilities/cmcurl/lib/cookie.h2
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.c2
-rw-r--r--Utilities/cmcurl/lib/curl_addrinfo.h2
-rw-r--r--Utilities/cmcurl/lib/curl_base64.h2
-rw-r--r--Utilities/cmcurl/lib/curl_config.h.cmake2
-rw-r--r--Utilities/cmcurl/lib/curl_ctype.h4
-rw-r--r--Utilities/cmcurl/lib/curl_des.c2
-rw-r--r--Utilities/cmcurl/lib/curl_des.h2
-rw-r--r--Utilities/cmcurl/lib/curl_endian.c2
-rw-r--r--Utilities/cmcurl/lib/curl_endian.h2
-rw-r--r--Utilities/cmcurl/lib/curl_fnmatch.c2
-rw-r--r--Utilities/cmcurl/lib/curl_fnmatch.h2
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.c2
-rw-r--r--Utilities/cmcurl/lib/curl_get_line.h2
-rw-r--r--Utilities/cmcurl/lib/curl_gethostname.c2
-rw-r--r--Utilities/cmcurl/lib/curl_gethostname.h2
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.c12
-rw-r--r--Utilities/cmcurl/lib/curl_gssapi.h2
-rw-r--r--Utilities/cmcurl/lib/curl_hmac.h2
-rw-r--r--Utilities/cmcurl/lib/curl_krb5.h2
-rw-r--r--Utilities/cmcurl/lib/curl_ldap.h2
-rw-r--r--Utilities/cmcurl/lib/curl_log.c223
-rw-r--r--Utilities/cmcurl/lib/curl_log.h138
-rw-r--r--Utilities/cmcurl/lib/curl_md4.h2
-rw-r--r--Utilities/cmcurl/lib/curl_md5.h2
-rw-r--r--Utilities/cmcurl/lib/curl_memory.h2
-rw-r--r--Utilities/cmcurl/lib/curl_memrchr.c2
-rw-r--r--Utilities/cmcurl/lib/curl_memrchr.h2
-rw-r--r--Utilities/cmcurl/lib/curl_multibyte.c2
-rw-r--r--Utilities/cmcurl/lib/curl_multibyte.h2
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.c15
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_core.h8
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.c2
-rw-r--r--Utilities/cmcurl/lib/curl_ntlm_wb.h2
-rw-r--r--Utilities/cmcurl/lib/curl_path.c77
-rw-r--r--Utilities/cmcurl/lib/curl_path.h2
-rw-r--r--Utilities/cmcurl/lib/curl_printf.h2
-rw-r--r--Utilities/cmcurl/lib/curl_range.c2
-rw-r--r--Utilities/cmcurl/lib/curl_range.h2
-rw-r--r--Utilities/cmcurl/lib/curl_rtmp.c4
-rw-r--r--Utilities/cmcurl/lib/curl_rtmp.h2
-rw-r--r--Utilities/cmcurl/lib/curl_sasl.c5
-rw-r--r--Utilities/cmcurl/lib/curl_sasl.h8
-rw-r--r--Utilities/cmcurl/lib/curl_setup.h31
-rw-r--r--Utilities/cmcurl/lib/curl_setup_once.h10
-rw-r--r--Utilities/cmcurl/lib/curl_sha256.h4
-rw-r--r--Utilities/cmcurl/lib/curl_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/curl_sspi.h2
-rw-r--r--Utilities/cmcurl/lib/curl_threads.c2
-rw-r--r--Utilities/cmcurl/lib/curl_threads.h2
-rw-r--r--Utilities/cmcurl/lib/curlx.h2
-rw-r--r--Utilities/cmcurl/lib/dict.c88
-rw-r--r--Utilities/cmcurl/lib/dict.h2
-rw-r--r--Utilities/cmcurl/lib/doh.c6
-rw-r--r--Utilities/cmcurl/lib/doh.h2
-rw-r--r--Utilities/cmcurl/lib/dynbuf.c5
-rw-r--r--Utilities/cmcurl/lib/dynbuf.h2
-rw-r--r--Utilities/cmcurl/lib/easy.c48
-rw-r--r--Utilities/cmcurl/lib/easy_lock.h2
-rw-r--r--Utilities/cmcurl/lib/easygetopt.c2
-rw-r--r--Utilities/cmcurl/lib/easyif.h2
-rw-r--r--Utilities/cmcurl/lib/easyoptions.c6
-rw-r--r--Utilities/cmcurl/lib/easyoptions.h2
-rw-r--r--Utilities/cmcurl/lib/escape.c62
-rw-r--r--Utilities/cmcurl/lib/escape.h2
-rw-r--r--Utilities/cmcurl/lib/file.c2
-rw-r--r--Utilities/cmcurl/lib/file.h2
-rw-r--r--Utilities/cmcurl/lib/fileinfo.c2
-rw-r--r--Utilities/cmcurl/lib/fileinfo.h2
-rw-r--r--Utilities/cmcurl/lib/fopen.c3
-rw-r--r--Utilities/cmcurl/lib/fopen.h2
-rw-r--r--Utilities/cmcurl/lib/formdata.c2
-rw-r--r--Utilities/cmcurl/lib/formdata.h2
-rw-r--r--Utilities/cmcurl/lib/ftp.c203
-rw-r--r--Utilities/cmcurl/lib/ftp.h56
-rw-r--r--Utilities/cmcurl/lib/ftplistparser.c45
-rw-r--r--Utilities/cmcurl/lib/ftplistparser.h36
-rw-r--r--Utilities/cmcurl/lib/functypes.h2
-rw-r--r--Utilities/cmcurl/lib/getenv.c2
-rw-r--r--Utilities/cmcurl/lib/getinfo.c2
-rw-r--r--Utilities/cmcurl/lib/getinfo.h2
-rw-r--r--Utilities/cmcurl/lib/gopher.c2
-rw-r--r--Utilities/cmcurl/lib/gopher.h2
-rw-r--r--Utilities/cmcurl/lib/h2h3.c9
-rw-r--r--Utilities/cmcurl/lib/h2h3.h3
-rw-r--r--Utilities/cmcurl/lib/hash.c2
-rw-r--r--Utilities/cmcurl/lib/hash.h2
-rw-r--r--Utilities/cmcurl/lib/headers.c19
-rw-r--r--Utilities/cmcurl/lib/headers.h2
-rw-r--r--Utilities/cmcurl/lib/hmac.c2
-rw-r--r--Utilities/cmcurl/lib/hostasyn.c4
-rw-r--r--Utilities/cmcurl/lib/hostip.c93
-rw-r--r--Utilities/cmcurl/lib/hostip.h4
-rw-r--r--Utilities/cmcurl/lib/hostip4.c2
-rw-r--r--Utilities/cmcurl/lib/hostip6.c2
-rw-r--r--Utilities/cmcurl/lib/hostsyn.c2
-rw-r--r--Utilities/cmcurl/lib/hsts.c30
-rw-r--r--Utilities/cmcurl/lib/hsts.h4
-rw-r--r--Utilities/cmcurl/lib/http.c361
-rw-r--r--Utilities/cmcurl/lib/http.h111
-rw-r--r--Utilities/cmcurl/lib/http2.c2358
-rw-r--r--Utilities/cmcurl/lib/http2.h61
-rw-r--r--Utilities/cmcurl/lib/http_aws_sigv4.c196
-rw-r--r--Utilities/cmcurl/lib/http_aws_sigv4.h2
-rw-r--r--Utilities/cmcurl/lib/http_chunks.c2
-rw-r--r--Utilities/cmcurl/lib/http_chunks.h2
-rw-r--r--Utilities/cmcurl/lib/http_digest.c2
-rw-r--r--Utilities/cmcurl/lib/http_digest.h2
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.c2
-rw-r--r--Utilities/cmcurl/lib/http_negotiate.h2
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.c2
-rw-r--r--Utilities/cmcurl/lib/http_ntlm.h2
-rw-r--r--Utilities/cmcurl/lib/http_proxy.c350
-rw-r--r--Utilities/cmcurl/lib/http_proxy.h19
-rw-r--r--Utilities/cmcurl/lib/idn.c37
-rw-r--r--Utilities/cmcurl/lib/idn.h10
-rw-r--r--Utilities/cmcurl/lib/if2ip.c2
-rw-r--r--Utilities/cmcurl/lib/if2ip.h2
-rw-r--r--Utilities/cmcurl/lib/imap.c18
-rw-r--r--Utilities/cmcurl/lib/imap.h24
-rw-r--r--Utilities/cmcurl/lib/inet_ntop.c13
-rw-r--r--Utilities/cmcurl/lib/inet_ntop.h2
-rw-r--r--Utilities/cmcurl/lib/inet_pton.c17
-rw-r--r--Utilities/cmcurl/lib/inet_pton.h2
-rw-r--r--Utilities/cmcurl/lib/krb5.c41
-rw-r--r--Utilities/cmcurl/lib/ldap.c10
-rw-r--r--Utilities/cmcurl/lib/libcurl.rc2
-rw-r--r--Utilities/cmcurl/lib/llist.c2
-rw-r--r--Utilities/cmcurl/lib/llist.h2
-rw-r--r--Utilities/cmcurl/lib/md4.c12
-rw-r--r--Utilities/cmcurl/lib/md5.c2
-rw-r--r--Utilities/cmcurl/lib/memdebug.c2
-rw-r--r--Utilities/cmcurl/lib/memdebug.h2
-rw-r--r--Utilities/cmcurl/lib/mime.c2
-rw-r--r--Utilities/cmcurl/lib/mime.h2
-rw-r--r--Utilities/cmcurl/lib/mprintf.c2
-rw-r--r--Utilities/cmcurl/lib/mqtt.c9
-rw-r--r--Utilities/cmcurl/lib/mqtt.h2
-rw-r--r--Utilities/cmcurl/lib/multi.c155
-rw-r--r--Utilities/cmcurl/lib/multihandle.h15
-rw-r--r--Utilities/cmcurl/lib/multiif.h2
-rw-r--r--Utilities/cmcurl/lib/netrc.c2
-rw-r--r--Utilities/cmcurl/lib/netrc.h2
-rw-r--r--Utilities/cmcurl/lib/nonblock.c2
-rw-r--r--Utilities/cmcurl/lib/nonblock.h2
-rw-r--r--Utilities/cmcurl/lib/noproxy.c15
-rw-r--r--Utilities/cmcurl/lib/noproxy.h5
-rw-r--r--Utilities/cmcurl/lib/openldap.c4
-rw-r--r--Utilities/cmcurl/lib/parsedate.c161
-rw-r--r--Utilities/cmcurl/lib/parsedate.h2
-rw-r--r--Utilities/cmcurl/lib/pingpong.c2
-rw-r--r--Utilities/cmcurl/lib/pingpong.h2
-rw-r--r--Utilities/cmcurl/lib/pop3.c14
-rw-r--r--Utilities/cmcurl/lib/pop3.h12
-rw-r--r--Utilities/cmcurl/lib/progress.c51
-rw-r--r--Utilities/cmcurl/lib/progress.h9
-rw-r--r--Utilities/cmcurl/lib/psl.c2
-rw-r--r--Utilities/cmcurl/lib/psl.h2
-rw-r--r--Utilities/cmcurl/lib/quic.h68
-rw-r--r--Utilities/cmcurl/lib/rand.c11
-rw-r--r--Utilities/cmcurl/lib/rand.h2
-rw-r--r--Utilities/cmcurl/lib/rename.c2
-rw-r--r--Utilities/cmcurl/lib/rename.h2
-rw-r--r--Utilities/cmcurl/lib/rtsp.c50
-rw-r--r--Utilities/cmcurl/lib/rtsp.h2
-rw-r--r--Utilities/cmcurl/lib/select.c6
-rw-r--r--Utilities/cmcurl/lib/select.h2
-rw-r--r--Utilities/cmcurl/lib/sendf.c359
-rw-r--r--Utilities/cmcurl/lib/sendf.h47
-rw-r--r--Utilities/cmcurl/lib/setopt.c249
-rw-r--r--Utilities/cmcurl/lib/setopt.h2
-rw-r--r--Utilities/cmcurl/lib/setup-os400.h4
-rw-r--r--Utilities/cmcurl/lib/setup-vms.h2
-rw-r--r--Utilities/cmcurl/lib/setup-win32.h2
-rw-r--r--Utilities/cmcurl/lib/sha256.c4
-rw-r--r--Utilities/cmcurl/lib/share.c34
-rw-r--r--Utilities/cmcurl/lib/share.h8
-rw-r--r--Utilities/cmcurl/lib/sigpipe.h3
-rw-r--r--Utilities/cmcurl/lib/slist.c2
-rw-r--r--Utilities/cmcurl/lib/slist.h2
-rw-r--r--Utilities/cmcurl/lib/smb.c13
-rw-r--r--Utilities/cmcurl/lib/smb.h4
-rw-r--r--Utilities/cmcurl/lib/smtp.c16
-rw-r--r--Utilities/cmcurl/lib/smtp.h20
-rw-r--r--Utilities/cmcurl/lib/sockaddr.h2
-rw-r--r--Utilities/cmcurl/lib/socketpair.c80
-rw-r--r--Utilities/cmcurl/lib/socketpair.h2
-rw-r--r--Utilities/cmcurl/lib/socks.c322
-rw-r--r--Utilities/cmcurl/lib/socks.h13
-rw-r--r--Utilities/cmcurl/lib/socks_gssapi.c48
-rw-r--r--Utilities/cmcurl/lib/socks_sspi.c35
-rw-r--r--Utilities/cmcurl/lib/speedcheck.c2
-rw-r--r--Utilities/cmcurl/lib/speedcheck.h2
-rw-r--r--Utilities/cmcurl/lib/splay.c2
-rw-r--r--Utilities/cmcurl/lib/splay.h2
-rw-r--r--Utilities/cmcurl/lib/strcase.c2
-rw-r--r--Utilities/cmcurl/lib/strcase.h2
-rw-r--r--Utilities/cmcurl/lib/strdup.c4
-rw-r--r--Utilities/cmcurl/lib/strdup.h4
-rw-r--r--Utilities/cmcurl/lib/strerror.c5
-rw-r--r--Utilities/cmcurl/lib/strerror.h2
-rw-r--r--Utilities/cmcurl/lib/strtok.c2
-rw-r--r--Utilities/cmcurl/lib/strtok.h2
-rw-r--r--Utilities/cmcurl/lib/strtoofft.c2
-rw-r--r--Utilities/cmcurl/lib/strtoofft.h2
-rw-r--r--Utilities/cmcurl/lib/system_win32.c2
-rw-r--r--Utilities/cmcurl/lib/system_win32.h2
-rw-r--r--Utilities/cmcurl/lib/telnet.c181
-rw-r--r--Utilities/cmcurl/lib/telnet.h2
-rw-r--r--Utilities/cmcurl/lib/tftp.c11
-rw-r--r--Utilities/cmcurl/lib/tftp.h2
-rw-r--r--Utilities/cmcurl/lib/timediff.c2
-rw-r--r--Utilities/cmcurl/lib/timediff.h2
-rw-r--r--Utilities/cmcurl/lib/timeval.c2
-rw-r--r--Utilities/cmcurl/lib/timeval.h2
-rw-r--r--Utilities/cmcurl/lib/transfer.c135
-rw-r--r--Utilities/cmcurl/lib/transfer.h2
-rw-r--r--Utilities/cmcurl/lib/url.c350
-rw-r--r--Utilities/cmcurl/lib/url.h18
-rw-r--r--Utilities/cmcurl/lib/urlapi-int.h2
-rw-r--r--Utilities/cmcurl/lib/urlapi.c158
-rw-r--r--Utilities/cmcurl/lib/urldata.h205
-rw-r--r--Utilities/cmcurl/lib/vauth/cleartext.c5
-rw-r--r--Utilities/cmcurl/lib/vauth/cram.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest.h2
-rw-r--r--Utilities/cmcurl/lib/vauth/digest_sspi.c4
-rw-r--r--Utilities/cmcurl/lib/vauth/gsasl.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_gssapi.c4
-rw-r--r--Utilities/cmcurl/lib/vauth/krb5_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm.h2
-rw-r--r--Utilities/cmcurl/lib/vauth/ntlm_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/oauth2.c5
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_gssapi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/spnego_sspi.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.c2
-rw-r--r--Utilities/cmcurl/lib/vauth/vauth.h2
-rw-r--r--Utilities/cmcurl/lib/version.c19
-rw-r--r--Utilities/cmcurl/lib/version_win32.c2
-rw-r--r--Utilities/cmcurl/lib/version_win32.h2
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_msh3.c850
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_msh3.h (renamed from Utilities/cmcurl/lib/vquic/msh3.h)22
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_ngtcp2.c2550
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_ngtcp2.h (renamed from Utilities/cmcurl/lib/wildcard.c)70
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_quiche.c1464
-rw-r--r--Utilities/cmcurl/lib/vquic/curl_quiche.h (renamed from Utilities/cmcurl/lib/vquic/quiche.h)42
-rw-r--r--Utilities/cmcurl/lib/vquic/msh3.c527
-rw-r--r--Utilities/cmcurl/lib/vquic/ngtcp2.c2266
-rw-r--r--Utilities/cmcurl/lib/vquic/quiche.c892
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic.c321
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic.h32
-rw-r--r--Utilities/cmcurl/lib/vquic/vquic_int.h (renamed from Utilities/cmcurl/lib/vquic/ngtcp2.h)81
-rw-r--r--Utilities/cmcurl/lib/vssh/libssh.c56
-rw-r--r--Utilities/cmcurl/lib/vssh/libssh2.c34
-rw-r--r--Utilities/cmcurl/lib/vssh/ssh.h6
-rw-r--r--Utilities/cmcurl/lib/vssh/wolfssh.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/bearssl.c59
-rw-r--r--Utilities/cmcurl/lib/vtls/bearssl.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/gskit.c14
-rw-r--r--Utilities/cmcurl/lib/vtls/gskit.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/gtls.c85
-rw-r--r--Utilities/cmcurl/lib/vtls/gtls.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/hostcheck.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/hostcheck.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/keylog.c2
-rw-r--r--Utilities/cmcurl/lib/vtls/keylog.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls.c64
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c4
-rw-r--r--Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/nss.c102
-rw-r--r--Utilities/cmcurl/lib/vtls/nssg.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.c846
-rw-r--r--Utilities/cmcurl/lib/vtls/openssl.h11
-rw-r--r--Utilities/cmcurl/lib/vtls/rustls.c105
-rw-r--r--Utilities/cmcurl/lib/vtls/rustls.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.c208
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel.h8
-rw-r--r--Utilities/cmcurl/lib/vtls/schannel_verify.c6
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.c199
-rw-r--r--Utilities/cmcurl/lib/vtls/sectransp.h4
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.c504
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls.h59
-rw-r--r--Utilities/cmcurl/lib/vtls/vtls_int.h18
-rw-r--r--Utilities/cmcurl/lib/vtls/wolfssl.c112
-rw-r--r--Utilities/cmcurl/lib/vtls/wolfssl.h2
-rw-r--r--Utilities/cmcurl/lib/vtls/x509asn1.c29
-rw-r--r--Utilities/cmcurl/lib/vtls/x509asn1.h2
-rw-r--r--Utilities/cmcurl/lib/warnless.c2
-rw-r--r--Utilities/cmcurl/lib/warnless.h2
-rw-r--r--Utilities/cmcurl/lib/wildcard.h70
-rw-r--r--Utilities/cmcurl/lib/ws.c143
-rw-r--r--Utilities/cmcurl/lib/ws.h18
-rwxr-xr-xbootstrap3
-rw-r--r--doxygen.config697
1959 files changed, 37464 insertions, 21685 deletions
diff --git a/.codespellrc b/.codespellrc
index a8be48762d..0abd94e8e2 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -1,7 +1,6 @@
[codespell]
check-filenames =
check-hidden =
-count =
# Disable warnings about binary files
quiet-level = 2
builtin = clear,rare,en-GB_to_en-US
diff --git a/.gitignore b/.gitignore
index f57271f427..3b9e52a48c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,27 +1,30 @@
+/CMakeUserPresets.json
+
# Common build directories
-build*/
+/build*/
-# Exclude MacOS Finder files.
+# MacOS Finder files.
.DS_Store
-*.user*
-
+# Python compile output.
*.pyc
-Help/_generated
-Testing
-CMakeUserPresets.json
+# See Utilities/Sphinx/tutorial_archive.cmake
+/Help/_generated
-# Visual Studio work directory
-.vs/
-# Visual Studio build directory
-out/
+# CLion work directory
+/.idea/
+# CLion build directories
+/cmake-build-*/
+
+# QtCreator files.
+/CMakeLists.txt.user*
# Visual Studio Code
-.vscode/
-.cache/
+/.vscode/
+/.cache/
-# CLion work directory
-.idea/
-# CLion build directories
-cmake-build-*/
+# Visual Studio work directory
+/.vs/
+# Visual Studio build directory
+/out/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 768a90298e..b26172d360 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -114,6 +114,7 @@ l:sphinx-fedora37:
extends:
- .fedora37_sphinx
- .cmake_build_linux
+ - .cmake_sphinx_artifacts
- .linux_x86_64_tags
- .run_automatically
variables:
@@ -909,6 +910,34 @@ t:macos-arm64-xcode:
variables:
CMAKE_CI_NO_MR: "true"
+t:macos-x86_64-ninja-ub:
+ extends:
+ - .macos_x86_64_ninja_ub
+ - .cmake_test_macos_external
+ - .macos_x86_64_tags_ext
+ - .cmake_junit_artifacts
+ - .run_dependent
+ dependencies:
+ - t:macos-x86_64-ninja
+ needs:
+ - t:macos-x86_64-ninja
+ variables:
+ CMAKE_CI_JOB_NIGHTLY: "true"
+
+t:macos-x86_64-xcode-ub:
+ extends:
+ - .macos_x86_64_xcode_ub
+ - .cmake_test_macos_external
+ - .macos_x86_64_tags_ext
+ - .cmake_junit_artifacts
+ - .run_dependent
+ dependencies:
+ - t:macos-x86_64-ninja
+ needs:
+ - t:macos-x86_64-ninja
+ variables:
+ CMAKE_CI_JOB_NIGHTLY: "true"
+
b:macos-package:
extends:
- .macos_package
@@ -1022,7 +1051,7 @@ t:windows-vs2019-x64:
t:windows-vs2022-x64-nmake:
extends:
- .windows_vs2022_x64_nmake
- - .cmake_test_windows_nmake
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent_vs2022
- .cmake_junit_artifacts
- .run_dependent
@@ -1036,7 +1065,7 @@ t:windows-vs2022-x64-nmake:
t:windows-vs2022-x64-jom:
extends:
- .windows_vs2022_x64_jom
- - .cmake_test_windows_jom
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent_vs2022
- .cmake_junit_artifacts
- .run_dependent
@@ -1050,7 +1079,7 @@ t:windows-vs2022-x64-jom:
t:windows-borland5.5:
extends:
- .windows_borland5.5
- - .cmake_test_windows_borland
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent
- .cmake_junit_artifacts
- .run_dependent
@@ -1064,7 +1093,7 @@ t:windows-borland5.5:
t:windows-borland5.8:
extends:
- .windows_borland5.8
- - .cmake_test_windows_borland
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent
- .cmake_junit_artifacts
- .run_dependent
@@ -1075,7 +1104,7 @@ t:windows-borland5.8:
variables:
CMAKE_CI_JOB_NIGHTLY: "true"
-t:windows-clang15.0-cl-ninja:
+t:windows-clang16.0-cl-ninja:
extends:
- .windows_clang_ninja
- .cmake_test_windows_external
@@ -1087,10 +1116,10 @@ t:windows-clang15.0-cl-ninja:
needs:
- t:windows-vs2022-x64-ninja
variables:
- CMAKE_CI_BUILD_NAME: windows_clang15.0_cl_ninja
+ CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_ninja
CMAKE_CI_JOB_NIGHTLY: "true"
-t:windows-clang15.0-cl-nmake:
+t:windows-clang16.0-cl-nmake:
extends:
- .windows_clang_nmake
- .cmake_test_windows_external
@@ -1102,10 +1131,10 @@ t:windows-clang15.0-cl-nmake:
needs:
- t:windows-vs2022-x64-ninja
variables:
- CMAKE_CI_BUILD_NAME: windows_clang15.0_cl_nmake
+ CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_nmake
CMAKE_CI_JOB_NIGHTLY: "true"
-t:windows-clang15.0-gnu-ninja:
+t:windows-clang16.0-gnu-ninja:
extends:
- .windows_clang_ninja
- .cmake_test_windows_external
@@ -1117,10 +1146,10 @@ t:windows-clang15.0-gnu-ninja:
needs:
- t:windows-vs2022-x64-ninja
variables:
- CMAKE_CI_BUILD_NAME: windows_clang15.0_gnu_ninja
+ CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_ninja
CMAKE_CI_JOB_NIGHTLY: "true"
-t:windows-clang15.0-gnu-nmake:
+t:windows-clang16.0-gnu-nmake:
extends:
- .windows_clang_nmake
- .cmake_test_windows_external
@@ -1132,7 +1161,7 @@ t:windows-clang15.0-gnu-nmake:
needs:
- t:windows-vs2022-x64-ninja
variables:
- CMAKE_CI_BUILD_NAME: windows_clang15.0_gnu_nmake
+ CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_nmake
CMAKE_CI_JOB_NIGHTLY: "true"
t:mingw_osdn_io-mingw_makefiles:
@@ -1166,7 +1195,7 @@ t:mingw_osdn_io-msys_makefiles:
t:windows-msvc-v71-nmake:
extends:
- .windows_msvc_v71_nmake
- - .cmake_test_windows_msvc
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent
- .cmake_junit_artifacts
- .run_dependent
@@ -1180,7 +1209,7 @@ t:windows-msvc-v71-nmake:
t:windows-openwatcom1.9:
extends:
- .windows_openwatcom1.9
- - .cmake_test_windows_openwatcom
+ - .cmake_test_windows_external
- .windows_x86_64_tags_concurrent
- .cmake_junit_artifacts
- .run_dependent
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 41f24ede94..6c4cc0df75 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -91,6 +91,15 @@
junit:
- build/junit.xml
+.cmake_sphinx_artifacts:
+ artifacts:
+ expire_in: 1d
+ when: always
+ paths:
+ # Take the sphinx logs.
+ - build/build-*.log
+ - build/linkcheck/output.*
+
.cmake_test_artifacts:
artifacts:
expire_in: 1d
diff --git a/.gitlab/ci/borland-env.ps1 b/.gitlab/ci/borland-env.ps1
new file mode 100755
index 0000000000..b3b532e37c
--- /dev/null
+++ b/.gitlab/ci/borland-env.ps1
@@ -0,0 +1,3 @@
+Invoke-Expression -Command .gitlab/ci/borland.ps1
+$pwdpath = $pwd.Path
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\bcc\bin;$env:PATH"
diff --git a/.gitlab/ci/clang.ps1 b/.gitlab/ci/clang.ps1
index 29db93dd98..1fc8d8e1e5 100755
--- a/.gitlab/ci/clang.ps1
+++ b/.gitlab/ci/clang.ps1
@@ -1,10 +1,10 @@
$erroractionpreference = "stop"
-if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang15.0")) {
- # LLVM/Clang 15.0
- # https://github.com/llvm/llvm-project/releases/tag/llvmorg-15.0.4
- $filename = "llvm-15.0.4-win-x86_64-1"
- $sha256sum = "9AA305084C20C27972E103E7B18AAC3F755E0534542AF62FC2F2BF5DDD3C4E1F"
+if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang16.0")) {
+ # LLVM/Clang 16.0
+ # https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0
+ $filename = "llvm-16.0.0-win-x86_64-1"
+ $sha256sum = "13F48356BA5892A82E8BB25EB283FDDAA8F23A0F209B6BF6525D2C5E1285B950"
} else {
throw ('unknown CMAKE_CI_BUILD_NAME: ' + "$env:CMAKE_CI_BUILD_NAME")
}
diff --git a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
index 74079592fa..dff0db1568 100644
--- a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
+++ b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
@@ -25,6 +25,22 @@ set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") # h5c++.mpich does not exist
+# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") # h5c++.openmpi does not exist
+# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
set(CMake_TEST_FindICU "ON" CACHE BOOL "")
set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
@@ -65,6 +81,7 @@ set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
+set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_debian10_ninja.cmake b/.gitlab/ci/configure_debian10_ninja.cmake
index e8d6d55cf6..211a2a74d9 100644
--- a/.gitlab/ci/configure_debian10_ninja.cmake
+++ b/.gitlab/ci/configure_debian10_ninja.cmake
@@ -29,6 +29,22 @@ set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") # h5c++.mpich does not exist
+# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") # h5c++.openmpi does not exist
+# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
set(CMake_TEST_FindICU "ON" CACHE BOOL "")
set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
@@ -71,6 +87,7 @@ set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "")
set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
+set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_fedora37_makefiles.cmake b/.gitlab/ci/configure_fedora37_makefiles.cmake
index 725cc46e52..c2f99822f1 100644
--- a/.gitlab/ci/configure_fedora37_makefiles.cmake
+++ b/.gitlab/ci/configure_fedora37_makefiles.cmake
@@ -28,6 +28,18 @@ set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/lib64/mpich/bin/h5pcc" CACHE FILEPATH "")
+# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/lib64/mpich/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist
+set(CMake_TEST_FindHDF5_MPICH_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib" CACHE STRING "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/lib64/mpich/bin/h5pfc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/lib64/openmpi/bin/h5pcc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_ENVMOD "PATH=path_list_prepend:/usr/lib64/openmpi/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/openmpi/lib" CACHE STRING "")
+# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/lib64/openmpi/bin/h5pc++" CACHE FILEPATH "") # h5pc++ does not exist
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/lib64/openmpi/bin/h5pfc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
set(CMake_TEST_FindICU "ON" CACHE BOOL "")
set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
@@ -45,6 +57,7 @@ set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "")
set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "")
set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "")
set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_ENVMOD "PATH=path_list_prepend:/usr/lib64/mpich/bin;LD_LIBRARY_PATH=path_list_prepend:/usr/lib64/mpich/lib" CACHE STRING "")
set(CMake_TEST_FindMPI "ON" CACHE BOOL "")
set(CMake_TEST_FindODBC "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "")
@@ -69,6 +82,7 @@ set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "")
set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
+set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja.cmake b/.gitlab/ci/configure_macos_arm64_ninja.cmake
index f59b43c7f2..f2068a12b8 100644
--- a/.gitlab/ci/configure_macos_arm64_ninja.cmake
+++ b/.gitlab/ci/configure_macos_arm64_ninja.cmake
@@ -1,3 +1,4 @@
+set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
index 3c5d8fed2d..5d1620d85b 100644
--- a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
+++ b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
@@ -1,3 +1,4 @@
+set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_macos_x86_64_ninja.cmake b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
index 3c5d8fed2d..5d1620d85b 100644
--- a/.gitlab/ci/configure_macos_x86_64_ninja.cmake
+++ b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
@@ -1,3 +1,4 @@
+set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake b/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake
new file mode 100644
index 0000000000..1b976d261a
--- /dev/null
+++ b/.gitlab/ci/configure_macos_x86_64_ninja_ub.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake b/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake
new file mode 100644
index 0000000000..1b976d261a
--- /dev/null
+++ b/.gitlab/ci/configure_macos_x86_64_xcode_ub.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_sphinx.cmake b/.gitlab/ci/configure_sphinx.cmake
index 3750309ae2..9f3f0bea7c 100644
--- a/.gitlab/ci/configure_sphinx.cmake
+++ b/.gitlab/ci/configure_sphinx.cmake
@@ -4,3 +4,6 @@ set(SPHINX_HTML ON CACHE BOOL "")
set(SPHINX_SINGLEHTML ON CACHE BOOL "")
set(SPHINX_QTHELP ON CACHE BOOL "")
set(SPHINX_TEXT ON CACHE BOOL "")
+if(NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+ set(SPHINX_LINKCHECK ON CACHE BOOL "")
+endif()
diff --git a/.gitlab/ci/configure_windows_clang_ninja.cmake b/.gitlab/ci/configure_windows_clang_ninja.cmake
index ba19834a88..a66e30230e 100644
--- a/.gitlab/ci/configure_windows_clang_ninja.cmake
+++ b/.gitlab/ci/configure_windows_clang_ninja.cmake
@@ -1 +1,5 @@
+if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)gnu(_|$)")
+ set(CMake_TEST_MODULE_COMPILATION "named,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "")
+ set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "")
+endif()
include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_clang_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs_common.cmake b/.gitlab/ci/configure_windows_vs_common.cmake
index 962f03db9f..daf9aaad3e 100644
--- a/.gitlab/ci/configure_windows_vs_common.cmake
+++ b/.gitlab/ci/configure_windows_vs_common.cmake
@@ -4,6 +4,7 @@ set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_Fortran "OFF" CACHE BOOL "")
+set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
set(CMake_TEST_Java OFF CACHE BOOL "")
set(CMake_TEST_MFC "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_windows_vs_common_ninja.cmake b/.gitlab/ci/configure_windows_vs_common_ninja.cmake
index 1ae1a66bd4..9f7acc3246 100644
--- a/.gitlab/ci/configure_windows_vs_common_ninja.cmake
+++ b/.gitlab/ci/configure_windows_vs_common_ninja.cmake
@@ -6,6 +6,7 @@ set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
set(CMake_TEST_FindOpenMP_Fortran "OFF" CACHE BOOL "")
+set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
set(CMake_TEST_MFC "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst b/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst
index 5e30e160a2..ca833230bc 100644
--- a/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst
+++ b/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst
@@ -59,6 +59,9 @@ libgrpc++-dev libgrpc-dev
libgsl-dev
libgtest-dev
libgtk2.0-dev
+libhdf5-dev
+libhdf5-mpich-dev
+libhdf5-openmpi-dev
libicu-dev
libinput-dev
libjpeg-dev
@@ -76,6 +79,7 @@ libsdl-dev
libsqlite3-dev
libtiff-dev
libuv1-dev
+libwxgtk3.0-dev
libx11-dev
libxalan-c-dev
libxerces-c-dev
diff --git a/.gitlab/ci/docker/debian10/deps_packages.lst b/.gitlab/ci/docker/debian10/deps_packages.lst
index 3df41f5bc5..0b79675846 100644
--- a/.gitlab/ci/docker/debian10/deps_packages.lst
+++ b/.gitlab/ci/docker/debian10/deps_packages.lst
@@ -62,6 +62,9 @@ libgrpc++-dev libgrpc-dev
libgsl-dev
libgtest-dev
libgtk2.0-dev
+libhdf5-dev
+libhdf5-mpich-dev
+libhdf5-openmpi-dev
libicu-dev
libinput-dev
libjpeg-dev
@@ -79,6 +82,7 @@ libsdl-dev
libsqlite3-dev
libtiff-dev
libuv1-dev
+libwxgtk3.0-dev
libx11-dev
libxalan-c-dev
libxerces-c-dev
diff --git a/.gitlab/ci/docker/fedora37/deps_packages.lst b/.gitlab/ci/docker/fedora37/deps_packages.lst
index 9ce80075ec..51f84ffe5b 100644
--- a/.gitlab/ci/docker/fedora37/deps_packages.lst
+++ b/.gitlab/ci/docker/fedora37/deps_packages.lst
@@ -73,6 +73,9 @@ grpc-devel grpc-plugins
gsl-devel
gtest-devel
gtk2-devel
+hdf5-devel
+hdf5-mpich-devel
+hdf5-openmpi-devel
ImageMagick-c++-devel
java-11-openjdk-devel
jsoncpp-devel
@@ -105,6 +108,7 @@ SDL-devel
sqlite-devel
swig
unixODBC-devel
+wxGTK-devel
xalan-c-devel
xerces-c-devel
xz-devel
diff --git a/.gitlab/ci/download_python3.cmake b/.gitlab/ci/download_python3.cmake
deleted file mode 100644
index 0f5b18b4ff..0000000000
--- a/.gitlab/ci/download_python3.cmake
+++ /dev/null
@@ -1,41 +0,0 @@
-cmake_minimum_required(VERSION 3.17)
-
-set(version "3.8.6")
-set(sha256sum "376e18eef7e3ea467f0e3af041b01fc7e2f12855506c2ab2653ceb5e0951212e")
-set(dirname "python-${version}-embed-win-x86_64")
-set(tarball "${dirname}.tar.xz")
-
-# Download the file.
-file(DOWNLOAD
- "https://cmake.org/files/dependencies/${tarball}"
- ".gitlab/${tarball}"
- STATUS download_status
- EXPECTED_HASH "SHA256=${sha256sum}")
-
-# Check the download status.
-list(GET download_status 0 res)
-if (res)
- list(GET download_status 1 err)
- message(FATAL_ERROR
- "Failed to download ${tarball}: ${err}")
-endif ()
-
-# Extract the file.
-execute_process(
- COMMAND
- "${CMAKE_COMMAND}"
- -E tar
- xzf "${tarball}"
- WORKING_DIRECTORY ".gitlab"
- RESULT_VARIABLE res
- ERROR_VARIABLE err
- ERROR_STRIP_TRAILING_WHITESPACE)
-if (res)
- message(FATAL_ERROR
- "Failed to extract ${tarball}: ${err}")
-endif ()
-
-# Move to a predictable directory.
-file(RENAME
- ".gitlab/${dirname}"
- ".gitlab/python3")
diff --git a/.gitlab/ci/env_fedora37_makefiles.cmake b/.gitlab/ci/env_fedora37_makefiles.cmake
index 2bcb6d04cc..ef13cda7b5 100644
--- a/.gitlab/ci/env_fedora37_makefiles.cmake
+++ b/.gitlab/ci/env_fedora37_makefiles.cmake
@@ -1,2 +1 @@
set(ENV{MY_RUBY_HOME} "/usr/local/rvm/rubies/ruby-3.0.4")
-set(ENV{PATH} "/usr/lib64/mpich/bin:$ENV{PATH}")
diff --git a/.gitlab/ci/env_fedora37_makefiles.sh b/.gitlab/ci/env_fedora37_makefiles.sh
index 217ff305df..c482642e69 100644
--- a/.gitlab/ci/env_fedora37_makefiles.sh
+++ b/.gitlab/ci/env_fedora37_makefiles.sh
@@ -1,3 +1,8 @@
if test "$CMAKE_CI_NIGHTLY" = "true"; then
source .gitlab/ci/ispc-env.sh
fi
+
+# Patch HDF5 Fortran compiler wrappers to work around Fedora bug.
+# https://bugzilla.redhat.com/show_bug.cgi?id=2183289
+sed -i '/^includedir=/ s|/mpich-x86_64||' /usr/lib64/mpich/bin/h5pfc
+sed -i '/^includedir=/ s|/openmpi-x86_64||' /usr/lib64/openmpi/bin/h5pfc
diff --git a/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake b/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake
new file mode 100644
index 0000000000..4b5c401ef9
--- /dev/null
+++ b/.gitlab/ci/env_macos_x86_64_ninja_ub.cmake
@@ -0,0 +1 @@
+set(ENV{CMAKE_OSX_ARCHITECTURES} "x86_64;arm64")
diff --git a/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake b/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake
new file mode 100644
index 0000000000..4b5c401ef9
--- /dev/null
+++ b/.gitlab/ci/env_macos_x86_64_xcode_ub.cmake
@@ -0,0 +1 @@
+set(ENV{CMAKE_OSX_ARCHITECTURES} "x86_64;arm64")
diff --git a/.gitlab/ci/env_windows_borland5.5.ps1 b/.gitlab/ci/env_windows_borland5.5.ps1
new file mode 100755
index 0000000000..0d2e46b67f
--- /dev/null
+++ b/.gitlab/ci/env_windows_borland5.5.ps1
@@ -0,0 +1 @@
+. .gitlab/ci/borland-env.ps1
diff --git a/.gitlab/ci/env_windows_borland5.8.ps1 b/.gitlab/ci/env_windows_borland5.8.ps1
new file mode 100755
index 0000000000..0d2e46b67f
--- /dev/null
+++ b/.gitlab/ci/env_windows_borland5.8.ps1
@@ -0,0 +1 @@
+. .gitlab/ci/borland-env.ps1
diff --git a/.gitlab/ci/env_windows_msvc_v71_nmake.ps1 b/.gitlab/ci/env_windows_msvc_v71_nmake.ps1
new file mode 100755
index 0000000000..cb3806d1ca
--- /dev/null
+++ b/.gitlab/ci/env_windows_msvc_v71_nmake.ps1
@@ -0,0 +1,2 @@
+Invoke-Expression -Command .gitlab/ci/msvc.ps1
+Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
diff --git a/.gitlab/ci/env_windows_openwatcom1.9.ps1 b/.gitlab/ci/env_windows_openwatcom1.9.ps1
new file mode 100755
index 0000000000..49c28f7a20
--- /dev/null
+++ b/.gitlab/ci/env_windows_openwatcom1.9.ps1
@@ -0,0 +1 @@
+. .gitlab/ci/openwatcom-env.ps1
diff --git a/.gitlab/ci/env_windows_vs2022_x64_jom.ps1 b/.gitlab/ci/env_windows_vs2022_x64_jom.ps1
new file mode 100755
index 0000000000..c933421d3a
--- /dev/null
+++ b/.gitlab/ci/env_windows_vs2022_x64_jom.ps1
@@ -0,0 +1,4 @@
+Invoke-Expression -Command .gitlab/ci/jom.ps1
+$pwdpath = $pwd.Path
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\jom;$env:PATH"
+Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
diff --git a/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1 b/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1
new file mode 100755
index 0000000000..62463cd98c
--- /dev/null
+++ b/.gitlab/ci/env_windows_vs2022_x64_nmake.ps1
@@ -0,0 +1 @@
+Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
diff --git a/.gitlab/ci/openwatcom-env.ps1 b/.gitlab/ci/openwatcom-env.ps1
new file mode 100755
index 0000000000..14ea523028
--- /dev/null
+++ b/.gitlab/ci/openwatcom-env.ps1
@@ -0,0 +1,7 @@
+Invoke-Expression -Command .gitlab/ci/openwatcom.ps1
+$pwdpath = $pwd.Path
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\watcom\binnt;$pwdpath\.gitlab\watcom\binw;$env:PATH"
+Set-Item -Force -Path "env:INCLUDE" -Value "$pwdpath\.gitlab\watcom\h;$pwdpath\.gitlab\watcom\h\nt"
+Set-Item -Force -Path "env:EDPATH" -Value "$pwdpath\.gitlab\watcom\eddat"
+Set-Item -Force -Path "env:WATCOM" -Value "$pwdpath\.gitlab\watcom"
+Set-Item -Force -Path "env:WLINKTMP" -Value "."
diff --git a/.gitlab/ci/python-env.ps1 b/.gitlab/ci/python-env.ps1
index 4e897d8dc6..ce16493e61 100755
--- a/.gitlab/ci/python-env.ps1
+++ b/.gitlab/ci/python-env.ps1
@@ -1,4 +1,4 @@
$pwdpath = $pwd.Path
-cmake -P .gitlab/ci/download_python3.cmake
+& "$pwsh" -File ".gitlab/ci/python.ps1"
Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\python3;$env:PATH"
python --version
diff --git a/.gitlab/ci/python.ps1 b/.gitlab/ci/python.ps1
new file mode 100755
index 0000000000..27f18071a6
--- /dev/null
+++ b/.gitlab/ci/python.ps1
@@ -0,0 +1,30 @@
+$erroractionpreference = "stop"
+
+$version = "3.11.3"
+
+if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") {
+ $sha256sum = "7419B2E98516FBD0B66A1237B80187FFB21D32E47B4A4235C2D9D6379597070F"
+ $arch = "amd64"
+} elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") {
+ $sha256sum = "03BAD6A7C898FC8F693982437AAB6DB698107B82EA93F76424195AE2C161246C"
+ $arch = "arm64"
+} else {
+ throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
+}
+
+$filename = "python-$version-embed-$arch"
+$tarball = "$filename.zip"
+
+$outdir = $pwd.Path
+$outdir = "$outdir\.gitlab"
+$ProgressPreference = 'SilentlyContinue'
+Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$tarball" -OutFile "$outdir\$tarball"
+$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256
+if ($hash.Hash -ne $sha256sum) {
+ exit 1
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir\python3")
+Remove-Item "$outdir\python3\*._pth" # Avoid sys.path specific to embedded python.
+Remove-Item "$outdir\$tarball"
diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md
new file mode 100644
index 0000000000..3b083d2c32
--- /dev/null
+++ b/.gitlab/issue_templates/Default.md
@@ -0,0 +1,9 @@
+<!--
+This issue tracker is for CMake upstream development:
+
+* If you are having trouble building a specific third-party project
+ that uses CMake, ask for help in that project's forums first.
+
+* If you have a coding or usage question, please ask for help
+ on the CMake discourse forums: https://discourse.cmake.org/
+-->
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 9a5340162b..3d62b01ea9 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -45,7 +45,7 @@
### Debian
.debian10:
- image: "kitware/cmake:ci-debian10-x86_64-2023-02-07"
+ image: "kitware/cmake:ci-debian10-x86_64-2023-03-29"
variables:
GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -60,7 +60,7 @@
CMAKE_CI_NO_INSTALL: 1
.debian10_aarch64:
- image: "kitware/cmake:ci-debian10-aarch64-2023-02-07"
+ image: "kitware/cmake:ci-debian10-aarch64-2023-03-29"
variables:
GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -69,7 +69,7 @@
### Fedora
.fedora37:
- image: "kitware/cmake:ci-fedora37-x86_64-2023-02-07"
+ image: "kitware/cmake:ci-fedora37-x86_64-2023-03-29"
variables:
GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes"
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 652a67abb0..6f0bea58e2 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -80,6 +80,14 @@
CMAKE_GENERATOR: Xcode
CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+.macos_x86_64_xcode_ub:
+ extends: .macos
+
+ variables:
+ CMAKE_CONFIGURATION: macos_x86_64_xcode_ub
+ CMAKE_GENERATOR: Xcode
+ CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+
.macos_x86_64_ninja_multi:
extends: .macos
@@ -88,6 +96,13 @@
CMAKE_GENERATOR: "Ninja Multi-Config"
CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+.macos_x86_64_ninja_ub:
+ extends: .macos
+
+ variables:
+ CMAKE_CONFIGURATION: macos_x86_64_ninja_ub
+ CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+
## Tags
.macos_x86_64_tags:
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml
index ded3e659ff..f1e86ad3e5 100644
--- a/.gitlab/os-windows.yml
+++ b/.gitlab/os-windows.yml
@@ -349,6 +349,10 @@
- . .gitlab/ci/qt-env.ps1
- . .gitlab/ci/python-env.ps1
+.before_script_windows_external: &before_script_windows_external
+ - . .gitlab/ci/env.ps1
+ - . .gitlab/ci/python-env.ps1
+
.cmake_build_windows:
stage: build
@@ -381,69 +385,7 @@
stage: test-ext
script:
- - . .gitlab/ci/env.ps1
- - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
-
- interruptible: true
-
-.cmake_test_windows_nmake:
- stage: test-ext
-
- script:
- - . .gitlab/ci/env.ps1
- - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
- - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
-
- interruptible: true
-
-.cmake_test_windows_jom:
- stage: test-ext
-
- script:
- - . .gitlab/ci/env.ps1
- - Invoke-Expression -Command .gitlab/ci/jom.ps1
- - $pwdpath = $pwd.Path
- - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\jom;$env:PATH"
- - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
- - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
-
- interruptible: true
-
-.cmake_test_windows_borland:
- stage: test-ext
-
- script:
- - . .gitlab/ci/env.ps1
- - Invoke-Expression -Command .gitlab/ci/borland.ps1
- - $pwdpath = $pwd.Path
- - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\bcc\bin;$env:PATH"
- - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
-
- interruptible: true
-
-.cmake_test_windows_msvc:
- stage: test-ext
-
- script:
- - . .gitlab/ci/env.ps1
- - Invoke-Expression -Command .gitlab/ci/msvc.ps1
- - Invoke-Expression -Command .gitlab/ci/vcvarsall.ps1
- - build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
-
- interruptible: true
-
-.cmake_test_windows_openwatcom:
- stage: test-ext
-
- script:
- - . .gitlab/ci/env.ps1
- - Invoke-Expression -Command .gitlab/ci/openwatcom.ps1
- - $pwdpath = $pwd.Path
- - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\watcom\binnt;$pwdpath\.gitlab\watcom\binw;$env:PATH"
- - Set-Item -Force -Path "env:INCLUDE" -Value "$pwdpath\.gitlab\watcom\h;$pwdpath\.gitlab\watcom\h\nt"
- - Set-Item -Force -Path "env:EDPATH" -Value "$pwdpath\.gitlab\watcom\eddat"
- - Set-Item -Force -Path "env:WATCOM" -Value "$pwdpath\.gitlab\watcom"
- - Set-Item -Force -Path "env:WLINKTMP" -Value "."
+ - *before_script_windows_external
- build/install/bin/ctest --output-on-failure -V -S .gitlab/ci/ctest_standalone.cmake
interruptible: true
diff --git a/Auxiliary/cmake-mode.el b/Auxiliary/cmake-mode.el
index a11becbe20..7590ee3c1b 100644
--- a/Auxiliary/cmake-mode.el
+++ b/Auxiliary/cmake-mode.el
@@ -372,7 +372,7 @@ optional argument topic will be appended to the argument list."
(interactive "s")
(let* ((bufname (if buffer buffer (concat "*CMake" type (if topic "-") topic "*")))
(buffer (if (get-buffer bufname) (get-buffer bufname) (generate-new-buffer bufname)))
- (command (concat cmake-mode-cmake-executable " " type " " topic))
+ (command (concat cmake-mode-cmake-executable " " type " " (shell-quote-argument topic)))
;; Turn of resizing of mini-windows for shell-command.
(resize-mini-windows nil)
)
@@ -391,7 +391,7 @@ optional argument topic will be appended to the argument list."
(interactive "s")
(let* ((bufname (if buffer buffer (concat "*CMake" type (if topic "-") topic "*")))
(buffer (if (get-buffer bufname) (get-buffer bufname) (generate-new-buffer bufname)))
- (command (concat cmake-mode-cmake-executable " " type " " topic))
+ (command (concat cmake-mode-cmake-executable " " type " " (shell-quote-argument topic)))
;; Turn of resizing of mini-windows for shell-command.
(resize-mini-windows nil)
)
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index 5e936c29db..d6b5b1946d 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -128,7 +128,10 @@ syn keyword cmakeProperty contained
\ CPACK_WIX_ACL
\ CROSSCOMPILING_EMULATOR
\ CUDA_ARCHITECTURES
+ \ CUDA_CUBIN_COMPILATION
\ CUDA_EXTENSIONS
+ \ CUDA_FATBIN_COMPILATION
+ \ CUDA_OPTIX_COMPILATION
\ CUDA_PTX_COMPILATION
\ CUDA_RESOLVE_DEVICE_SYMBOLS
\ CUDA_RUNTIME_LIBRARY
@@ -217,6 +220,7 @@ syn keyword cmakeProperty contained
\ INSTALL_RPATH
\ INSTALL_RPATH_USE_LINK_PATH
\ INTERFACE_AUTOUIC_OPTIONS
+ \ INTERFACE_AUTOMOC_MACRO_NAMES
\ INTERFACE_COMPILE_DEFINITIONS
\ INTERFACE_COMPILE_FEATURES
\ INTERFACE_COMPILE_OPTIONS
@@ -459,6 +463,7 @@ syn keyword cmakeVariable contained
\ BUILD_SHARED_LIBS
\ CACHE
\ CMAKE_ABSOLUTE_DESTINATION_FILES
+ \ CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
\ CMAKE_AIX_EXPORT_ALL_SYMBOLS
\ CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS
\ CMAKE_ANDROID_API
@@ -686,11 +691,14 @@ syn keyword cmakeVariable contained
\ CMAKE_AUTOMOC_MOC_OPTIONS
\ CMAKE_AUTOMOC_PATH_PREFIX
\ CMAKE_AUTOMOC_RELAXED_MODE
+ \ CMAKE_AUTOMOC_EXECUTABLE
\ CMAKE_AUTORCC
\ CMAKE_AUTORCC_OPTIONS
+ \ CMAKE_AUTORCC_EXECUTABLE
\ CMAKE_AUTOUIC
\ CMAKE_AUTOUIC_OPTIONS
\ CMAKE_AUTOUIC_SEARCH_PATHS
+ \ CMAKE_AUTOUIC_EXECUTABLE
\ CMAKE_BACKWARDS_COMPATIBILITY
\ CMAKE_BINARY_DIR
\ CMAKE_BUILD_RPATH
@@ -2095,6 +2103,7 @@ syn keyword cmakeKWadd_custom_command contained
\ COMMENT
\ CROSSCOMPILING_EMULATOR
\ DEPENDS
+ \ DEPENDS_EXPLICIT_ONLY
\ DEPFILE
\ GENERATED
\ IMPLICIT_DEPENDS
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0ce42fb3aa..6322aa6022 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
-cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
diff --git a/CompileFlags.cmake b/CompileFlags.cmake
index 6331af11cb..f94e079ad7 100644
--- a/CompileFlags.cmake
+++ b/CompileFlags.cmake
@@ -8,7 +8,7 @@ if(WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "Intel")
set(_INTEL_WINDOWS 1)
endif()
-if(WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "Clang"
+if(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "^(Clang|IntelLLVM)$"
AND "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
set(_CLANG_MSVC_WINDOWS 1)
endif()
@@ -22,18 +22,19 @@ if(MSVC OR _INTEL_WINDOWS OR _CLANG_MSVC_WINDOWS)
else()
endif()
-if(MSVC)
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CMAKE_CXX_LINKER_WRAPPER_FLAG}-stack:10000000")
-endif()
-
# MSVC 14.28 enables C5105, but the Windows SDK 10.0.18362.0 triggers it.
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 19.28)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -wd5105")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd5105")
endif()
-if(_CLANG_MSVC_WINDOWS AND "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Xlinker -stack:20000000")
+# Use a stack size large enough for CMake_DEFAULT_RECURSION_LIMIT.
+if(MSVC)
+ string(APPEND CMAKE_EXE_LINKER_FLAGS " ${CMAKE_CXX_LINKER_WRAPPER_FLAG}-stack:10000000")
+elseif(MINGW OR MSYS OR CYGWIN)
+ string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--stack,10000000")
+elseif(_CLANG_MSVC_WINDOWS AND "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+ string(APPEND CMAKE_EXE_LINKER_FLAGS " -Xlinker -stack:20000000")
endif()
#silence duplicate symbol warnings on AIX
diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt
index bd55e24c7b..21236fa427 100644
--- a/Help/command/FIND_XXX.txt
+++ b/Help/command/FIND_XXX.txt
@@ -140,21 +140,40 @@ If ``NO_DEFAULT_PATH`` is not specified, the search process is as follows:
|prefix_XXX_SUBDIR| for each ``<prefix>`` in
:variable:`CMAKE_SYSTEM_PREFIX_PATH`
-1. .. versionadded:: 3.12
- If called from within a find module or any other script loaded by a call to
- :command:`find_package(<PackageName>)`, search prefixes unique to the
- current package being found. Specifically, look in the
- :variable:`<PackageName>_ROOT` CMake variable and the
- :envvar:`<PackageName>_ROOT` environment variable.
- The package root variables are maintained as a stack, so if called from
- nested find modules or config packages, root paths from the parent's find
- module or config package will be searched after paths from the current
- module or package. In other words, the search order would be
- ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``,
- ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc.
- This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
- the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
- See policy :policy:`CMP0074`.
+1. If called from within a find module or any other script loaded by a call to
+ :command:`find_package(<PackageName>)`, search prefixes unique to the
+ current package being found. See policy :policy:`CMP0074`.
+
+ .. versionadded:: 3.12
+
+ Specifically, search paths specified by the following variables, in order:
+
+ a. :variable:`<PackageName>_ROOT` CMake variable,
+ where ``<PackageName>`` is the case-preserved package name.
+
+ b. :variable:`<PACKAGENAME>_ROOT` CMake variable,
+ where ``<PACKAGENAME>`` is the upper-cased package name.
+ See policy :policy:`CMP0144`.
+
+ .. versionadded:: 3.27
+
+ c. :envvar:`<PackageName>_ROOT` environment variable,
+ where ``<PackageName>`` is the case-preserved package name.
+
+ d. :envvar:`<PACKAGENAME>_ROOT` environment variable,
+ where ``<PACKAGENAME>`` is the upper-cased package name.
+ See policy :policy:`CMP0144`.
+
+ .. versionadded:: 3.27
+
+ The package root variables are maintained as a stack, so if called from
+ nested find modules or config packages, root paths from the parent's find
+ module or config package will be searched after paths from the current
+ module or package. In other words, the search order would be
+ ``<CurrentPackage>_ROOT``, ``ENV{<CurrentPackage>_ROOT}``,
+ ``<ParentPackage>_ROOT``, ``ENV{<ParentPackage>_ROOT}``, etc.
+ This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
+ the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
* |FIND_PACKAGE_ROOT_PREFIX_PATH_XXX|
diff --git a/Help/command/UNSET_NOTE.txt b/Help/command/UNSET_NOTE.txt
new file mode 100644
index 0000000000..8dc912533d
--- /dev/null
+++ b/Help/command/UNSET_NOTE.txt
@@ -0,0 +1,9 @@
+.. note::
+
+ When evaluating :ref:`Variable References` of the form ``${VAR}``, CMake
+ first searches for a normal variable with that name. If no such normal
+ variable exists, CMake will then search for a cache entry with that name.
+ Because of this, **unsetting a normal variable can expose a cache variable
+ that was previously hidden**. To force a variable reference of the form
+ ``${VAR}`` to return an empty string, use ``set(<variable> "")``, which
+ clears the normal variable but leaves it defined.
diff --git a/Help/command/add_compile_options.rst b/Help/command/add_compile_options.rst
index 0ccebc679b..869d0c2ced 100644
--- a/Help/command/add_compile_options.rst
+++ b/Help/command/add_compile_options.rst
@@ -11,6 +11,11 @@ Adds options to the :prop_dir:`COMPILE_OPTIONS` directory property.
These options are used when compiling targets from the current
directory and below.
+.. note::
+
+ These options are not used when linking.
+ See the :command:`add_link_options` command for that.
+
Arguments
^^^^^^^^^
@@ -48,5 +53,15 @@ See Also
* The command :command:`target_compile_options` adds target-specific options.
+* This command adds compile options for all languages.
+ Use the :genex:`COMPILE_LANGUAGE` generator expression to specify
+ per-language compile options.
+
* The source file property :prop_sf:`COMPILE_OPTIONS` adds options to one
source file.
+
+* :command:`add_link_options` adds options for linking.
+
+* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`
+ add language-wide flags passed to all invocations of the compiler.
+ This includes invocations that drive compiling and those that drive linking.
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index 293d3f01bc..1ccd4348ec 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -25,7 +25,8 @@ The first signature is for adding a custom command to produce an output:
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
- [COMMAND_EXPAND_LISTS])
+ [COMMAND_EXPAND_LISTS]
+ [DEPENDS_EXPLICIT_ONLY])
This defines a command to generate specified ``OUTPUT`` file(s).
A target created in the same directory (``CMakeLists.txt`` file)
@@ -357,6 +358,24 @@ The options are:
:ref:`Makefile Generators`, :ref:`Visual Studio Generators`,
and the :generator:`Xcode` generator.
+``DEPENDS_EXPLICIT_ONLY``
+
+ .. versionadded:: 3.27
+
+ Indicate that the command's ``DEPENDS`` argument represents all files
+ required by the command and implicit dependencies are not required.
+
+ Without this option, if any target uses the output of the custom command,
+ CMake will consider that target's dependencies as implicit dependencies for
+ the custom command in case this custom command requires files implicitly
+ created by those targets.
+
+ This option can be enabled on all custom commands by setting
+ :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` to ``ON``.
+
+ Only the :ref:`Ninja Generators` actually use this information to remove
+ unnecessary implicit dependencies.
+
Examples: Generating Files
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/command/add_link_options.rst b/Help/command/add_link_options.rst
index c09e106908..df72715431 100644
--- a/Help/command/add_link_options.rst
+++ b/Help/command/add_link_options.rst
@@ -38,3 +38,7 @@ See Also
* :command:`link_libraries`
* :command:`target_link_libraries`
* :command:`target_link_options`
+
+* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`
+ add language-wide flags passed to all invocations of the compiler.
+ This includes invocations that drive compiling and those that drive linking.
diff --git a/Help/command/add_test.rst b/Help/command/add_test.rst
index 53555a4b04..02dd3986aa 100644
--- a/Help/command/add_test.rst
+++ b/Help/command/add_test.rst
@@ -12,13 +12,24 @@ Add a test to the project to be run by :manual:`ctest(1)`.
Adds a test called ``<name>``. The test name may contain arbitrary
characters, expressed as a :ref:`Quoted Argument` or :ref:`Bracket Argument`
-if necessary. See policy :policy:`CMP0110`. The options are:
+if necessary. See policy :policy:`CMP0110`.
+
+CMake only generates tests if the :command:`enable_testing` command has been
+invoked. The :module:`CTest` module invokes ``enable_testing`` automatically
+unless ``BUILD_TESTING`` is set to ``OFF``.
+
+Tests added with the ``add_test(NAME)`` signature support using
+:manual:`generator expressions <cmake-generator-expressions(7)>`
+in test properties set by :command:`set_property(TEST)` or
+:command:`set_tests_properties`. Test properties may only be set in the
+directory the test is created in.
+
+``add_test`` options are:
``COMMAND``
- Specify the test command-line. If ``<command>`` specifies an
- executable target (created by :command:`add_executable`) it will
- automatically be replaced by the location of the executable created
- at build time.
+ Specify the test command-line. If ``<command>`` specifies an executable
+ target created by :command:`add_executable`, it will automatically be
+ replaced by the location of the executable created at build time.
The command may be specified using
:manual:`generator expressions <cmake-generator-expressions(7)>`.
@@ -27,38 +38,29 @@ if necessary. See policy :policy:`CMP0110`. The options are:
Restrict execution of the test only to the named configurations.
``WORKING_DIRECTORY``
- Set the :prop_test:`WORKING_DIRECTORY` test property to
- specify the working directory in which to execute the test.
- If not specified the test will be run with the current working
- directory set to the build directory corresponding to the
- current source directory.
-
- The working directory may be specified using
- :manual:`generator expressions <cmake-generator-expressions(7)>`.
+ Set the test property :prop_test:`WORKING_DIRECTORY` in which to execute the
+ test. If not specified, the test will be run in
+ :variable:`CMAKE_CURRENT_BINARY_DIR`. The working directory may be specified
+ using :manual:`generator expressions <cmake-generator-expressions(7)>`.
``COMMAND_EXPAND_LISTS``
.. versionadded:: 3.16
- Lists in ``COMMAND`` arguments will be expanded, including those
- created with
+ Lists in ``COMMAND`` arguments will be expanded, including those created with
:manual:`generator expressions <cmake-generator-expressions(7)>`.
-The given test command is expected to exit with code ``0`` to pass and
-non-zero to fail, or vice-versa if the :prop_test:`WILL_FAIL` test
-property is set. Any output written to stdout or stderr will be
-captured by :manual:`ctest(1)` but does not affect the pass/fail status
-unless the :prop_test:`PASS_REGULAR_EXPRESSION`,
-:prop_test:`FAIL_REGULAR_EXPRESSION` or
-:prop_test:`SKIP_REGULAR_EXPRESSION` test property is used.
+If the test command exits with code ``0`` the test passes. Non-zero exit code
+is a "failed" test. The test property :prop_test:`WILL_FAIL` inverts this
+logic. Note that system-level test failures such as segmentation faults or
+heap errors will still fail the test even if ``WILL_FALL`` is true. Output
+written to stdout or stderr is captured by :manual:`ctest(1)` and only
+affects the pass/fail status via the :prop_test:`PASS_REGULAR_EXPRESSION`,
+:prop_test:`FAIL_REGULAR_EXPRESSION`, or :prop_test:`SKIP_REGULAR_EXPRESSION`
+test properties.
.. versionadded:: 3.16
Added :prop_test:`SKIP_REGULAR_EXPRESSION` property.
-Tests added with the ``add_test(NAME)`` signature support using
-:manual:`generator expressions <cmake-generator-expressions(7)>`
-in test properties set by :command:`set_property(TEST)` or
-:command:`set_tests_properties`.
-
Example usage:
.. code-block:: cmake
@@ -71,16 +73,9 @@ This creates a test ``mytest`` whose command runs a ``testDriver`` tool
passing the configuration name and the full path to the executable
file produced by target ``myexe``.
-.. note::
-
- CMake will generate tests only if the :command:`enable_testing`
- command has been invoked. The :module:`CTest` module invokes the
- command automatically unless the ``BUILD_TESTING`` option is turned
- ``OFF``.
-
---------------------------------------------------------------------
-This command also supports a simpler, but less flexible, signature:
+The command syntax above is recommended over the older, less flexible form:
.. code-block:: cmake
diff --git a/Help/command/cmake_language.rst b/Help/command/cmake_language.rst
index 8801a9fdfe..707568cf79 100644
--- a/Help/command/cmake_language.rst
+++ b/Help/command/cmake_language.rst
@@ -27,163 +27,155 @@ those created via the :command:`macro` or :command:`function` commands.
Calling Commands
^^^^^^^^^^^^^^^^
-.. _CALL:
-
-.. code-block:: cmake
-
+.. signature::
cmake_language(CALL <command> [<arg>...])
-Calls the named ``<command>`` with the given arguments (if any).
-For example, the code:
+ Calls the named ``<command>`` with the given arguments (if any).
+ For example, the code:
-.. code-block:: cmake
+ .. code-block:: cmake
- set(message_command "message")
- cmake_language(CALL ${message_command} STATUS "Hello World!")
+ set(message_command "message")
+ cmake_language(CALL ${message_command} STATUS "Hello World!")
-is equivalent to
+ is equivalent to
-.. code-block:: cmake
+ .. code-block:: cmake
- message(STATUS "Hello World!")
+ message(STATUS "Hello World!")
-.. note::
- To ensure consistency of the code, the following commands are not allowed:
+ .. note::
+ To ensure consistency of the code, the following commands are not allowed:
- * ``if`` / ``elseif`` / ``else`` / ``endif``
- * ``block`` / ``endblock``
- * ``while`` / ``endwhile``
- * ``foreach`` / ``endforeach``
- * ``function`` / ``endfunction``
- * ``macro`` / ``endmacro``
+ * ``if`` / ``elseif`` / ``else`` / ``endif``
+ * ``block`` / ``endblock``
+ * ``while`` / ``endwhile``
+ * ``foreach`` / ``endforeach``
+ * ``function`` / ``endfunction``
+ * ``macro`` / ``endmacro``
Evaluating Code
^^^^^^^^^^^^^^^
-.. _EVAL:
-
-.. code-block:: cmake
-
+.. signature::
cmake_language(EVAL CODE <code>...)
+ :target: EVAL
-Evaluates the ``<code>...`` as CMake code.
+ Evaluates the ``<code>...`` as CMake code.
-For example, the code:
+ For example, the code:
-.. code-block:: cmake
+ .. code-block:: cmake
- set(A TRUE)
- set(B TRUE)
- set(C TRUE)
- set(condition "(A AND B) OR C")
+ set(A TRUE)
+ set(B TRUE)
+ set(C TRUE)
+ set(condition "(A AND B) OR C")
- cmake_language(EVAL CODE "
- if (${condition})
- message(STATUS TRUE)
- else()
- message(STATUS FALSE)
- endif()"
- )
+ cmake_language(EVAL CODE "
+ if (${condition})
+ message(STATUS TRUE)
+ else()
+ message(STATUS FALSE)
+ endif()"
+ )
-is equivalent to
+ is equivalent to
-.. code-block:: cmake
+ .. code-block:: cmake
- set(A TRUE)
- set(B TRUE)
- set(C TRUE)
- set(condition "(A AND B) OR C")
-
- file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake "
- if (${condition})
- message(STATUS TRUE)
- else()
- message(STATUS FALSE)
- endif()"
- )
+ set(A TRUE)
+ set(B TRUE)
+ set(C TRUE)
+ set(condition "(A AND B) OR C")
- include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake)
+ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/eval.cmake "
+ if (${condition})
+ message(STATUS TRUE)
+ else()
+ message(STATUS FALSE)
+ endif()"
+ )
+
+ include(${CMAKE_CURRENT_BINARY_DIR}/eval.cmake)
Deferring Calls
^^^^^^^^^^^^^^^
.. versionadded:: 3.19
-.. _DEFER:
-
-.. code-block:: cmake
-
+.. signature::
cmake_language(DEFER <options>... CALL <command> [<arg>...])
-Schedules a call to the named ``<command>`` with the given arguments (if any)
-to occur at a later time. By default, deferred calls are executed as if
-written at the end of the current directory's ``CMakeLists.txt`` file,
-except that they run even after a :command:`return` call. Variable
-references in arguments are evaluated at the time the deferred call is
-executed.
+ Schedules a call to the named ``<command>`` with the given arguments (if any)
+ to occur at a later time. By default, deferred calls are executed as if
+ written at the end of the current directory's ``CMakeLists.txt`` file,
+ except that they run even after a :command:`return` call. Variable
+ references in arguments are evaluated at the time the deferred call is
+ executed.
-The options are:
+ The options are:
-``DIRECTORY <dir>``
- Schedule the call for the end of the given directory instead of the
- current directory. The ``<dir>`` may reference either a source
- directory or its corresponding binary directory. Relative paths are
- treated as relative to the current source directory.
+ ``DIRECTORY <dir>``
+ Schedule the call for the end of the given directory instead of the
+ current directory. The ``<dir>`` may reference either a source
+ directory or its corresponding binary directory. Relative paths are
+ treated as relative to the current source directory.
- The given directory must be known to CMake, being either the top-level
- directory or one added by :command:`add_subdirectory`. Furthermore,
- the given directory must not yet be finished processing. This means
- it can be the current directory or one of its ancestors.
+ The given directory must be known to CMake, being either the top-level
+ directory or one added by :command:`add_subdirectory`. Furthermore,
+ the given directory must not yet be finished processing. This means
+ it can be the current directory or one of its ancestors.
-``ID <id>``
- Specify an identification for the deferred call.
- The ``<id>`` may not be empty and may not begin with a capital letter ``A-Z``.
- The ``<id>`` may begin with an underscore (``_``) only if it was generated
- automatically by an earlier call that used ``ID_VAR`` to get the id.
+ ``ID <id>``
+ Specify an identification for the deferred call.
+ The ``<id>`` may not be empty and may not begin with a capital letter ``A-Z``.
+ The ``<id>`` may begin with an underscore (``_``) only if it was generated
+ automatically by an earlier call that used ``ID_VAR`` to get the id.
-``ID_VAR <var>``
- Specify a variable in which to store the identification for the
- deferred call. If ``ID <id>`` is not given, a new identification
- will be generated and the generated id will start with an underscore (``_``).
+ ``ID_VAR <var>``
+ Specify a variable in which to store the identification for the
+ deferred call. If ``ID <id>`` is not given, a new identification
+ will be generated and the generated id will start with an underscore (``_``).
-The currently scheduled list of deferred calls may be retrieved:
+ The currently scheduled list of deferred calls may be retrieved:
-.. code-block:: cmake
+ .. code-block:: cmake
- cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)
+ cmake_language(DEFER [DIRECTORY <dir>] GET_CALL_IDS <var>)
-This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
-Lists>` of deferred call ids. The ids are for the directory scope in which
-the calls have been deferred to (i.e. where they will be executed), which can
-be different to the scope in which they were created. The ``DIRECTORY``
-option can be used to specify the scope for which to retrieve the call ids.
-If that option is not given, the call ids for the current directory scope will
-be returned.
+ This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
+ Lists>` of deferred call ids. The ids are for the directory scope in which
+ the calls have been deferred to (i.e. where they will be executed), which can
+ be different to the scope in which they were created. The ``DIRECTORY``
+ option can be used to specify the scope for which to retrieve the call ids.
+ If that option is not given, the call ids for the current directory scope
+ will be returned.
-Details of a specific call may be retrieved from its id:
+ Details of a specific call may be retrieved from its id:
-.. code-block:: cmake
+ .. code-block:: cmake
- cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)
+ cmake_language(DEFER [DIRECTORY <dir>] GET_CALL <id> <var>)
-This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
-Lists>` in which the first element is the name of the command to be
-called, and the remaining elements are its unevaluated arguments (any
-contained ``;`` characters are included literally and cannot be distinguished
-from multiple arguments). If multiple calls are scheduled with the same id,
-this retrieves the first one. If no call is scheduled with the given id in
-the specified ``DIRECTORY`` scope (or the current directory scope if no
-``DIRECTORY`` option is given), this stores an empty string in the variable.
+ This will store in ``<var>`` a :ref:`semicolon-separated list <CMake Language
+ Lists>` in which the first element is the name of the command to be
+ called, and the remaining elements are its unevaluated arguments (any
+ contained ``;`` characters are included literally and cannot be distinguished
+ from multiple arguments). If multiple calls are scheduled with the same id,
+ this retrieves the first one. If no call is scheduled with the given id in
+ the specified ``DIRECTORY`` scope (or the current directory scope if no
+ ``DIRECTORY`` option is given), this stores an empty string in the variable.
-Deferred calls may be canceled by their id:
+ Deferred calls may be canceled by their id:
-.. code-block:: cmake
+ .. code-block:: cmake
- cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)
+ cmake_language(DEFER [DIRECTORY <dir>] CANCEL_CALL <id>...)
-This cancels all deferred calls matching any of the given ids in the specified
-``DIRECTORY`` scope (or the current directory scope if no ``DIRECTORY`` option
-is given). Unknown ids are silently ignored.
+ This cancels all deferred calls matching any of the given ids in the specified
+ ``DIRECTORY`` scope (or the current directory scope if no ``DIRECTORY`` option
+ is given). Unknown ids are silently ignored.
Deferred Call Examples
""""""""""""""""""""""
@@ -229,8 +221,6 @@ also prints::
Deferred Message 1
Deferred Message 2
-
-.. _SET_DEPENDENCY_PROVIDER:
.. _dependency_providers:
Dependency Providers
@@ -241,51 +231,50 @@ Dependency Providers
.. note:: A high-level introduction to this feature can be found in the
:ref:`Using Dependencies Guide <dependency_providers_overview>`.
-.. code-block:: cmake
-
+.. signature::
cmake_language(SET_DEPENDENCY_PROVIDER <command>
SUPPORTED_METHODS <methods>...)
-When a call is made to :command:`find_package` or
-:command:`FetchContent_MakeAvailable`, the call may be forwarded to a
-dependency provider which then has the opportunity to fulfill the request.
-If the request is for one of the ``<methods>`` specified when the provider
-was set, CMake calls the provider's ``<command>`` with a set of
-method-specific arguments. If the provider does not fulfill the request,
-or if the provider doesn't support the request's method, or no provider
-is set, the built-in :command:`find_package` or
-:command:`FetchContent_MakeAvailable` implementation is used to fulfill
-the request in the usual way.
-
-One or more of the following values can be specified for the ``<methods>``
-when setting the provider:
-
-``FIND_PACKAGE``
- The provider command accepts :command:`find_package` requests.
-
-``FETCHCONTENT_MAKEAVAILABLE_SERIAL``
- The provider command accepts :command:`FetchContent_MakeAvailable`
- requests. It expects each dependency to be fed to the provider command
- one at a time, not the whole list in one go.
-
-Only one provider can be set at any point in time. If a provider is already
-set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new
-provider replaces the previously set one. The specified ``<command>`` must
-already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called.
-As a special case, providing an empty string for the ``<command>`` and no
-``<methods>`` will discard any previously set provider.
-
-The dependency provider can only be set while processing one of the files
-specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable.
-Thus, dependency providers can only be set as part of the first call to
-:command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)``
-outside of that context will result in an error.
-
-.. note::
- The choice of dependency provider should always be under the user's control.
- As a convenience, a project may choose to provide a file that users can
- list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but
- the use of such a file should always be the user's choice.
+ When a call is made to :command:`find_package` or
+ :command:`FetchContent_MakeAvailable`, the call may be forwarded to a
+ dependency provider which then has the opportunity to fulfill the request.
+ If the request is for one of the ``<methods>`` specified when the provider
+ was set, CMake calls the provider's ``<command>`` with a set of
+ method-specific arguments. If the provider does not fulfill the request,
+ or if the provider doesn't support the request's method, or no provider
+ is set, the built-in :command:`find_package` or
+ :command:`FetchContent_MakeAvailable` implementation is used to fulfill
+ the request in the usual way.
+
+ One or more of the following values can be specified for the ``<methods>``
+ when setting the provider:
+
+ ``FIND_PACKAGE``
+ The provider command accepts :command:`find_package` requests.
+
+ ``FETCHCONTENT_MAKEAVAILABLE_SERIAL``
+ The provider command accepts :command:`FetchContent_MakeAvailable`
+ requests. It expects each dependency to be fed to the provider command
+ one at a time, not the whole list in one go.
+
+ Only one provider can be set at any point in time. If a provider is already
+ set when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called, the new
+ provider replaces the previously set one. The specified ``<command>`` must
+ already exist when ``cmake_language(SET_DEPENDENCY_PROVIDER)`` is called.
+ As a special case, providing an empty string for the ``<command>`` and no
+ ``<methods>`` will discard any previously set provider.
+
+ The dependency provider can only be set while processing one of the files
+ specified by the :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable.
+ Thus, dependency providers can only be set as part of the first call to
+ :command:`project`. Calling ``cmake_language(SET_DEPENDENCY_PROVIDER)``
+ outside of that context will result in an error.
+
+ .. note::
+ The choice of dependency provider should always be under the user's control.
+ As a convenience, a project may choose to provide a file that users can
+ list in their :variable:`CMAKE_PROJECT_TOP_LEVEL_INCLUDES` variable, but
+ the use of such a file should always be the user's choice.
Provider commands
"""""""""""""""""
@@ -499,23 +488,21 @@ Getting current message log level
.. versionadded:: 3.25
-.. _GET_MESSAGE_LOG_LEVEL:
.. _query_message_log_level:
-.. code-block:: cmake
-
+.. signature::
cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)
-Writes the current :command:`message` logging level
-into the given ``<output_variable>``.
+ Writes the current :command:`message` logging level
+ into the given ``<output_variable>``.
-See :command:`message` for the possible logging levels.
+ See :command:`message` for the possible logging levels.
-The current message logging level can be set either using the
-:option:`--log-level <cmake --log-level>`
-command line option of the :manual:`cmake(1)` program or using
-the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable.
+ The current message logging level can be set either using the
+ :option:`--log-level <cmake --log-level>`
+ command line option of the :manual:`cmake(1)` program or using
+ the :variable:`CMAKE_MESSAGE_LOG_LEVEL` variable.
-If both the command line option and the variable are set, the command line
-option takes precedence. If neither are set, the default logging level
-is returned.
+ If both the command line option and the variable are set, the command line
+ option takes precedence. If neither are set, the default logging level
+ is returned.
diff --git a/Help/command/file.rst b/Help/command/file.rst
index df895d080b..30a7f4de7b 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -26,7 +26,7 @@ Synopsis
`Reading`_
file(`READ`_ <filename> <out-var> [...])
file(`STRINGS`_ <filename> <out-var> [...])
- file(`\<HASH\> <HASH_>`_ <filename> <out-var>)
+ file(`\<HASH\>`_ <filename> <out-var>)
file(`TIMESTAMP`_ <filename> <out-var> [...])
file(`GET_RUNTIME_DEPENDENCIES`_ [...])
@@ -68,1202 +68,1184 @@ Synopsis
Reading
^^^^^^^
-.. _READ:
-
-.. code-block:: cmake
-
+.. signature::
file(READ <filename> <variable>
[OFFSET <offset>] [LIMIT <max-in>] [HEX])
-Read content from a file called ``<filename>`` and store it in a
-``<variable>``. Optionally start from the given ``<offset>`` and
-read at most ``<max-in>`` bytes. The ``HEX`` option causes data to
-be converted to a hexadecimal representation (useful for binary data). If the
-``HEX`` option is specified, letters in the output (``a`` through ``f``) are in
-lowercase.
-
-.. _STRINGS:
-
-.. code-block:: cmake
+ Read content from a file called ``<filename>`` and store it in a
+ ``<variable>``. Optionally start from the given ``<offset>`` and
+ read at most ``<max-in>`` bytes. The ``HEX`` option causes data to
+ be converted to a hexadecimal representation (useful for binary data).
+ If the ``HEX`` option is specified, letters in the output
+ (``a`` through ``f``) are in lowercase.
+.. signature::
file(STRINGS <filename> <variable> [<options>...])
-Parse a list of ASCII strings from ``<filename>`` and store it in
-``<variable>``. Binary data in the file are ignored. Carriage return
-(``\r``, CR) characters are ignored. The options are:
-
-``LENGTH_MAXIMUM <max-len>``
- Consider only strings of at most a given length.
+ Parse a list of ASCII strings from ``<filename>`` and store it in
+ ``<variable>``. Binary data in the file are ignored. Carriage return
+ (``\r``, CR) characters are ignored. The options are:
-``LENGTH_MINIMUM <min-len>``
- Consider only strings of at least a given length.
+ ``LENGTH_MAXIMUM <max-len>``
+ Consider only strings of at most a given length.
-``LIMIT_COUNT <max-num>``
- Limit the number of distinct strings to be extracted.
+ ``LENGTH_MINIMUM <min-len>``
+ Consider only strings of at least a given length.
-``LIMIT_INPUT <max-in>``
- Limit the number of input bytes to read from the file.
+ ``LIMIT_COUNT <max-num>``
+ Limit the number of distinct strings to be extracted.
-``LIMIT_OUTPUT <max-out>``
- Limit the number of total bytes to store in the ``<variable>``.
+ ``LIMIT_INPUT <max-in>``
+ Limit the number of input bytes to read from the file.
-``NEWLINE_CONSUME``
- Treat newline characters (``\n``, LF) as part of string content
- instead of terminating at them.
+ ``LIMIT_OUTPUT <max-out>``
+ Limit the number of total bytes to store in the ``<variable>``.
-``NO_HEX_CONVERSION``
- Intel Hex and Motorola S-record files are automatically converted to
- binary while reading unless this option is given.
+ ``NEWLINE_CONSUME``
+ Treat newline characters (``\n``, LF) as part of string content
+ instead of terminating at them.
-``REGEX <regex>``
- Consider only strings that match the given regular expression,
- as described under :ref:`string(REGEX) <Regex Specification>`.
+ ``NO_HEX_CONVERSION``
+ Intel Hex and Motorola S-record files are automatically converted to
+ binary while reading unless this option is given.
-``ENCODING <encoding-type>``
- .. versionadded:: 3.1
+ ``REGEX <regex>``
+ Consider only strings that match the given regular expression,
+ as described under :ref:`string(REGEX) <Regex Specification>`.
- Consider strings of a given encoding. Currently supported encodings are:
- ``UTF-8``, ``UTF-16LE``, ``UTF-16BE``, ``UTF-32LE``, ``UTF-32BE``.
- If the ``ENCODING`` option is not provided and the file has a Byte Order Mark,
- the ``ENCODING`` option will be defaulted to respect the Byte Order Mark.
+ ``ENCODING <encoding-type>``
+ .. versionadded:: 3.1
- .. versionadded:: 3.2
- Added the ``UTF-16LE``, ``UTF-16BE``, ``UTF-32LE``, ``UTF-32BE`` encodings.
+ Consider strings of a given encoding. Currently supported encodings are:
+ ``UTF-8``, ``UTF-16LE``, ``UTF-16BE``, ``UTF-32LE``, ``UTF-32BE``.
+ If the ``ENCODING`` option is not provided and the file
+ has a Byte Order Mark, the ``ENCODING`` option will be defaulted
+ to respect the Byte Order Mark.
-For example, the code
+ .. versionadded:: 3.2
+ Added the ``UTF-16LE``, ``UTF-16BE``, ``UTF-32LE``, ``UTF-32BE`` encodings.
-.. code-block:: cmake
+ For example, the code
- file(STRINGS myfile.txt myfile)
+ .. code-block:: cmake
-stores a list in the variable ``myfile`` in which each item is a line
-from the input file.
+ file(STRINGS myfile.txt myfile)
-.. _HASH:
-
-.. code-block:: cmake
+ stores a list in the variable ``myfile`` in which each item is a line
+ from the input file.
+.. signature::
file(<HASH> <filename> <variable>)
+ :target: <HASH>
-Compute a cryptographic hash of the content of ``<filename>`` and
-store it in a ``<variable>``. The supported ``<HASH>`` algorithm names
-are those listed by the :ref:`string(\<HASH\>) <Supported Hash Algorithms>`
-command.
-
-.. _TIMESTAMP:
-
-.. code-block:: cmake
+ Compute a cryptographic hash of the content of ``<filename>`` and
+ store it in a ``<variable>``. The supported ``<HASH>`` algorithm names
+ are those listed by the :command:`string(<HASH>)` command.
+.. signature::
file(TIMESTAMP <filename> <variable> [<format>] [UTC])
-Compute a string representation of the modification time of ``<filename>``
-and store it in ``<variable>``. Should the command be unable to obtain a
-timestamp variable will be set to the empty string ("").
-
-See the :command:`string(TIMESTAMP)` command for documentation of
-the ``<format>`` and ``UTC`` options.
+ Compute a string representation of the modification time of ``<filename>``
+ and store it in ``<variable>``. Should the command be unable to obtain a
+ timestamp variable will be set to the empty string ("").
-.. _GET_RUNTIME_DEPENDENCIES:
+ See the :command:`string(TIMESTAMP)` command for documentation of
+ the ``<format>`` and ``UTC`` options.
-.. code-block:: cmake
+.. signature::
+ file(GET_RUNTIME_DEPENDENCIES [...])
- file(GET_RUNTIME_DEPENDENCIES
- [RESOLVED_DEPENDENCIES_VAR <deps_var>]
- [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
- [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>]
- [EXECUTABLES [<executable_files>...]]
- [LIBRARIES [<library_files>...]]
- [MODULES [<module_files>...]]
- [DIRECTORIES [<directories>...]]
- [BUNDLE_EXECUTABLE <bundle_executable_file>]
- [PRE_INCLUDE_REGEXES [<regexes>...]]
- [PRE_EXCLUDE_REGEXES [<regexes>...]]
- [POST_INCLUDE_REGEXES [<regexes>...]]
- [POST_EXCLUDE_REGEXES [<regexes>...]]
- [POST_INCLUDE_FILES [<files>...]]
- [POST_EXCLUDE_FILES [<files>...]]
- )
+ .. versionadded:: 3.16
-.. versionadded:: 3.16
+ Recursively get the list of libraries depended on by the given files:
-Recursively get the list of libraries depended on by the given files.
+ .. code-block:: cmake
-Please note that this sub-command is not intended to be used in project mode.
-It is intended for use at install time, either from code generated by the
-:command:`install(RUNTIME_DEPENDENCY_SET)` command, or from code provided by
-the project via :command:`install(CODE)` or :command:`install(SCRIPT)`.
-For example:
-
-.. code-block:: cmake
-
- install(CODE [[
file(GET_RUNTIME_DEPENDENCIES
- # ...
+ [RESOLVED_DEPENDENCIES_VAR <deps_var>]
+ [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>]
+ [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>]
+ [EXECUTABLES [<executable_files>...]]
+ [LIBRARIES [<library_files>...]]
+ [MODULES [<module_files>...]]
+ [DIRECTORIES [<directories>...]]
+ [BUNDLE_EXECUTABLE <bundle_executable_file>]
+ [PRE_INCLUDE_REGEXES [<regexes>...]]
+ [PRE_EXCLUDE_REGEXES [<regexes>...]]
+ [POST_INCLUDE_REGEXES [<regexes>...]]
+ [POST_EXCLUDE_REGEXES [<regexes>...]]
+ [POST_INCLUDE_FILES [<files>...]]
+ [POST_EXCLUDE_FILES [<files>...]]
)
- ]])
-
-The arguments are as follows:
-
-``RESOLVED_DEPENDENCIES_VAR <deps_var>``
- Name of the variable in which to store the list of resolved dependencies.
-
-``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>``
- Name of the variable in which to store the list of unresolved dependencies.
- If this variable is not specified, and there are any unresolved dependencies,
- an error is issued.
-
-``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>``
- Variable prefix in which to store conflicting dependency information.
- Dependencies are conflicting if two files with the same name are found in
- two different directories. The list of filenames that conflict are stored in
- ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list of paths
- that were found for that filename are stored in
- ``<conflicting_deps_prefix>_<filename>``.
-
-``EXECUTABLES <executable_files>``
- List of executable files to read for dependencies. These are executables that
- are typically created with :command:`add_executable`, but they do not have to
- be created by CMake. On Apple platforms, the paths to these files determine
- the value of ``@executable_path`` when recursively resolving the libraries.
- Specifying any kind of library (``STATIC``, ``MODULE``, or ``SHARED``) here
- will result in undefined behavior.
-
-``LIBRARIES <library_files>``
- List of library files to read for dependencies. These are libraries that are
- typically created with :command:`add_library(SHARED)`, but they do not have
- to be created by CMake. Specifying ``STATIC`` libraries, ``MODULE``
- libraries, or executables here will result in undefined behavior.
-
-``MODULES <module_files>``
- List of loadable module files to read for dependencies. These are modules
- that are typically created with :command:`add_library(MODULE)`, but they do
- not have to be created by CMake. They are typically used by calling
- ``dlopen()`` at runtime rather than linked at link time with ``ld -l``.
- Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables here
- will result in undefined behavior.
-
-``DIRECTORIES <directories>``
- List of additional directories to search for dependencies. On Linux
- platforms, these directories are searched if the dependency is not found in
- any of the other usual paths. If it is found in such a directory, a warning
- is issued, because it means that the file is incomplete (it does not list all
- of the directories that contain its dependencies). On Windows platforms,
- these directories are searched if the dependency is not found in any of the
- other search paths, but no warning is issued, because searching other paths
- is a normal part of Windows dependency resolution. On Apple platforms, this
- argument has no effect.
-
-``BUNDLE_EXECUTABLE <bundle_executable_file>``
- Executable to treat as the "bundle executable" when resolving libraries. On
- Apple platforms, this argument determines the value of ``@executable_path``
- when recursively resolving libraries for ``LIBRARIES`` and ``MODULES`` files.
- It has no effect on ``EXECUTABLES`` files. On other platforms, it has no
- effect. This is typically (but not always) one of the executables in the
- ``EXECUTABLES`` argument which designates the "main" executable of the
- package.
-
-The following arguments specify filters for including or excluding libraries to
-be resolved. See below for a full description of how they work.
-
-``PRE_INCLUDE_REGEXES <regexes>``
- List of pre-include regexes through which to filter the names of
- not-yet-resolved dependencies.
-
-``PRE_EXCLUDE_REGEXES <regexes>``
- List of pre-exclude regexes through which to filter the names of
- not-yet-resolved dependencies.
-
-``POST_INCLUDE_REGEXES <regexes>``
- List of post-include regexes through which to filter the names of resolved
- dependencies.
-
-``POST_EXCLUDE_REGEXES <regexes>``
- List of post-exclude regexes through which to filter the names of resolved
- dependencies.
-
-``POST_INCLUDE_FILES <files>``
- .. versionadded:: 3.21
-
- List of post-include filenames through which to filter the names of resolved
- dependencies. Symlinks are resolved when attempting to match these filenames.
-
-``POST_EXCLUDE_FILES <files>``
- .. versionadded:: 3.21
-
- List of post-exclude filenames through which to filter the names of resolved
- dependencies. Symlinks are resolved when attempting to match these filenames.
-
-These arguments can be used to exclude unwanted system libraries when
-resolving the dependencies, or to include libraries from a specific
-directory. The filtering works as follows:
-
-1. If the not-yet-resolved dependency matches any of the
- ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency
- resolution proceeds to step 4.
-2. If the not-yet-resolved dependency matches any of the
- ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency.
-3. Otherwise, dependency resolution proceeds.
-4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according to
- the linking rules of the platform (see below).
-5. If the dependency is found, and its full path matches one of the
- ``POST_INCLUDE_REGEXES`` or ``POST_INCLUDE_FILES``, the full path is added
- to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)``
- recursively resolves that library's own dependencies. Otherwise, resolution
- proceeds to step 6.
-6. If the dependency is found, but its full path matches one of the
- ``POST_EXCLUDE_REGEXES`` or ``POST_EXCLUDE_FILES``, it is not added to the
- resolved dependencies, and dependency resolution stops for that dependency.
-7. If the dependency is found, and its full path does not match either
- ``POST_INCLUDE_REGEXES``, ``POST_INCLUDE_FILES``, ``POST_EXCLUDE_REGEXES``,
- or ``POST_EXCLUDE_FILES``, the full path is added to the resolved
- dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves
- that library's own dependencies.
-
-Different platforms have different rules for how dependencies are resolved.
-These specifics are described here.
-
-On Linux platforms, library resolution works as follows:
-
-1. If the depending file does not have any ``RUNPATH`` entries, and the library
- exists in one of the depending file's ``RPATH`` entries, or its parents', in
- that order, the dependency is resolved to that file.
-2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the
- library exists in one of those entries, the dependency is resolved to that
- file.
-3. Otherwise, if the library exists in one of the directories listed by
- ``ldconfig``, the dependency is resolved to that file.
-4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries, the
- dependency is resolved to that file. In this case, a warning is issued,
- because finding a file in one of the ``DIRECTORIES`` means that the
- depending file is not complete (it does not list all the directories from
- which it pulls dependencies).
-5. Otherwise, the dependency is unresolved.
-
-On Windows platforms, library resolution works as follows:
-
-1. The dependent DLL name is converted to lowercase. Windows DLL names are
- case-insensitive, and some linkers mangle the case of the DLL dependency
- names. However, this makes it more difficult for ``PRE_INCLUDE_REGEXES``,
- ``PRE_EXCLUDE_REGEXES``, ``POST_INCLUDE_REGEXES``, and
- ``POST_EXCLUDE_REGEXES`` to properly filter DLL names - every regex would
- have to check for both uppercase and lowercase letters. For example:
-
- .. code-block:: cmake
-
- file(GET_RUNTIME_DEPENDENCIES
- # ...
- PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$"
- )
-
- Converting the DLL name to lowercase allows the regexes to only match
- lowercase names, thus simplifying the regex. For example:
-
- .. code-block:: cmake
-
- file(GET_RUNTIME_DEPENDENCIES
- # ...
- PRE_INCLUDE_REGEXES "^mylibrary\\.dll$"
- )
-
- This regex will match ``mylibrary.dll`` regardless of how it is cased,
- either on disk or in the depending file. (For example, it will match
- ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.)
-
- Please note that the directory portion of any resolved DLLs retains its
- casing and is not converted to lowercase. Only the filename portion is
- converted.
-
-2. (**Not yet implemented**) If the depending file is a Windows Store app, and
- the dependency is listed as a dependency in the application's package
- manifest, the dependency is resolved to that file.
-3. Otherwise, if the library exists in the same directory as the depending
- file, the dependency is resolved to that file.
-4. Otherwise, if the library exists in either the operating system's
- ``system32`` directory or the ``Windows`` directory, in that order, the
- dependency is resolved to that file.
-5. Otherwise, if the library exists in one of the directories specified by
- ``DIRECTORIES``, in the order they are listed, the dependency is resolved to
- that file. In this case, a warning is not issued, because searching other
- directories is a normal part of Windows library resolution.
-6. Otherwise, the dependency is unresolved.
-
-On Apple platforms, library resolution works as follows:
-
-1. If the dependency starts with ``@executable_path/``, and an ``EXECUTABLES``
- argument is in the process of being resolved, and replacing
- ``@executable_path/`` with the directory of the executable yields an
- existing file, the dependency is resolved to that file.
-2. Otherwise, if the dependency starts with ``@executable_path/``, and there is
- a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/`` with
- the directory of the bundle executable yields an existing file, the
- dependency is resolved to that file.
-3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing
- ``@loader_path/`` with the directory of the depending file yields an
- existing file, the dependency is resolved to that file.
-4. Otherwise, if the dependency starts with ``@rpath/``, and replacing
- ``@rpath/`` with one of the ``RPATH`` entries of the depending file yields
- an existing file, the dependency is resolved to that file. Note that
- ``RPATH`` entries that start with ``@executable_path/`` or ``@loader_path/``
- also have these items replaced with the appropriate path.
-5. Otherwise, if the dependency is an absolute file that exists, the dependency
- is resolved to that file.
-6. Otherwise, the dependency is unresolved.
-
-This function accepts several variables that determine which tool is used for
-dependency resolution:
-
-.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
-
- Determines which operating system and executable format the files are built
- for. This could be one of several values:
-
- * ``linux+elf``
- * ``windows+pe``
- * ``macos+macho``
-
- If this variable is not specified, it is determined automatically by system
- introspection.
-
-.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
-
- Determines the tool to use for dependency resolution. It could be one of
- several values, depending on the value of
- :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`:
-
- ================================================= =============================================
- ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL``
- ================================================= =============================================
- ``linux+elf`` ``objdump``
- ``windows+pe`` ``dumpbin``
- ``windows+pe`` ``objdump``
- ``macos+macho`` ``otool``
- ================================================= =============================================
-
- If this variable is not specified, it is determined automatically by system
- introspection.
-
-.. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
-
- Determines the path to the tool to use for dependency resolution. This is the
- actual path to ``objdump``, ``dumpbin``, or ``otool``.
-
- If this variable is not specified, it is determined by the value of
- ``CMAKE_OBJDUMP`` if set, else by system introspection.
-
- .. versionadded:: 3.18
- Use ``CMAKE_OBJDUMP`` if set.
-
-Writing
-^^^^^^^
-
-.. _WRITE:
-.. _APPEND:
-
-.. code-block:: cmake
-
- file(WRITE <filename> <content>...)
- file(APPEND <filename> <content>...)
-
-Write ``<content>`` into a file called ``<filename>``. If the file does
-not exist, it will be created. If the file already exists, ``WRITE``
-mode will overwrite it and ``APPEND`` mode will append to the end.
-Any directories in the path specified by ``<filename>`` that do not
-exist will be created.
-
-If the file is a build input, use the :command:`configure_file` command
-to update the file only when its content changes.
-
-.. _TOUCH:
-.. _TOUCH_NOCREATE:
-
-.. code-block:: cmake
-
- file(TOUCH [<files>...])
- file(TOUCH_NOCREATE [<files>...])
-
-.. versionadded:: 3.12
-Create a file with no content if it does not yet exist. If the file already
-exists, its access and/or modification will be updated to the time when the
-function call is executed.
+ Please note that this sub-command is not intended to be used in project mode.
+ It is intended for use at install time, either from code generated by the
+ :command:`install(RUNTIME_DEPENDENCY_SET)` command, or from code provided by
+ the project via :command:`install(CODE)` or :command:`install(SCRIPT)`.
+ For example:
+
+ .. code-block:: cmake
+
+ install(CODE [[
+ file(GET_RUNTIME_DEPENDENCIES
+ # ...
+ )
+ ]])
+
+ The arguments are as follows:
+
+ ``RESOLVED_DEPENDENCIES_VAR <deps_var>``
+ Name of the variable in which to store the list of resolved dependencies.
+
+ ``UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>``
+ Name of the variable in which to store the list of unresolved
+ dependencies. If this variable is not specified, and there are any
+ unresolved dependencies, an error is issued.
+
+ ``CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>``
+ Variable prefix in which to store conflicting dependency information.
+ Dependencies are conflicting if two files with the same name are found in
+ two different directories. The list of filenames that conflict are stored
+ in ``<conflicting_deps_prefix>_FILENAMES``. For each filename, the list
+ of paths that were found for that filename are stored in
+ ``<conflicting_deps_prefix>_<filename>``.
+
+ ``EXECUTABLES <executable_files>``
+ List of executable files to read for dependencies. These are executables
+ that are typically created with :command:`add_executable`, but they do
+ not have to be created by CMake. On Apple platforms, the paths to these
+ files determine the value of ``@executable_path`` when recursively
+ resolving the libraries. Specifying any kind of library (``STATIC``,
+ ``MODULE``, or ``SHARED``) here will result in undefined behavior.
+
+ ``LIBRARIES <library_files>``
+ List of library files to read for dependencies. These are libraries that
+ are typically created with :command:`add_library(SHARED)`, but they do
+ not have to be created by CMake. Specifying ``STATIC`` libraries,
+ ``MODULE`` libraries, or executables here will result in undefined
+ behavior.
+
+ ``MODULES <module_files>``
+ List of loadable module files to read for dependencies. These are modules
+ that are typically created with :command:`add_library(MODULE)`, but they
+ do not have to be created by CMake. They are typically used by calling
+ ``dlopen()`` at runtime rather than linked at link time with ``ld -l``.
+ Specifying ``STATIC`` libraries, ``SHARED`` libraries, or executables
+ here will result in undefined behavior.
+
+ ``DIRECTORIES <directories>``
+ List of additional directories to search for dependencies. On Linux
+ platforms, these directories are searched if the dependency is not found
+ in any of the other usual paths. If it is found in such a directory, a
+ warning is issued, because it means that the file is incomplete (it does
+ not list all of the directories that contain its dependencies).
+ On Windows platforms, these directories are searched if the dependency
+ is not found in any of the other search paths, but no warning is issued,
+ because searching other paths is a normal part of Windows dependency
+ resolution. On Apple platforms, this argument has no effect.
+
+ ``BUNDLE_EXECUTABLE <bundle_executable_file>``
+ Executable to treat as the "bundle executable" when resolving libraries.
+ On Apple platforms, this argument determines the value of
+ ``@executable_path`` when recursively resolving libraries for
+ ``LIBRARIES`` and ``MODULES`` files. It has no effect on ``EXECUTABLES``
+ files. On other platforms, it has no effect. This is typically (but not
+ always) one of the executables in the ``EXECUTABLES`` argument which
+ designates the "main" executable of the package.
+
+ The following arguments specify filters for including or excluding libraries
+ to be resolved. See below for a full description of how they work.
+
+ ``PRE_INCLUDE_REGEXES <regexes>``
+ List of pre-include regexes through which to filter the names of
+ not-yet-resolved dependencies.
+
+ ``PRE_EXCLUDE_REGEXES <regexes>``
+ List of pre-exclude regexes through which to filter the names of
+ not-yet-resolved dependencies.
+
+ ``POST_INCLUDE_REGEXES <regexes>``
+ List of post-include regexes through which to filter the names of
+ resolved dependencies.
+
+ ``POST_EXCLUDE_REGEXES <regexes>``
+ List of post-exclude regexes through which to filter the names of
+ resolved dependencies.
+
+ ``POST_INCLUDE_FILES <files>``
+ .. versionadded:: 3.21
+
+ List of post-include filenames through which to filter the names of
+ resolved dependencies. Symlinks are resolved when attempting to match
+ these filenames.
+
+ ``POST_EXCLUDE_FILES <files>``
+ .. versionadded:: 3.21
+
+ List of post-exclude filenames through which to filter the names of
+ resolved dependencies. Symlinks are resolved when attempting to match
+ these filenames.
+
+ These arguments can be used to exclude unwanted system libraries when
+ resolving the dependencies, or to include libraries from a specific
+ directory. The filtering works as follows:
+
+ 1. If the not-yet-resolved dependency matches any of the
+ ``PRE_INCLUDE_REGEXES``, steps 2 and 3 are skipped, and the dependency
+ resolution proceeds to step 4.
+
+ 2. If the not-yet-resolved dependency matches any of the
+ ``PRE_EXCLUDE_REGEXES``, dependency resolution stops for that dependency.
+
+ 3. Otherwise, dependency resolution proceeds.
+
+ 4. ``file(GET_RUNTIME_DEPENDENCIES)`` searches for the dependency according
+ to the linking rules of the platform (see below).
+
+ 5. If the dependency is found, and its full path matches one of the
+ ``POST_INCLUDE_REGEXES`` or ``POST_INCLUDE_FILES``, the full path is added
+ to the resolved dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)``
+ recursively resolves that library's own dependencies. Otherwise, resolution
+ proceeds to step 6.
+
+ 6. If the dependency is found, but its full path matches one of the
+ ``POST_EXCLUDE_REGEXES`` or ``POST_EXCLUDE_FILES``, it is not added to the
+ resolved dependencies, and dependency resolution stops for that dependency.
+
+ 7. If the dependency is found, and its full path does not match either
+ ``POST_INCLUDE_REGEXES``, ``POST_INCLUDE_FILES``, ``POST_EXCLUDE_REGEXES``,
+ or ``POST_EXCLUDE_FILES``, the full path is added to the resolved
+ dependencies, and ``file(GET_RUNTIME_DEPENDENCIES)`` recursively resolves
+ that library's own dependencies.
+
+ Different platforms have different rules for how dependencies are resolved.
+ These specifics are described here.
+
+ On Linux platforms, library resolution works as follows:
+
+ 1. If the depending file does not have any ``RUNPATH`` entries, and the
+ library exists in one of the depending file's ``RPATH`` entries, or its
+ parents', in that order, the dependency is resolved to that file.
+ 2. Otherwise, if the depending file has any ``RUNPATH`` entries, and the
+ library exists in one of those entries, the dependency is resolved to that
+ file.
+ 3. Otherwise, if the library exists in one of the directories listed by
+ ``ldconfig``, the dependency is resolved to that file.
+ 4. Otherwise, if the library exists in one of the ``DIRECTORIES`` entries,
+ the dependency is resolved to that file. In this case, a warning is
+ issued, because finding a file in one of the ``DIRECTORIES`` means that
+ the depending file is not complete (it does not list all the directories
+ from which it pulls dependencies).
+
+ 5. Otherwise, the dependency is unresolved.
-Use TOUCH_NOCREATE to touch a file if it exists but not create it. If a file
-does not exist it will be silently ignored.
+ On Windows platforms, library resolution works as follows:
-With TOUCH and TOUCH_NOCREATE the contents of an existing file will not be
-modified.
+ 1. DLL dependency names are converted to lowercase for matching filters.
+ Windows DLL names are case-insensitive, and some linkers mangle the
+ case of the DLL dependency names. However, this makes it more difficult
+ for ``PRE_INCLUDE_REGEXES``, ``PRE_EXCLUDE_REGEXES``,
+ ``POST_INCLUDE_REGEXES``, and ``POST_EXCLUDE_REGEXES`` to properly
+ filter DLL names - every regex would have to check for both uppercase
+ and lowercase letters. For example:
-.. _GENERATE:
+ .. code-block:: cmake
-.. code-block:: cmake
+ file(GET_RUNTIME_DEPENDENCIES
+ # ...
+ PRE_INCLUDE_REGEXES "^[Mm][Yy][Ll][Ii][Bb][Rr][Aa][Rr][Yy]\\.[Dd][Ll][Ll]$"
+ )
- file(GENERATE OUTPUT output-file
- <INPUT input-file|CONTENT content>
- [CONDITION expression] [TARGET target]
- [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
- FILE_PERMISSIONS <permissions>...]
- [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
-
-Generate an output file for each build configuration supported by the current
-:manual:`CMake Generator <cmake-generators(7)>`. Evaluate
-:manual:`generator expressions <cmake-generator-expressions(7)>`
-from the input content to produce the output content. The options are:
+ Converting the DLL name to lowercase allows the regexes to only match
+ lowercase names, thus simplifying the regex. For example:
-``CONDITION <condition>``
- Generate the output file for a particular configuration only if
- the condition is true. The condition must be either ``0`` or ``1``
- after evaluating generator expressions.
+ .. code-block:: cmake
-``CONTENT <content>``
- Use the content given explicitly as input.
+ file(GET_RUNTIME_DEPENDENCIES
+ # ...
+ PRE_INCLUDE_REGEXES "^mylibrary\\.dll$"
+ )
-``INPUT <input-file>``
- Use the content from a given file as input.
+ This regex will match ``mylibrary.dll`` regardless of how it is cased,
+ either on disk or in the depending file. (For example, it will match
+ ``mylibrary.dll``, ``MyLibrary.dll``, and ``MYLIBRARY.DLL``.)
- .. versionchanged:: 3.10
- A relative path is treated with respect to the value of
- :variable:`CMAKE_CURRENT_SOURCE_DIR`. See policy :policy:`CMP0070`.
+ .. versionchanged:: 3.27
-``OUTPUT <output-file>``
- Specify the output file name to generate. Use generator expressions
- such as :genex:`$<CONFIG>` to specify a configuration-specific
- output file name. Multiple configurations may generate the same output
- file only if the generated content is identical. Otherwise, the
- ``<output-file>`` must evaluate to an unique name for each configuration.
+ The conversion to lowercase only applies while matching filters.
+ Results reported after filtering case-preserve each DLL name as it is
+ found on disk, if resolved, and otherwise as it is referenced by the
+ dependent binary.
- .. versionchanged:: 3.10
- A relative path (after evaluating generator expressions) is treated
- with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
- See policy :policy:`CMP0070`.
+ Prior to CMake 3.27, the results were reported with lowercase DLL
+ file names, but the directory portion retained its casing.
-``TARGET <target>``
- .. versionadded:: 3.19
+ 2. (**Not yet implemented**) If the depending file is a Windows Store app,
+ and the dependency is listed as a dependency in the application's package
+ manifest, the dependency is resolved to that file.
- Specify which target to use when evaluating generator expressions that
- require a target for evaluation (e.g.
- :genex:`$<COMPILE_FEATURES:...>`,
- :genex:`$<TARGET_PROPERTY:prop>`).
+ 3. Otherwise, if the library exists in the same directory as the depending
+ file, the dependency is resolved to that file.
-``NO_SOURCE_PERMISSIONS``
- .. versionadded:: 3.20
+ 4. Otherwise, if the library exists in either the operating system's
+ ``system32`` directory or the ``Windows`` directory, in that order, the
+ dependency is resolved to that file.
- The generated file permissions default to the standard 644 value
- (-rw-r--r--).
+ 5. Otherwise, if the library exists in one of the directories specified by
+ ``DIRECTORIES``, in the order they are listed, the dependency is resolved
+ to that file. In this case, a warning is not issued, because searching
+ other directories is a normal part of Windows library resolution.
-``USE_SOURCE_PERMISSIONS``
- .. versionadded:: 3.20
+ 6. Otherwise, the dependency is unresolved.
- Transfer the file permissions of the ``INPUT`` file to the generated file.
- This is already the default behavior if none of the three permissions-related
- keywords are given (``NO_SOURCE_PERMISSIONS``, ``USE_SOURCE_PERMISSIONS``
- or ``FILE_PERMISSIONS``). The ``USE_SOURCE_PERMISSIONS`` keyword mostly
- serves as a way of making the intended behavior clearer at the call site.
- It is an error to specify this option without ``INPUT``.
+ On Apple platforms, library resolution works as follows:
-``FILE_PERMISSIONS <permissions>...``
- .. versionadded:: 3.20
+ 1. If the dependency starts with ``@executable_path/``, and an
+ ``EXECUTABLES`` argument is in the process of being resolved, and
+ replacing ``@executable_path/`` with the directory of the executable
+ yields an existing file, the dependency is resolved to that file.
- Use the specified permissions for the generated file.
+ 2. Otherwise, if the dependency starts with ``@executable_path/``, and there
+ is a ``BUNDLE_EXECUTABLE`` argument, and replacing ``@executable_path/``
+ with the directory of the bundle executable yields an existing file, the
+ dependency is resolved to that file.
-``NEWLINE_STYLE <style>``
- .. versionadded:: 3.20
+ 3. Otherwise, if the dependency starts with ``@loader_path/``, and replacing
+ ``@loader_path/`` with the directory of the depending file yields an
+ existing file, the dependency is resolved to that file.
- Specify the newline style for the generated file. Specify
- ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
- ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
+ 4. Otherwise, if the dependency starts with ``@rpath/``, and replacing
+ ``@rpath/`` with one of the ``RPATH`` entries of the depending file
+ yields an existing file, the dependency is resolved to that file.
+ Note that ``RPATH`` entries that start with ``@executable_path/`` or
+ ``@loader_path/`` also have these items replaced with the appropriate
+ path.
-Exactly one ``CONTENT`` or ``INPUT`` option must be given. A specific
-``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
-Generated files are modified and their timestamp updated on subsequent cmake
-runs only if their content is changed.
+ 5. Otherwise, if the dependency is an absolute file that exists,
+ the dependency is resolved to that file.
-Note also that ``file(GENERATE)`` does not create the output file until the
-generation phase. The output file will not yet have been written when the
-``file(GENERATE)`` command returns, it is written only after processing all
-of a project's ``CMakeLists.txt`` files.
+ 6. Otherwise, the dependency is unresolved.
-.. _CONFIGURE:
+ This function accepts several variables that determine which tool is used for
+ dependency resolution:
-.. code-block:: cmake
+ .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
- file(CONFIGURE OUTPUT output-file
- CONTENT content
- [ESCAPE_QUOTES] [@ONLY]
- [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
+ Determines which operating system and executable format the files are built
+ for. This could be one of several values:
-.. versionadded:: 3.18
+ * ``linux+elf``
+ * ``windows+pe``
+ * ``macos+macho``
-Generate an output file using the input given by ``CONTENT`` and substitute
-variable values referenced as ``@VAR@`` or ``${VAR}`` contained therein. The
-substitution rules behave the same as the :command:`configure_file` command.
-In order to match :command:`configure_file`'s behavior, generator expressions
-are not supported for both ``OUTPUT`` and ``CONTENT``.
+ If this variable is not specified, it is determined automatically by system
+ introspection.
-The arguments are:
+ .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
-``OUTPUT <output-file>``
- Specify the output file name to generate. A relative path is treated with
- respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
- ``<output-file>`` does not support generator expressions.
+ Determines the tool to use for dependency resolution. It could be one of
+ several values, depending on the value of
+ :variable:`CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`:
-``CONTENT <content>``
- Use the content given explicitly as input.
- ``<content>`` does not support generator expressions.
+ ================================================= =============================================
+ ``CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM`` ``CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL``
+ ================================================= =============================================
+ ``linux+elf`` ``objdump``
+ ``windows+pe`` ``objdump`` or ``dumpbin``
+ ``macos+macho`` ``otool``
+ ================================================= =============================================
-``ESCAPE_QUOTES``
- Escape any substituted quotes with backslashes (C-style).
-
-``@ONLY``
- Restrict variable replacement to references of the form ``@VAR@``.
- This is useful for configuring scripts that use ``${VAR}`` syntax.
-
-``NEWLINE_STYLE <style>``
- Specify the newline style for the output file. Specify
- ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
- ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
-
-Filesystem
-^^^^^^^^^^
-
-.. _GLOB:
-.. _GLOB_RECURSE:
-
-.. code-block:: cmake
-
- file(GLOB <variable>
- [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
- [<globbing-expressions>...])
- file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
- [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
- [<globbing-expressions>...])
+ If this variable is not specified, it is determined automatically by system
+ introspection.
-Generate a list of files that match the ``<globbing-expressions>`` and
-store it into the ``<variable>``. Globbing expressions are similar to
-regular expressions, but much simpler. If ``RELATIVE`` flag is
-specified, the results will be returned as relative paths to the given
-path.
+ .. variable:: CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
-.. versionchanged:: 3.6
- The results will be ordered lexicographically.
+ Determines the path to the tool to use for dependency resolution. This is
+ the actual path to ``objdump``, ``dumpbin``, or ``otool``.
-On Windows and macOS, globbing is case-insensitive even if the underlying
-filesystem is case-sensitive (both filenames and globbing expressions are
-converted to lowercase before matching). On other platforms, globbing is
-case-sensitive.
+ If this variable is not specified, it is determined by the value of
+ ``CMAKE_OBJDUMP`` if set, else by system introspection.
-.. versionadded:: 3.3
- By default ``GLOB`` lists directories - directories are omitted in result if
- ``LIST_DIRECTORIES`` is set to false.
+ .. versionadded:: 3.18
+ Use ``CMAKE_OBJDUMP`` if set.
-.. versionadded:: 3.12
- If the ``CONFIGURE_DEPENDS`` flag is specified, CMake will add logic
- to the main build system check target to rerun the flagged ``GLOB`` commands
- at build time. If any of the outputs change, CMake will regenerate the build
- system.
+Writing
+^^^^^^^
-.. note::
- We do not recommend using GLOB to collect a list of source files from
- your source tree. If no CMakeLists.txt file changes when a source is
- added or removed then the generated build system cannot know when to
- ask CMake to regenerate.
- The ``CONFIGURE_DEPENDS`` flag may not work reliably on all generators, or if
- a new generator is added in the future that cannot support it, projects using
- it will be stuck. Even if ``CONFIGURE_DEPENDS`` works reliably, there is
- still a cost to perform the check on every rebuild.
+.. signature::
+ file(WRITE <filename> <content>...)
+ file(APPEND <filename> <content>...)
-Examples of globbing expressions include::
+ Write ``<content>`` into a file called ``<filename>``. If the file does
+ not exist, it will be created. If the file already exists, ``WRITE``
+ mode will overwrite it and ``APPEND`` mode will append to the end.
+ Any directories in the path specified by ``<filename>`` that do not
+ exist will be created.
- *.cxx - match all files with extension cxx
- *.vt? - match all files with extension vta,...,vtz
- f[3-5].txt - match files f3.txt, f4.txt, f5.txt
+ If the file is a build input, use the :command:`configure_file` command
+ to update the file only when its content changes.
-The ``GLOB_RECURSE`` mode will traverse all the subdirectories of the
-matched directory and match the files. Subdirectories that are symlinks
-are only traversed if ``FOLLOW_SYMLINKS`` is given or policy
-:policy:`CMP0009` is not set to ``NEW``.
+.. signature::
+ file(TOUCH [<files>...])
+ file(TOUCH_NOCREATE [<files>...])
-.. versionadded:: 3.3
- By default ``GLOB_RECURSE`` omits directories from result list - setting
- ``LIST_DIRECTORIES`` to true adds directories to result list.
- If ``FOLLOW_SYMLINKS`` is given or policy :policy:`CMP0009` is not set to
- ``NEW`` then ``LIST_DIRECTORIES`` treats symlinks as directories.
+ .. versionadded:: 3.12
-Examples of recursive globbing include::
+ Create a file with no content if it does not yet exist. If the file already
+ exists, its access and/or modification will be updated to the time when the
+ function call is executed.
- /dir/*.py - match all python files in /dir and subdirectories
+ Use ``TOUCH_NOCREATE`` to touch a file if it exists but not create it.
+ If a file does not exist it will be silently ignored.
-.. _MAKE_DIRECTORY:
+ With ``TOUCH`` and ``TOUCH_NOCREATE``, the contents of an existing file
+ will not be modified.
-.. code-block:: cmake
+.. signature::
+ file(GENERATE [...])
- file(MAKE_DIRECTORY [<directories>...])
+ Generate an output file for each build configuration supported by the current
+ :manual:`CMake Generator <cmake-generators(7)>`. Evaluate
+ :manual:`generator expressions <cmake-generator-expressions(7)>`
+ from the input content to produce the output content.
-Create the given directories and their parents as needed.
+ .. code-block:: cmake
-.. _REMOVE:
-.. _REMOVE_RECURSE:
+ file(GENERATE OUTPUT <output-file>
+ <INPUT <input-file>|CONTENT <content>>
+ [CONDITION <expression>] [TARGET <target>]
+ [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |
+ FILE_PERMISSIONS <permissions>...]
+ [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
-.. code-block:: cmake
+ The options are:
- file(REMOVE [<files>...])
- file(REMOVE_RECURSE [<files>...])
+ ``CONDITION <condition>``
+ Generate the output file for a particular configuration only if
+ the condition is true. The condition must be either ``0`` or ``1``
+ after evaluating generator expressions.
-Remove the given files. The ``REMOVE_RECURSE`` mode will remove the given
-files and directories, also non-empty directories. No error is emitted if a
-given file does not exist. Relative input paths are evaluated with respect
-to the current source directory.
+ ``CONTENT <content>``
+ Use the content given explicitly as input.
-.. versionchanged:: 3.15
- Empty input paths are ignored with a warning. Previous versions of CMake
- interpreted empty strings as a relative path with respect to the current
- directory and removed its contents.
+ ``INPUT <input-file>``
+ Use the content from a given file as input.
-.. _RENAME:
+ .. versionchanged:: 3.10
+ A relative path is treated with respect to the value of
+ :variable:`CMAKE_CURRENT_SOURCE_DIR`. See policy :policy:`CMP0070`.
-.. code-block:: cmake
+ ``OUTPUT <output-file>``
+ Specify the output file name to generate. Use generator expressions
+ such as :genex:`$<CONFIG>` to specify a configuration-specific
+ output file name. Multiple configurations may generate the same output
+ file only if the generated content is identical. Otherwise, the
+ ``<output-file>`` must evaluate to an unique name for each configuration.
- file(RENAME <oldname> <newname>
- [RESULT <result>]
- [NO_REPLACE])
+ .. versionchanged:: 3.10
+ A relative path (after evaluating generator expressions) is treated
+ with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
+ See policy :policy:`CMP0070`.
-Move a file or directory within a filesystem from ``<oldname>`` to
-``<newname>``, replacing the destination atomically.
+ ``TARGET <target>``
+ .. versionadded:: 3.19
-The options are:
+ Specify which target to use when evaluating generator expressions that
+ require a target for evaluation (e.g.
+ :genex:`$<COMPILE_FEATURES:...>`,
+ :genex:`$<TARGET_PROPERTY:prop>`).
-``RESULT <result>``
- .. versionadded:: 3.21
+ ``NO_SOURCE_PERMISSIONS``
+ .. versionadded:: 3.20
- Set ``<result>`` variable to ``0`` on success or an error message otherwise.
- If ``RESULT`` is not specified and the operation fails, an error is emitted.
+ The generated file permissions default to the standard 644 value
+ (-rw-r--r--).
-``NO_REPLACE``
- .. versionadded:: 3.21
+ ``USE_SOURCE_PERMISSIONS``
+ .. versionadded:: 3.20
- If the ``<newname>`` path already exists, do not replace it.
- If ``RESULT <result>`` is used, the result variable will be
- set to ``NO_REPLACE``. Otherwise, an error is emitted.
+ Transfer the file permissions of the ``INPUT`` file to the generated
+ file. This is already the default behavior if none of the three
+ permissions-related keywords are given (``NO_SOURCE_PERMISSIONS``,
+ ``USE_SOURCE_PERMISSIONS`` or ``FILE_PERMISSIONS``). The
+ ``USE_SOURCE_PERMISSIONS`` keyword mostly serves as a way of making
+ the intended behavior clearer at the call site. It is an error to
+ specify this option without ``INPUT``.
-.. _COPY_FILE:
+ ``FILE_PERMISSIONS <permissions>...``
+ .. versionadded:: 3.20
-.. code-block:: cmake
+ Use the specified permissions for the generated file.
- file(COPY_FILE <oldname> <newname>
- [RESULT <result>]
- [ONLY_IF_DIFFERENT]
- [INPUT_MAY_BE_RECENT])
+ ``NEWLINE_STYLE <style>``
+ .. versionadded:: 3.20
-.. versionadded:: 3.21
+ Specify the newline style for the generated file. Specify
+ ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
+ ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
-Copy a file from ``<oldname>`` to ``<newname>``. Directories are not
-supported. Symlinks are ignored and ``<oldfile>``'s content is read and
-written to ``<newname>`` as a new file.
+ Exactly one ``CONTENT`` or ``INPUT`` option must be given. A specific
+ ``OUTPUT`` file may be named by at most one invocation of ``file(GENERATE)``.
+ Generated files are modified and their timestamp updated on subsequent cmake
+ runs only if their content is changed.
-The options are:
+ Note also that ``file(GENERATE)`` does not create the output file until the
+ generation phase. The output file will not yet have been written when the
+ ``file(GENERATE)`` command returns, it is written only after processing all
+ of a project's ``CMakeLists.txt`` files.
-``RESULT <result>``
- Set ``<result>`` variable to ``0`` on success or an error message otherwise.
- If ``RESULT`` is not specified and the operation fails, an error is emitted.
+.. signature::
+ file(CONFIGURE OUTPUT <output-file>
+ CONTENT <content>
+ [ESCAPE_QUOTES] [@ONLY]
+ [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
+ :target: CONFIGURE
-``ONLY_IF_DIFFERENT``
- If the ``<newname>`` path already exists, do not replace it if the file's
- contents are already the same as ``<oldname>`` (this avoids updating
- ``<newname>``'s timestamp).
+ .. versionadded:: 3.18
-``INPUT_MAY_BE_RECENT``
- .. versionadded:: 3.26
+ Generate an output file using the input given by ``CONTENT`` and substitute
+ variable values referenced as ``@VAR@`` or ``${VAR}`` contained therein. The
+ substitution rules behave the same as the :command:`configure_file` command.
+ In order to match :command:`configure_file`'s behavior, generator expressions
+ are not supported for both ``OUTPUT`` and ``CONTENT``.
- Tell CMake that the input file may have been recently created. This is
- meaningful only on Windows, where files may be inaccessible for a short
- time after they are created. With this option, if permission is denied,
- CMake will retry reading the input a few times.
+ The arguments are:
-This sub-command has some similarities to :command:`configure_file` with the
-``COPYONLY`` option. An important difference is that :command:`configure_file`
-creates a dependency on the source file, so CMake will be re-run if it changes.
-The ``file(COPY_FILE)`` sub-command does not create such a dependency.
+ ``OUTPUT <output-file>``
+ Specify the output file name to generate. A relative path is treated with
+ respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
+ ``<output-file>`` does not support generator expressions.
-See also the ``file(COPY)`` sub-command just below which provides
-further file-copying capabilities.
+ ``CONTENT <content>``
+ Use the content given explicitly as input.
+ ``<content>`` does not support generator expressions.
-.. _COPY:
-.. _INSTALL:
+ ``ESCAPE_QUOTES``
+ Escape any substituted quotes with backslashes (C-style).
-.. code-block:: cmake
+ ``@ONLY``
+ Restrict variable replacement to references of the form ``@VAR@``.
+ This is useful for configuring scripts that use ``${VAR}`` syntax.
- file(<COPY|INSTALL> <files>... DESTINATION <dir>
- [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS]
- [FILE_PERMISSIONS <permissions>...]
- [DIRECTORY_PERMISSIONS <permissions>...]
- [FOLLOW_SYMLINK_CHAIN]
- [FILES_MATCHING]
- [[PATTERN <pattern> | REGEX <regex>]
- [EXCLUDE] [PERMISSIONS <permissions>...]] [...])
+ ``NEWLINE_STYLE <style>``
+ Specify the newline style for the output file. Specify
+ ``UNIX`` or ``LF`` for ``\n`` newlines, or specify
+ ``DOS``, ``WIN32``, or ``CRLF`` for ``\r\n`` newlines.
-.. note::
+Filesystem
+^^^^^^^^^^
- For a simple file copying operation, the ``file(COPY_FILE)`` sub-command
- just above may be easier to use.
+.. signature::
+ file(GLOB <variable>
+ [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
+ [<globbing-expressions>...])
+ file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
+ [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS]
+ [<globbing-expressions>...])
-The ``COPY`` signature copies files, directories, and symlinks to a
-destination folder. Relative input paths are evaluated with respect
-to the current source directory, and a relative destination is
-evaluated with respect to the current build directory. Copying
-preserves input file timestamps, and optimizes out a file if it exists
-at the destination with the same timestamp. Copying preserves input
-permissions unless explicit permissions or ``NO_SOURCE_PERMISSIONS``
-are given (default is ``USE_SOURCE_PERMISSIONS``).
+ Generate a list of files that match the ``<globbing-expressions>`` and
+ store it into the ``<variable>``. Globbing expressions are similar to
+ regular expressions, but much simpler. If ``RELATIVE`` flag is
+ specified, the results will be returned as relative paths to the given
+ path.
+
+ .. versionchanged:: 3.6
+ The results will be ordered lexicographically.
+
+ On Windows and macOS, globbing is case-insensitive even if the underlying
+ filesystem is case-sensitive (both filenames and globbing expressions are
+ converted to lowercase before matching). On other platforms, globbing is
+ case-sensitive.
+
+ .. versionadded:: 3.3
+ By default ``GLOB`` lists directories. Directories are omitted in the
+ result if ``LIST_DIRECTORIES`` is set to false.
+
+ .. versionadded:: 3.12
+ If the ``CONFIGURE_DEPENDS`` flag is specified, CMake will add logic
+ to the main build system check target to rerun the flagged ``GLOB``
+ commands at build time. If any of the outputs change, CMake will regenerate
+ the build system.
+
+ .. note::
+ We do not recommend using GLOB to collect a list of source files from
+ your source tree. If no CMakeLists.txt file changes when a source is
+ added or removed then the generated build system cannot know when to
+ ask CMake to regenerate.
+ The ``CONFIGURE_DEPENDS`` flag may not work reliably on all generators, or
+ if a new generator is added in the future that cannot support it, projects
+ using it will be stuck. Even if ``CONFIGURE_DEPENDS`` works reliably, there
+ is still a cost to perform the check on every rebuild.
+
+ Examples of globbing expressions include:
+
+ ============== ======================================================
+ ``*.cxx`` match all files with extension ``cxx``
+ ``*.vt?`` match all files with extension ``vta``, ..., ``vtz``
+ ``f[3-5].txt`` match files ``f3.txt``, ``f4.txt``, ``f5.txt``
+ ============== ======================================================
+
+ The ``GLOB_RECURSE`` mode will traverse all the subdirectories of the
+ matched directory and match the files. Subdirectories that are symlinks
+ are only traversed if ``FOLLOW_SYMLINKS`` is given or policy
+ :policy:`CMP0009` is not set to ``NEW``.
+
+ .. versionadded:: 3.3
+ By default ``GLOB_RECURSE`` omits directories from result list. Setting
+ ``LIST_DIRECTORIES`` to true adds directories to result list.
+ If ``FOLLOW_SYMLINKS`` is given or policy :policy:`CMP0009` is not set to
+ ``NEW`` then ``LIST_DIRECTORIES`` treats symlinks as directories.
+
+ Examples of recursive globbing include:
+
+ ============== ======================================================
+ ``/dir/*.py`` match all python files in ``/dir`` and subdirectories
+ ============== ======================================================
+
+.. signature::
+ file(MAKE_DIRECTORY [<directories>...])
-.. versionadded:: 3.15
- If ``FOLLOW_SYMLINK_CHAIN`` is specified, ``COPY`` will recursively resolve
- the symlinks at the paths given until a real file is found, and install
- a corresponding symlink in the destination for each symlink encountered. For
- each symlink that is installed, the resolution is stripped of the directory,
- leaving only the filename, meaning that the new symlink points to a file in
- the same directory as the symlink. This feature is useful on some Unix systems,
- where libraries are installed as a chain of symlinks with version numbers, with
- less specific versions pointing to more specific versions.
- ``FOLLOW_SYMLINK_CHAIN`` will install all of these symlinks and the library
- itself into the destination directory. For example, if you have the following
- directory structure:
+ Create the given directories and their parents as needed.
-* ``/opt/foo/lib/libfoo.so.1.2.3``
-* ``/opt/foo/lib/libfoo.so.1.2 -> libfoo.so.1.2.3``
-* ``/opt/foo/lib/libfoo.so.1 -> libfoo.so.1.2``
-* ``/opt/foo/lib/libfoo.so -> libfoo.so.1``
+.. signature::
+ file(REMOVE [<files>...])
+ file(REMOVE_RECURSE [<files>...])
-and you do:
+ Remove the given files. The ``REMOVE_RECURSE`` mode will remove the given
+ files and directories, including non-empty directories. No error is emitted
+ if a given file does not exist. Relative input paths are evaluated with
+ respect to the current source directory.
-.. code-block:: cmake
+ .. versionchanged:: 3.15
+ Empty input paths are ignored with a warning. Previous versions of CMake
+ interpreted empty strings as a relative path with respect to the current
+ directory and removed its contents.
- file(COPY /opt/foo/lib/libfoo.so DESTINATION lib FOLLOW_SYMLINK_CHAIN)
+.. signature::
+ file(RENAME <oldname> <newname> [RESULT <result>] [NO_REPLACE])
-This will install all of the symlinks and ``libfoo.so.1.2.3`` itself into
-``lib``.
+ Move a file or directory within a filesystem from ``<oldname>`` to
+ ``<newname>``, replacing the destination atomically.
-See the :command:`install(DIRECTORY)` command for documentation of
-permissions, ``FILES_MATCHING``, ``PATTERN``, ``REGEX``, and
-``EXCLUDE`` options. Copying directories preserves the structure
-of their content even if options are used to select a subset of
-files.
+ The options are:
-The ``INSTALL`` signature differs slightly from ``COPY``: it prints
-status messages, and ``NO_SOURCE_PERMISSIONS`` is default.
+ ``RESULT <result>``
+ .. versionadded:: 3.21
-Installation scripts generated by the :command:`install` command
-use this signature (with some undocumented options for internal use).
+ Set ``<result>`` variable to ``0`` on success or an error message
+ otherwise. If ``RESULT`` is not specified and the operation fails,
+ an error is emitted.
-.. versionchanged:: 3.22
+ ``NO_REPLACE``
+ .. versionadded:: 3.21
- The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
- default copying behavior of :command:`file(INSTALL)`.
+ If the ``<newname>`` path already exists, do not replace it.
+ If ``RESULT <result>`` is used, the result variable will be
+ set to ``NO_REPLACE``. Otherwise, an error is emitted.
-.. _SIZE:
+.. signature::
+ file(COPY_FILE <oldname> <newname>
+ [RESULT <result>]
+ [ONLY_IF_DIFFERENT]
+ [INPUT_MAY_BE_RECENT])
-.. code-block:: cmake
+ .. versionadded:: 3.21
+ Copy a file from ``<oldname>`` to ``<newname>``. Directories are not
+ supported. Symlinks are ignored and ``<oldfile>``'s content is read and
+ written to ``<newname>`` as a new file.
+
+ The options are:
+
+ ``RESULT <result>``
+ Set ``<result>`` variable to ``0`` on success or an error message
+ otherwise. If ``RESULT`` is not specified and the operation fails,
+ an error is emitted.
+
+ ``ONLY_IF_DIFFERENT``
+ If the ``<newname>`` path already exists, do not replace it if the file's
+ contents are already the same as ``<oldname>`` (this avoids updating
+ ``<newname>``'s timestamp).
+
+ ``INPUT_MAY_BE_RECENT``
+ .. versionadded:: 3.26
+
+ Tell CMake that the input file may have been recently created. This is
+ meaningful only on Windows, where files may be inaccessible for a short
+ time after they are created. With this option, if permission is denied,
+ CMake will retry reading the input a few times.
+
+ This sub-command has some similarities to :command:`configure_file`
+ with the ``COPYONLY`` option. An important difference is that
+ :command:`configure_file` creates a dependency on the source file,
+ so CMake will be re-run if it changes. The ``file(COPY_FILE)``
+ sub-command does not create such a dependency.
+
+ See also the :command:`file(COPY)` sub-command just below which provides
+ further file-copying capabilities.
+
+.. signature::
+ file(COPY [...])
+ file(INSTALL [...])
+
+ The ``COPY`` signature copies files, directories, and symlinks to a
+ destination folder. Relative input paths are evaluated with respect
+ to the current source directory, and a relative destination is
+ evaluated with respect to the current build directory. Copying
+ preserves input file timestamps, and optimizes out a file if it exists
+ at the destination with the same timestamp. Copying preserves input
+ permissions unless explicit permissions or ``NO_SOURCE_PERMISSIONS``
+ are given (default is ``USE_SOURCE_PERMISSIONS``).
+
+ .. code-block:: cmake
+
+ file(<COPY|INSTALL> <files>... DESTINATION <dir>
+ [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS]
+ [FILE_PERMISSIONS <permissions>...]
+ [DIRECTORY_PERMISSIONS <permissions>...]
+ [FOLLOW_SYMLINK_CHAIN]
+ [FILES_MATCHING]
+ [[PATTERN <pattern> | REGEX <regex>]
+ [EXCLUDE] [PERMISSIONS <permissions>...]] [...])
+
+ .. note::
+
+ For a simple file copying operation, the :command:`file(COPY_FILE)`
+ sub-command just above may be easier to use.
+
+ .. versionadded:: 3.15
+ If ``FOLLOW_SYMLINK_CHAIN`` is specified, ``COPY`` will recursively resolve
+ the symlinks at the paths given until a real file is found, and install
+ a corresponding symlink in the destination for each symlink encountered.
+ For each symlink that is installed, the resolution is stripped of the
+ directory, leaving only the filename, meaning that the new symlink points
+ to a file in the same directory as the symlink. This feature is useful on
+ some Unix systems, where libraries are installed as a chain of symlinks
+ with version numbers, with less specific versions pointing to more specific
+ versions. ``FOLLOW_SYMLINK_CHAIN`` will install all of these symlinks and
+ the library itself into the destination directory. For example, if you have
+ the following directory structure:
+
+ * ``/opt/foo/lib/libfoo.so.1.2.3``
+ * ``/opt/foo/lib/libfoo.so.1.2 -> libfoo.so.1.2.3``
+ * ``/opt/foo/lib/libfoo.so.1 -> libfoo.so.1.2``
+ * ``/opt/foo/lib/libfoo.so -> libfoo.so.1``
+
+ and you do:
+
+ .. code-block:: cmake
+
+ file(COPY /opt/foo/lib/libfoo.so DESTINATION lib FOLLOW_SYMLINK_CHAIN)
+
+ This will install all of the symlinks and ``libfoo.so.1.2.3`` itself into
+ ``lib``.
+
+ See the :command:`install(DIRECTORY)` command for documentation of
+ permissions, ``FILES_MATCHING``, ``PATTERN``, ``REGEX``, and
+ ``EXCLUDE`` options. Copying directories preserves the structure
+ of their content even if options are used to select a subset of
+ files.
+
+ The ``INSTALL`` signature differs slightly from ``COPY``: it prints
+ status messages, and ``NO_SOURCE_PERMISSIONS`` is default. Installation
+ scripts generated by the :command:`install` command use this signature
+ (with some undocumented options for internal use).
+
+ .. versionchanged:: 3.22
+
+ The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
+ default copying behavior of :command:`file(INSTALL)`.
+
+.. signature::
file(SIZE <filename> <variable>)
-.. versionadded:: 3.14
-
-Determine the file size of the ``<filename>`` and put the result in
-``<variable>`` variable. Requires that ``<filename>`` is a valid path
-pointing to a file and is readable.
-
-.. _READ_SYMLINK:
+ .. versionadded:: 3.14
-.. code-block:: cmake
+ Determine the file size of the ``<filename>`` and put the result in
+ ``<variable>`` variable. Requires that ``<filename>`` is a valid path
+ pointing to a file and is readable.
+.. signature::
file(READ_SYMLINK <linkname> <variable>)
-.. versionadded:: 3.14
+ .. versionadded:: 3.14
-This subcommand queries the symlink ``<linkname>`` and stores the path it
-points to in the result ``<variable>``. If ``<linkname>`` does not exist or
-is not a symlink, CMake issues a fatal error.
+ Query the symlink ``<linkname>`` and stores the path it points to
+ in the result ``<variable>``. If ``<linkname>`` does not exist
+ or is not a symlink, CMake issues a fatal error.
-Note that this command returns the raw symlink path and does not resolve
-a relative path. The following is an example of how to ensure that an
-absolute path is obtained:
+ Note that this command returns the raw symlink path and does not resolve
+ a relative path. The following is an example of how to ensure that an
+ absolute path is obtained:
-.. code-block:: cmake
+ .. code-block:: cmake
- set(linkname "/path/to/foo.sym")
- file(READ_SYMLINK "${linkname}" result)
- if(NOT IS_ABSOLUTE "${result}")
- get_filename_component(dir "${linkname}" DIRECTORY)
- set(result "${dir}/${result}")
- endif()
-
-.. _CREATE_LINK:
-
-.. code-block:: cmake
+ set(linkname "/path/to/foo.sym")
+ file(READ_SYMLINK "${linkname}" result)
+ if(NOT IS_ABSOLUTE "${result}")
+ get_filename_component(dir "${linkname}" DIRECTORY)
+ set(result "${dir}/${result}")
+ endif()
+.. signature::
file(CREATE_LINK <original> <linkname>
[RESULT <result>] [COPY_ON_ERROR] [SYMBOLIC])
-.. versionadded:: 3.14
-
-Create a link ``<linkname>`` that points to ``<original>``.
-It will be a hard link by default, but providing the ``SYMBOLIC`` option
-results in a symbolic link instead. Hard links require that ``original``
-exists and is a file, not a directory. If ``<linkname>`` already exists,
-it will be overwritten.
-
-The ``<result>`` variable, if specified, receives the status of the operation.
-It is set to ``0`` upon success or an error message otherwise. If ``RESULT``
-is not specified and the operation fails, a fatal error is emitted.
+ .. versionadded:: 3.14
-Specifying ``COPY_ON_ERROR`` enables copying the file as a fallback if
-creating the link fails. It can be useful for handling situations such as
-``<original>`` and ``<linkname>`` being on different drives or mount points,
-which would make them unable to support a hard link.
+ Create a link ``<linkname>`` that points to ``<original>``.
+ It will be a hard link by default, but providing the ``SYMBOLIC`` option
+ results in a symbolic link instead. Hard links require that ``original``
+ exists and is a file, not a directory. If ``<linkname>`` already exists,
+ it will be overwritten.
-.. _CHMOD:
+ The ``<result>`` variable, if specified, receives the status of the
+ operation. It is set to ``0`` upon success or an error message otherwise.
+ If ``RESULT`` is not specified and the operation fails, a fatal error is
+ emitted.
-.. code-block:: cmake
+ Specifying ``COPY_ON_ERROR`` enables copying the file as a fallback if
+ creating the link fails. It can be useful for handling situations such as
+ ``<original>`` and ``<linkname>`` being on different drives or mount points,
+ which would make them unable to support a hard link.
+.. signature::
file(CHMOD <files>... <directories>...
- [PERMISSIONS <permissions>...]
- [FILE_PERMISSIONS <permissions>...]
- [DIRECTORY_PERMISSIONS <permissions>...])
-
-.. versionadded:: 3.19
-
-Set the permissions for the ``<files>...`` and ``<directories>...`` specified.
-Valid permissions are ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``,
-``GROUP_READ``, ``GROUP_WRITE``, ``GROUP_EXECUTE``, ``WORLD_READ``,
-``WORLD_WRITE``, ``WORLD_EXECUTE``, ``SETUID``, ``SETGID``.
-
-Valid combination of keywords are:
+ [PERMISSIONS <permissions>...]
+ [FILE_PERMISSIONS <permissions>...]
+ [DIRECTORY_PERMISSIONS <permissions>...])
-``PERMISSIONS``
- All items are changed.
+ .. versionadded:: 3.19
-``FILE_PERMISSIONS``
- Only files are changed.
+ Set the permissions for the ``<files>...`` and ``<directories>...``
+ specified. Valid permissions are ``OWNER_READ``, ``OWNER_WRITE``,
+ ``OWNER_EXECUTE``, ``GROUP_READ``, ``GROUP_WRITE``, ``GROUP_EXECUTE``,
+ ``WORLD_READ``, ``WORLD_WRITE``, ``WORLD_EXECUTE``, ``SETUID``, ``SETGID``.
-``DIRECTORY_PERMISSIONS``
- Only directories are changed.
+ Valid combination of keywords are:
-``PERMISSIONS`` and ``FILE_PERMISSIONS``
- ``FILE_PERMISSIONS`` overrides ``PERMISSIONS`` for files.
+ ``PERMISSIONS``
+ All items are changed.
-``PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
- ``DIRECTORY_PERMISSIONS`` overrides ``PERMISSIONS`` for directories.
+ ``FILE_PERMISSIONS``
+ Only files are changed.
-``FILE_PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
- Use ``FILE_PERMISSIONS`` for files and ``DIRECTORY_PERMISSIONS`` for
- directories.
+ ``DIRECTORY_PERMISSIONS``
+ Only directories are changed.
+ ``PERMISSIONS`` and ``FILE_PERMISSIONS``
+ ``FILE_PERMISSIONS`` overrides ``PERMISSIONS`` for files.
-.. _CHMOD_RECURSE:
+ ``PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
+ ``DIRECTORY_PERMISSIONS`` overrides ``PERMISSIONS`` for directories.
-.. code-block:: cmake
+ ``FILE_PERMISSIONS`` and ``DIRECTORY_PERMISSIONS``
+ Use ``FILE_PERMISSIONS`` for files and ``DIRECTORY_PERMISSIONS`` for
+ directories.
+.. signature::
file(CHMOD_RECURSE <files>... <directories>...
[PERMISSIONS <permissions>...]
[FILE_PERMISSIONS <permissions>...]
[DIRECTORY_PERMISSIONS <permissions>...])
-.. versionadded:: 3.19
+ .. versionadded:: 3.19
+
+ Same as :cref:`CHMOD`, but change the permissions of files and directories
+ present in the ``<directories>...`` recursively.
-Same as `CHMOD`_, but change the permissions of files and directories present in
-the ``<directories>...`` recursively.
Path Conversion
^^^^^^^^^^^^^^^
-.. _REAL_PATH:
-
-.. code-block:: cmake
-
+.. signature::
file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
-.. versionadded:: 3.19
-
-Compute the absolute path to an existing file or directory with symlinks
-resolved.
-
-``BASE_DIRECTORY <dir>``
- If the provided ``<path>`` is a relative path, it is evaluated relative to the
- given base directory ``<dir>``. If no base directory is provided, the default
- base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`.
+ .. versionadded:: 3.19
-``EXPAND_TILDE``
- .. versionadded:: 3.21
+ Compute the absolute path to an existing file or directory with symlinks
+ resolved. The options are:
- If the ``<path>`` is ``~`` or starts with ``~/``, the ``~`` is replaced by
- the user's home directory. The path to the home directory is obtained from
- environment variables. On Windows, the ``USERPROFILE`` environment variable
- is used, falling back to the ``HOME`` environment variable if ``USERPROFILE``
- is not defined. On all other platforms, only ``HOME`` is used.
+ ``BASE_DIRECTORY <dir>``
+ If the provided ``<path>`` is a relative path, it is evaluated relative
+ to the given base directory ``<dir>``. If no base directory is provided,
+ the default base directory will be :variable:`CMAKE_CURRENT_SOURCE_DIR`.
-.. _RELATIVE_PATH:
+ ``EXPAND_TILDE``
+ .. versionadded:: 3.21
-.. code-block:: cmake
+ If the ``<path>`` is ``~`` or starts with ``~/``, the ``~`` is replaced
+ by the user's home directory. The path to the home directory is obtained
+ from environment variables. On Windows, the ``USERPROFILE`` environment
+ variable is used, falling back to the ``HOME`` environment variable
+ if ``USERPROFILE`` is not defined. On all other platforms, only ``HOME``
+ is used.
+.. signature::
file(RELATIVE_PATH <variable> <directory> <file>)
-Compute the relative path from a ``<directory>`` to a ``<file>`` and
-store it in the ``<variable>``.
-
-.. _TO_CMAKE_PATH:
-.. _TO_NATIVE_PATH:
-
-.. code-block:: cmake
+ Compute the relative path from a ``<directory>`` to a ``<file>`` and
+ store it in the ``<variable>``.
+.. signature::
file(TO_CMAKE_PATH "<path>" <variable>)
file(TO_NATIVE_PATH "<path>" <variable>)
-The ``TO_CMAKE_PATH`` mode converts a native ``<path>`` into a cmake-style
-path with forward-slashes (``/``). The input can be a single path or a
-system search path like ``$ENV{PATH}``. A search path will be converted
-to a cmake-style list separated by ``;`` characters.
+ The ``TO_CMAKE_PATH`` mode converts a native ``<path>`` into a cmake-style
+ path with forward-slashes (``/``). The input can be a single path or a
+ system search path like ``$ENV{PATH}``. A search path will be converted
+ to a cmake-style list separated by ``;`` characters.
-The ``TO_NATIVE_PATH`` mode converts a cmake-style ``<path>`` into a native
-path with platform-specific slashes (``\`` on Windows hosts and ``/``
-elsewhere).
+ The ``TO_NATIVE_PATH`` mode converts a cmake-style ``<path>`` into a native
+ path with platform-specific slashes (``\`` on Windows hosts and ``/``
+ elsewhere).
-Always use double quotes around the ``<path>`` to be sure it is treated
-as a single argument to this command.
+ Always use double quotes around the ``<path>`` to be sure it is treated
+ as a single argument to this command.
Transfer
^^^^^^^^
-.. _DOWNLOAD:
-.. _UPLOAD:
+.. signature::
+ file(DOWNLOAD <url> [<file>] [<options>...])
+ file(UPLOAD <file> <url> [<options>...])
-.. code-block:: cmake
+ The ``DOWNLOAD`` subcommand downloads the given ``<url>`` to a local
+ ``<file>``. The ``UPLOAD`` mode uploads a local ``<file>`` to a given
+ ``<url>``.
- file(DOWNLOAD <url> [<file>] [<options>...])
- file(UPLOAD <file> <url> [<options>...])
+ .. versionadded:: 3.19
+ If ``<file>`` is not specified for ``file(DOWNLOAD)``, the file is not
+ saved. This can be useful if you want to know if a file can be downloaded
+ (for example, to check that it exists) without actually saving it anywhere.
-The ``DOWNLOAD`` subcommand downloads the given ``<url>`` to a local ``<file>``.
-The ``UPLOAD`` mode uploads a local ``<file>`` to a given ``<url>``.
+ Options to both ``DOWNLOAD`` and ``UPLOAD`` are:
-.. versionadded:: 3.19
- If ``<file>`` is not specified for ``file(DOWNLOAD)``, the file is not saved.
- This can be useful if you want to know if a file can be downloaded (for example,
- to check that it exists) without actually saving it anywhere.
+ ``INACTIVITY_TIMEOUT <seconds>``
+ Terminate the operation after a period of inactivity.
-Options to both ``DOWNLOAD`` and ``UPLOAD`` are:
+ ``LOG <variable>``
+ Store a human-readable log of the operation in a variable.
-``INACTIVITY_TIMEOUT <seconds>``
- Terminate the operation after a period of inactivity.
+ ``SHOW_PROGRESS``
+ Print progress information as status messages until the operation is
+ complete.
-``LOG <variable>``
- Store a human-readable log of the operation in a variable.
+ ``STATUS <variable>``
+ Store the resulting status of the operation in a variable.
+ The status is a ``;`` separated list of length 2.
+ The first element is the numeric return value for the operation,
+ and the second element is a string value for the error.
+ A ``0`` numeric error means no error in the operation.
-``SHOW_PROGRESS``
- Print progress information as status messages until the operation is
- complete.
+ ``TIMEOUT <seconds>``
+ Terminate the operation after a given total time has elapsed.
-``STATUS <variable>``
- Store the resulting status of the operation in a variable.
- The status is a ``;`` separated list of length 2.
- The first element is the numeric return value for the operation,
- and the second element is a string value for the error.
- A ``0`` numeric error means no error in the operation.
+ ``USERPWD <username>:<password>``
+ .. versionadded:: 3.7
-``TIMEOUT <seconds>``
- Terminate the operation after a given total time has elapsed.
+ Set username and password for operation.
-``USERPWD <username>:<password>``
- .. versionadded:: 3.7
+ ``HTTPHEADER <HTTP-header>``
+ .. versionadded:: 3.7
- Set username and password for operation.
+ HTTP header for ``DOWNLOAD`` and ``UPLOAD`` operations. ``HTTPHEADER``
+ can be repeated for multiple options:
-``HTTPHEADER <HTTP-header>``
- .. versionadded:: 3.7
+ .. code-block:: cmake
- HTTP header for operation. Suboption can be repeated several times.
+ file(DOWNLOAD <url>
+ HTTPHEADER "Authorization: Bearer <auth-token>"
+ HTTPHEADER "UserAgent: Mozilla/5.0")
-``NETRC <level>``
- .. versionadded:: 3.11
+ ``NETRC <level>``
+ .. versionadded:: 3.11
- Specify whether the .netrc file is to be used for operation. If this
- option is not specified, the value of the :variable:`CMAKE_NETRC` variable
- will be used instead.
- Valid levels are:
+ Specify whether the .netrc file is to be used for operation. If this
+ option is not specified, the value of the :variable:`CMAKE_NETRC`
+ variable will be used instead.
- ``IGNORED``
- The .netrc file is ignored.
- This is the default.
- ``OPTIONAL``
- The .netrc file is optional, and information in the URL is preferred.
- The file will be scanned to find which ever information is not specified
- in the URL.
- ``REQUIRED``
- The .netrc file is required, and information in the URL is ignored.
+ Valid levels are:
-``NETRC_FILE <file>``
- .. versionadded:: 3.11
+ ``IGNORED``
+ The .netrc file is ignored.
+ This is the default.
- Specify an alternative .netrc file to the one in your home directory,
- if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
- is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable will
- be used instead.
+ ``OPTIONAL``
+ The .netrc file is optional, and information in the URL is preferred.
+ The file will be scanned to find which ever information is not
+ specified in the URL.
-``TLS_VERIFY <ON|OFF>``
- Specify whether to verify the server certificate for ``https://`` URLs.
- The default is to *not* verify. If this option is not specified, the value
- of the :variable:`CMAKE_TLS_VERIFY` variable will be used instead.
+ ``REQUIRED``
+ The .netrc file is required, and information in the URL is ignored.
- .. versionadded:: 3.18
- Added support to ``file(UPLOAD)``.
+ ``NETRC_FILE <file>``
+ .. versionadded:: 3.11
-``TLS_CAINFO <file>``
- Specify a custom Certificate Authority file for ``https://`` URLs. If this
- option is not specified, the value of the :variable:`CMAKE_TLS_CAINFO`
- variable will be used instead.
+ Specify an alternative .netrc file to the one in your home directory,
+ if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
+ is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable
+ will be used instead.
- .. versionadded:: 3.18
- Added support to ``file(UPLOAD)``.
+ ``TLS_VERIFY <ON|OFF>``
+ Specify whether to verify the server certificate for ``https://`` URLs.
+ The default is to *not* verify. If this option is not specified, the
+ value of the :variable:`CMAKE_TLS_VERIFY` variable will be used instead.
-For ``https://`` URLs CMake must be built with OpenSSL support. ``TLS/SSL``
-certificates are not checked by default. Set ``TLS_VERIFY`` to ``ON`` to
-check certificates.
+ .. versionadded:: 3.18
+ Added support to ``file(UPLOAD)``.
-Additional options to ``DOWNLOAD`` are:
+ ``TLS_CAINFO <file>``
+ Specify a custom Certificate Authority file for ``https://`` URLs.
+ If this option is not specified, the value of the
+ :variable:`CMAKE_TLS_CAINFO` variable will be used instead.
-``EXPECTED_HASH ALGO=<value>``
+ .. versionadded:: 3.18
+ Added support to ``file(UPLOAD)``.
- Verify that the downloaded content hash matches the expected value, where
- ``ALGO`` is one of the algorithms supported by ``file(<HASH>)``.
- If the file already exists and matches the hash, the download is skipped.
- If the file already exists and does not match the hash, the file is
- downloaded again. If after download the file does not match the hash, the
- operation fails with an error. It is an error to specify this option if
- ``DOWNLOAD`` is not given a ``<file>``.
+ For ``https://`` URLs CMake must be built with OpenSSL support. ``TLS/SSL``
+ certificates are not checked by default. Set ``TLS_VERIFY`` to ``ON`` to
+ check certificates.
-``EXPECTED_MD5 <value>``
- Historical short-hand for ``EXPECTED_HASH MD5=<value>``. It is an error to
- specify this if ``DOWNLOAD`` is not given a ``<file>``.
+ Additional options to ``DOWNLOAD`` are:
-``RANGE_START <value>``
- .. versionadded:: 3.24
+ ``EXPECTED_HASH <algorithm>=<value>``
+ Verify that the downloaded content hash matches the expected value, where
+ ``<algorithm>`` is one of the algorithms supported by :cref:`<HASH>`.
+ If the file already exists and matches the hash, the download is skipped.
+ If the file already exists and does not match the hash, the file is
+ downloaded again. If after download the file does not match the hash, the
+ operation fails with an error. It is an error to specify this option if
+ ``DOWNLOAD`` is not given a ``<file>``.
- Offset of the start of the range in file in bytes. Could be omitted to
- download up to the specified ``RANGE_END``.
+ ``EXPECTED_MD5 <value>``
+ Historical short-hand for ``EXPECTED_HASH MD5=<value>``. It is an error
+ to specify this if ``DOWNLOAD`` is not given a ``<file>``.
-``RANGE_END <value>``
- .. versionadded:: 3.24
+ ``RANGE_START <value>``
+ .. versionadded:: 3.24
- Offset of the end of the range in file in bytes. Could be omitted to
- download everything from the specified ``RANGE_START`` to the end of file.
+ Offset of the start of the range in file in bytes. Could be omitted to
+ download up to the specified ``RANGE_END``.
-Locking
-^^^^^^^
+ ``RANGE_END <value>``
+ .. versionadded:: 3.24
-.. _LOCK:
+ Offset of the end of the range in file in bytes. Could be omitted to
+ download everything from the specified ``RANGE_START`` to the end of
+ file.
-.. code-block:: cmake
+Locking
+^^^^^^^
+.. signature::
file(LOCK <path> [DIRECTORY] [RELEASE]
[GUARD <FUNCTION|FILE|PROCESS>]
[RESULT_VARIABLE <variable>]
[TIMEOUT <seconds>])
-.. versionadded:: 3.2
-
-Lock a file specified by ``<path>`` if no ``DIRECTORY`` option present and file
-``<path>/cmake.lock`` otherwise. File will be locked for scope defined by
-``GUARD`` option (default value is ``PROCESS``). ``RELEASE`` option can be used
-to unlock file explicitly. If option ``TIMEOUT`` is not specified CMake will
-wait until lock succeed or until fatal error occurs. If ``TIMEOUT`` is set to
-``0`` lock will be tried once and result will be reported immediately. If
-``TIMEOUT`` is not ``0`` CMake will try to lock file for the period specified
-by ``<seconds>`` value. Any errors will be interpreted as fatal if there is no
-``RESULT_VARIABLE`` option. Otherwise result will be stored in ``<variable>``
-and will be ``0`` on success or error message on failure.
-
-Note that lock is advisory - there is no guarantee that other processes will
-respect this lock, i.e. lock synchronize two or more CMake instances sharing
-some modifiable resources. Similar logic applied to ``DIRECTORY`` option -
-locking parent directory doesn't prevent other ``LOCK`` commands to lock any
-child directory or file.
-
-Trying to lock file twice is not allowed. Any intermediate directories and
-file itself will be created if they not exist. ``GUARD`` and ``TIMEOUT``
-options ignored on ``RELEASE`` operation.
+ .. versionadded:: 3.2
+
+ Lock a file specified by ``<path>`` if no ``DIRECTORY`` option present and
+ file ``<path>/cmake.lock`` otherwise. The file will be locked for the scope
+ defined by the ``GUARD`` option (default value is ``PROCESS``). The
+ ``RELEASE`` option can be used to unlock the file explicitly. If the
+ ``TIMEOUT`` option is not specified, CMake will wait until the lock succeeds
+ or until a fatal error occurs. If ``TIMEOUT`` is set to ``0``, locking will
+ be tried once and the result will be reported immediately. If ``TIMEOUT``
+ is not ``0``, CMake will try to lock the file for the period specified by
+ the ``TIMEOUT <seconds>`` value. Any errors will be interpreted as fatal if
+ there is no ``RESULT_VARIABLE`` option. Otherwise, the result will be stored
+ in ``<variable>`` and will be ``0`` on success or an error message on
+ failure.
+
+ Note that lock is advisory; there is no guarantee that other processes will
+ respect this lock, i.e. lock synchronize two or more CMake instances sharing
+ some modifiable resources. Similar logic applies to the ``DIRECTORY`` option;
+ locking a parent directory doesn't prevent other ``LOCK`` commands from
+ locking any child directory or file.
+
+ Trying to lock the same file twice is not allowed. Any intermediate
+ directories and the file itself will be created if they not exist. The
+ ``GUARD`` and ``TIMEOUT`` options are ignored on the ``RELEASE`` operation.
Archiving
^^^^^^^^^
-.. _ARCHIVE_CREATE:
-
-.. code-block:: cmake
-
+.. signature::
file(ARCHIVE_CREATE OUTPUT <archive>
PATHS <paths>...
[FORMAT <format>]
- [COMPRESSION <compression> [COMPRESSION_LEVEL <compression-level>]]
+ [COMPRESSION <compression>
+ [COMPRESSION_LEVEL <compression-level>]]
[MTIME <mtime>]
[VERBOSE])
+ :target: ARCHIVE_CREATE
+ :break: verbatim
-.. versionadded:: 3.18
-
-Creates the specified ``<archive>`` file with the files and directories
-listed in ``<paths>``. Note that ``<paths>`` must list actual files or
-directories, wildcards are not supported.
-
-Use the ``FORMAT`` option to specify the archive format. Supported values
-for ``<format>`` are ``7zip``, ``gnutar``, ``pax``, ``paxr``, ``raw`` and
-``zip``. If ``FORMAT`` is not given, the default format is ``paxr``.
+ .. versionadded:: 3.18
-Some archive formats allow the type of compression to be specified.
-The ``7zip`` and ``zip`` archive formats already imply a specific type of
-compression. The other formats use no compression by default, but can be
-directed to do so with the ``COMPRESSION`` option. Valid values for
-``<compression>`` are ``None``, ``BZip2``, ``GZip``, ``XZ``, and ``Zstd``.
+ Creates the specified ``<archive>`` file with the files and directories
+ listed in ``<paths>``. Note that ``<paths>`` must list actual files or
+ directories; wildcards are not supported.
-.. versionadded:: 3.19
- The compression level can be specified with the ``COMPRESSION_LEVEL`` option.
- The ``<compression-level>`` should be between 0-9, with the default being 0.
- The ``COMPRESSION`` option must be present when ``COMPRESSION_LEVEL`` is given.
+ Use the ``FORMAT`` option to specify the archive format. Supported values
+ for ``<format>`` are ``7zip``, ``gnutar``, ``pax``, ``paxr``, ``raw`` and
+ ``zip``. If ``FORMAT`` is not given, the default format is ``paxr``.
-.. versionadded:: 3.26
- The ``<compression-level>`` of the ``Zstd`` algorithm can be set between 0-19.
+ Some archive formats allow the type of compression to be specified.
+ The ``7zip`` and ``zip`` archive formats already imply a specific type of
+ compression. The other formats use no compression by default, but can be
+ directed to do so with the ``COMPRESSION`` option. Valid values for
+ ``<compression>`` are ``None``, ``BZip2``, ``GZip``, ``XZ``, and ``Zstd``.
-.. note::
- With ``FORMAT`` set to ``raw`` only one file will be compressed with the
- compression type specified by ``COMPRESSION``.
+ .. versionadded:: 3.19
+ The compression level can be specified with the ``COMPRESSION_LEVEL``
+ option. The ``<compression-level>`` should be between 0-9, with the
+ default being 0. The ``COMPRESSION`` option must be present when
+ ``COMPRESSION_LEVEL`` is given.
-The ``VERBOSE`` option enables verbose output for the archive operation.
+ .. versionadded:: 3.26
+ The ``<compression-level>`` of the ``Zstd`` algorithm can be set
+ between 0-19.
-To specify the modification time recorded in tarball entries, use
-the ``MTIME`` option.
+ .. note::
+ With ``FORMAT`` set to ``raw``, only one file will be compressed with the
+ compression type specified by ``COMPRESSION``.
-.. _ARCHIVE_EXTRACT:
+ The ``VERBOSE`` option enables verbose output for the archive operation.
-.. code-block:: cmake
+ To specify the modification time recorded in tarball entries, use
+ the ``MTIME`` option.
- file(ARCHIVE_EXTRACT INPUT <archive>
+.. signature::
+ file(ARCHIVE_EXTRACT
+ INPUT <archive>
[DESTINATION <dir>]
[PATTERNS <patterns>...]
[LIST_ONLY]
[VERBOSE]
[TOUCH])
+ :target: ARCHIVE_EXTRACT
-.. versionadded:: 3.18
+ .. versionadded:: 3.18
-Extracts or lists the content of the specified ``<archive>``.
+ Extracts or lists the content of the specified ``<archive>``.
-The directory where the content of the archive will be extracted to can
-be specified using the ``DESTINATION`` option. If the directory does not
-exist, it will be created. If ``DESTINATION`` is not given, the current
-binary directory will be used.
+ The directory where the content of the archive will be extracted to can
+ be specified using the ``DESTINATION`` option. If the directory does not
+ exist, it will be created. If ``DESTINATION`` is not given, the current
+ binary directory will be used.
-If required, you may select which files and directories to list or extract
-from the archive using the specified ``<patterns>``. Wildcards are supported.
-If the ``PATTERNS`` option is not given, the entire archive will be listed or
-extracted.
+ If required, you may select which files and directories to list or extract
+ from the archive using the specified ``<patterns>``. Wildcards are
+ supported. If the ``PATTERNS`` option is not given, the entire archive will
+ be listed or extracted.
-``LIST_ONLY`` will list the files in the archive rather than extract them.
+ ``LIST_ONLY`` will list the files in the archive rather than extract them.
-.. versionadded:: 3.24
- The ``TOUCH`` option gives extracted files a current local
- timestamp instead of extracting file timestamps from the archive.
+ .. versionadded:: 3.24
+ The ``TOUCH`` option gives extracted files a current local
+ timestamp instead of extracting file timestamps from the archive.
-With ``VERBOSE``, the command will produce verbose output.
+ With ``VERBOSE``, the command will produce verbose output.
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index de4cb88435..b82088eaa0 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -367,17 +367,37 @@ The set of installation prefixes is constructed using the following
steps. If ``NO_DEFAULT_PATH`` is specified all ``NO_*`` options are
enabled.
-1. .. versionadded:: 3.12
- Search paths specified in the :variable:`<PackageName>_ROOT` CMake
- variable and the :envvar:`<PackageName>_ROOT` environment variable,
- where ``<PackageName>`` is the package to be found
- (the case-preserved first argument to ``find_package``).
- The package root variables are maintained as a stack so if
- called from within a find module, root paths from the parent's find
- module will also be searched after paths for the current package.
- This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
- the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
- See policy :policy:`CMP0074`.
+1. Search prefixes unique to the current ``<PackageName>`` being found.
+ See policy :policy:`CMP0074`.
+
+ .. versionadded:: 3.12
+
+ Specifically, search prefixes specified by the following variables,
+ in order:
+
+ a. :variable:`<PackageName>_ROOT` CMake variable,
+ where ``<PackageName>`` is the case-preserved package name.
+
+ b. :variable:`<PACKAGENAME>_ROOT` CMake variable,
+ where ``<PACKAGENAME>`` is the upper-cased package name.
+ See policy :policy:`CMP0144`.
+
+ .. versionadded:: 3.27
+
+ c. :envvar:`<PackageName>_ROOT` environment variable,
+ where ``<PackageName>`` is the case-preserved package name.
+
+ d. :envvar:`<PACKAGENAME>_ROOT` environment variable,
+ where ``<PACKAGENAME>`` is the upper-cased package name.
+ See policy :policy:`CMP0144`.
+
+ .. versionadded:: 3.27
+
+ The package root variables are maintained as a stack so if
+ called from within a find module, root paths from the parent's find
+ module will also be searched after paths for the current package.
+ This can be skipped if ``NO_PACKAGE_ROOT_PATH`` is passed or by setting
+ the :variable:`CMAKE_FIND_USE_PACKAGE_ROOT_PATH` to ``FALSE``.
2. Search paths specified in cmake-specific cache variables. These
are intended to be used on the command line with a :option:`-DVAR=VALUE <cmake -D>`.
diff --git a/Help/command/get_filename_component.rst b/Help/command/get_filename_component.rst
index 899ede60d5..7d74a33c57 100644
--- a/Help/command/get_filename_component.rst
+++ b/Help/command/get_filename_component.rst
@@ -4,9 +4,9 @@ get_filename_component
Get a specific component of a full filename.
.. versionchanged:: 3.20
- This command has been superseded by :command:`cmake_path` command, except
- ``REALPATH`` now offered by :ref:`file(REAL_PATH)<REAL_PATH>` command and
- ``PROGRAM`` now available in :command:`separate_arguments(PROGRAM)` command.
+ This command has been superseded by the :command:`cmake_path` command, except
+ for ``REALPATH``, which is now offered by :command:`file(REAL_PATH)`, and
+ ``PROGRAM``, now available in :command:`separate_arguments(PROGRAM)`.
.. versionchanged:: 3.24
The undocumented feature offering the capability to query the ``Windows``
diff --git a/Help/command/if.rst b/Help/command/if.rst
index 684c1135b1..be855e1e0b 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -39,7 +39,7 @@ the ``if``, ``elseif`` and :command:`while` clauses.
Compound conditions are evaluated in the following order of precedence:
-1. Parentheses.
+1. `Parentheses`_.
2. Unary tests such as `EXISTS`_, `COMMAND`_, and `DEFINED`_.
@@ -57,262 +57,284 @@ Compound conditions are evaluated in the following order of precedence:
Basic Expressions
"""""""""""""""""
-``if(<constant>)``
- True if the constant is ``1``, ``ON``, ``YES``, ``TRUE``, ``Y``,
- or a non-zero number (including floating point numbers).
- False if the constant is ``0``, ``OFF``,
- ``NO``, ``FALSE``, ``N``, ``IGNORE``, ``NOTFOUND``, the empty string,
- or ends in the suffix ``-NOTFOUND``. Named boolean constants are
- case-insensitive. If the argument is not one of these specific
- constants, it is treated as a variable or string (see `Variable Expansion`_
- further below) and one of the following two forms applies.
-
-``if(<variable>)``
- True if given a variable that is defined to a value that is not a false
- constant. False otherwise, including if the variable is undefined.
- Note that macro arguments are not variables.
- :ref:`Environment Variables <CMake Language Environment Variables>` also
- cannot be tested this way, e.g. ``if(ENV{some_var})`` will always evaluate
- to false.
-
-``if(<string>)``
- A quoted string always evaluates to false unless:
-
- * The string's value is one of the true constants, or
- * Policy :policy:`CMP0054` is not set to ``NEW`` and the string's value
- happens to be a variable name that is affected by :policy:`CMP0054`'s
- behavior.
+.. signature:: if(<constant>)
+ :target: constant
+
+ True if the constant is ``1``, ``ON``, ``YES``, ``TRUE``, ``Y``,
+ or a non-zero number (including floating point numbers).
+ False if the constant is ``0``, ``OFF``,
+ ``NO``, ``FALSE``, ``N``, ``IGNORE``, ``NOTFOUND``, the empty string,
+ or ends in the suffix ``-NOTFOUND``. Named boolean constants are
+ case-insensitive. If the argument is not one of these specific
+ constants, it is treated as a variable or string (see `Variable Expansion`_
+ further below) and one of the following two forms applies.
+
+.. signature:: if(<variable>)
+ :target: variable
+
+ True if given a variable that is defined to a value that is not a false
+ constant. False otherwise, including if the variable is undefined.
+ Note that macro arguments are not variables.
+ :ref:`Environment Variables <CMake Language Environment Variables>` also
+ cannot be tested this way, e.g. ``if(ENV{some_var})`` will always evaluate
+ to false.
+
+.. signature:: if(<string>)
+ :target: string
+
+ A quoted string always evaluates to false unless:
+
+ * The string's value is one of the true constants, or
+ * Policy :policy:`CMP0054` is not set to ``NEW`` and the string's value
+ happens to be a variable name that is affected by :policy:`CMP0054`'s
+ behavior.
Logic Operators
"""""""""""""""
-.. _NOT:
+.. signature:: if(NOT <condition>)
+
+ True if the condition is not true.
-``if(NOT <condition>)``
- True if the condition is not true.
+.. signature:: if(<cond1> AND <cond2>)
+ :target: AND
-.. _AND:
+ True if both conditions would be considered true individually.
-``if(<cond1> AND <cond2>)``
- True if both conditions would be considered true individually.
+.. signature:: if(<cond1> OR <cond2>)
+ :target: OR
-.. _OR:
+ True if either condition would be considered true individually.
-``if(<cond1> OR <cond2>)``
- True if either condition would be considered true individually.
+.. signature:: if((condition) AND (condition OR (condition)))
+ :target: parentheses
-``if((condition) AND (condition OR (condition)))``
- The conditions inside the parenthesis are evaluated first and then
- the remaining condition is evaluated as in the other examples.
- Where there are nested parenthesis the innermost are evaluated as part
- of evaluating the condition that contains them.
+ The conditions inside the parenthesis are evaluated first and then
+ the remaining condition is evaluated as in the other examples.
+ Where there are nested parenthesis the innermost are evaluated as part
+ of evaluating the condition that contains them.
Existence Checks
""""""""""""""""
-.. _COMMAND:
+.. signature:: if(COMMAND <command-name>)
+
+ True if the given name is a command, macro or function that can be
+ invoked.
+
+.. signature:: if(POLICY <policy-id>)
+
+ True if the given name is an existing policy (of the form ``CMP<NNNN>``).
-``if(COMMAND command-name)``
- True if the given name is a command, macro or function that can be
- invoked.
+.. signature:: if(TARGET <target-name>)
-``if(POLICY policy-id)``
- True if the given name is an existing policy (of the form ``CMP<NNNN>``).
+ True if the given name is an existing logical target name created
+ by a call to the :command:`add_executable`, :command:`add_library`,
+ or :command:`add_custom_target` command that has already been invoked
+ (in any directory).
-``if(TARGET target-name)``
- True if the given name is an existing logical target name created
- by a call to the :command:`add_executable`, :command:`add_library`,
- or :command:`add_custom_target` command that has already been invoked
- (in any directory).
+.. signature:: if(TEST <test-name>)
+
+ .. versionadded:: 3.3
-``if(TEST test-name)``
- .. versionadded:: 3.3
True if the given name is an existing test name created by the
:command:`add_test` command.
-.. _DEFINED:
+.. signature:: if(DEFINED <name>|CACHE{<name>}|ENV{<name>})
-``if(DEFINED <name>|CACHE{<name>}|ENV{<name>})``
- True if a variable, cache variable or environment variable
- with given ``<name>`` is defined. The value of the variable
- does not matter. Note the following caveats:
+ True if a variable, cache variable or environment variable
+ with given ``<name>`` is defined. The value of the variable
+ does not matter. Note the following caveats:
- * Macro arguments are not variables.
- * It is not possible to test directly whether a `<name>` is a non-cache
- variable. The expression ``if(DEFINED someName)`` will evaluate to true
- if either a cache or non-cache variable ``someName`` exists. In
- comparison, the expression ``if(DEFINED CACHE{someName})`` will only
- evaluate to true if a cache variable ``someName`` exists. Both expressions
- need to be tested if you need to know whether a non-cache variable exists:
- ``if(DEFINED someName AND NOT DEFINED CACHE{someName})``.
+ * Macro arguments are not variables.
+ * It is not possible to test directly whether a `<name>` is a non-cache
+ variable. The expression ``if(DEFINED someName)`` will evaluate to true
+ if either a cache or non-cache variable ``someName`` exists. In
+ comparison, the expression ``if(DEFINED CACHE{someName})`` will only
+ evaluate to true if a cache variable ``someName`` exists. Both expressions
+ need to be tested if you need to know whether a non-cache variable exists:
+ ``if(DEFINED someName AND NOT DEFINED CACHE{someName})``.
.. versionadded:: 3.14
Added support for ``CACHE{<name>}`` variables.
-``if(<variable|string> IN_LIST <variable>)``
- .. versionadded:: 3.3
+.. signature:: if(<variable|string> IN_LIST <variable>)
+ :target: IN_LIST
+
+ .. versionadded:: 3.3
+
True if the given element is contained in the named list variable.
File Operations
"""""""""""""""
-.. _EXISTS:
+.. signature:: if(EXISTS <path-to-file-or-directory>)
+
+ True if the named file or directory exists. Behavior is well-defined
+ only for explicit full paths (a leading ``~/`` is not expanded as
+ a home directory and is considered a relative path).
+ Resolves symbolic links, i.e. if the named file or directory is a
+ symbolic link, returns true if the target of the symbolic link exists.
+
+ False if the given path is an empty string.
-``if(EXISTS path-to-file-or-directory)``
- True if the named file or directory exists. Behavior is well-defined
- only for explicit full paths (a leading ``~/`` is not expanded as
- a home directory and is considered a relative path).
- Resolves symbolic links, i.e. if the named file or directory is a
- symbolic link, returns true if the target of the symbolic link exists.
+.. signature:: if(<file1> IS_NEWER_THAN <file2>)
+ :target: IS_NEWER_THAN
- False if the given path is an empty string.
+ True if ``file1`` is newer than ``file2`` or if one of the two files doesn't
+ exist. Behavior is well-defined only for full paths. If the file
+ time stamps are exactly the same, an ``IS_NEWER_THAN`` comparison returns
+ true, so that any dependent build operations will occur in the event
+ of a tie. This includes the case of passing the same file name for
+ both file1 and file2.
-``if(file1 IS_NEWER_THAN file2)``
- True if ``file1`` is newer than ``file2`` or if one of the two files doesn't
- exist. Behavior is well-defined only for full paths. If the file
- time stamps are exactly the same, an ``IS_NEWER_THAN`` comparison returns
- true, so that any dependent build operations will occur in the event
- of a tie. This includes the case of passing the same file name for
- both file1 and file2.
+.. signature:: if(IS_DIRECTORY <path>)
-``if(IS_DIRECTORY path)``
- True if ``path`` is a directory. Behavior is well-defined only
- for full paths.
+ True if ``path`` is a directory. Behavior is well-defined only
+ for full paths.
- False if the given path is an empty string.
+ False if the given path is an empty string.
-``if(IS_SYMLINK file-name)``
- True if the given name is a symbolic link. Behavior is well-defined
- only for full paths.
+.. signature:: if(IS_SYMLINK <path>)
-``if(IS_ABSOLUTE path)``
- True if the given path is an absolute path. Note the following special
- cases:
+ True if the given path is a symbolic link. Behavior is well-defined
+ only for full paths.
- * An empty ``path`` evaluates to false.
- * On Windows hosts, any ``path`` that begins with a drive letter and colon
- (e.g. ``C:``), a forward slash or a backslash will evaluate to true.
- This means a path like ``C:no\base\dir`` will evaluate to true, even
- though the non-drive part of the path is relative.
- * On non-Windows hosts, any ``path`` that begins with a tilde (``~``)
- evaluates to true.
+.. signature:: if(IS_ABSOLUTE <path>)
+
+ True if the given path is an absolute path. Note the following special
+ cases:
+
+ * An empty ``path`` evaluates to false.
+ * On Windows hosts, any ``path`` that begins with a drive letter and colon
+ (e.g. ``C:``), a forward slash or a backslash will evaluate to true.
+ This means a path like ``C:no\base\dir`` will evaluate to true, even
+ though the non-drive part of the path is relative.
+ * On non-Windows hosts, any ``path`` that begins with a tilde (``~``)
+ evaluates to true.
Comparisons
"""""""""""
-.. _MATCHES:
+.. signature:: if(<variable|string> MATCHES <regex>)
+ :target: MATCHES
-``if(<variable|string> MATCHES regex)``
- True if the given string or variable's value matches the given regular
- expression. See :ref:`Regex Specification` for regex format.
+ True if the given string or variable's value matches the given regular
+ expression. See :ref:`Regex Specification` for regex format.
- .. versionadded:: 3.9
- ``()`` groups are captured in :variable:`CMAKE_MATCH_<n>` variables.
+ .. versionadded:: 3.9
+ ``()`` groups are captured in :variable:`CMAKE_MATCH_<n>` variables.
-.. _LESS:
+.. signature:: if(<variable|string> LESS <variable|string>)
+ :target: LESS
-``if(<variable|string> LESS <variable|string>)``
- True if the given string or variable's value is a valid number and less
- than that on the right.
+ True if the given string or variable's value is a valid number and less
+ than that on the right.
-.. _GREATER:
+.. signature:: if(<variable|string> GREATER <variable|string>)
+ :target: GREATER
+
+ True if the given string or variable's value is a valid number and greater
+ than that on the right.
-``if(<variable|string> GREATER <variable|string>)``
- True if the given string or variable's value is a valid number and greater
- than that on the right.
+.. signature:: if(<variable|string> EQUAL <variable|string>)
+ :target: EQUAL
-.. _EQUAL:
+ True if the given string or variable's value is a valid number and equal
+ to that on the right.
-``if(<variable|string> EQUAL <variable|string>)``
- True if the given string or variable's value is a valid number and equal
- to that on the right.
+.. signature:: if(<variable|string> LESS_EQUAL <variable|string>)
+ :target: LESS_EQUAL
-.. _LESS_EQUAL:
+ .. versionadded:: 3.7
-``if(<variable|string> LESS_EQUAL <variable|string>)``
- .. versionadded:: 3.7
True if the given string or variable's value is a valid number and less
than or equal to that on the right.
-.. _GREATER_EQUAL:
+.. signature:: if(<variable|string> GREATER_EQUAL <variable|string>)
+ :target: GREATER_EQUAL
+
+ .. versionadded:: 3.7
-``if(<variable|string> GREATER_EQUAL <variable|string>)``
- .. versionadded:: 3.7
True if the given string or variable's value is a valid number and greater
than or equal to that on the right.
-.. _STRLESS:
+.. signature:: if(<variable|string> STRLESS <variable|string>)
+ :target: STRLESS
-``if(<variable|string> STRLESS <variable|string>)``
- True if the given string or variable's value is lexicographically less
- than the string or variable on the right.
+ True if the given string or variable's value is lexicographically less
+ than the string or variable on the right.
-.. _STRGREATER:
+.. signature:: if(<variable|string> STRGREATER <variable|string>)
+ :target: STRGREATER
-``if(<variable|string> STRGREATER <variable|string>)``
- True if the given string or variable's value is lexicographically greater
- than the string or variable on the right.
+ True if the given string or variable's value is lexicographically greater
+ than the string or variable on the right.
-.. _STREQUAL:
+.. signature:: if(<variable|string> STREQUAL <variable|string>)
+ :target: STREQUAL
-``if(<variable|string> STREQUAL <variable|string>)``
- True if the given string or variable's value is lexicographically equal
- to the string or variable on the right.
+ True if the given string or variable's value is lexicographically equal
+ to the string or variable on the right.
-.. _STRLESS_EQUAL:
+.. signature:: if(<variable|string> STRLESS_EQUAL <variable|string>)
+ :target: STRLESS_EQUAL
+
+ .. versionadded:: 3.7
-``if(<variable|string> STRLESS_EQUAL <variable|string>)``
- .. versionadded:: 3.7
True if the given string or variable's value is lexicographically less
than or equal to the string or variable on the right.
-.. _STRGREATER_EQUAL:
+.. signature:: if(<variable|string> STRGREATER_EQUAL <variable|string>)
+ :target: STRGREATER_EQUAL
+
+ .. versionadded:: 3.7
-``if(<variable|string> STRGREATER_EQUAL <variable|string>)``
- .. versionadded:: 3.7
True if the given string or variable's value is lexicographically greater
than or equal to the string or variable on the right.
Version Comparisons
"""""""""""""""""""
-.. _VERSION_LESS:
+.. signature:: if(<variable|string> VERSION_LESS <variable|string>)
+ :target: VERSION_LESS
-``if(<variable|string> VERSION_LESS <variable|string>)``
- Component-wise integer version number comparison (version format is
- ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
- Any non-integer version component or non-integer trailing part of a version
- component effectively truncates the string at that point.
+ Component-wise integer version number comparison (version format is
+ ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
+ Any non-integer version component or non-integer trailing part of a version
+ component effectively truncates the string at that point.
-.. _VERSION_GREATER:
+.. signature:: if(<variable|string> VERSION_GREATER <variable|string>)
+ :target: VERSION_GREATER
-``if(<variable|string> VERSION_GREATER <variable|string>)``
- Component-wise integer version number comparison (version format is
- ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
- Any non-integer version component or non-integer trailing part of a version
- component effectively truncates the string at that point.
+ Component-wise integer version number comparison (version format is
+ ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
+ Any non-integer version component or non-integer trailing part of a version
+ component effectively truncates the string at that point.
-.. _VERSION_EQUAL:
+.. signature:: if(<variable|string> VERSION_EQUAL <variable|string>)
+ :target: VERSION_EQUAL
-``if(<variable|string> VERSION_EQUAL <variable|string>)``
- Component-wise integer version number comparison (version format is
- ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
- Any non-integer version component or non-integer trailing part of a version
- component effectively truncates the string at that point.
+ Component-wise integer version number comparison (version format is
+ ``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
+ Any non-integer version component or non-integer trailing part of a version
+ component effectively truncates the string at that point.
-.. _VERSION_LESS_EQUAL:
+.. signature:: if(<variable|string> VERSION_LESS_EQUAL <variable|string>)
+ :target: VERSION_LESS_EQUAL
+
+ .. versionadded:: 3.7
-``if(<variable|string> VERSION_LESS_EQUAL <variable|string>)``
- .. versionadded:: 3.7
Component-wise integer version number comparison (version format is
``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
Any non-integer version component or non-integer trailing part of a version
component effectively truncates the string at that point.
-.. _VERSION_GREATER_EQUAL:
+.. signature:: if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)
+ :target: VERSION_GREATER_EQUAL
+
+ .. versionadded:: 3.7
-``if(<variable|string> VERSION_GREATER_EQUAL <variable|string>)``
- .. versionadded:: 3.7
Component-wise integer version number comparison (version format is
``major[.minor[.patch[.tweak]]]``, omitted components are treated as zero).
Any non-integer version component or non-integer trailing part of a version
@@ -321,9 +343,9 @@ Version Comparisons
Path Comparisons
""""""""""""""""
-.. _PATH_EQUAL:
+.. signature:: if(<variable|string> PATH_EQUAL <variable|string>)
+ :target: PATH_EQUAL
-``if(<variable|string> PATH_EQUAL <variable|string>)``
.. versionadded:: 3.24
Compares the two paths component-by-component. Only if every component of
@@ -386,35 +408,35 @@ constant.
Automatic evaluation applies in the other cases whenever the
above-documented condition syntax accepts ``<variable|string>``:
-* The left hand argument to ``MATCHES`` is first checked to see if it is
- a defined variable, if so the variable's value is used, otherwise the
+* The left hand argument to `MATCHES`_ is first checked to see if it is
+ a defined variable. If so, the variable's value is used, otherwise the
original value is used.
-* If the left hand argument to ``MATCHES`` is missing it returns false
+* If the left hand argument to `MATCHES`_ is missing it returns false
without error
-* Both left and right hand arguments to ``LESS``, ``GREATER``, ``EQUAL``,
- ``LESS_EQUAL``, and ``GREATER_EQUAL``, are independently tested to see if
- they are defined variables, if so their defined values are used otherwise
+* Both left and right hand arguments to `LESS`_, `GREATER`_, `EQUAL`_,
+ `LESS_EQUAL`_, and `GREATER_EQUAL`_, are independently tested to see if
+ they are defined variables. If so, their defined values are used otherwise
the original value is used.
-* Both left and right hand arguments to ``STRLESS``, ``STRGREATER``,
- ``STREQUAL``, ``STRLESS_EQUAL``, and ``STRGREATER_EQUAL`` are independently
- tested to see if they are defined variables, if so their defined values are
+* Both left and right hand arguments to `STRLESS`_, `STRGREATER`_,
+ `STREQUAL`_, `STRLESS_EQUAL`_, and `STRGREATER_EQUAL`_ are independently
+ tested to see if they are defined variables. If so, their defined values are
used otherwise the original value is used.
-* Both left and right hand arguments to ``VERSION_LESS``,
- ``VERSION_GREATER``, ``VERSION_EQUAL``, ``VERSION_LESS_EQUAL``, and
- ``VERSION_GREATER_EQUAL`` are independently tested to see if they are defined
- variables, if so their defined values are used otherwise the original value
+* Both left and right hand arguments to `VERSION_LESS`_,
+ `VERSION_GREATER`_, `VERSION_EQUAL`_, `VERSION_LESS_EQUAL`_, and
+ `VERSION_GREATER_EQUAL`_ are independently tested to see if they are defined
+ variables. If so, their defined values are used otherwise the original value
is used.
-* The right hand argument to ``NOT`` is tested to see if it is a boolean
- constant, if so the value is used, otherwise it is assumed to be a
+* The right hand argument to `NOT`_ is tested to see if it is a boolean
+ constant. If so, the value is used, otherwise it is assumed to be a
variable and it is dereferenced.
-* The left and right hand arguments to ``AND`` and ``OR`` are independently
- tested to see if they are boolean constants, if so they are used as
+* The left and right hand arguments to `AND`_ and `OR`_ are independently
+ tested to see if they are boolean constants. If so, they are used as
such, otherwise they are assumed to be variables and are dereferenced.
.. versionchanged:: 3.1
diff --git a/Help/command/install.rst b/Help/command/install.rst
index 126888aa7a..d5092ae12e 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -158,6 +158,9 @@ that may be installed:
``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``);
* On AIX, the *linker import file* created for executables with
:prop_tgt:`ENABLE_EXPORTS` enabled.
+ * On macOS, the *linker import file* created for shared libraries with
+ :prop_tgt:`ENABLE_EXPORTS` enabled (except when marked as ``FRAMEWORK``,
+ see below).
``LIBRARY``
Target artifacts of this kind include:
@@ -308,6 +311,11 @@ the following additional arguments:
value of ``COMPONENT``. It is an error to use this parameter outside of a
``LIBRARY`` block.
+ .. versionchanged:: 3.27
+ This parameter is also usable for an ``ARCHIVE`` block to manage
+ the linker import file created, on macOS, for shared libraries with
+ :prop_tgt:`ENABLE_EXPORTS` enabled.
+
Consider the following example:
.. code-block:: cmake
@@ -342,6 +350,11 @@ the following additional arguments:
option installs nothing. It is an error to use this parameter outside of a
``LIBRARY`` block.
+ .. versionchanged:: 3.27
+ This parameter is also usable for an ``ARCHIVE`` block to manage
+ the linker import file created, on macOS, for shared libraries with
+ :prop_tgt:`ENABLE_EXPORTS` enabled.
+
When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
``COMPONENT`` may be used to specify the installation component of the
namelink, but ``COMPONENT`` should generally be preferred.
@@ -355,6 +368,11 @@ the following additional arguments:
installs the library. It is an error to use this parameter outside of a
``LIBRARY`` block.
+ .. versionchanged:: 3.27
+ This parameter is also usable for an ``ARCHIVE`` block to manage
+ the linker import file created, on macOS, for shared libraries with
+ :prop_tgt:`ENABLE_EXPORTS` enabled.
+
If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
is not recommended to use ``NAMELINK_SKIP`` in conjunction with
``NAMELINK_COMPONENT``.
diff --git a/Help/command/list.rst b/Help/command/list.rst
index 33c4f80d24..191003a700 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -36,23 +36,25 @@ Synopsis
Introduction
^^^^^^^^^^^^
-The list subcommands ``APPEND``, ``INSERT``, ``FILTER``, ``PREPEND``,
-``POP_BACK``, ``POP_FRONT``, ``REMOVE_AT``, ``REMOVE_ITEM``,
-``REMOVE_DUPLICATES``, ``REVERSE`` and ``SORT`` may create
-new values for the list within the current CMake variable scope. Similar to
-the :command:`set` command, the LIST command creates new variable values in
-the current scope, even if the list itself is actually defined in a parent
-scope. To propagate the results of these operations upwards, use
-:command:`set` with ``PARENT_SCOPE``, :command:`set` with
-``CACHE INTERNAL``, or some other means of value propagation.
+The list subcommands :cref:`APPEND`, :cref:`INSERT`, :cref:`FILTER`,
+:cref:`PREPEND`, :cref:`POP_BACK`, :cref:`POP_FRONT`, :cref:`REMOVE_AT`,
+:cref:`REMOVE_ITEM`, :cref:`REMOVE_DUPLICATES`, :cref:`REVERSE` and
+:cref:`SORT` may create new values for the list within the current CMake
+variable scope. Similar to the :command:`set` command, the ``list`` command
+creates new variable values in the current scope, even if the list itself is
+actually defined in a parent scope. To propagate the results of these
+operations upwards, use :command:`set` with ``PARENT_SCOPE``,
+:command:`set` with ``CACHE INTERNAL``, or some other means of value
+propagation.
.. note::
A list in cmake is a ``;`` separated group of strings. To create a
- list the set command can be used. For example, ``set(var a b c d e)``
- creates a list with ``a;b;c;d;e``, and ``set(var "a b c d e")`` creates a
- string or a list with one item in it. (Note macro arguments are not
- variables, and therefore cannot be used in LIST commands.)
+ list, the :command:`set` command can be used. For example,
+ ``set(var a b c d e)`` creates a list with ``a;b;c;d;e``, and
+ ``set(var "a b c d e")`` creates a string or a list with one item in it.
+ (Note that macro arguments are not variables, and therefore cannot be used
+ in ``LIST`` commands.)
.. note::
@@ -66,76 +68,54 @@ scope. To propagate the results of these operations upwards, use
Reading
^^^^^^^
-.. _LENGTH:
-
-.. code-block:: cmake
-
+.. signature::
list(LENGTH <list> <output variable>)
-Returns the list's length.
-
-.. _GET:
-
-.. code-block:: cmake
+ Returns the list's length.
+.. signature::
list(GET <list> <element index> [<element index> ...] <output variable>)
-Returns the list of elements specified by indices from the list.
-
-.. _JOIN:
+ Returns the list of elements specified by indices from the list.
-.. code-block:: cmake
+.. signature:: list(JOIN <list> <glue> <output variable>)
- list(JOIN <list> <glue> <output variable>)
+ .. versionadded:: 3.12
-.. versionadded:: 3.12
-
-Returns a string joining all list's elements using the glue string.
-To join multiple strings, which are not part of a list, use ``JOIN`` operator
-from :command:`string` command.
-
-.. _SUBLIST:
-
-.. code-block:: cmake
+ Returns a string joining all list's elements using the glue string.
+ To join multiple strings, which are not part of a list,
+ use :command:`string(JOIN)`.
+.. signature::
list(SUBLIST <list> <begin> <length> <output variable>)
-.. versionadded:: 3.12
+ .. versionadded:: 3.12
-Returns a sublist of the given list.
-If ``<length>`` is 0, an empty list will be returned.
-If ``<length>`` is -1 or the list is smaller than ``<begin>+<length>`` then
-the remaining elements of the list starting at ``<begin>`` will be returned.
+ Returns a sublist of the given list.
+ If ``<length>`` is 0, an empty list will be returned.
+ If ``<length>`` is -1 or the list is smaller than ``<begin>+<length>`` then
+ the remaining elements of the list starting at ``<begin>`` will be returned.
Search
^^^^^^
-.. _FIND:
-
-.. code-block:: cmake
-
+.. signature::
list(FIND <list> <value> <output variable>)
-Returns the index of the element specified in the list or -1
-if it wasn't found.
+ Returns the index of the element specified in the list
+ or ``-1`` if it wasn't found.
Modification
^^^^^^^^^^^^
-.. _APPEND:
-
-.. code-block:: cmake
-
+.. signature::
list(APPEND <list> [<element> ...])
-Appends elements to the list. If no variable named ``<list>`` exists in the
-current scope its value is treated as empty and the elements are appended to
-that empty list.
-
-.. _FILTER:
-
-.. code-block:: cmake
+ Appends elements to the list. If no variable named ``<list>`` exists in the
+ current scope its value is treated as empty and the elements are appended to
+ that empty list.
+.. signature::
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)
.. versionadded:: 3.6
@@ -146,219 +126,205 @@ In ``REGEX`` mode, items will be matched against the given regular expression.
For more information on regular expressions look under
:ref:`string(REGEX) <Regex Specification>`.
-.. _INSERT:
-
-.. code-block:: cmake
-
+.. signature::
list(INSERT <list> <element_index> <element> [<element> ...])
-Inserts elements to the list to the specified index. It is an
-error to specify an out-of-range index. Valid indexes are 0 to `N`
-where `N` is the length of the list, inclusive. An empty list
-has length 0. If no variable named ``<list>`` exists in the
-current scope its value is treated as empty and the elements are
-inserted in that empty list.
-
-.. _POP_BACK:
-
-.. code-block:: cmake
+ Inserts elements to the list to the specified index. It is an
+ error to specify an out-of-range index. Valid indexes are 0 to `N`
+ where `N` is the length of the list, inclusive. An empty list
+ has length 0. If no variable named ``<list>`` exists in the
+ current scope its value is treated as empty and the elements are
+ inserted in that empty list.
+.. signature::
list(POP_BACK <list> [<out-var>...])
-.. versionadded:: 3.15
+ .. versionadded:: 3.15
-If no variable name is given, removes exactly one element. Otherwise,
-with `N` variable names provided, assign the last `N` elements' values
-to the given variables and then remove the last `N` values from
-``<list>``.
-
-.. _POP_FRONT:
-
-.. code-block:: cmake
+ If no variable name is given, removes exactly one element. Otherwise,
+ with `N` variable names provided, assign the last `N` elements' values
+ to the given variables and then remove the last `N` values from
+ ``<list>``.
+.. signature::
list(POP_FRONT <list> [<out-var>...])
-.. versionadded:: 3.15
-
-If no variable name is given, removes exactly one element. Otherwise,
-with `N` variable names provided, assign the first `N` elements' values
-to the given variables and then remove the first `N` values from
-``<list>``.
+ .. versionadded:: 3.15
-.. _PREPEND:
-
-.. code-block:: cmake
+ If no variable name is given, removes exactly one element. Otherwise,
+ with `N` variable names provided, assign the first `N` elements' values
+ to the given variables and then remove the first `N` values from
+ ``<list>``.
+.. signature::
list(PREPEND <list> [<element> ...])
-.. versionadded:: 3.15
-
-Insert elements to the 0th position in the list. If no variable named
-``<list>`` exists in the current scope its value is treated as empty and
-the elements are prepended to that empty list.
+ .. versionadded:: 3.15
-.. _REMOVE_ITEM:
-
-.. code-block:: cmake
+ Insert elements to the 0th position in the list. If no variable named
+ ``<list>`` exists in the current scope its value is treated as empty and
+ the elements are prepended to that empty list.
+.. signature::
list(REMOVE_ITEM <list> <value> [<value> ...])
-Removes all instances of the given items from the list.
-
-.. _REMOVE_AT:
-
-.. code-block:: cmake
+ Removes all instances of the given items from the list.
+.. signature::
list(REMOVE_AT <list> <index> [<index> ...])
-Removes items at given indices from the list.
-
-.. _REMOVE_DUPLICATES:
-
-.. code-block:: cmake
+ Removes items at given indices from the list.
+.. signature::
list(REMOVE_DUPLICATES <list>)
-Removes duplicated items in the list. The relative order of items is preserved,
-but if duplicates are encountered, only the first instance is preserved.
-
-.. _TRANSFORM:
-
-.. code-block:: cmake
+ Removes duplicated items in the list. The relative order of items
+ is preserved, but if duplicates are encountered,
+ only the first instance is preserved.
+.. signature::
list(TRANSFORM <list> <ACTION> [<SELECTOR>]
- [OUTPUT_VARIABLE <output variable>])
+ [OUTPUT_VARIABLE <output variable>])
-.. versionadded:: 3.12
+ .. versionadded:: 3.12
-Transforms the list by applying an action to all or, by specifying a
-``<SELECTOR>``, to the selected elements of the list, storing the result
-in-place or in the specified output variable.
+ Transforms the list by applying an action to all or, by specifying a
+ ``<SELECTOR>``, to the selected elements of the list, storing the result
+ in-place or in the specified output variable.
-.. note::
+ .. note::
- The ``TRANSFORM`` sub-command does not change the number of elements in the
- list. If a ``<SELECTOR>`` is specified, only some elements will be changed,
- the other ones will remain the same as before the transformation.
+ The ``TRANSFORM`` sub-command does not change the number of elements in the
+ list. If a ``<SELECTOR>`` is specified, only some elements will be changed,
+ the other ones will remain the same as before the transformation.
-``<ACTION>`` specifies the action to apply to the elements of the list.
-The actions have exactly the same semantics as sub-commands of the
-:command:`string` command. ``<ACTION>`` must be one of the following:
+ ``<ACTION>`` specifies the action to apply to the elements of the list.
+ The actions have exactly the same semantics as sub-commands of the
+ :command:`string` command. ``<ACTION>`` must be one of the following:
-``APPEND``, ``PREPEND``: Append, prepend specified value to each element of
-the list.
+ :command:`APPEND <string(APPEND)>`, :command:`PREPEND <string(PREPEND)>`
+ Append, prepend specified value to each element of the list.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
+ list(TRANSFORM <list> <APPEND|PREPEND> <value> ...)
-``TOUPPER``, ``TOLOWER``: Convert each element of the list to upper, lower
-characters.
+ :command:`TOUPPER <string(TOUPPER)>`, :command:`TOLOWER <string(TOLOWER)>`
+ Convert each element of the list to upper, lower characters.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> <TOLOWER|TOUPPER> ...)
+ list(TRANSFORM <list> <TOLOWER|TOUPPER> ...)
-``STRIP``: Remove leading and trailing spaces from each element of the
-list.
+ :command:`STRIP <string(STRIP)>`
+ Remove leading and trailing spaces from each element of the list.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> STRIP ...)
+ list(TRANSFORM <list> STRIP ...)
-``GENEX_STRIP``: Strip any
-:manual:`generator expressions <cmake-generator-expressions(7)>` from each
-element of the list.
+ :command:`GENEX_STRIP <string(GENEX_STRIP)>`
+ Strip any
+ :manual:`generator expressions <cmake-generator-expressions(7)>`
+ from each element of the list.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> GENEX_STRIP ...)
+ list(TRANSFORM <list> GENEX_STRIP ...)
-``REPLACE``: Match the regular expression as many times as possible and
-substitute the replacement expression for the match for each element
-of the list
-(Same semantic as ``REGEX REPLACE`` from :command:`string` command).
+ :command:`REPLACE <string(REGEX REPLACE)>`:
+ Match the regular expression as many times as possible and substitute
+ the replacement expression for the match for each element of the list
+ (same semantic as :command:`string(REGEX REPLACE)`).
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> REPLACE <regular_expression>
- <replace_expression> ...)
+ list(TRANSFORM <list> REPLACE <regular_expression>
+ <replace_expression> ...)
-``<SELECTOR>`` determines which elements of the list will be transformed.
-Only one type of selector can be specified at a time. When given,
-``<SELECTOR>`` must be one of the following:
+ ``<SELECTOR>`` determines which elements of the list will be transformed.
+ Only one type of selector can be specified at a time.
+ When given, ``<SELECTOR>`` must be one of the following:
-``AT``: Specify a list of indexes.
+ ``AT``
+ Specify a list of indexes.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
+ list(TRANSFORM <list> <ACTION> AT <index> [<index> ...] ...)
-``FOR``: Specify a range with, optionally, an increment used to iterate over
-the range.
+ ``FOR``
+ Specify a range with, optionally,
+ an increment used to iterate over the range.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
+ list(TRANSFORM <list> <ACTION> FOR <start> <stop> [<step>] ...)
-``REGEX``: Specify a regular expression. Only elements matching the regular
-expression will be transformed.
+ ``REGEX``
+ Specify a regular expression.
+ Only elements matching the regular expression will be transformed.
- .. code-block:: cmake
+ .. code-block:: cmake
- list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
+ list(TRANSFORM <list> <ACTION> REGEX <regular_expression> ...)
Ordering
^^^^^^^^
-.. _REVERSE:
+.. signature::
+ list(REVERSE <list>)
-.. code-block:: cmake
+ Reverses the contents of the list in-place.
- list(REVERSE <list>)
+.. signature::
+ list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
-Reverses the contents of the list in-place.
+ Sorts the list in-place alphabetically.
-.. _SORT:
+ .. versionadded:: 3.13
+ Added the ``COMPARE``, ``CASE``, and ``ORDER`` options.
-.. code-block:: cmake
+ .. versionadded:: 3.18
+ Added the ``COMPARE NATURAL`` option.
- list(SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
+ Use the ``COMPARE`` keyword to select the comparison method for sorting.
+ The ``<compare>`` option should be one of:
-Sorts the list in-place alphabetically.
+ ``STRING``
+ Sorts a list of strings alphabetically.
+ This is the default behavior if the ``COMPARE`` option is not given.
-.. versionadded:: 3.13
- Added the ``COMPARE``, ``CASE``, and ``ORDER`` options.
+ ``FILE_BASENAME``
+ Sorts a list of pathnames of files by their basenames.
-.. versionadded:: 3.18
- Added the ``COMPARE NATURAL`` option.
+ ``NATURAL``
+ Sorts a list of strings using natural order
+ (see ``strverscmp(3)`` manual), i.e. such that contiguous digits
+ are compared as whole numbers.
+ For example: the following list `10.0 1.1 2.1 8.0 2.0 3.1`
+ will be sorted as `1.1 2.0 2.1 3.1 8.0 10.0` if the ``NATURAL``
+ comparison is selected where it will be sorted as
+ `1.1 10.0 2.0 2.1 3.1 8.0` with the ``STRING`` comparison.
-Use the ``COMPARE`` keyword to select the comparison method for sorting.
-The ``<compare>`` option should be one of:
+ Use the ``CASE`` keyword to select a case sensitive or case insensitive
+ sort mode. The ``<case>`` option should be one of:
-* ``STRING``: Sorts a list of strings alphabetically. This is the
- default behavior if the ``COMPARE`` option is not given.
-* ``FILE_BASENAME``: Sorts a list of pathnames of files by their basenames.
-* ``NATURAL``: Sorts a list of strings using natural order
- (see ``strverscmp(3)`` manual), i.e. such that contiguous digits
- are compared as whole numbers.
- For example: the following list `10.0 1.1 2.1 8.0 2.0 3.1`
- will be sorted as `1.1 2.0 2.1 3.1 8.0 10.0` if the ``NATURAL``
- comparison is selected where it will be sorted as
- `1.1 10.0 2.0 2.1 3.1 8.0` with the ``STRING`` comparison.
+ ``SENSITIVE``
+ List items are sorted in a case-sensitive manner.
+ This is the default behavior if the ``CASE`` option is not given.
-Use the ``CASE`` keyword to select a case sensitive or case insensitive
-sort mode. The ``<case>`` option should be one of:
+ ``INSENSITIVE``
+ List items are sorted case insensitively. The order of
+ items which differ only by upper/lowercase is not specified.
-* ``SENSITIVE``: List items are sorted in a case-sensitive manner. This is
- the default behavior if the ``CASE`` option is not given.
-* ``INSENSITIVE``: List items are sorted case insensitively. The order of
- items which differ only by upper/lowercase is not specified.
+ To control the sort order, the ``ORDER`` keyword can be given.
+ The ``<order>`` option should be one of:
-To control the sort order, the ``ORDER`` keyword can be given.
-The ``<order>`` option should be one of:
+ ``ASCENDING``
+ Sorts the list in ascending order.
+ This is the default behavior when the ``ORDER`` option is not given.
-* ``ASCENDING``: Sorts the list in ascending order. This is the default
- behavior when the ``ORDER`` option is not given.
-* ``DESCENDING``: Sorts the list in descending order.
+ ``DESCENDING``
+ Sorts the list in descending order.
diff --git a/Help/command/math.rst b/Help/command/math.rst
index 8386aabc6f..6989ebcacd 100644
--- a/Help/command/math.rst
+++ b/Help/command/math.rst
@@ -9,7 +9,8 @@ Evaluate a mathematical expression.
Evaluates a mathematical ``<expression>`` and sets ``<variable>`` to the
resulting value. The result of the expression must be representable as a
-64-bit signed integer.
+64-bit signed integer. Floating point inputs are invalid e.g. ``1.1 * 10``.
+Non-integer results e.g. ``3 / 2`` are truncated.
The mathematical expression must be given as a string (i.e. enclosed in
double quotation marks). An example is ``"5 * (10 + 13)"``.
diff --git a/Help/command/separate_arguments.rst b/Help/command/separate_arguments.rst
index f66af35a57..4f0b25eb26 100644
--- a/Help/command/separate_arguments.rst
+++ b/Help/command/separate_arguments.rst
@@ -69,7 +69,7 @@ be one of the following keywords:
The contents of ``out`` will be: ``/path/to/cc;-c;main.c``
-.. _`Parsing C Command-Line Arguments`: https://msdn.microsoft.com/library/a1y7w461.aspx
+.. _`Parsing C Command-Line Arguments`: https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
.. code-block:: cmake
diff --git a/Help/command/set.rst b/Help/command/set.rst
index c724844813..aeb88b360c 100644
--- a/Help/command/set.rst
+++ b/Help/command/set.rst
@@ -8,109 +8,119 @@ and cache entries.
Signatures of this command that specify a ``<value>...`` placeholder
expect zero or more arguments. Multiple arguments will be joined as
-a :ref:`semicolon-separated list <CMake Language Lists>` to form the actual variable
-value to be set. Zero arguments will cause normal variables to be
-unset. See the :command:`unset` command to unset variables explicitly.
+a :ref:`semicolon-separated list <CMake Language Lists>` to form the
+actual variable value to be set.
Set Normal Variable
^^^^^^^^^^^^^^^^^^^
-.. code-block:: cmake
-
+.. signature::
set(<variable> <value>... [PARENT_SCOPE])
+ :target: normal
+
+ Set or unset ``<variable>`` in the current function or directory scope:
+
+ * If at least one ``<value>...`` is given, set the variable to that value.
+ * If no value is given, unset the variable. This is equivalent to
+ :command:`unset(<variable>) <unset>`.
-Sets the given ``<variable>`` in the current function or directory scope.
+ If the ``PARENT_SCOPE`` option is given the variable will be set in
+ the scope above the current scope. Each new directory or :command:`function`
+ command creates a new scope. A scope can also be created with the
+ :command:`block` command. This command will set the value of a variable into
+ the parent directory, calling function or encompassing scope (whichever is
+ applicable to the case at hand). The previous state of the variable's value
+ stays the same in the current scope (e.g., if it was undefined before, it is
+ still undefined and if it had a value, it is still that value).
-If the ``PARENT_SCOPE`` option is given the variable will be set in
-the scope above the current scope. Each new directory or :command:`function`
-command creates a new scope. A scope can also be created with the
-:command:`block` command. This command will set the value of a variable into
-the parent directory, calling function or encompassing scope (whichever is
-applicable to the case at hand). The previous state of the variable's value
-stays the same in the current scope (e.g., if it was undefined before, it is
-still undefined and if it had a value, it is still that value).
+ The :command:`block(PROPAGATE)` and :command:`return(PROPAGATE)` commands
+ can be used as an alternate method to the :command:`set(PARENT_SCOPE)`
+ and :command:`unset(PARENT_SCOPE)` commands to update the parent scope.
-The :command:`block(PROPAGATE)` and :command:`return(PROPAGATE)` commands can
-be used as an alternate method to the :command:`set(PARENT_SCOPE)` and
-:command:`unset(PARENT_SCOPE)` commands to update the parent scope.
+.. include:: UNSET_NOTE.txt
Set Cache Entry
^^^^^^^^^^^^^^^
-.. code-block:: cmake
-
+.. signature::
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
-
-Sets the given cache ``<variable>`` (cache entry). Since cache entries
-are meant to provide user-settable values this does not overwrite
-existing cache entries by default. Use the ``FORCE`` option to
-overwrite existing entries.
-
-The ``<type>`` must be specified as one of:
-
-``BOOL``
- Boolean ``ON/OFF`` value. :manual:`cmake-gui(1)` offers a checkbox.
-
-``FILEPATH``
- Path to a file on disk. :manual:`cmake-gui(1)` offers a file dialog.
-
-``PATH``
- Path to a directory on disk. :manual:`cmake-gui(1)` offers a file dialog.
-
-``STRING``
- A line of text. :manual:`cmake-gui(1)` offers a text field or a
- drop-down selection if the :prop_cache:`STRINGS` cache entry
- property is set.
-
-``INTERNAL``
- A line of text. :manual:`cmake-gui(1)` does not show internal entries.
- They may be used to store variables persistently across runs.
- Use of this type implies ``FORCE``.
-
-The ``<docstring>`` must be specified as a line of text providing
-a quick summary of the option for presentation to :manual:`cmake-gui(1)`
-users.
-
-If the cache entry does not exist prior to the call or the ``FORCE``
-option is given then the cache entry will be set to the given value.
-
-.. note::
-
- The content of the cache variable will not be directly accessible if a normal
- variable of the same name already exists (see :ref:`rules of variable
- evaluation <CMake Language Variables>`). If policy :policy:`CMP0126` is set
- to ``OLD``, any normal variable binding in the current scope will be removed.
-
-It is possible for the cache entry to exist prior to the call but
-have no type set if it was created on the :manual:`cmake(1)` command
-line by a user through the :option:`-D\<var\>=\<value\> <cmake -D>` option without
-specifying a type. In this case the ``set`` command will add the
-type. Furthermore, if the ``<type>`` is ``PATH`` or ``FILEPATH``
-and the ``<value>`` provided on the command line is a relative path,
-then the ``set`` command will treat the path as relative to the
-current working directory and convert it to an absolute path.
+ :target: CACHE
+
+ Sets the given cache ``<variable>`` (cache entry). Since cache entries
+ are meant to provide user-settable values this does not overwrite
+ existing cache entries by default. Use the ``FORCE`` option to
+ overwrite existing entries.
+
+ The ``<type>`` must be specified as one of:
+
+ ``BOOL``
+ Boolean ``ON/OFF`` value.
+ :manual:`cmake-gui(1)` offers a checkbox.
+
+ ``FILEPATH``
+ Path to a file on disk.
+ :manual:`cmake-gui(1)` offers a file dialog.
+
+ ``PATH``
+ Path to a directory on disk.
+ :manual:`cmake-gui(1)` offers a file dialog.
+
+ ``STRING``
+ A line of text.
+ :manual:`cmake-gui(1)` offers a text field or a drop-down selection
+ if the :prop_cache:`STRINGS` cache entry property is set.
+
+ ``INTERNAL``
+ A line of text.
+ :manual:`cmake-gui(1)` does not show internal entries.
+ They may be used to store variables persistently across runs.
+ Use of this type implies ``FORCE``.
+
+ The ``<docstring>`` must be specified as a line of text
+ providing a quick summary of the option
+ for presentation to :manual:`cmake-gui(1)` users.
+
+ If the cache entry does not exist prior to the call or the ``FORCE``
+ option is given then the cache entry will be set to the given value.
+
+ .. note::
+
+ The content of the cache variable will not be directly accessible
+ if a normal variable of the same name already exists
+ (see :ref:`rules of variable evaluation <CMake Language Variables>`).
+ If policy :policy:`CMP0126` is set to ``OLD``, any normal variable
+ binding in the current scope will be removed.
+
+ It is possible for the cache entry to exist prior to the call but
+ have no type set if it was created on the :manual:`cmake(1)` command
+ line by a user through the :option:`-D\<var\>=\<value\> <cmake -D>` option
+ without specifying a type. In this case the ``set`` command will add the
+ type. Furthermore, if the ``<type>`` is ``PATH`` or ``FILEPATH``
+ and the ``<value>`` provided on the command line is a relative path,
+ then the ``set`` command will treat the path as relative to the
+ current working directory and convert it to an absolute path.
Set Environment Variable
^^^^^^^^^^^^^^^^^^^^^^^^
-.. code-block:: cmake
-
+.. signature::
set(ENV{<variable>} [<value>])
+ :target: ENV
-Sets an :manual:`Environment Variable <cmake-env-variables(7)>`
-to the given value.
-Subsequent calls of ``$ENV{<variable>}`` will return this new value.
+ Sets an :manual:`Environment Variable <cmake-env-variables(7)>`
+ to the given value.
+ Subsequent calls of ``$ENV{<variable>}`` will return this new value.
-This command affects only the current CMake process, not the process
-from which CMake was called, nor the system environment at large,
-nor the environment of subsequent build or test processes.
+ This command affects only the current CMake process, not the process
+ from which CMake was called, nor the system environment at large,
+ nor the environment of subsequent build or test processes.
-If no argument is given after ``ENV{<variable>}`` or if ``<value>`` is
-an empty string, then this command will clear any existing value of the
-environment variable.
+ If no argument is given after ``ENV{<variable>}`` or if ``<value>`` is
+ an empty string, then this command will clear any existing value of the
+ environment variable.
-Arguments after ``<value>`` are ignored. If extra arguments are found,
-then an author warning is issued.
+ Arguments after ``<value>`` are ignored. If extra arguments are found,
+ then an author warning is issued.
See Also
^^^^^^^^
diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst
index d446a2d6fe..8dd4b94b9f 100644
--- a/Help/command/set_property.rst
+++ b/Help/command/set_property.rst
@@ -82,15 +82,15 @@ It must be one of the following:
to the installation prefix.
``TEST``
- Scope may name zero or more existing tests.
- See also the :command:`set_tests_properties` command.
+ Scope is limited to the directory the command is called in. It may name zero
+ or more existing tests. See also command :command:`set_tests_properties`.
Test property values may be specified using
:manual:`generator expressions <cmake-generator-expressions(7)>`
for tests created by the :command:`add_test(NAME)` signature.
``CACHE``
- Scope must name zero or more cache existing entries.
+ Scope must name zero or more existing cache entries.
The required ``PROPERTY`` option is immediately followed by the name of
the property to set. Remaining arguments are used to compose the
diff --git a/Help/command/string.rst b/Help/command/string.rst
index c24b9bc16a..e226aa1a48 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -32,7 +32,7 @@ Synopsis
string(`COMPARE`_ <op> <string1> <string2> <out-var>)
`Hashing`_
- string(`\<HASH\> <HASH_>`_ <out-var> <input>)
+ string(`\<HASH\>`_ <out-var> <input>)
`Generation`_
string(`ASCII`_ <number>... <out-var>)
@@ -45,16 +45,16 @@ Synopsis
`JSON`_
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
- {:ref:`GET <JSON_GET>` | :ref:`TYPE <JSON_TYPE>` | :ref:`LENGTH <JSON_LENGTH>` | :ref:`REMOVE <JSON_REMOVE>`}
+ {`GET <JSON GET_>`_ | `TYPE <JSON TYPE_>`_ | `LENGTH <JSON LENGTH_>`_ | `REMOVE <JSON REMOVE_>`_}
<json-string> <member|index> [<member|index> ...])
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
- :ref:`MEMBER <JSON_MEMBER>` <json-string>
+ `MEMBER <JSON MEMBER_>`_ <json-string>
[<member|index> ...] <index>)
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
- :ref:`SET <JSON_SET>` <json-string>
+ `SET <JSON SET_>`_ <json-string>
<member|index> [<member|index> ...] <value>)
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
- :ref:`EQUAL <JSON_EQUAL>` <json-string1> <json-string2>)
+ `EQUAL <JSON EQUAL_>`_ <json-string1> <json-string2>)
Search and Replace
^^^^^^^^^^^^^^^^^^
@@ -62,75 +62,60 @@ Search and Replace
Search and Replace With Plain Strings
"""""""""""""""""""""""""""""""""""""
-.. _FIND:
-
-.. code-block:: cmake
-
+.. signature::
string(FIND <string> <substring> <output_variable> [REVERSE])
-Return the position where the given ``<substring>`` was found in
-the supplied ``<string>``. If the ``REVERSE`` flag was used, the command will
-search for the position of the last occurrence of the specified
-``<substring>``. If the ``<substring>`` is not found, a position of -1 is
-returned.
-
-The ``string(FIND)`` subcommand treats all strings as ASCII-only characters.
-The index stored in ``<output_variable>`` will also be counted in bytes,
-so strings containing multi-byte characters may lead to unexpected results.
+ Return the position where the given ``<substring>`` was found in
+ the supplied ``<string>``. If the ``REVERSE`` flag was used, the command
+ will search for the position of the last occurrence of the specified
+ ``<substring>``. If the ``<substring>`` is not found, a position of -1 is
+ returned.
-.. _REPLACE:
-
-.. code-block:: cmake
+ The ``string(FIND)`` subcommand treats all strings as ASCII-only characters.
+ The index stored in ``<output_variable>`` will also be counted in bytes,
+ so strings containing multi-byte characters may lead to unexpected results.
+.. signature::
string(REPLACE <match_string>
<replace_string> <output_variable>
<input> [<input>...])
-Replace all occurrences of ``<match_string>`` in the ``<input>``
-with ``<replace_string>`` and store the result in the ``<output_variable>``.
+ Replace all occurrences of ``<match_string>`` in the ``<input>``
+ with ``<replace_string>`` and store the result in the ``<output_variable>``.
Search and Replace With Regular Expressions
"""""""""""""""""""""""""""""""""""""""""""
-.. _`REGEX MATCH`:
-
-.. code-block:: cmake
-
+.. signature::
string(REGEX MATCH <regular_expression>
<output_variable> <input> [<input>...])
-Match the ``<regular_expression>`` once and store the match in the
-``<output_variable>``.
-All ``<input>`` arguments are concatenated before matching.
-Regular expressions are specified in the subsection just below.
-
-.. _`REGEX MATCHALL`:
-
-.. code-block:: cmake
+ Match the ``<regular_expression>`` once and store the match in the
+ ``<output_variable>``.
+ All ``<input>`` arguments are concatenated before matching.
+ Regular expressions are specified in the subsection just below.
+.. signature::
string(REGEX MATCHALL <regular_expression>
<output_variable> <input> [<input>...])
-Match the ``<regular_expression>`` as many times as possible and store the
-matches in the ``<output_variable>`` as a list.
-All ``<input>`` arguments are concatenated before matching.
-
-.. _`REGEX REPLACE`:
-
-.. code-block:: cmake
+ Match the ``<regular_expression>`` as many times as possible and store the
+ matches in the ``<output_variable>`` as a list.
+ All ``<input>`` arguments are concatenated before matching.
+.. signature::
string(REGEX REPLACE <regular_expression>
<replacement_expression> <output_variable>
<input> [<input>...])
-Match the ``<regular_expression>`` as many times as possible and substitute
-the ``<replacement_expression>`` for the match in the output.
-All ``<input>`` arguments are concatenated before matching.
+ Match the ``<regular_expression>`` as many times as possible and substitute
+ the ``<replacement_expression>`` for the match in the output.
+ All ``<input>`` arguments are concatenated before matching.
-The ``<replacement_expression>`` may refer to parenthesis-delimited
-subexpressions of the match using ``\1``, ``\2``, ..., ``\9``. Note that
-two backslashes (``\\1``) are required in CMake code to get a backslash
-through argument parsing.
+ The ``<replacement_expression>`` may refer to parenthesis-delimited
+ subexpressions of the match using ``\1``, ``\2``, ..., ``\9``. Note that
+ two backslashes (``\\1``) are required in CMake code to get a backslash
+ through argument parsing.
.. _`Regex Specification`:
@@ -201,130 +186,100 @@ newlines, and backslashes (respectively) to pass in a regex. For example:
Manipulation
^^^^^^^^^^^^
-.. _APPEND:
-
-.. code-block:: cmake
-
+.. signature::
string(APPEND <string_variable> [<input>...])
-.. versionadded:: 3.4
-
-Append all the ``<input>`` arguments to the string.
+ .. versionadded:: 3.4
-.. _PREPEND:
-
-.. code-block:: cmake
+ Append all the ``<input>`` arguments to the string.
+.. signature::
string(PREPEND <string_variable> [<input>...])
-.. versionadded:: 3.10
-
-Prepend all the ``<input>`` arguments to the string.
-
-.. _CONCAT:
+ .. versionadded:: 3.10
-.. code-block:: cmake
+ Prepend all the ``<input>`` arguments to the string.
+.. signature::
string(CONCAT <output_variable> [<input>...])
-Concatenate all the ``<input>`` arguments together and store
-the result in the named ``<output_variable>``.
-
-.. _JOIN:
-
-.. code-block:: cmake
+ Concatenate all the ``<input>`` arguments together and store
+ the result in the named ``<output_variable>``.
+.. signature::
string(JOIN <glue> <output_variable> [<input>...])
-.. versionadded:: 3.12
-
-Join all the ``<input>`` arguments together using the ``<glue>``
-string and store the result in the named ``<output_variable>``.
-
-To join a list's elements, prefer to use the ``JOIN`` operator
-from the :command:`list` command. This allows for the elements to have
-special characters like ``;`` in them.
+ .. versionadded:: 3.12
-.. _TOLOWER:
+ Join all the ``<input>`` arguments together using the ``<glue>``
+ string and store the result in the named ``<output_variable>``.
-.. code-block:: cmake
+ To join a list's elements, prefer to use the ``JOIN`` operator
+ from the :command:`list` command. This allows for the elements to have
+ special characters like ``;`` in them.
+.. signature::
string(TOLOWER <string> <output_variable>)
-Convert ``<string>`` to lower characters.
-
-.. _TOUPPER:
-
-.. code-block:: cmake
+ Convert ``<string>`` to lower characters.
+.. signature::
string(TOUPPER <string> <output_variable>)
-Convert ``<string>`` to upper characters.
-
-.. _LENGTH:
-
-.. code-block:: cmake
+ Convert ``<string>`` to upper characters.
+.. signature::
string(LENGTH <string> <output_variable>)
-Store in an ``<output_variable>`` a given string's length in bytes.
-Note that this means if ``<string>`` contains multi-byte characters, the
-result stored in ``<output_variable>`` will *not* be the number of characters.
-
-.. _SUBSTRING:
-
-.. code-block:: cmake
+ Store in an ``<output_variable>`` a given string's length in bytes.
+ Note that this means if ``<string>`` contains multi-byte characters,
+ the result stored in ``<output_variable>`` will *not* be
+ the number of characters.
+.. signature::
string(SUBSTRING <string> <begin> <length> <output_variable>)
-Store in an ``<output_variable>`` a substring of a given ``<string>``. If
-``<length>`` is ``-1`` the remainder of the string starting at ``<begin>``
-will be returned.
-
-.. versionchanged:: 3.2
- If ``<string>`` is shorter than ``<length>`` then the end of the string
- is used instead. Previous versions of CMake reported an error in this case.
-
-Both ``<begin>`` and ``<length>`` are counted in bytes, so care must
-be exercised if ``<string>`` could contain multi-byte characters.
+ Store in an ``<output_variable>`` a substring of a given ``<string>``. If
+ ``<length>`` is ``-1`` the remainder of the string starting at ``<begin>``
+ will be returned.
-.. _STRIP:
+ .. versionchanged:: 3.2
+ If ``<string>`` is shorter than ``<length>``
+ then the end of the string is used instead.
+ Previous versions of CMake reported an error in this case.
-.. code-block:: cmake
+ Both ``<begin>`` and ``<length>`` are counted in bytes, so care must
+ be exercised if ``<string>`` could contain multi-byte characters.
+.. signature::
string(STRIP <string> <output_variable>)
-Store in an ``<output_variable>`` a substring of a given ``<string>`` with
-leading and trailing spaces removed.
-
-.. _GENEX_STRIP:
-
-.. code-block:: cmake
+ Store in an ``<output_variable>`` a substring of a given ``<string>``
+ with leading and trailing spaces removed.
+.. signature::
string(GENEX_STRIP <string> <output_variable>)
-.. versionadded:: 3.1
-
-Strip any :manual:`generator expressions <cmake-generator-expressions(7)>`
-from the input ``<string>`` and store the result in the ``<output_variable>``.
-
-.. _REPEAT:
+ .. versionadded:: 3.1
-.. code-block:: cmake
+ Strip any :manual:`generator expressions <cmake-generator-expressions(7)>`
+ from the input ``<string>`` and store the result
+ in the ``<output_variable>``.
+.. signature::
string(REPEAT <string> <count> <output_variable>)
-.. versionadded:: 3.15
+ .. versionadded:: 3.15
-Produce the output string as the input ``<string>`` repeated ``<count>`` times.
+ Produce the output string as the input ``<string>``
+ repeated ``<count>`` times.
Comparison
^^^^^^^^^^
.. _COMPARE:
-.. code-block:: cmake
-
+.. signature::
string(COMPARE LESS <string1> <string2> <output_variable>)
string(COMPARE GREATER <string1> <string2> <output_variable>)
string(COMPARE EQUAL <string1> <string2> <output_variable>)
@@ -332,240 +287,217 @@ Comparison
string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)
-Compare the strings and store true or false in the ``<output_variable>``.
+ Compare the strings and store true or false in the ``<output_variable>``.
-.. versionadded:: 3.7
- Added the ``LESS_EQUAL`` and ``GREATER_EQUAL`` options.
+ .. versionadded:: 3.7
+ Added the ``LESS_EQUAL`` and ``GREATER_EQUAL`` options.
.. _`Supported Hash Algorithms`:
Hashing
^^^^^^^
-.. _`HASH`:
-
-.. code-block:: cmake
-
+.. signature::
string(<HASH> <output_variable> <input>)
+ :target: <HASH>
+
+ Compute a cryptographic hash of the ``<input>`` string.
+ The supported ``<HASH>`` algorithm names are:
+
+ ``MD5``
+ Message-Digest Algorithm 5, RFC 1321.
+ ``SHA1``
+ US Secure Hash Algorithm 1, RFC 3174.
+ ``SHA224``
+ US Secure Hash Algorithms, RFC 4634.
+ ``SHA256``
+ US Secure Hash Algorithms, RFC 4634.
+ ``SHA384``
+ US Secure Hash Algorithms, RFC 4634.
+ ``SHA512``
+ US Secure Hash Algorithms, RFC 4634.
+ ``SHA3_224``
+ Keccak SHA-3.
+ ``SHA3_256``
+ Keccak SHA-3.
+ ``SHA3_384``
+ Keccak SHA-3.
+ ``SHA3_512``
+ Keccak SHA-3.
-Compute a cryptographic hash of the ``<input>`` string.
-The supported ``<HASH>`` algorithm names are:
-
-``MD5``
- Message-Digest Algorithm 5, RFC 1321.
-``SHA1``
- US Secure Hash Algorithm 1, RFC 3174.
-``SHA224``
- US Secure Hash Algorithms, RFC 4634.
-``SHA256``
- US Secure Hash Algorithms, RFC 4634.
-``SHA384``
- US Secure Hash Algorithms, RFC 4634.
-``SHA512``
- US Secure Hash Algorithms, RFC 4634.
-``SHA3_224``
- Keccak SHA-3.
-``SHA3_256``
- Keccak SHA-3.
-``SHA3_384``
- Keccak SHA-3.
-``SHA3_512``
- Keccak SHA-3.
-
-.. versionadded:: 3.8
- Added the ``SHA3_*`` hash algorithms.
+ .. versionadded:: 3.8
+ Added the ``SHA3_*`` hash algorithms.
Generation
^^^^^^^^^^
-.. _ASCII:
-
-.. code-block:: cmake
-
+.. signature::
string(ASCII <number> [<number> ...] <output_variable>)
-Convert all numbers into corresponding ASCII characters.
-
-.. _HEX:
-
-.. code-block:: cmake
+ Convert all numbers into corresponding ASCII characters.
+.. signature::
string(HEX <string> <output_variable>)
-.. versionadded:: 3.18
-
-Convert each byte in the input ``<string>`` to its hexadecimal representation
-and store the concatenated hex digits in the ``<output_variable>``. Letters in
-the output (``a`` through ``f``) are in lowercase.
+ .. versionadded:: 3.18
-.. _CONFIGURE:
-
-.. code-block:: cmake
+ Convert each byte in the input ``<string>`` to its hexadecimal representation
+ and store the concatenated hex digits in the ``<output_variable>``.
+ Letters in the output (``a`` through ``f``) are in lowercase.
+.. signature::
string(CONFIGURE <string> <output_variable>
[@ONLY] [ESCAPE_QUOTES])
-Transform a ``<string>`` like :command:`configure_file` transforms a file.
-
-.. _MAKE_C_IDENTIFIER:
-
-.. code-block:: cmake
+ Transform a ``<string>`` like :command:`configure_file` transforms a file.
+.. signature::
string(MAKE_C_IDENTIFIER <string> <output_variable>)
-Convert each non-alphanumeric character in the input ``<string>`` to an
-underscore and store the result in the ``<output_variable>``. If the first
-character of the ``<string>`` is a digit, an underscore will also be prepended
-to the result.
-
-.. _RANDOM:
-
-.. code-block:: cmake
+ Convert each non-alphanumeric character in the input ``<string>`` to an
+ underscore and store the result in the ``<output_variable>``. If the first
+ character of the ``<string>`` is a digit, an underscore will also be
+ prepended to the result.
+.. signature::
string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>]
[RANDOM_SEED <seed>] <output_variable>)
-Return a random string of given ``<length>`` consisting of
-characters from the given ``<alphabet>``. Default length is 5 characters
-and default alphabet is all numbers and upper and lower case letters.
-If an integer ``RANDOM_SEED`` is given, its value will be used to seed the
-random number generator.
-
-.. _TIMESTAMP:
-
-.. code-block:: cmake
+ Return a random string of given ``<length>`` consisting of
+ characters from the given ``<alphabet>``. Default length is 5 characters
+ and default alphabet is all numbers and upper and lower case letters.
+ If an integer ``RANDOM_SEED`` is given, its value will be used to seed the
+ random number generator.
+.. signature::
string(TIMESTAMP <output_variable> [<format_string>] [UTC])
-Write a string representation of the current date
-and/or time to the ``<output_variable>``.
-
-If the command is unable to obtain a timestamp, the ``<output_variable>``
-will be set to the empty string ``""``.
+ Write a string representation of the current date
+ and/or time to the ``<output_variable>``.
-The optional ``UTC`` flag requests the current date/time representation to
-be in Coordinated Universal Time (UTC) rather than local time.
+ If the command is unable to obtain a timestamp, the ``<output_variable>``
+ will be set to the empty string ``""``.
-The optional ``<format_string>`` may contain the following format
-specifiers:
-
-``%%``
- .. versionadded:: 3.8
+ The optional ``UTC`` flag requests the current date/time representation to
+ be in Coordinated Universal Time (UTC) rather than local time.
- A literal percent sign (%).
+ The optional ``<format_string>`` may contain the following format
+ specifiers:
-``%d``
- The day of the current month (01-31).
+ ``%%``
+ .. versionadded:: 3.8
-``%H``
- The hour on a 24-hour clock (00-23).
+ A literal percent sign (%).
-``%I``
- The hour on a 12-hour clock (01-12).
+ ``%d``
+ The day of the current month (01-31).
-``%j``
- The day of the current year (001-366).
+ ``%H``
+ The hour on a 24-hour clock (00-23).
-``%m``
- The month of the current year (01-12).
+ ``%I``
+ The hour on a 12-hour clock (01-12).
-``%b``
- .. versionadded:: 3.7
+ ``%j``
+ The day of the current year (001-366).
- Abbreviated month name (e.g. Oct).
+ ``%m``
+ The month of the current year (01-12).
-``%B``
- .. versionadded:: 3.10
+ ``%b``
+ .. versionadded:: 3.7
- Full month name (e.g. October).
+ Abbreviated month name (e.g. Oct).
-``%M``
- The minute of the current hour (00-59).
+ ``%B``
+ .. versionadded:: 3.10
-``%s``
- .. versionadded:: 3.6
+ Full month name (e.g. October).
- Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
+ ``%M``
+ The minute of the current hour (00-59).
-``%S``
- The second of the current minute. 60 represents a leap second. (00-60)
+ ``%s``
+ .. versionadded:: 3.6
-``%f``
- .. versionadded:: 3.23
+ Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
- The microsecond of the current second (000000-999999).
+ ``%S``
+ The second of the current minute. 60 represents a leap second. (00-60)
-``%U``
- The week number of the current year (00-53).
+ ``%f``
+ .. versionadded:: 3.23
-``%V``
- .. versionadded:: 3.22
+ The microsecond of the current second (000000-999999).
- The ISO 8601 week number of the current year (01-53).
+ ``%U``
+ The week number of the current year (00-53).
-``%w``
- The day of the current week. 0 is Sunday. (0-6)
+ ``%V``
+ .. versionadded:: 3.22
-``%a``
- .. versionadded:: 3.7
+ The ISO 8601 week number of the current year (01-53).
- Abbreviated weekday name (e.g. Fri).
+ ``%w``
+ The day of the current week. 0 is Sunday. (0-6)
-``%A``
- .. versionadded:: 3.10
+ ``%a``
+ .. versionadded:: 3.7
- Full weekday name (e.g. Friday).
+ Abbreviated weekday name (e.g. Fri).
-``%y``
- The last two digits of the current year (00-99).
+ ``%A``
+ .. versionadded:: 3.10
-``%Y``
- The current year.
+ Full weekday name (e.g. Friday).
-``%z``
- .. versionadded:: 3.26
+ ``%y``
+ The last two digits of the current year (00-99).
- The offset of the time zone from UTC, in hours and minutes,
- with format ``+hhmm`` or ``-hhmm``.
+ ``%Y``
+ The current year.
-``%Z``
- .. versionadded:: 3.26
+ ``%z``
+ .. versionadded:: 3.26
- The time zone name.
+ The offset of the time zone from UTC, in hours and minutes,
+ with format ``+hhmm`` or ``-hhmm``.
-Unknown format specifiers will be ignored and copied to the output
-as-is.
+ ``%Z``
+ .. versionadded:: 3.26
-If no explicit ``<format_string>`` is given, it will default to:
+ The time zone name.
-::
+ Unknown format specifiers will be ignored and copied to the output
+ as-is.
- %Y-%m-%dT%H:%M:%S for local time.
- %Y-%m-%dT%H:%M:%SZ for UTC.
+ If no explicit ``<format_string>`` is given, it will default to:
-.. versionadded:: 3.8
- If the ``SOURCE_DATE_EPOCH`` environment variable is set,
- its value will be used instead of the current time.
- See https://reproducible-builds.org/specs/source-date-epoch/ for details.
+ ::
-.. _UUID:
+ %Y-%m-%dT%H:%M:%S for local time.
+ %Y-%m-%dT%H:%M:%SZ for UTC.
-.. code-block:: cmake
+ .. versionadded:: 3.8
+ If the ``SOURCE_DATE_EPOCH`` environment variable is set,
+ its value will be used instead of the current time.
+ See https://reproducible-builds.org/specs/source-date-epoch/ for details.
+.. signature::
string(UUID <output_variable> NAMESPACE <namespace> NAME <name>
TYPE <MD5|SHA1> [UPPER])
-.. versionadded:: 3.1
+ .. versionadded:: 3.1
-Create a universally unique identifier (aka GUID) as per RFC4122
-based on the hash of the combined values of ``<namespace>``
-(which itself has to be a valid UUID) and ``<name>``.
-The hash algorithm can be either ``MD5`` (Version 3 UUID) or
-``SHA1`` (Version 5 UUID).
-A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``
-where each ``x`` represents a lower case hexadecimal character.
-Where required, an uppercase representation can be requested
-with the optional ``UPPER`` flag.
+ Create a universally unique identifier (aka GUID) as per RFC4122
+ based on the hash of the combined values of ``<namespace>``
+ (which itself has to be a valid UUID) and ``<name>``.
+ The hash algorithm can be either ``MD5`` (Version 3 UUID) or
+ ``SHA1`` (Version 5 UUID).
+ A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``
+ where each ``x`` represents a lower case hexadecimal character.
+ Where required, an uppercase representation can be requested
+ with the optional ``UPPER`` flag.
.. _JSON:
@@ -586,78 +518,72 @@ Functionality for querying a JSON string.
option is not present, a fatal error message is generated. If no error
occurs, the ``<error-variable>`` will be set to ``NOTFOUND``.
-.. _JSON_GET:
-.. code-block:: cmake
-
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
GET <json-string> <member|index> [<member|index> ...])
+ :target: JSON GET
-Get an element from ``<json-string>`` at the location given
-by the list of ``<member|index>`` arguments.
-Array and object elements will be returned as a JSON string.
-Boolean elements will be returned as ``ON`` or ``OFF``.
-Null elements will be returned as an empty string.
-Number and string types will be returned as strings.
-
-.. _JSON_TYPE:
-.. code-block:: cmake
+ Get an element from ``<json-string>`` at the location given
+ by the list of ``<member|index>`` arguments.
+ Array and object elements will be returned as a JSON string.
+ Boolean elements will be returned as ``ON`` or ``OFF``.
+ Null elements will be returned as an empty string.
+ Number and string types will be returned as strings.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
TYPE <json-string> <member|index> [<member|index> ...])
+ :target: JSON TYPE
-Get the type of an element in ``<json-string>`` at the location
-given by the list of ``<member|index>`` arguments. The ``<out-var>``
-will be set to one of ``NULL``, ``NUMBER``, ``STRING``, ``BOOLEAN``,
-``ARRAY``, or ``OBJECT``.
-
-.. _JSON_MEMBER:
-.. code-block:: cmake
+ Get the type of an element in ``<json-string>`` at the location
+ given by the list of ``<member|index>`` arguments. The ``<out-var>``
+ will be set to one of ``NULL``, ``NUMBER``, ``STRING``, ``BOOLEAN``,
+ ``ARRAY``, or ``OBJECT``.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
MEMBER <json-string>
[<member|index> ...] <index>)
+ :target: JSON MEMBER
-Get the name of the ``<index>``-th member in ``<json-string>`` at the location
-given by the list of ``<member|index>`` arguments.
-Requires an element of object type.
-
-.. _JSON_LENGTH:
-.. code-block:: cmake
+ Get the name of the ``<index>``-th member in ``<json-string>``
+ at the location given by the list of ``<member|index>`` arguments.
+ Requires an element of object type.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
LENGTH <json-string> [<member|index> ...])
+ :target: JSON LENGTH
-Get the length of an element in ``<json-string>`` at the location
-given by the list of ``<member|index>`` arguments.
-Requires an element of array or object type.
-
-.. _JSON_REMOVE:
-.. code-block:: cmake
+ Get the length of an element in ``<json-string>`` at the location
+ given by the list of ``<member|index>`` arguments.
+ Requires an element of array or object type.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
REMOVE <json-string> <member|index> [<member|index> ...])
+ :target: JSON REMOVE
-Remove an element from ``<json-string>`` at the location
-given by the list of ``<member|index>`` arguments. The JSON string
-without the removed element will be stored in ``<out-var>``.
-
-.. _JSON_SET:
-.. code-block:: cmake
+ Remove an element from ``<json-string>`` at the location
+ given by the list of ``<member|index>`` arguments. The JSON string
+ without the removed element will be stored in ``<out-var>``.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-variable>]
SET <json-string> <member|index> [<member|index> ...] <value>)
+ :target: JSON SET
-Set an element in ``<json-string>`` at the location
-given by the list of ``<member|index>`` arguments to ``<value>``.
-The contents of ``<value>`` should be valid JSON.
-
-.. _JSON_EQUAL:
-.. code-block:: cmake
+ Set an element in ``<json-string>`` at the location
+ given by the list of ``<member|index>`` arguments to ``<value>``.
+ The contents of ``<value>`` should be valid JSON.
+.. signature::
string(JSON <out-var> [ERROR_VARIABLE <error-var>]
EQUAL <json-string1> <json-string2>)
+ :target: JSON EQUAL
-Compare the two JSON objects given by ``<json-string1>`` and ``<json-string2>``
-for equality. The contents of ``<json-string1>`` and ``<json-string2>``
-should be valid JSON. The ``<out-var>`` will be set to a true value if the
-JSON objects are considered equal, or a false value otherwise.
+ Compare the two JSON objects given by ``<json-string1>``
+ and ``<json-string2>`` for equality. The contents of ``<json-string1>``
+ and ``<json-string2>`` should be valid JSON. The ``<out-var>``
+ will be set to a true value if the JSON objects are considered equal,
+ or a false value otherwise.
diff --git a/Help/command/target_compile_options.rst b/Help/command/target_compile_options.rst
index 698f62d60a..7cfb24b18f 100644
--- a/Help/command/target_compile_options.rst
+++ b/Help/command/target_compile_options.rst
@@ -15,6 +15,11 @@ are used when compiling the given ``<target>``, which must have been
created by a command such as :command:`add_executable` or
:command:`add_library` and must not be an :ref:`ALIAS target <Alias Targets>`.
+.. note::
+
+ These options are not used when linking the target.
+ See the :command:`target_link_options` command for that.
+
Arguments
^^^^^^^^^
@@ -50,9 +55,17 @@ See Also
* For file-specific settings, there is the source file property :prop_sf:`COMPILE_OPTIONS`.
+* This command adds compile options for all languages in a target.
+ Use the :genex:`COMPILE_LANGUAGE` generator expression to specify
+ per-language compile options.
+
* :command:`target_compile_features`
* :command:`target_link_libraries`
* :command:`target_link_directories`
* :command:`target_link_options`
* :command:`target_precompile_headers`
* :command:`target_sources`
+
+* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`
+ add language-wide flags passed to all invocations of the compiler.
+ This includes invocations that drive compiling and those that drive linking.
diff --git a/Help/command/target_link_options.rst b/Help/command/target_link_options.rst
index 0d026f2456..dca9598611 100644
--- a/Help/command/target_link_options.rst
+++ b/Help/command/target_link_options.rst
@@ -62,3 +62,7 @@ See Also
* :command:`target_link_directories`
* :command:`target_precompile_headers`
* :command:`target_sources`
+
+* :variable:`CMAKE_<LANG>_FLAGS` and :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`
+ add language-wide flags passed to all invocations of the compiler.
+ This includes invocations that drive compiling and those that drive linking.
diff --git a/Help/command/unset.rst b/Help/command/unset.rst
index 1cd1398ac7..522be89800 100644
--- a/Help/command/unset.rst
+++ b/Help/command/unset.rst
@@ -12,19 +12,14 @@ Unset Normal Variable or Cache Entry
Removes a normal variable from the current scope, causing it
to become undefined. If ``CACHE`` is present, then a cache variable
-is removed instead of a normal variable. Note that when evaluating
-:ref:`Variable References` of the form ``${VAR}``, CMake first searches
-for a normal variable with that name. If no such normal variable exists,
-CMake will then search for a cache entry with that name. Because of this
-unsetting a normal variable can expose a cache variable that was previously
-hidden. To force a variable reference of the form ``${VAR}`` to return an
-empty string, use ``set(<variable> "")``, which clears the normal variable
-but leaves it defined.
+is removed instead of a normal variable.
If ``PARENT_SCOPE`` is present then the variable is removed from the scope
above the current scope. See the same option in the :command:`set` command
for further details.
+.. include:: UNSET_NOTE.txt
+
Unset Environment Variable
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/cpack_gen/nuget.rst b/Help/cpack_gen/nuget.rst
index 1f2e76281d..8ee2816903 100644
--- a/Help/cpack_gen/nuget.rst
+++ b/Help/cpack_gen/nuget.rst
@@ -258,7 +258,7 @@ List of CPack NuGet generator specific variables:
.. _nuget.org: https://www.nuget.org
.. _version specification: https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges
-.. _SPDX license identifier: https://spdx.github.io/spdx-spec/SPDX-license-list
-.. _SPDX specification: https://spdx.github.io/spdx-spec/SPDX-license-expressions
+.. _SPDX license identifier: https://spdx.org/licenses
+.. _SPDX specification: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions
.. NuGet spec docs https://docs.microsoft.com/en-us/nuget/reference/nuspec
diff --git a/Help/cpack_gen/wix.rst b/Help/cpack_gen/wix.rst
index c880049320..af01252454 100644
--- a/Help/cpack_gen/wix.rst
+++ b/Help/cpack_gen/wix.rst
@@ -111,7 +111,7 @@ Windows using WiX.
simply provide the name of the culture. If you specify more than one
culture identifier in a comma or semicolon delimited list, the first one
that is found will be used. You can find a list of supported languages at:
- https://wixtoolset.org//documentation/manual/v3/wixui/wixui_localization.html
+ https://wixtoolset.org/docs/v3/wixui/wixui_localization/
.. variable:: CPACK_WIX_TEMPLATE
@@ -319,7 +319,7 @@ Windows using WiX.
for using WiX extensions. Each declaration should be in the form name=url, where
name is the plain namespace without the usual xmlns: prefix and url is an unquoted
namespace url. A list of commonly known WiX schemata can be found here:
- https://wixtoolset.org/documentation/manual/v3/xsd/
+ https://wixtoolset.org/docs/v3/xsd/
.. variable:: CPACK_WIX_SKIP_WIX_UI_EXTENSION
diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst
index db92022089..c6fb7a653c 100644
--- a/Help/dev/documentation.rst
+++ b/Help/dev/documentation.rst
@@ -168,46 +168,175 @@ documentation:
See the `cmake-variables(7)`_ manual
and the `set()`_ command.
-Documentation objects in the CMake Domain come from two sources.
-First, the CMake extension to Sphinx transforms every document named
-with the form ``Help/<type>/<file-name>.rst`` to a domain object with
-type ``<type>``. The object name is extracted from the document title,
-which is expected to be of the form::
-
- <object-name>
- -------------
-
-and to appear at or near the top of the ``.rst`` file before any other
-lines starting in a letter, digit, ``<``, or ``$``. If no such title appears
-literally in the ``.rst`` file, the object name is the ``<file-name>``.
-If a title does appear, it is expected that ``<file-name>`` is equal
-to ``<object-name>`` with any ``<`` and ``>`` characters removed,
-or in the case of a ``$<genex-name>`` or ``$<genex-name:...>``, the
-``genex-name``.
-
-Second, the CMake Domain provides directives to define objects inside
-other documents:
+Documentation objects in the CMake Domain come from two sources:
+
+1. The CMake extension to Sphinx transforms every document named
+ with the form ``Help/<type>/<file-name>.rst`` to a domain object with
+ type ``<type>``. The object name is extracted from the document title,
+ which is expected to be of the form::
+
+ <object-name>
+ -------------
+
+ and to appear at or near the top of the ``.rst`` file before any other lines
+ starting in a letter, digit, ``<``, or ``$``. If no such title appears
+ literally in the ``.rst`` file, the object name is the ``<file-name>``.
+ If a title does appear, it is expected that ``<file-name>`` is equal
+ to ``<object-name>`` with any ``<`` and ``>`` characters removed,
+ or in the case of a ``$<genex-name>`` or ``$<genex-name:...>``, the
+ ``genex-name``.
+
+2. `CMake Domain directives`_ may be used in documents to explicitly define
+ some object types:
+
+ * `command directive`_
+ * `envvar directive`_
+ * `genex directive`_
+ * `variable directive`_
+
+ Object types for which no directive is available must be defined using
+ the document transform above.
+
+CMake Domain Directives
+-----------------------
+
+The CMake Domain provides the following directives.
+
+``command`` directive
+^^^^^^^^^^^^^^^^^^^^^
+
+Document a "command" object:
+
+.. code-block:: rst
+
+ .. command:: <command-name>
+
+ This indented block documents <command-name>.
+
+The directive requires a single argument, the command name.
+
+``envvar`` directive
+^^^^^^^^^^^^^^^^^^^^
+
+Document an "envvar" object:
.. code-block:: rst
- .. command:: <command-name>
+ .. envvar:: <envvar-name>
+
+ This indented block documents <envvar-name>.
+
+The directive requires a single argument, the environment variable name.
- This indented block documents <command-name>.
+``genex`` directive
+^^^^^^^^^^^^^^^^^^^
- .. envvar:: <envvar-name>
+Document a "genex" object:
- This indented block documents <envvar-name>.
+.. code-block:: rst
.. genex:: <genex-name>
This indented block documents <genex-name>.
+The directive requires a single argument, the generator expression name.
+
+The optional ``:target:`` option allows a custom target name to be specified.
+Because this will affect the ability to reference the "genex" object using the
+``:genex:`` role, this option should be used very sparingly.
+
+``signature`` directive
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Document `CMake Command Signatures <Style: CMake Command Signatures_>`_
+within a ``Help/command/<command-name>.rst`` document.
+
+.. code-block:: rst
+
+ .. signature:: <command-name>(<signature>)
+
+ This indented block documents one or more signatures of a CMake command.
+
+The ``signature`` directive requires one argument, the signature summary:
+
+* One or more signatures must immediately follow the ``::``.
+ The first signature may optionally be placed on the same line.
+ A blank line following the ``signature`` directive will result in a
+ documentation generation error: ``1 argument(s) required, 0 supplied``.
+
+* Signatures may be split across multiple lines, but the final ``)`` of each
+ signature must be the last character on its line.
+
+* Blank lines between signatures are not allowed. (Content after a blank line
+ is treated as part of the description.)
+
+* Whitespace in signatures is not preserved. To document a complex signature,
+ abbreviate it in the ``signature`` directive argument and specify the full
+ signature in a ``code-block`` in the description.
+
+The ``signature`` directive generates a hyperlink target for each signature:
+
+* Default target names are automatically extracted from leading "keyword"
+ arguments in the signatures, where a keyword is any sequence of
+ non-space starting with a letter. For example, the signature
+ ``string(REGEX REPLACE <match-regex> ...)`` generates the target
+ ``REGEX REPLACE``, similar to ``.. _`REGEX REPLACE`:``.
+
+* Custom target names may be specified using a ``:target:`` option.
+ For example:
+
+ .. code-block:: rst
+
+ .. signature::
+ cmake_path(GET <path-var> ROOT_NAME <out-var>)
+ cmake_path(GET <path-var> ROOT_PATH <out-var>)
+ :target:
+ GET ROOT_NAME
+ GET ROOT_PATH
+
+ Provide a custom target name for each signature, one per line.
+ The first target may optionally be placed on the same line as ``:target:``.
+
+* If a target name is already in use earlier in the document, no hyperlink
+ target will be generated.
+
+* The targets may be referenced from within the same document using
+ ```REF`_`` or ```TEXT <REF_>`_`` syntax. Like reStructuredText section
+ headers, the targets do not work with Sphinx ``:ref:`` syntax, however
+ they can be globally referenced using e.g. ``:command:`string(APPEND)```.
+
+Although whitespace in the signature is not preserved, by default, line breaks
+are suppressed inside of square- or angle-brackets. This behavior can be
+controlled using the ``:break:`` option; note, however, that there is no way
+to *force* a line break. The default value is 'smart'. Allowable values are:
+
+ ``all``
+ Allow line breaks at any whitespace.
+
+ ``smart`` (default)
+ Allow line breaks at whitespace, except between matched square- or
+ angle-brackets. For example, if a signature contains the text
+ ``<input>... [OUTPUT_VARIABLE <out-var>]``, a line break would be allowed
+ after ``<input>...`` but not between ``OUTPUT_VARIABLE`` and ``<out-var>``.
+
+ ``verbatim``
+ Allow line breaks only where the source document contains a newline.
+
+The directive treats its content as the documentation of the signature(s).
+Indent the signature documentation accordingly.
+
+``variable`` directive
+^^^^^^^^^^^^^^^^^^^^^^
+
+Document a "variable" object:
+
+.. code-block:: rst
+
.. variable:: <variable-name>
This indented block documents <variable-name>.
-Object types for which no directive is available must be defined using
-the first approach above.
+The directive requires a single argument, the variable name.
.. _`Sphinx Domain`: http://sphinx-doc.org/domains.html
.. _`cmake(1)`: https://cmake.org/cmake/help/latest/manual/cmake.1.html
@@ -266,6 +395,10 @@ object names like ``OUTPUT_NAME_<CONFIG>``. The form ``a <b>``,
with a space preceding ``<``, is still interpreted as a link text
with an explicit target.
+Additionally, the ``cref`` role may be used to create references
+to local targets that have literal styling. This is especially
+useful for referencing a subcommand in the command's documentation.
+
.. _`list()`: https://cmake.org/cmake/help/latest/command/list.html
.. _`list(APPEND)`: https://cmake.org/cmake/help/latest/command/list.html
.. _`list(APPEND) sub-command`: https://cmake.org/cmake/help/latest/command/list.html
@@ -329,11 +462,11 @@ paragraph.
Style: CMake Command Signatures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Command signatures should be marked up as plain literal blocks, not as
-cmake ``code-blocks``.
-
-Signatures are separated from preceding content by a section header.
-That is, use:
+A ``Help/command/<command-name>.rst`` document defines one ``command``
+object in the `CMake Domain`_, but some commands have multiple signatures.
+Use the CMake Domain's `signature directive`_ to document each signature.
+Separate signatures from preceding content by a section header.
+For example:
.. code-block:: rst
@@ -342,17 +475,23 @@ That is, use:
Normal Libraries
^^^^^^^^^^^^^^^^
- ::
-
+ .. signature::
add_library(<lib> ...)
- This signature is used for ...
+ This signature is used for ...
+
+Use the following conventions in command signature documentation:
+
+* Use an angle-bracket ``<placeholder>`` for arguments to be specified
+ by the caller. Refer to them in prose using
+ `inline literal <Style: Inline Literals_>`_ syntax.
+
+* Wrap optional parts with square brackets.
+
+* Mark repeatable parts with a trailing ellipsis (``...``).
-Signatures of commands should wrap optional parts with square brackets,
-and should mark list of optional arguments with an ellipsis (``...``).
-Elements of the signature which are specified by the user should be
-specified with angle brackets, and may be referred to in prose using
-``inline-literal`` syntax.
+The ``signature`` directive may be used multiple times for different
+signatures of the same command.
Style: Boolean Constants
^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/envvar/CMAKE_MAXIMUM_RECURSION_DEPTH.rst b/Help/envvar/CMAKE_MAXIMUM_RECURSION_DEPTH.rst
new file mode 100644
index 0000000000..2d65b60bc4
--- /dev/null
+++ b/Help/envvar/CMAKE_MAXIMUM_RECURSION_DEPTH.rst
@@ -0,0 +1,10 @@
+CMAKE_MAXIMUM_RECURSION_DEPTH
+-----------------------------
+
+.. versionadded:: 3.27
+
+.. include:: ENV_VAR.txt
+
+Maximum recursion depth for CMake scripts. This environment variable is
+used if the :variable:`CMAKE_MAXIMUM_RECURSION_DEPTH` variable is not set.
+See that variable's documentation for details.
diff --git a/Help/envvar/PackageName_ROOT.rst b/Help/envvar/PackageName_ROOT.rst
index fa8c385f4c..6e9c7444fd 100644
--- a/Help/envvar/PackageName_ROOT.rst
+++ b/Help/envvar/PackageName_ROOT.rst
@@ -17,3 +17,17 @@ by ``:`` on UNIX or ``;`` on Windows (the same as the ``PATH`` environment
variable convention on those platforms).
See also the :variable:`<PackageName>_ROOT` CMake variable.
+
+.. envvar:: <PACKAGENAME>_ROOT
+
+ .. versionadded:: 3.27
+
+ Calls to :command:`find_package(<PackageName>)` will also search in
+ prefixes specified by the upper-case ``<PACKAGENAME>_ROOT`` environment
+ variable. See policy :policy:`CMP0144`.
+
+.. note::
+
+ Note that the ``<PackageName>_ROOT`` and ``<PACKAGENAME>_ROOT``
+ environment variables are distinct only on platforms that have
+ case-sensitive environments.
diff --git a/Help/generator/CodeBlocks.rst b/Help/generator/CodeBlocks.rst
index 85da71543c..5c48bc9d88 100644
--- a/Help/generator/CodeBlocks.rst
+++ b/Help/generator/CodeBlocks.rst
@@ -1,6 +1,12 @@
CodeBlocks
----------
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Generates CodeBlocks project files.
Project files for CodeBlocks will be created in the top directory and
diff --git a/Help/generator/CodeLite.rst b/Help/generator/CodeLite.rst
index 4f276df718..faec9b9f7a 100644
--- a/Help/generator/CodeLite.rst
+++ b/Help/generator/CodeLite.rst
@@ -1,6 +1,12 @@
CodeLite
----------
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Generates CodeLite project files.
Project files for CodeLite will be created in the top directory and
diff --git a/Help/generator/Eclipse CDT4.rst b/Help/generator/Eclipse CDT4.rst
index 634e2b6ddf..9c1610d617 100644
--- a/Help/generator/Eclipse CDT4.rst
+++ b/Help/generator/Eclipse CDT4.rst
@@ -1,6 +1,12 @@
Eclipse CDT4
------------
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Generates Eclipse CDT 4.0 project files.
Project files for Eclipse will be created in the top directory. In
diff --git a/Help/generator/Kate.rst b/Help/generator/Kate.rst
index 129bf631be..11354f28cd 100644
--- a/Help/generator/Kate.rst
+++ b/Help/generator/Kate.rst
@@ -1,6 +1,12 @@
Kate
----
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Generates Kate project files.
A project file for Kate will be created in the top directory in the top level
@@ -22,5 +28,8 @@ This "extra" generator may be specified as:
``Kate - Ninja``
Generate with :generator:`Ninja`.
+``Kate - Ninja Multi-Config``
+ Generate with :generator:`Ninja Multi-Config`.
+
``Kate - Unix Makefiles``
Generate with :generator:`Unix Makefiles`.
diff --git a/Help/generator/Sublime Text 2.rst b/Help/generator/Sublime Text 2.rst
index 0a07ea983b..a45ab08252 100644
--- a/Help/generator/Sublime Text 2.rst
+++ b/Help/generator/Sublime Text 2.rst
@@ -1,6 +1,12 @@
Sublime Text 2
--------------
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Generates Sublime Text 2 project files.
Project files for Sublime Text 2 will be created in the top directory
diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst
index 34349560e3..816969dd28 100644
--- a/Help/generator/Visual Studio 9 2008.rst
+++ b/Help/generator/Visual Studio 9 2008.rst
@@ -1,7 +1,15 @@
Visual Studio 9 2008
--------------------
-Generates Visual Studio 9 2008 project files.
+Deprecated. Generates Visual Studio 9 2008 project files.
+
+.. note::
+ This generator is deprecated and will be removed in a future version
+ of CMake. It will still be possible to build with VS 9 2008 tools
+ using the :generator:`Visual Studio 12 2013` generator (or above,
+ and with VS 10 2010 also installed) with
+ :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``,
+ or by using the :generator:`NMake Makefiles` generator.
Platform Selection
^^^^^^^^^^^^^^^^^^
diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst
index e198789c79..3a11a34531 100644
--- a/Help/guide/ide-integration/index.rst
+++ b/Help/guide/ide-integration/index.rst
@@ -145,7 +145,7 @@ The following IDEs support CMake natively:
* `VSCode`_ (via a plugin)
.. _CLion: https://www.jetbrains.com/clion/
-.. _KDevelop: https://www.kdevelop.org/
+.. _KDevelop: https://kdevelop.org/
.. _QtCreator: https://www.qt.io/product/development-tools
.. _Vim: https://www.vim.org/
.. _Visual Studio: https://visualstudio.microsoft.com/
diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst
index 3dac68a80f..37b0668d4e 100644
--- a/Help/guide/tutorial/A Basic Starting Point.rst
+++ b/Help/guide/tutorial/A Basic Starting Point.rst
@@ -160,7 +160,7 @@ The last command to call for a basic project is
:name: CMakeLists.txt-add_executable
:language: cmake
:start-after: # add the executable
- :end-before: # TODO 9:
+ :end-before: # TODO 3:
.. raw:: html
@@ -240,7 +240,7 @@ the following:
:name: tutorial.cxx-cxx11
:language: c++
:start-after: // convert input to double
- :end-before: // TODO 12:
+ :end-before: // TODO 6:
.. raw:: html
@@ -265,7 +265,7 @@ add the :variable:`CMAKE_CXX_STANDARD` declarations above the call to
:name: CMakeLists.txt-CXX_STANDARD
:language: cmake
:start-after: # specify the C++ standard
- :end-before: # TODO 7:
+ :end-before: # configure a header file
.. raw:: html
@@ -375,7 +375,7 @@ specified CMake variables replaced:
:name: CMakeLists.txt-configure_file
:language: cmake
:start-after: # to the source code
- :end-before: # TODO 8:
+ :end-before: # TODO 2:
.. raw:: html
@@ -420,7 +420,6 @@ be replaced with the corresponding version numbers from the project in
:caption: TODO 10: TutorialConfig.h.in
:name: TutorialConfig.h.in
:language: c++
- :end-before: // TODO 13:
.. raw:: html
diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst
index 6f9714e474..aba9f7ad4d 100644
--- a/Help/guide/tutorial/Adding Generator Expressions.rst
+++ b/Help/guide/tutorial/Adding Generator Expressions.rst
@@ -105,7 +105,7 @@ The specific lines to remove are as follows:
:name: CMakeLists.txt-CXX_STANDARD-variable-remove
:language: cmake
:start-after: # specify the C++ standard
- :end-before: # TODO 5: Create helper variables
+ :end-before: # TODO 6: Create helper variables
Next, we need to create an interface library, ``tutorial_compiler_flags``. And
then use :command:`target_compile_features` to add the compiler feature
@@ -128,7 +128,8 @@ then use :command:`target_compile_features` to add the compiler feature
</details>
Finally, with our interface library set up, we need to link our
-executable ``Target`` and our ``MathFunctions`` library to our new
+executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary``
+library to our new
``tutorial_compiler_flags`` library. Respectively, the code will look like
this:
@@ -147,7 +148,7 @@ this:
</details>
-and this:
+this:
.. raw:: html
@@ -158,12 +159,30 @@ and this:
:name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
:language: cmake
:start-after: # link our compiler flags interface library
- :end-before: # TODO 1
+ :end-before: target_link_libraries(MathFunctions
+
+.. raw:: html
+
+ </details>
+
+and this:
+
+.. raw:: html
+
+ <details><summary>TODO 4: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/MathFunctions/CMakeLists.txt
+ :caption: TODO 4: MathFunctions/CMakeLists.txt
+ :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
+ :language: cmake
+ :start-after: target_link_libraries(SqrtLibrary
+ :end-before: endif()
.. raw:: html
</details>
+
With this, all of our code still requires C++ 11 to build. Notice
though that with this method, it gives us the ability to be specific about
which targets get specific requirements. In addition, we create a single
@@ -199,8 +218,8 @@ Files to Edit
Getting Started
---------------
-Start with the resulting files from Exercise 1. Complete ``TODO 4`` through
-``TODO 7``.
+Start with the resulting files from Exercise 1. Complete ``TODO 5`` through
+``TODO 8``.
First, in the top level ``CMakeLists.txt`` file, we need to set the
:command:`cmake_minimum_required` to ``3.15``. In this exercise we are going
@@ -230,10 +249,10 @@ version ``3.15``:
.. raw:: html
- <details><summary>TODO 4: Click to show/hide answer</summary>
+ <details><summary>TODO 5: Click to show/hide answer</summary>
.. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 4: CMakeLists.txt
+ :caption: TODO 5: CMakeLists.txt
:name: MathFunctions-CMakeLists.txt-minimum-required-step4
:language: cmake
:end-before: # set the project name and version
@@ -249,10 +268,10 @@ variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
.. raw:: html
- <details><summary>TODO 5: Click to show/hide answer</summary>
+ <details><summary>TODO 6: Click to show/hide answer</summary>
.. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 5: CMakeLists.txt
+ :caption: TODO 6: CMakeLists.txt
:name: CMakeLists.txt-compile_lang_and_id
:language: cmake
:start-after: # the BUILD_INTERFACE genex
@@ -270,10 +289,10 @@ interface library.
.. raw:: html
- <details><summary>TODO 6: Click to show/hide answer</summary>
+ <details><summary>TODO 7: Click to show/hide answer</summary>
.. code-block:: cmake
- :caption: TODO 6: CMakeLists.txt
+ :caption: TODO 7: CMakeLists.txt
:name: CMakeLists.txt-compile_flags
target_compile_options(tutorial_compiler_flags INTERFACE
@@ -292,14 +311,14 @@ condition. The resulting full code looks like the following:
.. raw:: html
- <details><summary>TODO 7: Click to show/hide answer</summary>
+ <details><summary>TODO 8: Click to show/hide answer</summary>
.. literalinclude:: Step5/CMakeLists.txt
- :caption: TODO 7: CMakeLists.txt
+ :caption: TODO 8: CMakeLists.txt
:name: CMakeLists.txt-target_compile_options-genex
:language: cmake
:start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
- :end-before: # should we use our own math functions
+ :end-before: # configure a header file to pass some of the CMake settings
.. raw:: html
diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst
index b69abd2920..b3147736a3 100644
--- a/Help/guide/tutorial/Adding System Introspection.rst
+++ b/Help/guide/tutorial/Adding System Introspection.rst
@@ -119,7 +119,7 @@ our source code can tell what resources are available. If both ``log`` and
:name: MathFunctions/CMakeLists.txt-target_compile_definitions
:language: cmake
:start-after: # add compile definitions
- :end-before: # install libs
+ :end-before: # state
.. raw:: html
@@ -136,7 +136,8 @@ Since we may be using ``log`` and ``exp``, we need to modify
:caption: TODO 4: MathFunctions/mysqrt.cxx
:name: MathFunctions/mysqrt.cxx-include-cmath
:language: c++
- :end-before: #include <iostream>
+ :start-after: #include "mysqrt.h"
+ :end-before: include <iostream>
.. raw:: html
diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
index 4aef050585..74b7496688 100644
--- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
+++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
@@ -100,7 +100,7 @@ follows:
:name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
:language: cmake
:start-after: # to find MathFunctions.h
- :end-before: # TODO 3: Link to
+ :end-before: option
.. raw:: html
@@ -119,7 +119,7 @@ safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
:name: CMakeLists.txt-remove-EXTRA_INCLUDES
:language: cmake
:start-after: # add the MathFunctions library
- :end-before: # add the executable
+ :end-before: # TODO 2: Link to tutorial_compiler_flags
.. raw:: html
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 b1f984076d..c71a889725 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
@@ -18,41 +18,49 @@ In the ``MathFunctions`` subdirectory, a new source file named
After reviewing the file, we can see that the table is produced as valid C++
code and that the output filename is passed in as an argument.
-The next step is to add the appropriate commands to the
-``MathFunctions/CMakeLists.txt`` file to build the MakeTable executable and
+The next step is to create ``MathFunctions/MakeTable.cmake``. Then, add the
+appropriate commands to the file to build the ``MakeTable`` executable and
then run it as part of the build process. A few commands are needed to
accomplish this.
-First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for
-``MakeTable`` is added as any other executable would be added.
+First, we add an executable for ``MakeTable``.
-.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
- :caption: MathFunctions/CMakeLists.txt
- :name: MathFunctions/CMakeLists.txt-add_executable-MakeTable
+.. literalinclude:: Step9/MathFunctions/MakeTable.cmake
+ :caption: MathFunctions/MakeTable.cmake
+ :name: MathFunctions/MakeTable.cmake-add_executable-MakeTable
:language: cmake
:start-after: # first we add the executable that generates the table
- :end-before: # add the command to generate the source code
+ :end-before: target_link_libraries
+
+After creating the executable, we add the ``tutorial_compiler_flags`` to our
+executable using :command:`target_link_libraries`.
+
+.. literalinclude:: Step9/MathFunctions/MakeTable.cmake
+ :caption: MathFunctions/MakeTable.cmake
+ :name: MathFunctions/MakeTable.cmake-link-tutorial-compiler-flags
+ :language: cmake
+ :start-after: add_executable
+ :end-before: # add the command to generate
Then we add a custom command that specifies how to produce ``Table.h``
by running MakeTable.
-.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
- :caption: MathFunctions/CMakeLists.txt
- :name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h
+.. literalinclude:: Step9/MathFunctions/MakeTable.cmake
+ :caption: MathFunctions/MakeTable.cmake
+ :name: MathFunctions/MakeTable.cmake-add_custom_command-Table.h
:language: cmake
:start-after: # add the command to generate the source code
- :end-before: # add the main library
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.
+of sources for the library ``SqrtLibrary``.
.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
:caption: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-add_library-Table.h
:language: cmake
- :start-after: # add the main library
- :end-before: # state that anybody linking
+ :start-after: # library that just does sqrt
+ :end-before: # state that we depend on
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``.
@@ -62,7 +70,17 @@ directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``.
:name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h
:language: cmake
:start-after: # state that we depend on our bin
- :end-before: # install libs
+ :end-before: target_link_libraries
+
+As the last step, we need to include
+``MakeTable.cmake`` at the top of the ``MathFunctions/CMakeLists.txt``.
+
+.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
+ :caption: MathFunctions/CMakeLists.txt
+ :name: MathFunctions/CMakeLists.txt-include-MakeTable.cmake
+ :language: cmake
+ :start-after: # generate Table.h
+ :end-before: # library that just does sqrt
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:
diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst
index a56c327ee6..d606f305e8 100644
--- a/Help/guide/tutorial/Adding a Library.rst
+++ b/Help/guide/tutorial/Adding a Library.rst
@@ -51,11 +51,13 @@ then use this library instead of the standard square root function provided by
the compiler.
For this tutorial we will put the library into a subdirectory called
-``MathFunctions``. This directory already contains a header file,
-``MathFunctions.h``, and a source file ``mysqrt.cxx``. We will not need to
-modify either of these files. The source file has one function called
+``MathFunctions``. This directory already contains the header files
+``MathFunctions.h`` and ``mysqrt.h``. Their respective source files
+``MathFunctions.cxx`` and ``mysqrt.cxx`` are also provided. We will not need
+to modify any of these files. ``mysqrt.cxx`` has one function called
``mysqrt`` that provides similar functionality to the compiler's ``sqrt``
-function.
+function. ``MathFunctions.cxx`` contains one function ``sqrt`` which serves
+to hide the implementation details of ``sqrt``.
From the ``Help/guide/tutorial/Step2`` directory, start with ``TODO 1`` and
complete through ``TODO 6``.
@@ -91,18 +93,18 @@ Solution
In the ``CMakeLists.txt`` file in the ``MathFunctions`` directory, we create
a library target called ``MathFunctions`` with :command:`add_library`. The
-source file for the library is passed as an argument to
+source files for the library are passed as an argument to
:command:`add_library`. This looks like the following line:
-.. raw:: html
+.. raw:: html/
<details><summary>TODO 1: Click to show/hide answer</summary>
-.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+.. code-block:: cmake
:caption: TODO 1: MathFunctions/CMakeLists.txt
:name: MathFunctions/CMakeLists.txt-add_library
- :language: cmake
- :end-before: # TODO 1
+
+ add_library(MathFunctions MathFunctions.cxx mysqrt.cxx)
.. raw:: html
@@ -171,36 +173,40 @@ Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``:
<details><summary>TODO 5: Click to show/hide answer</summary>
-.. code-block:: c++
- :caption: TODO 5 : tutorial.cxx
- :name: tutorial.cxx-include_MathFunctions.h
-
- #include "MathFunctions.h"
+.. literalinclude:: Step3/tutorial.cxx
+ :caption: TODO 5: tutorial.cxx
+ :name: CMakeLists.txt-include-MathFunctions.h
+ :language: cmake
+ :start-after: #include <string>
+ :end-before: #include "TutorialConfig.h"
.. raw:: html
</details>
-Lastly, replace ``sqrt`` with our library function ``mysqrt``.
+Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``.
.. raw:: html
<details><summary>TODO 6: Click to show/hide answer</summary>
-.. code-block:: c++
- :caption: TODO 6 : tutorial.cxx
- :name: tutorial.cxx-call_mysqrt
-
- const double outputValue = mysqrt(inputValue);
+.. literalinclude:: Step3/tutorial.cxx
+ :caption: TODO 6: tutorial.cxx
+ :name: CMakeLists.txt-option
+ :language: cmake
+ :start-after: const double inputValue = std::stod(argv[1]);
+ :end-before: std::cout
.. raw:: html
</details>
-Exercise 2 - Making Our Library Optional
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Exercise 2 - Adding an Option
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Now let us make the MathFunctions library optional. While for the tutorial
+Now let us add an option in the MathFunctions library to allow developers to
+select either the custom square root implementation or the built in standard
+implementation. While for the tutorial
there really isn't any need to do so, for larger projects this is a common
occurrence.
@@ -219,30 +225,32 @@ Helpful Resources
-----------------
* :command:`if`
-* :command:`list`
* :command:`option`
-* :command:`cmakedefine <configure_file>`
+* :command:`target_compile_definitions`
Files to Edit
-------------
-* ``CMakeLists.txt``
-* ``tutorial.cxx``
-* ``TutorialConfig.h.in``
+* ``MathFunctions/CMakeLists.txt``
+* ``MathFunctions/MathFunctions.cxx``
Getting Started
---------------
Start with the resulting files from Exercise 1. Complete ``TODO 7`` through
-``TODO 13``.
+``TODO 14``.
First create a variable ``USE_MYMATH`` using the :command:`option` command
-in the top-level ``CMakeLists.txt`` file. In that same file, use that option
-to determine whether to build and use the ``MathFunctions`` library.
+in ``MathFunctions/CMakeLists.txt``. In that same file, use that option
+to pass a compile definition to the ``MathFunctions`` library.
-Then, update ``tutorial.cxx`` and ``TutorialConfig.h.in`` to use
+Then, update ``MathFunctions.cxx`` to redirect compilation based on
``USE_MYMATH``.
+Lastly, prevent ``mysqrt.cxx`` from being compiled when ``USE_MYMATH`` is on
+by making it its own library inside of the ``USE_MYMATH`` block of
+``MathFunctions/CMakeLists.txt``.
+
Build and Run
-------------
@@ -279,7 +287,7 @@ or ``mysqrt``?
Solution
--------
-The first step is to add an option to the top-level ``CMakeLists.txt`` file.
+The first step is to add an option to ``MathFunctions/CMakeLists.txt``.
This option will be displayed in the :manual:`cmake-gui <cmake-gui(1)>` and
:manual:`ccmake <ccmake(1)>` with a default value of ``ON`` that can be
changed by the user.
@@ -288,172 +296,159 @@ changed by the user.
<details><summary>TODO 7: Click to show/hide answer</summary>
-.. literalinclude:: Step3/CMakeLists.txt
- :caption: TODO 7: CMakeLists.txt
- :name: CMakeLists.txt-option
+.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+ :caption: TODO 7: MathFunctions/CMakeLists.txt
+ :name: CMakeLists.txt-option-library-level
:language: cmake
:start-after: # should we use our own math functions
- :end-before: # configure a header file to pass some of the CMake settings
+ :end-before: if (USE_MYMATH)
.. raw:: html
</details>
-Next, make building and linking the ``MathFunctions`` library
-conditional.
-
-Start by creating a :command:`list` of the optional library targets for our
-project. At the moment, it is just ``MathFunctions``. Let's name our list
-``EXTRA_LIBS``.
+Next, make building and linking our library with ``mysqrt`` function
+conditional using this new option.
-Similarly, we need to make a :command:`list` for the optional includes which
-we will call ``EXTRA_INCLUDES``. In this list, we will ``APPEND`` the path of
-the header file needed for our library.
-
-Next, create an :command:`if` statement which checks the value of
+Create an :command:`if` statement which checks the value of
``USE_MYMATH``. Inside the :command:`if` block, put the
-:command:`add_subdirectory` command from Exercise 1 with the additional
-:command:`list` commands.
-
-When ``USE_MYMATH`` is ``ON``, the lists will be generated and will be added to
-our project. When ``USE_MYMATH`` is ``OFF``, the lists stay empty. With this
-strategy, we allow users to toggle ``USE_MYMATH`` to manipulate what library is
-used in the build.
-
-The top-level CMakeLists.txt file will now look like the following:
+:command:`target_compile_definitions` command with the compile
+definition ``USE_MYMATH``.
.. raw:: html
<details><summary>TODO 8: Click to show/hide answer</summary>
-.. literalinclude:: Step3/CMakeLists.txt
- :caption: TODO 8: CMakeLists.txt
+.. code-block:: cmake
+ :caption: TODO 8: MathFunctions/CMakeLists.txt
:name: CMakeLists.txt-USE_MYMATH
- :language: cmake
- :start-after: # add the MathFunctions library
- :end-before: # add the executable
+
+ if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+ endif()
.. raw:: html
</details>
-Now that we have these two lists, we need to update
-:command:`target_link_libraries` and :command:`target_include_directories` to
-use them. Changing them is fairly straightforward.
+When ``USE_MYMATH`` is ``ON``, the compile definition ``USE_MYMATH`` will
+be set. We can then use this compile definition to enable or disable
+sections of our source code.
-For :command:`target_link_libraries`, we replace the written out
-library names with ``EXTRA_LIBS``. This looks like the following:
+The corresponding changes to the source code are fairly straightforward.
+In ``MathFunctions.cxx``, we make ``USE_MYMATH`` control which square root
+function is used:
.. raw:: html
<details><summary>TODO 9: Click to show/hide answer</summary>
-.. literalinclude:: Step3/CMakeLists.txt
- :caption: TODO 9: CMakeLists.txt
- :name: CMakeLists.txt-target_link_libraries-EXTRA_LIBS
- :language: cmake
- :start-after: add_executable(Tutorial tutorial.cxx)
- :end-before: # TODO 3
+.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx
+ :caption: TODO 9: MathFunctions/MathFunctions.cxx
+ :name: MathFunctions-USE_MYMATH-if
+ :language: c++
+ :start-after: which square root function should we use?
+ :end-before: }
.. raw:: html
</details>
-Then, we do the same thing with :command:`target_include_directories` and
-``EXTRA_INCLUDES``.
+Next, we need to include ``mysqrt.h`` if ``USE_MYMATH`` is defined.
.. raw:: html
<details><summary>TODO 10: Click to show/hide answer</summary>
-.. literalinclude:: Step3/CMakeLists.txt
- :caption: TODO 10 : CMakeLists.txt
- :name: CMakeLists.txt-target_link_libraries-EXTRA_INCLUDES
- :language: cmake
- :start-after: # so that we will find TutorialConfig.h
+.. literalinclude:: Step3/MathFunctions/MathFunctions.cxx
+ :caption: TODO 10: MathFunctions/MathFunctions.cxx
+ :name: MathFunctions-USE_MYMATH-if-include
+ :language: c++
+ :start-after: include <cmath>
+ :end-before: namespace mathfunctions
.. raw:: html
</details>
-Note that this is a classic approach when dealing with many components. We
-will cover the modern approach in the Step 3 of the tutorial.
-
-The corresponding changes to the source code are fairly straightforward.
-First, in ``tutorial.cxx``, we include the ``MathFunctions.h`` header if
-``USE_MYMATH`` is defined.
+Finally, we need to include ``cmath`` now that we are using ``std::sqrt``.
.. raw:: html
<details><summary>TODO 11: Click to show/hide answer</summary>
-.. literalinclude:: Step3/tutorial.cxx
- :caption: TODO 11 : tutorial.cxx
- :name: tutorial.cxx-ifdef-include
- :language: c++
- :start-after: // should we include the MathFunctions header
- :end-before: int main
+.. code-block:: c++
+ :caption: TODO 11 : MathFunctions/MathFunctions.cxx
+ :name: tutorial.cxx-include_cmath
+
+ #include <cmath>
.. raw:: html
</details>
-Then, in the same file, we make ``USE_MYMATH`` control which square root
-function is used:
+At this point, if ``USE_MYMATH`` is ``OFF``, ``mysqrt.cxx`` would not be used
+but it will still be compiled because the ``MathFunctions`` target has
+``mysqrt.cxx`` listed under sources.
+
+There are a few ways to fix this. The first option is to use
+:command:`target_sources` to add ``mysqrt.cxx`` from within the ``USE_MYMATH``
+block. Another option is to create an additional library within the
+``USE_MYMATH`` block which is responsible for compiling ``mysqrt.cxx``. For
+the sake of this tutorial, we are going to create an additional library.
+
+First, from within ``USE_MYMATH`` create a library called ``SqrtLibrary``
+that has sources ``mysqrt.cxx``.
.. raw:: html
<details><summary>TODO 12: Click to show/hide answer</summary>
-.. literalinclude:: Step3/tutorial.cxx
- :caption: TODO 12 : tutorial.cxx
- :name: tutorial.cxx-ifdef-const
- :language: c++
- :start-after: // which square root function should we use?
- :end-before: std::cout << "The square root of
+.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+ :caption: TODO 12 : MathFunctions/CMakeLists.txt
+ :name: MathFunctions/CMakeLists.txt-add_library-SqrtLibrary
+ :language: cmake
+ :start-after: # library that just does sqrt
+ :end-before: target_link_libraries(MathFunctions
.. raw:: html
</details>
-Since the source code now requires ``USE_MYMATH`` we can add it to
-``TutorialConfig.h.in`` with the following line:
+Next, we link ``SqrtLibrary`` onto ``MathFunctions`` when ``USE_MYMATH`` is
+enabled.
.. raw:: html
<details><summary>TODO 13: Click to show/hide answer</summary>
-.. literalinclude:: Step3/TutorialConfig.h.in
- :caption: TODO 13 : TutorialConfig.h.in
- :name: TutorialConfig.h.in-cmakedefine
- :language: c++
- :lines: 4
+.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+ :caption: TODO 13 : MathFunctions/CMakeLists.txt
+ :name: MathFunctions/CMakeLists.txt-target_link_libraries-SqrtLibrary
+ :language: cmake
+ :lines: 16-18
.. raw:: html
</details>
-With these changes, our library is now completely optional to whoever is
-building and using it.
-
-Bonus Question
---------------
-
-Why is it important that we configure ``TutorialConfig.h.in``
-after the option for ``USE_MYMATH``? What would happen if we inverted the two?
-
-Answer
-------
+Finally, we can remove ``mysqrt.cxx`` from our ``MathFunctions`` library
+source list because it will be pulled in when ``SqrtLibrary`` is included.
.. raw:: html
- <details><summary>Click to show/hide answer</summary>
+ <details><summary>TODO 14: Click to show/hide answer</summary>
-We configure after because ``TutorialConfig.h.in`` uses the value of
-``USE_MYMATH``. If we configure the file before
-calling :command:`option`, we won't be using the expected value of
-``USE_MYMATH``.
+.. literalinclude:: Step3/MathFunctions/CMakeLists.txt
+ :caption: TODO 14 : MathFunctions/CMakeLists.txt
+ :name: MathFunctions/CMakeLists.txt-remove-mysqrt.cxx-MathFunctions
+ :language: cmake
+ :end-before: # TODO 1:
.. raw:: html
</details>
+
+With these changes, the ``mysqrt`` function is now completely optional to
+whoever is building and using the ``MathFunctions`` library. Users can toggle
+``USE_MYMATH`` to manipulate what library is used in the build.
diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
index d256db20a4..b22150643f 100644
--- a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
@@ -15,16 +15,7 @@ if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
- # first we add the executable that generates the table
- add_executable(MakeTable MakeTable.cxx)
- target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
-
- # 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
- )
+ include(MakeTable.cmake) # generates Table.h
# library that just does sqrt
add_library(SqrtLibrary STATIC
diff --git a/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake
new file mode 100644
index 0000000000..12865a9831
--- /dev/null
+++ b/Help/guide/tutorial/Complete/MathFunctions/MakeTable.cmake
@@ -0,0 +1,10 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
+
+# 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
+ )
diff --git a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx
index 0145300629..c0991b9b98 100644
--- a/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx
+++ b/Help/guide/tutorial/Complete/MathFunctions/MathFunctions.cxx
@@ -10,6 +10,7 @@
namespace mathfunctions {
double sqrt(double x)
{
+// which square root function should we use?
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
diff --git a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
index 7befe1d3fd..504e42fd58 100644
--- a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
+++ b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
@@ -10,49 +10,22 @@ To accomplish this we need to add :variable:`BUILD_SHARED_LIBS` to the
top-level ``CMakeLists.txt``. We use the :command:`option` command as it allows
users to optionally select if the value should be ``ON`` or ``OFF``.
-Next we are going to refactor ``MathFunctions`` to become a real library that
-encapsulates using ``mysqrt`` or ``sqrt``, instead of requiring the calling
-code to do this logic. This will also mean that ``USE_MYMATH`` will not control
-building ``MathFunctions``, but instead will control the behavior of this
-library.
-
-The first step is to update the starting section of the top-level
-``CMakeLists.txt`` to look like:
-
.. literalinclude:: Step11/CMakeLists.txt
:caption: CMakeLists.txt
:name: CMakeLists.txt-option-BUILD_SHARED_LIBS
:language: cmake
- :end-before: # add the binary tree
-
-Now that we have made ``MathFunctions`` always be used, we will need to update
-the logic of that library. So, in ``MathFunctions/CMakeLists.txt`` we need to
-create a SqrtLibrary that will conditionally be built and installed when
-``USE_MYMATH`` is enabled. Now, since this is a tutorial, we are going to
-explicitly require that SqrtLibrary is built statically.
+ :start-after: set(CMAKE_RUNTIME_OUTPUT_DIRECTORY
+ :end-before: # configure a header file to pass the version number only
-The end result is that ``MathFunctions/CMakeLists.txt`` should look like:
+Next, we need to specify output directories for our static and shared
+libraries.
-.. literalinclude:: Step11/MathFunctions/CMakeLists.txt
- :caption: MathFunctions/CMakeLists.txt
- :name: MathFunctions/CMakeLists.txt-add_library-STATIC
+.. literalinclude:: Step11/CMakeLists.txt
+ :caption: CMakeLists.txt
+ :name: CMakeLists.txt-cmake-output-directories
:language: cmake
- :lines: 1-36,42-
-
-Next, update ``MathFunctions/mysqrt.cxx`` to use the ``mathfunctions`` and
-``detail`` namespaces:
-
-.. literalinclude:: Step11/MathFunctions/mysqrt.cxx
- :caption: MathFunctions/mysqrt.cxx
- :name: MathFunctions/mysqrt.cxx-namespace
- :language: c++
-
-We also need to make some changes in ``tutorial.cxx``, so that it no longer
-uses ``USE_MYMATH``:
-
-#. Always include ``MathFunctions.h``
-#. Always use ``mathfunctions::sqrt``
-#. Don't include ``cmath``
+ :start-after: # we don't need to tinker with the path to run the executable
+ :end-before: # configure a header file to pass the version number only
Finally, update ``MathFunctions/MathFunctions.h`` to use dll export defines:
diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt
index 5c661aa229..2dd6db50f9 100644
--- a/Help/guide/tutorial/Step10/CMakeLists.txt
+++ b/Help/guide/tutorial/Step10/CMakeLists.txt
@@ -16,22 +16,15 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
-# configure a header file to pass some of the CMake settings
-# to the source code
+# configure a header file to pass the version number only
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
index fa73321670..36b3fe10a5 100644
--- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
@@ -1,32 +1,42 @@
-# 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 the library that runs
+add_library(MathFunctions MathFunctions.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 our binary dir to find Table.h
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
-# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if(USE_MYMATH)
+
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ include(MakeTable.cmake) # generates Table.h
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+ )
+
+ # state that we depend on our binary dir to find Table.h
+ target_include_directories(SqrtLibrary PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ )
+
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# install libs
set(installable_libs MathFunctions tutorial_compiler_flags)
+if(TARGET SqrtLibrary)
+ list(APPEND installable_libs SqrtLibrary)
+endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake
new file mode 100644
index 0000000000..12865a9831
--- /dev/null
+++ b/Help/guide/tutorial/Step10/MathFunctions/MakeTable.cmake
@@ -0,0 +1,10 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
+
+# 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
+ )
diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx
index 0145300629..c0991b9b98 100644
--- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx
+++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.cxx
@@ -10,6 +10,7 @@
namespace mathfunctions {
double sqrt(double x)
{
+// which square root function should we use?
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
diff --git a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
index cd36bccffd..1e916e1192 100644
--- a/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
@@ -1 +1,3 @@
-double mysqrt(double x);
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
index 7d80ee9648..8153f18bed 100644
--- a/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
@@ -5,6 +5,8 @@
// include the generated table
#include "Table.h"
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -31,3 +33,5 @@ double mysqrt(double x)
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step10/TutorialConfig.h.in b/Help/guide/tutorial/Step10/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step10/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step10/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step10/tutorial.cxx b/Help/guide/tutorial/Step10/tutorial.cxx
index b3c6a4f43c..37a03336ca 100644
--- a/Help/guide/tutorial/Step10/tutorial.cxx
+++ b/Help/guide/tutorial/Step10/tutorial.cxx
@@ -1,15 +1,11 @@
// A simple program that computes the square root of a number
-#include <cmath>
#include <iostream>
+#include <sstream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
index a60fb633b5..813bf90c08 100644
--- a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
@@ -13,16 +13,7 @@ if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
- # first we add the executable that generates the table
- add_executable(MakeTable MakeTable.cxx)
- target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
-
- # 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
- )
+ include(MakeTable.cmake) # generates Table.h
# library that just does sqrt
add_library(SqrtLibrary STATIC
diff --git a/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake
new file mode 100644
index 0000000000..12865a9831
--- /dev/null
+++ b/Help/guide/tutorial/Step11/MathFunctions/MakeTable.cmake
@@ -0,0 +1,10 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
+
+# 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
+ )
diff --git a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx
index 0145300629..c0991b9b98 100644
--- a/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx
+++ b/Help/guide/tutorial/Step11/MathFunctions/MathFunctions.cxx
@@ -10,6 +10,7 @@
namespace mathfunctions {
double sqrt(double x)
{
+// which square root function should we use?
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
diff --git a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
index a85f3cb700..38694dd352 100644
--- a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
@@ -15,16 +15,7 @@ if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
- # first we add the executable that generates the table
- add_executable(MakeTable MakeTable.cxx)
- target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
-
- # 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
- )
+ include(MakeTable.cmake) # generates Table.h
# library that just does sqrt
add_library(SqrtLibrary STATIC
diff --git a/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake
new file mode 100644
index 0000000000..12865a9831
--- /dev/null
+++ b/Help/guide/tutorial/Step12/MathFunctions/MakeTable.cmake
@@ -0,0 +1,10 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
+
+# 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
+ )
diff --git a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx
index 0145300629..c0991b9b98 100644
--- a/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx
+++ b/Help/guide/tutorial/Step12/MathFunctions/MathFunctions.cxx
@@ -10,6 +10,7 @@
namespace mathfunctions {
double sqrt(double x)
{
+// which square root function should we use?
#ifdef USE_MYMATH
return detail::mysqrt(x);
#else
diff --git a/Help/guide/tutorial/Step2/CMakeLists.txt b/Help/guide/tutorial/Step2/CMakeLists.txt
index 2b96128e16..0a06ed7d49 100644
--- a/Help/guide/tutorial/Step2/CMakeLists.txt
+++ b/Help/guide/tutorial/Step2/CMakeLists.txt
@@ -7,36 +7,20 @@ project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
-# TODO 7: Create a variable USE_MYMATH using option and set default to ON
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
-# TODO 8: Use list() and APPEND to create a list of optional libraries
-# called EXTRA_LIBS and a list of optional include directories called
-# EXTRA_INCLUDES. Add the MathFunctions library and source directory to
-# the appropriate lists.
-#
-# Only call add_subdirectory and only add MathFunctions specific values
-# to EXTRA_LIBS and EXTRA_INCLUDES if USE_MYMATH is true.
-
# TODO 2: Use add_subdirectory() to add MathFunctions to this project
# add the executable
add_executable(Tutorial tutorial.cxx)
-# TODO 9: Use EXTRA_LIBS instead of the MathFunctions specific values
-# in target_link_libraries.
-
# TODO 3: Use target_link_libraries to link the library to our executable
# TODO 4: Add MathFunctions to Tutorial's target_include_directories()
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
-# TODO 10: Use EXTRA_INCLUDES instead of the MathFunctions specific values
-# in target_include_directories.
-
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
diff --git a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt
index b7779b7f28..c3cd806cff 100644
--- a/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt
@@ -1,2 +1,15 @@
-# TODO 1: Add a library called MathFunctions
+# TODO 14: Remove mysqrt.cxx from the list of sources
+
+# TODO 1: Add a library called MathFunctions with sources MathFunctions.cxx
+# and mysqrt.cxx
# Hint: You will need the add_library command
+
+# TODO 7: Create a variable USE_MYMATH using option and set default to ON
+
+# TODO 8: If USE_MYMATH is ON, use target_compile_definitions to pass
+# USE_MYMATH as a precompiled definition to our source files
+
+# TODO 12: When USE_MYMATH is ON, add a library for SqrtLibrary with
+# source mysqrt.cxx
+
+# TODO 13: When USE_MYMATH is ON, link SqrtLibrary to the MathFunctions Library
diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..781d0ecc4e
--- /dev/null
+++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,15 @@
+#include "MathFunctions.h"
+
+// TODO 11: include cmath
+
+// TODO 10: Wrap the mysqrt include in a precompiled ifdef based on USE_MYMATH
+#include "mysqrt.h"
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+ // TODO 9: If USE_MYMATH is defined, use detail::mysqrt.
+ // Otherwise, use std::sqrt.
+ return detail::mysqrt(x);
+}
+}
diff --git a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step2/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx
index 1e4d97ae7c..ba0ac645b5 100644
--- a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.cxx
@@ -1,5 +1,9 @@
+#include "mysqrt.h"
+
#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -20,3 +24,5 @@ double mysqrt(double x)
}
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step2/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step2/TutorialConfig.h.in b/Help/guide/tutorial/Step2/TutorialConfig.h.in
index 6c09e1aca4..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step2/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step2/TutorialConfig.h.in
@@ -1,5 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-
-// TODO 13: use cmakedefine to define USE_MYMATH
diff --git a/Help/guide/tutorial/Step2/tutorial.cxx b/Help/guide/tutorial/Step2/tutorial.cxx
index 87f5e0f645..7a2a5951e6 100644
--- a/Help/guide/tutorial/Step2/tutorial.cxx
+++ b/Help/guide/tutorial/Step2/tutorial.cxx
@@ -3,11 +3,8 @@
#include <iostream>
#include <string>
-#include "TutorialConfig.h"
-
-// TODO 11: Only include MathFunctions if USE_MYMATH is defined
-
// TODO 5: Include MathFunctions.h
+#include "TutorialConfig.h"
int main(int argc, char* argv[])
{
@@ -22,9 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // TODO 12: Use mysqrt if USE_MYMATH is defined and sqrt otherwise
-
- // TODO 6: Replace sqrt with mysqrt
+ // TODO 6: Replace sqrt with mathfunctions::sqrt
// calculate square root
const double outputValue = sqrt(inputValue);
diff --git a/Help/guide/tutorial/Step3/CMakeLists.txt b/Help/guide/tutorial/Step3/CMakeLists.txt
index 007770a373..f051826a7d 100644
--- a/Help/guide/tutorial/Step3/CMakeLists.txt
+++ b/Help/guide/tutorial/Step3/CMakeLists.txt
@@ -7,9 +7,6 @@ project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
@@ -17,16 +14,13 @@ configure_file(TutorialConfig.h.in TutorialConfig.h)
# TODO 2: Remove EXTRA_INCLUDES list
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
- list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
-endif()
+add_subdirectory(MathFunctions)
+list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC MathFunctions)
# TODO 3: Remove use of EXTRA_INCLUDES
diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
index 7bf05e02c5..6f86ffef5b 100644
--- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
@@ -1,5 +1,18 @@
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions MathFunctions.cxx)
# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
+endif()
diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step3/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx
index abe767d5ae..ba0ac645b5 100644
--- a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.cxx
@@ -1,7 +1,9 @@
-#include <iostream>
+#include "mysqrt.h"
-#include "MathFunctions.h"
+#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -22,3 +24,5 @@ double mysqrt(double x)
}
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step3/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step3/TutorialConfig.h.in b/Help/guide/tutorial/Step3/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step3/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step3/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step3/tutorial.cxx b/Help/guide/tutorial/Step3/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step3/tutorial.cxx
+++ b/Help/guide/tutorial/Step3/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step4/CMakeLists.txt b/Help/guide/tutorial/Step4/CMakeLists.txt
index fa4aab270b..7531fb41ab 100644
--- a/Help/guide/tutorial/Step4/CMakeLists.txt
+++ b/Help/guide/tutorial/Step4/CMakeLists.txt
@@ -1,4 +1,4 @@
-# TODO 4: Update the minimum required version to 3.15
+# TODO 5: Update the minimum required version to 3.15
cmake_minimum_required(VERSION 3.10)
@@ -15,41 +15,35 @@ project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
-# TODO 5: Create helper variables to determine which compiler we are using:
+# TODO 6: Create helper variables to determine which compiler we are using:
# * Create a new variable gcc_like_cxx that is true if we are using CXX and
# any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
# * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
# Hint: Use set() and COMPILE_LANG_AND_ID
-# TODO 6: Add warning flag compile options to the interface library
+# TODO 7: Add warning flag compile options to the interface library
# tutorial_compiler_flags.
# * For gcc_like_cxx, add flags -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused
# * For msvc_cxx, add flags -W3
# Hint: Use target_compile_options()
-# TODO 7: With nested generator expressions, only use the flags for the
+# TODO 8: With nested generator expressions, only use the flags for the
# build-tree
# Hint: Use BUILD_INTERFACE
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
# TODO 2: Link to tutorial_compiler_flags
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC MathFunctions)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
index 5f7369cae7..ffab4f0a80 100644
--- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
@@ -1,9 +1,25 @@
-add_library(MathFunctions mysqrt.cxx)
+# create the MathFunctions library
+add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ # TODO 4: Link to tutorial_compiler_flags
+
+ target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
+endif()
# TODO 3: Link to tutorial_compiler_flags
diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step4/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx
index abe767d5ae..ba0ac645b5 100644
--- a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.cxx
@@ -1,7 +1,9 @@
-#include <iostream>
+#include "mysqrt.h"
-#include "MathFunctions.h"
+#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -22,3 +24,5 @@ double mysqrt(double x)
}
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step4/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step4/TutorialConfig.h.in b/Help/guide/tutorial/Step4/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step4/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step4/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step4/tutorial.cxx b/Help/guide/tutorial/Step4/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step4/tutorial.cxx
+++ b/Help/guide/tutorial/Step4/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step5/CMakeLists.txt b/Help/guide/tutorial/Step5/CMakeLists.txt
index 279ddf93a3..ad814f69eb 100644
--- a/Help/guide/tutorial/Step5/CMakeLists.txt
+++ b/Help/guide/tutorial/Step5/CMakeLists.txt
@@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
index 6cd88d7ec2..0c688f274d 100644
--- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
@@ -1,13 +1,28 @@
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ # link our compiler flags interface library
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# TODO 1: Create a variable called installable_libs that is a list of all
# libraries we want to install (e.g. MathFunctions and tutorial_compiler_flags)
diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step5/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx
index abe767d5ae..ba0ac645b5 100644
--- a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.cxx
@@ -1,7 +1,9 @@
-#include <iostream>
+#include "mysqrt.h"
-#include "MathFunctions.h"
+#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -22,3 +24,5 @@ double mysqrt(double x)
}
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step5/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step5/TutorialConfig.h.in b/Help/guide/tutorial/Step5/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step5/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step5/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step5/tutorial.cxx b/Help/guide/tutorial/Step5/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step5/tutorial.cxx
+++ b/Help/guide/tutorial/Step5/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt
index c11e307736..a86d60aae0 100644
--- a/Help/guide/tutorial/Step6/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/CMakeLists.txt
@@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
index b4724c4a10..b1b925eccc 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
@@ -1,16 +1,33 @@
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# install libs
set(installable_libs MathFunctions tutorial_compiler_flags)
+if(TARGET SqrtLibrary)
+ list(APPEND installable_libs SqrtLibrary)
+endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step6/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
index abe767d5ae..ba0ac645b5 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
@@ -1,7 +1,9 @@
-#include <iostream>
+#include "mysqrt.h"
-#include "MathFunctions.h"
+#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -22,3 +24,5 @@ double mysqrt(double x)
}
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step6/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step6/TutorialConfig.h.in b/Help/guide/tutorial/Step6/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step6/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step6/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step6/tutorial.cxx b/Help/guide/tutorial/Step6/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step6/tutorial.cxx
+++ b/Help/guide/tutorial/Step6/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt
index d26a90cee8..97ec6aaa38 100644
--- a/Help/guide/tutorial/Step7/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/CMakeLists.txt
@@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
index e5bdc4da15..897ec0e644 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
@@ -1,36 +1,54 @@
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions MathFunctions.cxx)
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
-# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
-# TODO 1: Include CheckCXXSourceCompiles
+ # TODO 1: Include CheckCXXSourceCompiles
-# TODO 2: Use check_cxx_source_compiles with simple C++ code to verify
-# availability of:
-# * std::log
-# * std::exp
-# Store the results in HAVE_LOG and HAVE_EXP respectively.
+ # TODO 2: Use check_cxx_source_compiles with simple C++ code to verify
+ # availability of:
+ # * std::log
+ # * std::exp
+ # Store the results in HAVE_LOG and HAVE_EXP respectively.
-# Hint: Sample C++ code which uses log:
-# #include <cmath>
-# int main() {
-# std::log(1.0);
-# return 0;
-# }
+ # Hint: Sample C++ code which uses log:
+ # #include <cmath>
+ # int main() {
+ # std::log(1.0);
+ # return 0;
+ # }
-# TODO 3: Conditionally on HAVE_LOG and HAVE_EXP, add private compile
-# definitions "HAVE_LOG" and "HAVE_EXP" to the MathFunctions target.
+ # TODO 3: Conditionally on HAVE_LOG and HAVE_EXP, add private compile
+ # definitions "HAVE_LOG" and "HAVE_EXP" to the SqrtLibrary target.
-#Hint: Use target_compile_definitions()
+ # Hint: Use target_compile_definitions()
+
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+# link our compiler flags interface library
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# install libs
set(installable_libs MathFunctions tutorial_compiler_flags)
+if(TARGET SqrtLibrary)
+ list(APPEND installable_libs SqrtLibrary)
+endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step7/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
index 3d2492a648..9963cff7dc 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
@@ -1,8 +1,9 @@
-#include <iostream>
+#include "mysqrt.h"
-// TODO 4: include cmath
-#include "MathFunctions.h"
+#include <iostream>
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -32,3 +33,5 @@ double mysqrt(double x)
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step7/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step7/TutorialConfig.h.in b/Help/guide/tutorial/Step7/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step7/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step7/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step7/tutorial.cxx b/Help/guide/tutorial/Step7/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step7/tutorial.cxx
+++ b/Help/guide/tutorial/Step7/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt
index cb87281b9e..97ec6aaa38 100644
--- a/Help/guide/tutorial/Step8/CMakeLists.txt
+++ b/Help/guide/tutorial/Step8/CMakeLists.txt
@@ -16,23 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
index f81b563409..872a24a80e 100644
--- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
@@ -1,39 +1,58 @@
-add_library(MathFunctions mysqrt.cxx)
+add_library(MathFunctions MathFunctions.cxx)
+
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ )
+
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
+ # 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(SqrtLibrary
+ PRIVATE "HAVE_LOG" "HAVE_EXP"
+ )
+ endif()
+
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
- INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
-
-# 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()
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# install libs
set(installable_libs MathFunctions tutorial_compiler_flags)
+if(TARGET SqrtLibrary)
+ list(APPEND installable_libs SqrtLibrary)
+endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step8/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
index 7eecd26b5d..28ab042de6 100644
--- a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
@@ -1,8 +1,10 @@
+#include "mysqrt.h"
+
#include <cmath>
#include <iostream>
-#include "MathFunctions.h"
-
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -30,3 +32,5 @@ double mysqrt(double x)
#endif
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step8/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step8/TutorialConfig.h.in b/Help/guide/tutorial/Step8/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step8/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step8/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step8/tutorial.cxx b/Help/guide/tutorial/Step8/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step8/tutorial.cxx
+++ b/Help/guide/tutorial/Step8/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt
index d26a90cee8..97ec6aaa38 100644
--- a/Help/guide/tutorial/Step9/CMakeLists.txt
+++ b/Help/guide/tutorial/Step9/CMakeLists.txt
@@ -16,22 +16,17 @@ target_compile_options(tutorial_compiler_flags INTERFACE
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-
# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)
# add the MathFunctions library
-if(USE_MYMATH)
- add_subdirectory(MathFunctions)
- list(APPEND EXTRA_LIBS MathFunctions)
-endif()
+add_subdirectory(MathFunctions)
# add the executable
add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
+
+target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
index 8e04f97492..54cecf812d 100644
--- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
@@ -1,34 +1,41 @@
-# 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 MathFunctions.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}
- )
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+ )
-# link our compiler flags interface library
-target_link_libraries(MathFunctions tutorial_compiler_flags)
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
+if (USE_MYMATH)
+ target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
+
+ # generate Table.h
+ include(MakeTable.cmake)
+
+ # library that just does sqrt
+ add_library(SqrtLibrary STATIC
+ mysqrt.cxx
+ ${CMAKE_CURRENT_BINARY_DIR}/Table.h
+ )
+
+ # state that we depend on our binary dir to find Table.h
+ target_include_directories(SqrtLibrary PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ )
+
+ target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+ target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
+endif()
+
+target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# install libs
set(installable_libs MathFunctions tutorial_compiler_flags)
+if(TARGET SqrtLibrary)
+ list(APPEND installable_libs SqrtLibrary)
+endif()
install(TARGETS ${installable_libs} DESTINATION lib)
# install include headers
install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake
new file mode 100644
index 0000000000..12865a9831
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/MakeTable.cmake
@@ -0,0 +1,10 @@
+# first we add the executable that generates the table
+add_executable(MakeTable MakeTable.cxx)
+target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
+
+# 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
+ )
diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx
new file mode 100644
index 0000000000..dc28b4b530
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx
@@ -0,0 +1,19 @@
+#include "MathFunctions.h"
+
+#include <cmath>
+
+#ifdef USE_MYMATH
+# include "mysqrt.h"
+#endif
+
+namespace mathfunctions {
+double sqrt(double x)
+{
+// which square root function should we use?
+#ifdef USE_MYMATH
+ return detail::mysqrt(x);
+#else
+ return std::sqrt(x);
+#endif
+}
+}
diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h
index cd36bccffd..d5c2f224bf 100644
--- a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h
+++ b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.h
@@ -1 +1,5 @@
-double mysqrt(double x);
+#pragma once
+
+namespace mathfunctions {
+double sqrt(double x);
+}
diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx
index 7d80ee9648..477d715205 100644
--- a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx
+++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.cxx
@@ -1,10 +1,12 @@
-#include <iostream>
+#include "mysqrt.h"
-#include "MathFunctions.h"
+#include <iostream>
// include the generated table
#include "Table.h"
+namespace mathfunctions {
+namespace detail {
// a hack square root calculation using simple operations
double mysqrt(double x)
{
@@ -31,3 +33,5 @@ double mysqrt(double x)
return result;
}
+}
+}
diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h
new file mode 100644
index 0000000000..593d41e7a6
--- /dev/null
+++ b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace mathfunctions {
+namespace detail {
+double mysqrt(double x);
+}
+}
diff --git a/Help/guide/tutorial/Step9/TutorialConfig.h.in b/Help/guide/tutorial/Step9/TutorialConfig.h.in
index e23f5213c7..7e4d7fa1fa 100644
--- a/Help/guide/tutorial/Step9/TutorialConfig.h.in
+++ b/Help/guide/tutorial/Step9/TutorialConfig.h.in
@@ -1,4 +1,3 @@
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
-#cmakedefine USE_MYMATH
diff --git a/Help/guide/tutorial/Step9/tutorial.cxx b/Help/guide/tutorial/Step9/tutorial.cxx
index b3c6a4f43c..a3a2bdce00 100644
--- a/Help/guide/tutorial/Step9/tutorial.cxx
+++ b/Help/guide/tutorial/Step9/tutorial.cxx
@@ -3,13 +3,9 @@
#include <iostream>
#include <string>
+#include "MathFunctions.h"
#include "TutorialConfig.h"
-// should we include the MathFunctions header?
-#ifdef USE_MYMATH
-# include "MathFunctions.h"
-#endif
-
int main(int argc, char* argv[])
{
if (argc < 2) {
@@ -23,12 +19,7 @@ int main(int argc, char* argv[])
// convert input to double
const double inputValue = std::stod(argv[1]);
- // which square root function should we use?
-#ifdef USE_MYMATH
- const double outputValue = mysqrt(inputValue);
-#else
- const double outputValue = sqrt(inputValue);
-#endif
+ const double outputValue = mathfunctions::sqrt(inputValue);
std::cout << "The square root of " << inputValue << " is " << outputValue
<< std::endl;
diff --git a/Help/manual/ccmake.1.rst b/Help/manual/ccmake.1.rst
index a09857b3a7..5b118d1315 100644
--- a/Help/manual/ccmake.1.rst
+++ b/Help/manual/ccmake.1.rst
@@ -8,6 +8,7 @@ Synopsis
.. parsed-literal::
+ ccmake [<options>] -B <path-to-build> [-S <path-to-source>]
ccmake [<options>] <path-to-source | path-to-existing-build>
Description
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst
index b9d621bafa..b88b8645c8 100644
--- a/Help/manual/cmake-buildsystem.7.rst
+++ b/Help/manual/cmake-buildsystem.7.rst
@@ -37,6 +37,8 @@ is defined as an executable formed by compiling and linking ``zipapp.cpp``.
When linking the ``zipapp`` executable, the ``archive`` static library is
linked in.
+.. _`Binary Executables`:
+
Binary Executables
------------------
@@ -797,6 +799,10 @@ An *archive* output artifact of a buildsystem target may be:
created by the :command:`add_executable` command when its
:prop_tgt:`ENABLE_EXPORTS` target property is set.
+* On macOS: the linker import file (e.g. ``.tbd``) of a shared library target
+ created by the :command:`add_library` command with the ``SHARED`` option and
+ when its :prop_tgt:`ENABLE_EXPORTS` target property is set.
+
The :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
target properties may be used to control archive output artifact locations
and names in the build tree.
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index 4c29b80f13..1f0c9111aa 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -20,6 +20,7 @@ Environment Variables that Change Behavior
.. toctree::
:maxdepth: 1
+ /envvar/CMAKE_MAXIMUM_RECURSION_DEPTH
/envvar/CMAKE_PREFIX_PATH
/envvar/SSL_CERT_DIR
/envvar/SSL_CERT_FILE
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index c3e87d77c9..9da37999b1 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -446,16 +446,20 @@ command. All paths are expected to be in cmake-style format.
components from a path. See :ref:`Path Structure And Terminology` for the
meaning of each path component.
+ .. versionchanged:: 3.27
+ All operations now accept a list of paths as argument. When a list of paths
+ is specified, the operation will be applied to each path.
+
::
- $<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>
+ $<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.
@@ -470,9 +474,14 @@ These expressions provide the generation-time capabilities equivalent to the
options of the :command:`cmake_path` command. All paths are expected to be
in cmake-style format.
+.. versionchanged:: 3.27
+ All operations now accept a list of paths as argument. When a list of paths
+ is specified, the operation will be applied to each path.
+
+
.. _GenEx PATH-CMAKE_PATH:
-.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path>
+.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path...>
.. versionadded:: 3.24
@@ -483,7 +492,7 @@ in cmake-style format.
When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
<Normalization>` after the conversion.
-.. genex:: $<PATH:APPEND,path,input,...>
+.. genex:: $<PATH:APPEND,path...,input,...>
.. versionadded:: 3.24
@@ -493,7 +502,7 @@ in cmake-style format.
See :ref:`cmake_path(APPEND) <APPEND>` for more details.
-.. genex:: $<PATH:REMOVE_FILENAME,path>
+.. genex:: $<PATH:REMOVE_FILENAME,path...>
.. versionadded:: 3.24
@@ -503,7 +512,7 @@ in cmake-style format.
See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details.
-.. genex:: $<PATH:REPLACE_FILENAME,path,input>
+.. genex:: $<PATH:REPLACE_FILENAME,path...,input>
.. versionadded:: 3.24
@@ -513,7 +522,7 @@ in cmake-style format.
See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details.
-.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path>
+.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path...>
.. versionadded:: 3.24
@@ -521,7 +530,7 @@ in cmake-style format.
See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details.
-.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path,input>
+.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path...,input>
.. versionadded:: 3.24
@@ -530,14 +539,14 @@ in cmake-style format.
See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details.
-.. genex:: $<PATH:NORMAL_PATH,path>
+.. 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>
+.. genex:: $<PATH:RELATIVE_PATH,path...,base_directory>
.. versionadded:: 3.24
@@ -547,7 +556,7 @@ in cmake-style format.
See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more
details.
-.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory>
+.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path...,base_directory>
.. versionadded:: 3.24
@@ -1409,6 +1418,7 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
expression is being evaluated.
.. genex:: $<TARGET_PROPERTY:prop>
+ :target: TARGET_PROPERTY:prop
Value of the property ``prop`` on the target for which the expression
is being evaluated. Note that for generator expressions in
@@ -1494,6 +1504,76 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
Note that ``tgt`` is not added as a dependency of the target this
expression is evaluated on (see policy :policy:`CMP0112`).
+.. genex:: $<TARGET_IMPORT_FILE:tgt>
+
+ .. versionadded:: 3.27
+
+ Full path to the linker import file. On DLL platforms, it would be the
+ ``.lib`` file. On AIX, for the executables, and on macOS, for the shared
+ libraries, it could be, respectively, the ``.imp`` or ``.tbd`` import file,
+ depending of the value of :prop_tgt:`ENABLE_EXPORTS` property.
+
+ An empty string is returned when there is no import file associated with the
+ target.
+
+.. genex:: $<TARGET_IMPORT_FILE_BASE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Base name of file linker import file of the target ``tgt`` without prefix and
+ suffix. For example, if target file name is ``libbase.tbd``, the base name is
+ ``base``.
+
+ See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`
+ target properties and their configuration specific variants
+ :prop_tgt:`OUTPUT_NAME_<CONFIG>` and :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`.
+
+ The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+ properties can also be considered.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_PREFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Prefix of the import file of the target ``tgt``.
+
+ See also the :prop_tgt:`IMPORT_PREFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_SUFFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Suffix of the import file of the target ``tgt``.
+
+ The suffix corresponds to the file extension (such as ".lib" or ".tbd").
+
+ See also the :prop_tgt:`IMPORT_SUFFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Name of the import file of the target target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_IMPORT_FILE_DIR:tgt>
+
+ Directory of the import file of the target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
.. genex:: $<TARGET_LINKER_FILE:tgt>
File used when linking to the ``tgt`` target. This will usually
@@ -1501,13 +1581,22 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
but for a shared library on DLL platforms, it would be the ``.lib``
import library associated with the DLL.
+ .. versionadded:: 3.27
+ On macOS, it could be the ``.tbd`` import file associated with the shared
+ library, depending of the value of :prop_tgt:`ENABLE_EXPORTS` property.
+
+ This generator expression is equivalent to
+ :genex:`$<TARGET_LINKER_LIBRARY_FILE>` or
+ :genex:`$<TARGET_LINKER_IMPORT_FILE>` generator expressions, depending of the
+ characteristics of the target and the platform.
+
.. genex:: $<TARGET_LINKER_FILE_BASE_NAME:tgt>
.. versionadded:: 3.15
Base name of file used to link the target ``tgt``, i.e.
- ``$<TARGET_LINKER_FILE_NAME:tgt>`` without prefix and suffix. For example,
- if target file name is ``libbase.a``, the base name is ``base``.
+ :genex:`$<TARGET_LINKER_FILE_NAME:tgt>` without prefix and suffix. For
+ example, if target file name is ``libbase.a``, the base name is ``base``.
See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
@@ -1561,9 +1650,151 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
Note that ``tgt`` is not added as a dependency of the target this
expression is evaluated on (see policy :policy:`CMP0112`).
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE:tgt>
+
+ .. versionadded:: 3.27
+
+ File used when linking o the ``tgt`` target is done using directly the
+ library, and not an import file. This will usually be the library that
+ ``tgt`` represents (``.a``, ``.so``, ``.dylib``). So, on DLL platforms, it
+ will be an empty string.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_BASE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Base name of library file used to link the target ``tgt``, i.e.
+ :genex:`$<TARGET_LINKER_LIBRARY_FILE_NAME:tgt>` without prefix and suffix.
+ For example, if target file name is ``libbase.a``, the base name is ``base``.
+
+ See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+ and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
+ specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`,
+ :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and
+ :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`.
+
+ The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+ properties can also be considered.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_PREFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Prefix of the library file used to link target ``tgt``.
+
+ See also the :prop_tgt:`PREFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_SUFFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Suffix of the library file used to link target ``tgt``.
+
+ The suffix corresponds to the file extension (such as ".a" or ".dylib").
+
+ See also the :prop_tgt:`SUFFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Name of the library file used to link target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_LIBRARY_FILE_DIR:tgt>
+
+ .. versionadded:: 3.27
+
+ Directory of the library file used to link target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE:tgt>
+
+ .. versionadded:: 3.27
+
+ File used when linking to the ``tgt`` target is done using an import
+ file. This will usually be the import file that ``tgt`` represents
+ (``.lib``, ``.tbd``). So, when no import file is involved in the link step,
+ an empty string is returned.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_BASE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Base name of the import file used to link the target ``tgt``, i.e.
+ :genex:`$<TARGET_LINKER_IMPORT_FILE_NAME:tgt>` without prefix and suffix.
+ For example, if target file name is ``libbase.tbd``, the base name is ``base``.
+
+ See also the :prop_tgt:`OUTPUT_NAME` and :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+ target properties and their configuration
+ specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>` and
+ :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`.
+
+ The :prop_tgt:`<CONFIG>_POSTFIX` and :prop_tgt:`DEBUG_POSTFIX` target
+ properties can also be considered.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_PREFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Prefix of the import file used to link target ``tgt``.
+
+ See also the :prop_tgt:`IMPORT_PREFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_SUFFIX:tgt>
+
+ .. versionadded:: 3.27
+
+ Suffix of the import file used to link target ``tgt``.
+
+ The suffix corresponds to the file extension (such as ".lib" or ".tbd").
+
+ See also the :prop_tgt:`IMPORT_SUFFIX` target property.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Name of the import file used to link target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_LINKER_IMPORT_FILE_DIR:tgt>
+
+ .. versionadded:: 3.27
+
+ Directory of the import file used to link target ``tgt``.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
.. genex:: $<TARGET_SONAME_FILE:tgt>
File with soname (``.so.3``) where ``tgt`` is the name of a target.
+
.. genex:: $<TARGET_SONAME_FILE_NAME:tgt>
Name of file with soname (``.so.3``).
@@ -1573,11 +1804,35 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
.. genex:: $<TARGET_SONAME_FILE_DIR:tgt>
- Directory of with soname (``.so.3``).
+ Directory of file with soname (``.so.3``).
Note that ``tgt`` is not added as a dependency of the target this
expression is evaluated on (see policy :policy:`CMP0112`).
+.. genex:: $<TARGET_SONAME_IMPORT_FILE:tgt>
+
+ .. versionadded:: 3.27
+
+ Import file with soname (``.3.tbd``) where ``tgt`` is the name of a target.
+
+.. genex:: $<TARGET_SONAME_IMPORT_FILE_NAME:tgt>
+
+ .. versionadded:: 3.27
+
+ Name of the import file with soname (``.3.tbd``).
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
+.. genex:: $<TARGET_SONAME_IMPORT_FILE_DIR:tgt>
+
+ .. versionadded:: 3.27
+
+ Directory of the import file with soname (``.3.tbd``).
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
+
.. genex:: $<TARGET_PDB_FILE:tgt>
.. versionadded:: 3.1
@@ -1668,7 +1923,9 @@ In the following, the phrase "the ``tgt`` filename" means the name of the
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
+ dependencies. If only the directories of the DLLs are needed, see the
+ :genex:`TARGET_RUNTIME_DLL_DIRS` generator expression.
+ Using this generator expression on targets other than
executables, ``SHARED`` libraries, and ``MODULE`` libraries is an error.
**On non-DLL platforms, this expression always evaluates to an empty string**.
@@ -1700,6 +1957,20 @@ On platforms that support runtime paths (``RPATH``), refer to the
:prop_tgt:`INSTALL_RPATH` target property.
On Apple platforms, refer to the :prop_tgt:`INSTALL_NAME_DIR` target property.
+.. genex:: $<TARGET_RUNTIME_DLL_DIRS:tgt>
+
+ .. versionadded:: 3.27
+
+ List of the directories which contain the DLLs that the target depends on at
+ runtime (see :genex:`TARGET_RUNTIME_DLLS`). 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, this expression always evaluates to an empty string**.
+
+ This generator expression can e.g. be used to create a batch file using
+ :command:`file(GENERATE)` which sets the PATH environment variable accordingly.
+
Export And Install Expressions
------------------------------
@@ -1725,8 +1996,10 @@ Export And Install Expressions
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.
+ :prop_tgt:`INSTALL_NAME_DIR` property, the ``INSTALL_NAME_DIR`` argument of
+ :command:`install(RUNTIME_DEPENDENCY_SET)`, the code argument of
+ :command:`install(CODE)`, or the file argument of :command:`install(SCRIPT)`,
+ and empty otherwise.
Multi-level Expression Evaluation
---------------------------------
diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst
index ed5bbbf6cb..9647f0d11d 100644
--- a/Help/manual/cmake-generators.7.rst
+++ b/Help/manual/cmake-generators.7.rst
@@ -107,6 +107,12 @@ Other Generators
Extra Generators
================
+.. deprecated:: 3.27
+
+ Support for "Extra Generators" is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
Some of the `CMake Generators`_ listed in the :manual:`cmake(1)`
command-line tool :option:`--help <cmake --help>` output may have
variants that specify an extra generator for an auxiliary IDE tool.
diff --git a/Help/manual/cmake-gui.1.rst b/Help/manual/cmake-gui.1.rst
index cdb860f3ff..26083ca245 100644
--- a/Help/manual/cmake-gui.1.rst
+++ b/Help/manual/cmake-gui.1.rst
@@ -9,9 +9,9 @@ Synopsis
.. parsed-literal::
cmake-gui [<options>]
+ cmake-gui [<options>] -B <path-to-build> [-S <path-to-source>]
cmake-gui [<options>] <path-to-source | path-to-existing-build>
- cmake-gui [<options>] -S <path-to-source> -B <path-to-build>
- cmake-gui [<options>] --browse-manual
+ cmake-gui [<options>] --browse-manual [<filename>]
Description
===========
@@ -46,9 +46,11 @@ Options
Name of the preset to use from the project's
:manual:`presets <cmake-presets(7)>` files, if it has them.
-.. option:: --browse-manual
+.. option:: --browse-manual [<filename>]
- Open the CMake reference manual in a browser and immediately exit.
+ Open the CMake reference manual in a browser and immediately exit. If
+ ``<filename>`` is specified, open that file within the reference manual
+ instead of ``index.html``.
.. include:: OPTIONS_HELP.txt
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 22e9eb2b6d..8c7189a72b 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,19 @@ The :variable:`CMAKE_MINIMUM_REQUIRED_VERSION` variable may also be used
to determine whether to report an error on use of deprecated macros or
functions.
+Policies Introduced by CMake 3.27
+=================================
+
+.. toctree::
+ :maxdepth: 1
+
+ CMP0149: Visual Studio generators select latest Windows SDK by default. </policy/CMP0149>
+ CMP0148: The FindPythonInterp and FindPythonLibs modules are removed. </policy/CMP0148>
+ CMP0147: Visual Studio generators build custom commands in parallel. </policy/CMP0147>
+ CMP0146: The FindCUDA module is removed. </policy/CMP0146>
+ CMP0145: The Dart and FindDart modules are removed. </policy/CMP0145>
+ CMP0144: find_package uses upper-case PACKAGENAME_ROOT variables. </policy/CMP0144>
+
Policies Introduced by CMake 3.26
=================================
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index da699d86af..7794e45ce5 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -63,6 +63,9 @@ The root object recognizes the following fields:
``6``
.. versionadded:: 3.25
+ ``7``
+ .. versionadded:: 3.27
+
``cmakeMinimumRequired``
An optional object representing the minimum version of CMake needed to
build this project. This object consists of the following fields:
@@ -359,6 +362,52 @@ that may contain the following fields:
An optional boolean. Setting this to ``true`` is equivalent to passing
:option:`--debug-find <cmake --debug-find>` on the command line.
+``trace``
+ An optional object specifying trace options. This is allowed in preset
+ files specifying version ``7``. The object may contain the following fields:
+
+ ``mode``
+ An optional string that specifies the trace mode. Valid values are:
+
+ ``on``
+ Causes a trace of all calls made and from where to be printed.
+ Equivalent to passing :option:`--trace <cmake --trace>` on the command
+ line.
+
+ ``off``
+ A trace of all calls will not be printed.
+
+ ``expand``
+ Causes a trace with variables expanded of all calls made and from where
+ to be printed. Equivalent to passing :option:`--trace-expand <cmake --trace-expand>`
+ on the command line.
+
+ ``format``
+ An optional string that specifies the format output of the trace.
+ Valid values are:
+
+ ``human``
+ Prints each trace line in a human-readable format.
+ This is the default format. Equivalent to passing
+ :option:`--trace-format=human <cmake --trace-format>`
+ on the command line.
+
+ ``json-v1``
+ Prints each line as a separate JSON document. Equivalent to passing
+ :option:`--trace-format=json-v1 <cmake --trace-format>`
+ on the command line.
+
+ ``source``
+ An optional array of strings representing the paths of source files to
+ be traced. This field can also be a string, which is equivalent to an
+ array containing one string. Equivalent to passing
+ :option:`--trace-source <cmake --trace-source>` on the command line.
+
+ ``redirect``
+ An optional string specifying a path to a trace output file. Equivalent
+ to passing :option:`--trace-redirect <cmake --trace-redirect>`
+ on the command line.
+
Build Preset
^^^^^^^^^^^^
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index fb84f5ae2d..c0e2ee2bdc 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -175,7 +175,10 @@ Properties on Targets
/prop_tgt/CONFIG_POSTFIX
/prop_tgt/CROSSCOMPILING_EMULATOR
/prop_tgt/CUDA_ARCHITECTURES
+ /prop_tgt/CUDA_CUBIN_COMPILATION
/prop_tgt/CUDA_EXTENSIONS
+ /prop_tgt/CUDA_FATBIN_COMPILATION
+ /prop_tgt/CUDA_OPTIX_COMPILATION
/prop_tgt/CUDA_PTX_COMPILATION
/prop_tgt/CUDA_RESOLVE_DEVICE_SYMBOLS
/prop_tgt/CUDA_RUNTIME_LIBRARY
@@ -202,6 +205,7 @@ Properties on Targets
/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY
/prop_tgt/DEPRECATION
/prop_tgt/DISABLE_PRECOMPILE_HEADERS
+ /prop_tgt/DLL_NAME_WITH_SOVERSION
/prop_tgt/DOTNET_SDK
/prop_tgt/DOTNET_TARGET_FRAMEWORK
/prop_tgt/DOTNET_TARGET_FRAMEWORK_VERSION
@@ -269,6 +273,7 @@ Properties on Targets
/prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH
/prop_tgt/INSTALL_RPATH
/prop_tgt/INSTALL_RPATH_USE_LINK_PATH
+ /prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES
/prop_tgt/INTERFACE_AUTOUIC_OPTIONS
/prop_tgt/INTERFACE_COMPILE_DEFINITIONS
/prop_tgt/INTERFACE_COMPILE_FEATURES
diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst
index 8a83807149..a831fa6ef1 100644
--- a/Help/manual/cmake-toolchains.7.rst
+++ b/Help/manual/cmake-toolchains.7.rst
@@ -84,7 +84,7 @@ Toolchain Features
==================
CMake provides the :command:`try_compile` command and wrapper macros such as
-:module:`CheckCXXSourceCompiles`, :module:`CheckCXXSymbolExists` and
+:module:`CheckSourceCompiles`, :module:`CheckCXXSymbolExists` and
:module:`CheckIncludeFile` to test capability and availability of various
toolchain features. These APIs test the toolchain in some way and cache the
result so that the test does not have to be performed again the next time
@@ -273,7 +273,7 @@ supported out of the box. Other versions may require one to set
Cross Compiling for Windows 10 Universal Applications
-----------------------------------------------------
-A toolchain file to configure a Visual Studio generator for a
+A toolchain file to configure :ref:`Visual Studio Generators` for a
Windows 10 Universal Application may look like this:
.. code-block:: cmake
@@ -283,9 +283,10 @@ Windows 10 Universal Application may look like this:
A Windows 10 Universal Application targets both Windows Store and
Windows Phone. Specify the :variable:`CMAKE_SYSTEM_VERSION` variable
-to be ``10.0`` to build with the latest available Windows 10 SDK.
-Specify a more specific version (e.g. ``10.0.10240.0`` for RTM)
-to build with the corresponding SDK.
+to be ``10.0`` or higher.
+
+CMake selects a Windows SDK as described by documentation of the
+:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable.
Cross Compiling for Windows Phone
---------------------------------
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 23d8256622..f3212de4df 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -132,6 +132,7 @@ Variables that Provide Information
/variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
/variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION
/variable/CMAKE_VS_VERSION_BUILD_NUMBER
+ /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM
/variable/CMAKE_XCODE_BUILD_SYSTEM
@@ -166,6 +167,7 @@ Variables that Change Behavior
/variable/BUILD_SHARED_LIBS
/variable/CMAKE_ABSOLUTE_DESTINATION_FILES
+ /variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
/variable/CMAKE_APPBUNDLE_PATH
/variable/CMAKE_AUTOMOC_RELAXED_MODE
/variable/CMAKE_BACKWARDS_COMPATIBILITY
@@ -226,6 +228,8 @@ Variables that Change Behavior
/variable/CMAKE_INSTALL_MESSAGE
/variable/CMAKE_INSTALL_PREFIX
/variable/CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT
+ /variable/CMAKE_KATE_FILES_MODE
+ /variable/CMAKE_KATE_MAKE_ARGUMENTS
/variable/CMAKE_LIBRARY_PATH
/variable/CMAKE_LINK_DIRECTORIES_BEFORE
/variable/CMAKE_LINK_LIBRARIES_ONLY_TARGETS
@@ -400,11 +404,14 @@ Variables that Control the Build
/variable/CMAKE_AUTOMOC_MACRO_NAMES
/variable/CMAKE_AUTOMOC_MOC_OPTIONS
/variable/CMAKE_AUTOMOC_PATH_PREFIX
+ /variable/CMAKE_AUTOMOC_EXECUTABLE
/variable/CMAKE_AUTORCC
/variable/CMAKE_AUTORCC_OPTIONS
+ /variable/CMAKE_AUTORCC_EXECUTABLE
/variable/CMAKE_AUTOUIC
/variable/CMAKE_AUTOUIC_OPTIONS
/variable/CMAKE_AUTOUIC_SEARCH_PATHS
+ /variable/CMAKE_AUTOUIC_EXECUTABLE
/variable/CMAKE_BUILD_RPATH
/variable/CMAKE_BUILD_RPATH_USE_ORIGIN
/variable/CMAKE_BUILD_WITH_INSTALL_NAME_DIR
@@ -424,7 +431,9 @@ Variables that Control the Build
/variable/CMAKE_DEFAULT_CONFIGS
/variable/CMAKE_DEPENDS_USE_COMPILER
/variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
+ /variable/CMAKE_DLL_NAME_WITH_SOVERSION
/variable/CMAKE_ENABLE_EXPORTS
+ /variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS
/variable/CMAKE_EXE_LINKER_FLAGS
/variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
/variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
@@ -504,6 +513,7 @@ Variables that Control the Build
/variable/CMAKE_POSITION_INDEPENDENT_CODE
/variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY
/variable/CMAKE_RUNTIME_OUTPUT_DIRECTORY_CONFIG
+ /variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
/variable/CMAKE_SHARED_LINKER_FLAGS
/variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG
/variable/CMAKE_SHARED_LINKER_FLAGS_CONFIG_INIT
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index e48ecd972d..1ea7626103 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -9,8 +9,8 @@ Synopsis
.. parsed-literal::
`Generate a Project Buildsystem`_
+ cmake [<options>] -B <path-to-build> [-S <path-to-source>]
cmake [<options>] <path-to-source | path-to-existing-build>
- cmake [<options>] -S <path-to-source> -B <path-to-build>
`Build a Project`_
cmake --build <dir> [<options>] [-- <build-tool-options>]
@@ -116,6 +116,20 @@ Generate a Project Buildsystem
Run CMake with one of the following command signatures to specify the
source and build trees and generate a buildsystem:
+``cmake [<options>] -B <path-to-build> [-S <path-to-source>]``
+
+ .. versionadded:: 3.13
+
+ Uses ``<path-to-build>`` as the build tree and ``<path-to-source>``
+ as the source tree. The specified paths may be absolute or relative
+ to the current working directory. The source tree must contain a
+ ``CMakeLists.txt`` file. The build tree will be created automatically
+ if it does not already exist. For example:
+
+ .. code-block:: console
+
+ $ cmake -S src -B build
+
``cmake [<options>] <path-to-source>``
Uses the current working directory as the build tree, and
``<path-to-source>`` as the source tree. The specified path may
@@ -141,20 +155,6 @@ source and build trees and generate a buildsystem:
$ cd build
$ cmake .
-``cmake [<options>] -S <path-to-source> -B <path-to-build>``
-
- .. versionadded:: 3.13
-
- Uses ``<path-to-build>`` as the build tree and ``<path-to-source>``
- as the source tree. The specified paths may be absolute or relative
- to the current working directory. The source tree must contain a
- ``CMakeLists.txt`` file. The build tree will be created automatically
- if it does not already exist. For example:
-
- .. code-block:: console
-
- $ cmake -S src -B build
-
In all cases the ``<options>`` may be zero or more of the `Options`_ below.
The above styles for specifying the source and build trees may be mixed.
@@ -167,14 +167,14 @@ the current working directory (cwd) is used for the other. For example:
============================== ============ ===========
Command Line Source Dir Build Dir
============================== ============ ===========
+ ``cmake -B build`` `cwd` ``build``
+ ``cmake -B build src`` ``src`` ``build``
+ ``cmake -B build -S src`` ``src`` ``build``
``cmake src`` ``src`` `cwd`
``cmake build`` (existing) `loaded` ``build``
``cmake -S src`` ``src`` `cwd`
``cmake -S src build`` ``src`` ``build``
``cmake -S src -B build`` ``src`` ``build``
- ``cmake -B build`` `cwd` ``build``
- ``cmake -B build src`` ``src`` ``build``
- ``cmake -B build -S src`` ``src`` ``build``
============================== ============ ===========
.. versionchanged:: 3.23
@@ -781,7 +781,7 @@ Available commands are:
(:option:`-A ... <cmake -A>`). The value is a list of platforms known to
be supported.
``extraGenerators``
- A list of strings with all the extra generators compatible with
+ A list of strings with all the :ref:`Extra Generators` compatible with
the generator.
``fileApi``
@@ -1080,11 +1080,18 @@ Available commands are:
situations instead. Use ``--`` to stop interpreting options and treat all
remaining arguments as paths, even if they start with ``-``.
-.. option:: sleep <number>...
+.. option:: sleep <number>
.. versionadded:: 3.0
- Sleep for given number of seconds.
+ Sleep for ``<number>`` seconds. ``<number>`` may be a floating point number.
+ A practical minimum is about 0.1 seconds due to overhead in starting/stopping
+ CMake executable. This can be useful in a CMake script to insert a delay:
+
+ .. code-block:: cmake
+
+ # Sleep for about 0.5 seconds
+ execute_process(COMMAND ${CMAKE_COMMAND} -E sleep 0.5)
.. option:: tar [cxt][vf][zjJ] file.tar [<options>] [--] [<pathname>...]
@@ -1189,7 +1196,7 @@ Available commands are:
.. option:: time <command> [<args>...]
- Run command and display elapsed time.
+ Run ``<command>`` and display elapsed time (including overhead of CMake frontend).
.. versionadded:: 3.5
The command now properly passes arguments with spaces or special characters
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 5e82faae0d..994ae47b77 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -11,7 +11,7 @@ Synopsis
.. parsed-literal::
`Run Tests`_
- ctest [<options>]
+ ctest [<options>] [--test-dir <path-to-build>]
`Build and Test Mode`_
ctest --build-and-test <path-to-source> <path-to-build>
@@ -354,7 +354,8 @@ Run Tests
.. option:: --test-dir <dir>
- Specify the directory in which to look for tests.
+ Specify the directory in which to look for tests, typically a CMake project
+ build directory. If not specified, the current directory is used.
.. option:: --test-output-size-passed <size>
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index 348116b38a..adfb1cb135 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -89,6 +89,23 @@
"include": { "$ref": "#/definitions/include"}
},
"additionalProperties": false
+ },
+ {
+ "properties": {
+ "version": {
+ "const": 7,
+ "description": "A required integer representing the version of the JSON schema."
+ },
+ "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+ "vendor": { "$ref": "#/definitions/vendor" },
+ "configurePresets": { "$ref": "#/definitions/configurePresetsV7"},
+ "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
+ "testPresets": { "$ref": "#/definitions/testPresetsV6"},
+ "packagePresets": { "$ref": "#/definitions/packagePresetsV6"},
+ "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" },
+ "include": { "$ref": "#/definitions/include"}
+ },
+ "additionalProperties": false
}
],
"required": [
@@ -119,6 +136,59 @@
"description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, the keys should be a vendor-specific domain name followed by a /-separated path. For example, the Example IDE 1.0 could use example.com/ExampleIDE/1.0. The value of each field can be anything desired by the vendor, though will typically be a map.",
"properties": {}
},
+ "configurePresetsItemsV7": {
+ "type": "array",
+ "description": "A configure preset object.",
+ "items": {
+ "type": "object",
+ "description": "A configure preset object.",
+ "properties": {
+ "trace": {
+ "type": "object",
+ "description": "An optional object specifying trace options.",
+ "properties": {
+ "mode": {
+ "type": "string",
+ "description": "An optional string that specifies the trace mode.",
+ "enum": [
+ "on", "off", "expand"
+ ]
+ },
+ "format": {
+ "type": "string",
+ "description": "An optional string that specifies the trace output format.",
+ "enum": [
+ "human", "json-v1"
+ ]
+ },
+ "source": {
+ "anyOf": [
+ {
+ "type": "string",
+ "description": "An optional string representing the path to one source file to be traced.",
+ "minLength": 1
+ },
+ {
+ "type": "array",
+ "description": "An optional array of strings representing the paths to source files to be traced.",
+ "items": {
+ "type": "string",
+ "description": "A string representing the path to one source file to be traced.",
+ "minLength": 1
+ }
+ }
+ ]
+ },
+ "redirect": {
+ "type": "string",
+ "description": "An optional string specifying a path to a trace output file."
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+ }
+ },
"configurePresetsItemsV3": {
"type": "array",
"description": "A configure preset object.",
@@ -393,6 +463,43 @@
}
}
},
+ "configurePresetsV7": {
+ "type": "array",
+ "description": "An optional array of configure preset objects.",
+ "allOf": [
+ { "$ref": "#/definitions/configurePresetsItemsV1" },
+ { "$ref": "#/definitions/configurePresetsItemsV3" },
+ { "$ref": "#/definitions/configurePresetsItemsV7" }
+ ],
+ "items": {
+ "properties": {
+ "name": {},
+ "hidden": {},
+ "inherits": {},
+ "vendor": {},
+ "displayName": {},
+ "description": {},
+ "generator": {},
+ "architecture": {},
+ "toolset": {},
+ "toolchainFile": {},
+ "binaryDir": {},
+ "installDir": {},
+ "cmakeExecutable": {},
+ "cacheVariables": {},
+ "environment": {},
+ "warnings": {},
+ "errors": {},
+ "debug": {},
+ "condition": {},
+ "trace": {}
+ },
+ "required": [
+ "name"
+ ],
+ "additionalProperties": false
+ }
+ },
"configurePresetsV3": {
"type": "array",
"description": "An optional array of configure preset objects.",
diff --git a/Help/module/CMAKE_REQUIRED_DEFINITIONS.txt b/Help/module/CMAKE_REQUIRED_DEFINITIONS.txt
new file mode 100644
index 0000000000..17289c378a
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_DEFINITIONS.txt
@@ -0,0 +1,4 @@
+ ``CMAKE_REQUIRED_DEFINITIONS``
+ A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
+ ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
+ ``<resultVar>`` will also be added automatically.
diff --git a/Help/module/CMAKE_REQUIRED_FLAGS.txt b/Help/module/CMAKE_REQUIRED_FLAGS.txt
new file mode 100644
index 0000000000..80ae23999b
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_FLAGS.txt
@@ -0,0 +1,6 @@
+ ``CMAKE_REQUIRED_FLAGS``
+ String of additional flags to pass to the compiler. The string must be
+ space-delimited--a :ref:`;-list <CMake Language Lists>` will not work.
+ The contents of :variable:`CMAKE_<LANG>_FLAGS <CMAKE_<LANG>_FLAGS>` and
+ its associated configuration-specific variable are automatically added
+ to the compiler command before the contents of ``CMAKE_REQUIRED_FLAGS``.
diff --git a/Help/module/CMAKE_REQUIRED_INCLUDES.txt b/Help/module/CMAKE_REQUIRED_INCLUDES.txt
new file mode 100644
index 0000000000..c8993bbfce
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_INCLUDES.txt
@@ -0,0 +1,4 @@
+ ``CMAKE_REQUIRED_INCLUDES``
+ A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
+ the compiler. These will be the only header search paths used--the contents
+ of the :prop_dir:`INCLUDE_DIRECTORIES` directory property will be ignored.
diff --git a/Help/module/CMAKE_REQUIRED_LIBRARIES.txt b/Help/module/CMAKE_REQUIRED_LIBRARIES.txt
new file mode 100644
index 0000000000..8611b9edf6
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_LIBRARIES.txt
@@ -0,0 +1,5 @@
+ ``CMAKE_REQUIRED_LIBRARIES``
+ A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
+ command. These can be the name of system libraries or they can be
+ :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
+ further details).
diff --git a/Help/module/CMAKE_REQUIRED_LINK_OPTIONS.txt b/Help/module/CMAKE_REQUIRED_LINK_OPTIONS.txt
new file mode 100644
index 0000000000..f2a2474374
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_LINK_OPTIONS.txt
@@ -0,0 +1,5 @@
+ ``CMAKE_REQUIRED_LINK_OPTIONS``
+ .. versionadded:: 3.14
+
+ A :ref:`;-list <CMake Language Lists>` of options to add to the link
+ command (see :command:`try_compile` for further details).
diff --git a/Help/module/CMAKE_REQUIRED_QUIET.txt b/Help/module/CMAKE_REQUIRED_QUIET.txt
new file mode 100644
index 0000000000..aae8059f6d
--- /dev/null
+++ b/Help/module/CMAKE_REQUIRED_QUIET.txt
@@ -0,0 +1,5 @@
+ ``CMAKE_REQUIRED_QUIET``
+ .. versionadded:: 3.1
+
+ If this variable evaluates to a boolean true value, all status messages
+ associated with the check will be suppressed.
diff --git a/Help/policy/CMP0144.rst b/Help/policy/CMP0144.rst
new file mode 100644
index 0000000000..3959d96142
--- /dev/null
+++ b/Help/policy/CMP0144.rst
@@ -0,0 +1,26 @@
+CMP0144
+-------
+
+.. versionadded:: 3.27
+
+:command:`find_package` uses upper-case ``<PACKAGENAME>_ROOT`` variables.
+
+In CMake 3.27 and above the :command:`find_package(<PackageName>)` command now
+searches prefixes specified by the upper-case :variable:`<PACKAGENAME>_ROOT`
+CMake variable and the :envvar:`<PACKAGENAME>_ROOT` environment variable
+in addition to the case-preserved :variable:`<PackageName>_ROOT` and
+:envvar:`<PackageName>_ROOT` variables used since policy :policy:`CMP0074`.
+This policy provides compatibility with projects that have not been
+updated to avoid using ``<PACKAGENAME>_ROOT`` variables for other purposes.
+
+The ``OLD`` behavior for this policy is to ignore ``<PACKAGENAME>_ROOT``
+variables if the original ``<PackageName>`` has lower-case characters.
+The ``NEW`` behavior for this policy is to use ``<PACKAGENAME>_ROOT``
+variables.
+
+This policy was introduced in CMake version 3.27. CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0145.rst b/Help/policy/CMP0145.rst
new file mode 100644
index 0000000000..bb1c02eab0
--- /dev/null
+++ b/Help/policy/CMP0145.rst
@@ -0,0 +1,30 @@
+CMP0145
+-------
+
+.. versionadded:: 3.27
+
+The :module:`Dart` and :module:`FindDart` modules are removed.
+
+These modules were added very early in CMake's development to support
+driving tests with a "DART" tool, but DART has not been distributed or
+maintained for many years. Projects would ``include(Dart)`` to use it,
+and the ``Dart`` module would run ``find_package(Dart)`` internally.
+Since :manual:`ctest(1)` was created, the ``Dart`` module has just been
+a compatibility shim that finds ``Dart`` to support some legacy
+functionality and then forwards to the :module:`CTest` module.
+
+CMake 3.27 and above prefer to not provide the :module:`Dart` or
+:module:`FindDart` modules. This policy provides compatibility for
+projects that have not been ported away from them. Projects using the
+``Dart`` module should be updated to use the :module:`CTest` module directly.
+
+The ``OLD`` behavior of this policy is for ``include(Dart)`` and
+``find_package(Dart)`` to load the deprecated modules. The ``NEW``
+behavior is for uses of the modules to fail as if they do not exist.
+
+This policy was introduced in CMake version 3.27. CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0146.rst b/Help/policy/CMP0146.rst
new file mode 100644
index 0000000000..c7cac22044
--- /dev/null
+++ b/Help/policy/CMP0146.rst
@@ -0,0 +1,29 @@
+CMP0146
+-------
+
+.. versionadded:: 3.27
+
+The :module:`FindCUDA` module is removed.
+
+The :module:`FindCUDA` module has been deprecated since CMake 3.10.
+CMake 3.27 and above prefer to not provide the module.
+This policy provides compatibility for projects that have not been
+ported away from it.
+
+Projects using the :module:`FindCUDA` module should be updated to use
+CMake's first-class ``CUDA`` language support. List ``CUDA`` among the
+languages named in the top-level call to the :command:`project` command,
+or call the :command:`enable_language` command with ``CUDA``.
+Then one can add CUDA (``.cu``) sources directly to targets,
+similar to other languages.
+
+The ``OLD`` behavior of this policy is for ``find_package(CUDA)`` to
+load the deprecated module. The ``NEW`` behavior is for uses of the
+module to fail as if it does not exist.
+
+This policy was introduced in CMake version 3.27. CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0147.rst b/Help/policy/CMP0147.rst
new file mode 100644
index 0000000000..0f25096633
--- /dev/null
+++ b/Help/policy/CMP0147.rst
@@ -0,0 +1,24 @@
+CMP0147
+-------
+
+.. versionadded:: 3.27
+
+:ref:`Visual Studio Generators` build custom commands in parallel.
+
+Visual Studio 15.8 (2017) and newer support building custom commands in
+parallel. CMake 3.27 and above prefer to enable this behavior by adding
+a ``BuildInParallel`` setting to custom commands in ``.vcxproj`` files.
+This policy provides compatibility for projects that have not been updated
+to expect this, e.g., because their custom commands were accidentally
+relying on serial execution by MSBuild.
+
+The ``OLD`` behavior for this policy is to not add ``BuildInParallel``.
+The ``NEW`` behavior for this policy is to add ``BuildInParallel`` for
+VS 15.8 and newer.
+
+This policy was introduced in CMake version 3.27. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0148.rst b/Help/policy/CMP0148.rst
new file mode 100644
index 0000000000..2f5c43d209
--- /dev/null
+++ b/Help/policy/CMP0148.rst
@@ -0,0 +1,29 @@
+CMP0148
+-------
+
+.. versionadded:: 3.27
+
+The :module:`FindPythonInterp` and :module:`FindPythonLibs` modules are removed.
+
+These modules have been deprecated since CMake 3.12.
+CMake 3.27 and above prefer to not provide the modules.
+This policy provides compatibility for projects that have not been
+ported away from them.
+
+Projects using the :module:`FindPythonInterp` and/or :module:`FindPythonLibs`
+modules should be updated to use one of their replacements:
+
+* :module:`FindPython3`
+* :module:`FindPython2`
+* :module:`FindPython`
+
+The ``OLD`` behavior of this policy is for ``find_package(PythonInterp)``
+and ``find_package(PythonLibs)`` to load the deprecated modules. The ``NEW``
+behavior is for uses of the modules to fail as if they do not exist.
+
+This policy was introduced in CMake version 3.27. CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0149.rst b/Help/policy/CMP0149.rst
new file mode 100644
index 0000000000..714eeaff2b
--- /dev/null
+++ b/Help/policy/CMP0149.rst
@@ -0,0 +1,47 @@
+CMP0149
+-------
+
+.. versionadded:: 3.27
+
+:ref:`Visual Studio Generators` select latest Windows SDK by default.
+
+Visual Studio Generators select a Windows SDK version to put in the
+``WindowsTargetPlatformVersion`` setting in ``.vcxproj`` files.
+CMake sets the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`
+variable to the selected SDK version.
+
+Prior to CMake 3.27, the SDK version was always selected by the value of
+the :variable:`CMAKE_SYSTEM_VERSION` variable. Users or toolchain files
+could set that variable to one of the exact Windows SDK versions available
+on the host system. Since :variable:`CMAKE_SYSTEM_VERSION` defaults to
+:variable:`CMAKE_HOST_SYSTEM_VERSION`, and it is not guaranteed that a
+matching Windows SDK version is available, CMake had to fall back to
+using the latest Windows SDK version if no exact match was available.
+This approach was problematic:
+
+* The latest Windows SDK might or might not be selected based on whether
+ the host version of Windows happens to match an available SDK version.
+
+* An old Windows SDK version might be selected that has not been updated
+ for newer language standards such as C11.
+
+CMake 3.27 and higher prefer to ignore the exact value of
+:variable:`CMAKE_SYSTEM_VERSION` and by default select the latest SDK
+version available. An exact SDK version may be specified explicitly
+using a ``version=`` field in the :variable:`CMAKE_GENERATOR_PLATFORM`
+variable. See :ref:`Visual Studio Platform Selection`.
+
+This policy provides compatibility for projects, toolchain files, and
+build scripts that have not been ported away from using
+:variable:`CMAKE_SYSTEM_VERSION` to specify an exact SDK version.
+
+The ``OLD`` behavior for this policy is to use the exact value of
+:variable:`CMAKE_SYSTEM_VERSION` if possible. The ``NEW`` behavior
+for this policy is to ignore it.
+
+This policy was introduced in CMake version 3.27. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_test/WILL_FAIL.rst b/Help/prop_test/WILL_FAIL.rst
index f1f94a43a7..4926f40779 100644
--- a/Help/prop_test/WILL_FAIL.rst
+++ b/Help/prop_test/WILL_FAIL.rst
@@ -3,5 +3,6 @@ WILL_FAIL
If set to true, this will invert the pass/fail flag of the test.
-This property can be used for tests that are expected to fail and
-return a non zero return code.
+This property can be used for tests that are expected to fail and return a
+non-zero return code. Note that system-level test failures such as segmentation
+faults or heap errors will still fail the test even if ``WILL_FALL`` is true.
diff --git a/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst b/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst
index 677e06df0f..abb627cdad 100644
--- a/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst
+++ b/Help/prop_tgt/ARCHIVE_OUTPUT_DIRECTORY.rst
@@ -6,4 +6,7 @@ ARCHIVE_OUTPUT_DIRECTORY
.. |CMAKE_XXX_OUTPUT_DIRECTORY| replace:: :variable:`CMAKE_ARCHIVE_OUTPUT_DIRECTORY`
.. include:: XXX_OUTPUT_DIRECTORY.txt
+.. |IDEM| replace:: in the same directory
+.. include:: MACOS_IMPORT_FILES.txt
+
See also the :prop_tgt:`ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>` target property.
diff --git a/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst b/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst
index 6150193739..1f1c4671f1 100644
--- a/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst
+++ b/Help/prop_tgt/ARCHIVE_OUTPUT_NAME.rst
@@ -5,4 +5,7 @@ ARCHIVE_OUTPUT_NAME
.. |xxx| replace:: archive
.. include:: XXX_OUTPUT_NAME.txt
+.. |IDEM| replace:: with the same name
+.. include:: MACOS_IMPORT_FILES.txt
+
See also the :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` target property.
diff --git a/Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst b/Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst
index 072e7f732a..a4a9ba2303 100644
--- a/Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst
+++ b/Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst
@@ -3,7 +3,7 @@ AUTOMOC_MACRO_NAMES
.. versionadded:: 3.10
-A :ref:`semicolon-separated list <CMake Language Lists>` list of macro names used by
+A :ref:`semicolon-separated list <CMake Language Lists>` of macro names used by
:prop_tgt:`AUTOMOC` to determine if a C++ file needs to be processed by ``moc``.
This property is only used if the :prop_tgt:`AUTOMOC` property is ``ON``
@@ -21,6 +21,8 @@ then the file will be processed by ``moc``.
By default ``AUTOMOC_MACRO_NAMES`` is initialized from
:variable:`CMAKE_AUTOMOC_MACRO_NAMES`.
+See also the :prop_tgt:`INTERFACE_AUTOMOC_MACRO_NAMES` target property.
+
See the :manual:`cmake-qt(7)` manual for more information on using CMake
with Qt.
@@ -29,6 +31,8 @@ Example
In this case the ``Q_OBJECT`` macro is hidden inside another macro
called ``CUSTOM_MACRO``. To let CMake know that source files that contain
-``CUSTOM_MACRO`` need to be ``moc`` processed, we call::
+``CUSTOM_MACRO`` need to be ``moc`` processed, we call:
+
+.. code-block:: cmake
set_property(TARGET tgt APPEND PROPERTY AUTOMOC_MACRO_NAMES "CUSTOM_MACRO")
diff --git a/Help/prop_tgt/COMPILE_OPTIONS.rst b/Help/prop_tgt/COMPILE_OPTIONS.rst
index 2b93db177f..8b032adeee 100644
--- a/Help/prop_tgt/COMPILE_OPTIONS.rst
+++ b/Help/prop_tgt/COMPILE_OPTIONS.rst
@@ -11,6 +11,10 @@ The options will be added after flags in the
variables, but before those propagated from dependencies by the
:prop_tgt:`INTERFACE_COMPILE_OPTIONS` property.
+This property adds compile options for all languages in a target.
+Use the :genex:`COMPILE_LANGUAGE` generator expression to specify
+per-language compile options.
+
This property is initialized by the :prop_dir:`COMPILE_OPTIONS` directory
property when a target is created, and is used by the generators to set
the options for the compiler.
diff --git a/Help/prop_tgt/CUDA_CUBIN_COMPILATION.rst b/Help/prop_tgt/CUDA_CUBIN_COMPILATION.rst
new file mode 100644
index 0000000000..f8860aeda3
--- /dev/null
+++ b/Help/prop_tgt/CUDA_CUBIN_COMPILATION.rst
@@ -0,0 +1,14 @@
+CUDA_CUBIN_COMPILATION
+----------------------
+
+.. versionadded:: 3.27
+
+Compile CUDA sources to ``.cubin`` files instead of ``.obj`` files
+within :ref:`Object Libraries`.
+
+For example:
+
+.. code-block:: cmake
+
+ add_library(mycubin OBJECT a.cu b.cu)
+ set_property(TARGET mycubin PROPERTY CUDA_CUBIN_COMPILATION ON)
diff --git a/Help/prop_tgt/CUDA_FATBIN_COMPILATION.rst b/Help/prop_tgt/CUDA_FATBIN_COMPILATION.rst
new file mode 100644
index 0000000000..3d3c715339
--- /dev/null
+++ b/Help/prop_tgt/CUDA_FATBIN_COMPILATION.rst
@@ -0,0 +1,14 @@
+CUDA_FATBIN_COMPILATION
+-----------------------
+
+.. versionadded:: 3.27
+
+Compile CUDA sources to ``.fatbin`` files instead of ``.obj`` files
+within :ref:`Object Libraries`.
+
+For example:
+
+.. code-block:: cmake
+
+ add_library(myfbins OBJECT a.cu b.cu)
+ set_property(TARGET myfbins PROPERTY CUDA_FATBIN_COMPILATION ON)
diff --git a/Help/prop_tgt/CUDA_OPTIX_COMPILATION.rst b/Help/prop_tgt/CUDA_OPTIX_COMPILATION.rst
new file mode 100644
index 0000000000..c2a06a88b7
--- /dev/null
+++ b/Help/prop_tgt/CUDA_OPTIX_COMPILATION.rst
@@ -0,0 +1,14 @@
+CUDA_OPTIX_COMPILATION
+----------------------
+
+.. versionadded:: 3.27
+
+Compile CUDA sources to ``.optixir`` files instead of ``.obj`` files
+within :ref:`Object Libraries`.
+
+For example:
+
+.. code-block:: cmake
+
+ add_library(myoptix OBJECT a.cu b.cu)
+ set_property(TARGET myoptix PROPERTY CUDA_OPTIX_COMPILATION ON)
diff --git a/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst
new file mode 100644
index 0000000000..59ef00f0a1
--- /dev/null
+++ b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst
@@ -0,0 +1,17 @@
+DLL_NAME_WITH_SOVERSION
+-----------------------
+
+.. versionadded:: 3.27
+
+This property control whether the :prop_tgt:`SOVERSION` target
+property are added to the filename of generated DLL filenames
+for the Windows platform, which is selected when the
+:variable:`WIN32` variable is set.
+
+The value of the listed property is appended to the
+basename of the runtime component of the shared library
+target as ``-<SOVERSION>``.
+
+Please note that setting this property has no effect
+if versioned filenames are globally disabled with the
+:variable:`CMAKE_PLATFORM_NO_VERSIONED_SONAME` variable.
diff --git a/Help/prop_tgt/ENABLE_EXPORTS.rst b/Help/prop_tgt/ENABLE_EXPORTS.rst
index 0b1064ab4e..3e9b285534 100644
--- a/Help/prop_tgt/ENABLE_EXPORTS.rst
+++ b/Help/prop_tgt/ENABLE_EXPORTS.rst
@@ -1,7 +1,7 @@
ENABLE_EXPORTS
--------------
-Specify whether an executable exports symbols for loadable modules.
+Specify whether an executable or a shared library exports symbols.
Normally an executable does not export any symbols because it is the
final program. It is possible for an executable to export symbols to
@@ -28,4 +28,29 @@ varies by platform:
automatically bind symbols when the module is loaded.
This property is initialized by the value of the variable
-:variable:`CMAKE_ENABLE_EXPORTS` if it is set when a target is created.
+:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` if it is set when an executable
+target is created.
+
+.. versionadded:: 3.27
+ On macOS, to link with a shared library (standard one as well as framework),
+ a linker import file (e.g. a text-based stubs file, with ``.tbd`` extension)
+ can be used instead of the shared library itself.
+
+The generation of these linker import files, as well as the consumption, is
+controlled by this property. When this property is set to true, CMake will
+generate a ``.tbd`` file for each shared library created by
+:command:`add_library` command. This allow other targets to use this ``.tbd``
+file to link to the library with the :command:`target_link_libraries`
+command.
+
+.. note::
+
+ For compatibility purpose, this property will be ignored if
+ :prop_tgt:`XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <XCODE_ATTRIBUTE_<an-attribute>>`
+ target property or the
+ :variable:`CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS <CMAKE_XCODE_ATTRIBUTE_<an-attribute>>`
+ variable is set to ``NO``.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS` if it is set when a shared
+library target is created.
diff --git a/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst b/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst
index 6de1baa235..a4746d3d16 100644
--- a/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst
+++ b/Help/prop_tgt/IMPORTED_CONFIGURATIONS.rst
@@ -1,11 +1,22 @@
IMPORTED_CONFIGURATIONS
-----------------------
-Configurations provided for an IMPORTED target.
-
-Set this to the list of configuration names available for an IMPORTED
-target. The names correspond to configurations defined in the project
-from which the target is imported. If the importing project uses a
-different set of configurations the names may be mapped using the
-MAP_IMPORTED_CONFIG_<CONFIG> property. Ignored for non-imported
-targets.
+Configurations provided for an :ref:`imported target <Imported targets>`.
+
+Set this to the list of configuration names available for an imported
+target. For each configuration named, the imported target's artifacts
+must be specified in other target properties:
+
+* :prop_tgt:`IMPORTED_LOCATION_<CONFIG>`, or
+* :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` (on DLL platforms, on AIX for
+ :ref:`Executables <Binary Executables>` or on Apple for
+ :ref:`Shared Libraries <Normal Libraries>`), or
+* :prop_tgt:`IMPORTED_OBJECTS_<CONFIG>` (for :ref:`Object Libraries`), or
+* :prop_tgt:`IMPORTED_LIBNAME_<CONFIG>` (for :ref:`Interface Libraries`).
+
+The configuration names correspond to those defined in the project from
+which the target is imported. If the importing project uses a different
+set of configurations, the names may be mapped using the
+:prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` target property.
+
+The ``IMPORTED_CONFIGURATIONS`` property is ignored for non-imported targets.
diff --git a/Help/prop_tgt/IMPORTED_IMPLIB.rst b/Help/prop_tgt/IMPORTED_IMPLIB.rst
index c8b6fde95c..e67acbaa54 100644
--- a/Help/prop_tgt/IMPORTED_IMPLIB.rst
+++ b/Help/prop_tgt/IMPORTED_IMPLIB.rst
@@ -3,7 +3,22 @@ IMPORTED_IMPLIB
Full path to the import library for an ``IMPORTED`` target.
-Set this to the location of the ``.lib`` part of a Windows DLL, or on
-AIX set it to an import file created for executables that export symbols
-(see the :prop_tgt:`ENABLE_EXPORTS` target property).
-Ignored for non-imported targets.
+This property may be set:
+
+* On DLL platforms, to the location of the ``.lib`` part of the DLL.
+* On AIX, to an import file (e.g. ``.imp``) created for executables that export
+ symbols (see the :prop_tgt:`ENABLE_EXPORTS` target property).
+* On macOS, to an import file (e.g. ``.tbd``) created for shared libraries (see
+ the :prop_tgt:`ENABLE_EXPORTS` target property). For frameworks this is the
+ location of the ``.tbd`` file symlink just inside the framework folder.
+
+The ``IMPORTED_IMPLIB`` target property may be overridden for a
+given configuration ``<CONFIG>`` by the configuration-specific
+:prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` target property. Furthermore,
+the :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` target property may be
+used to map between a project's configurations and those of an imported
+target. If none of these is set then the name of any other configuration
+listed in the :prop_tgt:`IMPORTED_CONFIGURATIONS` target property may be
+selected and its :prop_tgt:`IMPORTED_IMPLIB_<CONFIG>` value used.
+
+This property is ignored for non-imported targets.
diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst
index ddd910aafb..a7207d845b 100644
--- a/Help/prop_tgt/IMPORTED_LOCATION.rst
+++ b/Help/prop_tgt/IMPORTED_LOCATION.rst
@@ -27,5 +27,5 @@ selected and its :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` value used.
To get the location of an imported target read one of the :prop_tgt:`LOCATION`
or ``LOCATION_<CONFIG>`` properties.
-For platforms with import libraries (e.g. Windows) see also
+For platforms with import libraries (e.g. Windows, AIX or macOS) see also
:prop_tgt:`IMPORTED_IMPLIB`.
diff --git a/Help/prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES.rst b/Help/prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES.rst
new file mode 100644
index 0000000000..502775c8e3
--- /dev/null
+++ b/Help/prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES.rst
@@ -0,0 +1,89 @@
+INTERFACE_AUTOMOC_MACRO_NAMES
+-----------------------------
+
+.. versionadded:: 3.27
+
+A :ref:`semicolon-separated list <CMake Language Lists>` of macro names for
+:prop_tgt:`AUTOMOC` to be propagated to consumers.
+
+When a target with :prop_tgt:`AUTOMOC` enabled links to a library that sets
+``INTERFACE_AUTOMOC_MACRO_NAMES``, the target inherits the listed macro names
+and merges them with those specified in its own :prop_tgt:`AUTOMOC_MACRO_NAMES`
+property. The target will then automatically generate MOC files for source
+files that contain the inherited macro names too, not just the macro names
+specified in its own :prop_tgt:`AUTOMOC_MACRO_NAMES` property.
+
+By default ``INTERFACE_AUTOMOC_MACRO_NAMES`` is empty.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
+
+Example 1
+^^^^^^^^^
+
+In this example, ``myapp`` inherits the macro names ``STATIC_LIB_1`` and
+``STATIC_LIB_2`` from ``static_lib``. The ``moc`` tool will then automatically
+be run on any of the ``myapp`` sources which contain ``STATIC_LIB_1`` or
+``STATIC_LIB_2``.
+
+.. code-block:: cmake
+
+ set(AUTOMOC ON)
+ add_executable(myapp main.cpp)
+ target_link_libraries(myapp PRIVATE static_lib)
+
+ add_library(static_lib STATIC static.cpp)
+ set_property(TARGET static_lib PROPERTY
+ INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LIB_1;STATIC_LIB_2"
+ )
+
+Example 2
+^^^^^^^^^
+
+In this example, the ``INTERFACE_AUTOMOC_MACRO_NAMES`` target property of the
+various ``*_deep_lib`` libraries will propagate to ``shared_lib``,
+``static_lib`` and ``interface_lib``. Because the linking relationships are
+specified as ``PUBLIC`` and ``INTERFACE``, those macro names will also further
+propagate transitively up to ``app``.
+
+.. code-block:: cmake
+
+ set(AUTOMOC ON)
+
+ add_library(shared_deep_lib SHARED deep_lib.cpp)
+ add_library(static_deep_lib STATIC deep_lib.cpp)
+ add_library(interface_deep_lib INTERFACE)
+
+ set_property(TARGET shared_deep_lib PROPERTY
+ INTERFACE_AUTOMOC_MACRO_NAMES "SHARED_LINK_LIB"
+ )
+ set_property(TARGET static_deep_lib PROPERTY
+ INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LINK_LIB"
+ )
+ set_property(TARGET interface_deep_lib PROPERTY
+ INTERFACE_AUTOMOC_MACRO_NAMES "INTERFACE_LINK_LIB"
+ )
+
+ add_library(shared_lib SHARED lib.cpp)
+ add_library(static_lib STATIC lib.cpp)
+ add_library(interface_lib INTERFACE)
+
+ # PUBLIC and INTERFACE here ensure the macro names propagate to any
+ # consumers of shared_lib, static_lib or interface_lib too
+ target_link_libraries(shared_lib PUBLIC shared_deep_lib)
+ target_link_libraries(static_lib PUBLIC static_deep_lib)
+ target_link_libraries(interface_lib INTERFACE interface_deep_lib)
+
+ # This consumer will receive all three of the above custom macro names as
+ # transitive usage requirements
+ add_executable(app main.cpp)
+ target_link_libraries(app PRIVATE shared_lib static_lib interface_lib)
+
+In the above:
+
+* ``shared_lib`` sources will be processed by ``moc`` if they contain
+ ``SHARED_LINK_LIB``.
+* ``static_lib`` sources will be processed by ``moc`` if they contain
+ ``STATIC_LINK_LIB``.
+* ``app`` sources will be processed by ``moc`` if they contain
+ ``SHARED_LINK_LIB``, ``STATIC_LINK_LIB`` or ``INTERFACE_LINK_LIB``.
diff --git a/Help/prop_tgt/LANG_CLANG_TIDY.rst b/Help/prop_tgt/LANG_CLANG_TIDY.rst
index 31f1876e3b..1e109334b8 100644
--- a/Help/prop_tgt/LANG_CLANG_TIDY.rst
+++ b/Help/prop_tgt/LANG_CLANG_TIDY.rst
@@ -25,3 +25,8 @@ command line.
This property is initialized by the value of
the :variable:`CMAKE_<LANG>_CLANG_TIDY` variable if it is set
when a target is created.
+
+.. versionadded:: 3.27
+
+ This property supports
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/LANG_CPPCHECK.rst b/Help/prop_tgt/LANG_CPPCHECK.rst
index 80acbc02a9..0b2dee6e6c 100644
--- a/Help/prop_tgt/LANG_CPPCHECK.rst
+++ b/Help/prop_tgt/LANG_CPPCHECK.rst
@@ -15,3 +15,8 @@ tool returns non-zero.
This property is initialized by the value of the
:variable:`CMAKE_<LANG>_CPPCHECK` variable if it is set when a target is
created.
+
+.. versionadded:: 3.27
+
+ This property supports
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/LANG_CPPLINT.rst b/Help/prop_tgt/LANG_CPPLINT.rst
index be6db46b36..38a1669c13 100644
--- a/Help/prop_tgt/LANG_CPPLINT.rst
+++ b/Help/prop_tgt/LANG_CPPLINT.rst
@@ -13,3 +13,8 @@ and report any problems.
This property is initialized by the value of the
:variable:`CMAKE_<LANG>_CPPLINT` variable if it is set when a target is
created.
+
+.. versionadded:: 3.27
+
+ This property supports
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst b/Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst
index eebef561bb..19b97f7a47 100644
--- a/Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst
+++ b/Help/prop_tgt/LANG_INCLUDE_WHAT_YOU_USE.rst
@@ -13,3 +13,8 @@ compiler and report a warning if the tool reports any problems.
This property is initialized by the value of
the :variable:`CMAKE_<LANG>_INCLUDE_WHAT_YOU_USE` variable if it is set
when a target is created.
+
+.. versionadded:: 3.27
+
+ This property supports
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst b/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst
index f6ca5ad43d..d39ec203aa 100644
--- a/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst
+++ b/Help/prop_tgt/LANG_LINKER_LAUNCHER.rst
@@ -14,3 +14,8 @@ arguments to the tool. This is useful for tools such as static analyzers.
This property is initialized by the value of the
:variable:`CMAKE_<LANG>_LINKER_LAUNCHER` variable if it is set when a target is
created.
+
+.. versionadded:: 3.27
+
+ The property value may use
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/MACOS_IMPORT_FILES.txt b/Help/prop_tgt/MACOS_IMPORT_FILES.txt
new file mode 100644
index 0000000000..3c98fc8ca8
--- /dev/null
+++ b/Help/prop_tgt/MACOS_IMPORT_FILES.txt
@@ -0,0 +1,12 @@
+.. note::
+
+ On macOS, this property will be ignored for the linker import files (e.g.
+ ``.tbd`` files, see :prop_tgt:`ENABLE_EXPORTS` property for details) when:
+
+ * The :prop_tgt:`FRAMEWORK` is set, because the framework layout cannot be
+ changed.
+ * The :generator:`Xcode` generator is used, due to the limitations and
+ constraints of the ``Xcode`` tool.
+
+ In both cases, the linker import files will be generated |IDEM| as the shared
+ library.
diff --git a/Help/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst b/Help/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst
index 50cf203b06..ef3ceb0f76 100644
--- a/Help/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst
+++ b/Help/prop_tgt/VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst
@@ -7,6 +7,11 @@ Visual Studio Windows Target Platform Minimum Version
For Windows 10. Specifies the minimum version of the OS that is being
targeted. For example ``10.0.10240.0``. If the value is not specified, the
-value of :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` will be used on
-WindowsStore projects otherwise the target platform minimum version will not
-be specified for the project.
+value of the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable
+will be used on WindowsStore projects. Otherwise the target platform
+minimum version will not be specified for the project.
+
+.. versionadded:: 3.27
+ This property is initialized by the value of the
+ :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION` variable
+ if it is set when a target is created.
diff --git a/Help/release/3.11.rst b/Help/release/3.11.rst
index 957dd4f1ff..6e1520aaed 100644
--- a/Help/release/3.11.rst
+++ b/Help/release/3.11.rst
@@ -174,7 +174,7 @@ Modules
to removal of the ``javah`` tool by `JEP 313`_.
.. _`FLAME`: https://github.com/flame
-.. _`JEP 313`: https://openjdk.java.net/jeps/313
+.. _`JEP 313`: https://openjdk.org/jeps/313
Autogen
-------
diff --git a/Help/release/dev/0-sample-topic.rst b/Help/release/dev/0-sample-topic.rst
new file mode 100644
index 0000000000..e4cc01e23f
--- /dev/null
+++ b/Help/release/dev/0-sample-topic.rst
@@ -0,0 +1,7 @@
+0-sample-topic
+--------------
+
+* This is a sample release note for the change in a topic.
+ Developers should add similar notes for each topic branch
+ making a noteworthy change. Each document should be named
+ and titled to match the topic name to avoid merge conflicts.
diff --git a/Help/release/dev/Apple-tbd-files-management.rst b/Help/release/dev/Apple-tbd-files-management.rst
new file mode 100644
index 0000000000..edcfe55436
--- /dev/null
+++ b/Help/release/dev/Apple-tbd-files-management.rst
@@ -0,0 +1,6 @@
+Apple-tbd-files-management
+--------------------------
+
+* Support for text-based stubs (i.e. ``.tbd`` files) was added on macOS
+ platform. This capability is managed through the :prop_tgt:`ENABLE_EXPORTS`
+ property.
diff --git a/Help/release/dev/FindCUDA-remove.rst b/Help/release/dev/FindCUDA-remove.rst
new file mode 100644
index 0000000000..e8b09d4759
--- /dev/null
+++ b/Help/release/dev/FindCUDA-remove.rst
@@ -0,0 +1,6 @@
+FindCUDA-remove
+---------------
+
+* The :module:`FindCUDA` module has been fully deprecated via policy
+ :policy:`CMP0146`. Port projects to CMake's first-class ``CUDA``
+ language support.
diff --git a/Help/release/dev/FindCUDAToolkit-target-for-cudla.rst b/Help/release/dev/FindCUDAToolkit-target-for-cudla.rst
new file mode 100644
index 0000000000..9de456ec90
--- /dev/null
+++ b/Help/release/dev/FindCUDAToolkit-target-for-cudla.rst
@@ -0,0 +1,4 @@
+FindCUDAToolkit-target-for-cudla
+--------------------------------
+
+* The :module:`FindCUDAToolkit` module now provides an imported target for ``cudla``, if found.
diff --git a/Help/release/dev/FindOpenGL-gles.rst b/Help/release/dev/FindOpenGL-gles.rst
new file mode 100644
index 0000000000..fcbc5167a1
--- /dev/null
+++ b/Help/release/dev/FindOpenGL-gles.rst
@@ -0,0 +1,5 @@
+FindOpenGL-gles
+---------------
+
+* The :module:`FindOpenGL` module gained support for components
+ ``GLES2`` and ``GLES3``.
diff --git a/Help/release/dev/FindPython-Windows-ARM.rst b/Help/release/dev/FindPython-Windows-ARM.rst
new file mode 100644
index 0000000000..d88a65acb0
--- /dev/null
+++ b/Help/release/dev/FindPython-Windows-ARM.rst
@@ -0,0 +1,5 @@
+FindPython-Windows-ARM
+----------------------
+
+* :module:`FindPython`, :module:`FindPython2` and :module:`FindPython3` modules
+ learn to manage ``Windows/ARM`` platform.
diff --git a/Help/release/dev/FindwxWidgets-imported-target.rst b/Help/release/dev/FindwxWidgets-imported-target.rst
new file mode 100644
index 0000000000..c04e0acf58
--- /dev/null
+++ b/Help/release/dev/FindwxWidgets-imported-target.rst
@@ -0,0 +1,4 @@
+FindwxWidgets-imported-target
+-----------------------------
+
+* The :module:`FindwxWidgets` module now provides an imported target.
diff --git a/Help/release/dev/PATH-genex-support-list.rst b/Help/release/dev/PATH-genex-support-list.rst
new file mode 100644
index 0000000000..ce87fddd43
--- /dev/null
+++ b/Help/release/dev/PATH-genex-support-list.rst
@@ -0,0 +1,5 @@
+PATH-genex-supports-list
+------------------------
+
+* The :genex:`$<PATH>` generator expression learned to process list of paths
+ for decomposition and transformation operations.
diff --git a/Help/release/dev/autogen-exe-vars.rst b/Help/release/dev/autogen-exe-vars.rst
new file mode 100644
index 0000000000..a386b4f231
--- /dev/null
+++ b/Help/release/dev/autogen-exe-vars.rst
@@ -0,0 +1,7 @@
+autogen-exe-vars
+----------------
+
+* The :variable:`CMAKE_AUTOMOC_EXECUTABLE`,
+ :variable:`CMAKE_AUTORCC_EXECUTABLE`, and
+ :variable:`CMAKE_AUTOUIC_EXECUTABLE` variables were added to initialize the
+ corresponding target properties as targets are created.
diff --git a/Help/release/dev/automoc-macro-names.rst b/Help/release/dev/automoc-macro-names.rst
new file mode 100644
index 0000000000..9c037b3472
--- /dev/null
+++ b/Help/release/dev/automoc-macro-names.rst
@@ -0,0 +1,5 @@
+automoc-macro-names
+-------------------
+
+* The :prop_tgt:`INTERFACE_AUTOMOC_MACRO_NAMES` target property was added to
+ specify macro names for ``moc`` as a transitive usage requirement.
diff --git a/Help/release/dev/cuda-support-new-compile-modes.rst b/Help/release/dev/cuda-support-new-compile-modes.rst
new file mode 100644
index 0000000000..2d24c167a4
--- /dev/null
+++ b/Help/release/dev/cuda-support-new-compile-modes.rst
@@ -0,0 +1,14 @@
+cuda-support-new-compile-modes
+------------------------------
+
+* A :prop_tgt:`CUDA_CUBIN_COMPILATION` target property was added to
+ :ref:`Object Libraries` to support compiling to ``.cubin`` files
+ instead of host object files. Currently only supported with NVIDIA.
+
+* A :prop_tgt:`CUDA_FATBIN_COMPILATION` target property was added to
+ :ref:`Object Libraries` to support compiling to ``.fatbin`` files
+ instead of host object files. Currently only supported with NVIDIA.
+
+* A :prop_tgt:`CUDA_OPTIX_COMPILATION` target property was added to
+ :ref:`Object Libraries` to support compiling to ``.optixir`` files
+ instead of host object files. Currently only supported with NVIDIA.
diff --git a/Help/release/dev/cxx-module-extensions.rst b/Help/release/dev/cxx-module-extensions.rst
new file mode 100644
index 0000000000..92df86a51e
--- /dev/null
+++ b/Help/release/dev/cxx-module-extensions.rst
@@ -0,0 +1,5 @@
+cxx-module-extensions
+---------------------
+
+* Source file extensions ``.ccm``, ``.cxxm``, or ``.c++m`` are now
+ treated as C++.
diff --git a/Help/release/dev/deprecate-extra-generators.rst b/Help/release/dev/deprecate-extra-generators.rst
new file mode 100644
index 0000000000..ceb2f4e1a8
--- /dev/null
+++ b/Help/release/dev/deprecate-extra-generators.rst
@@ -0,0 +1,5 @@
+deprecate-extra-generators
+--------------------------
+
+* The :ref:`Extra Generators` have been deprecated. IDEs may use the
+ :manual:`cmake-file-api(7)` to view CMake-generated project build trees.
diff --git a/Help/release/dev/deprecate-policy-old.rst b/Help/release/dev/deprecate-policy-old.rst
new file mode 100644
index 0000000000..9c388665aa
--- /dev/null
+++ b/Help/release/dev/deprecate-policy-old.rst
@@ -0,0 +1,7 @@
+deprecate-policy-old
+--------------------
+
+* Compatibility with versions of CMake older than 3.5 is now deprecated
+ and will be removed from a future version. Calls to
+ :command:`cmake_minimum_required` or :command:`cmake_policy` that set
+ the policy version to an older value now issue a deprecation diagnostic.
diff --git a/Help/release/dev/dll-name-soversion.rst b/Help/release/dev/dll-name-soversion.rst
new file mode 100644
index 0000000000..56d0842d82
--- /dev/null
+++ b/Help/release/dev/dll-name-soversion.rst
@@ -0,0 +1,7 @@
+dll-name-soversion
+------------------
+
+* The :variable:`CMAKE_DLL_NAME_WITH_SOVERSION` variable and associated
+ :prop_tgt:`DLL_NAME_WITH_SOVERSION` target property were added to
+ optionally append the :prop_tgt:`SOVERSION` to the filename of the
+ ``.dll`` part of a shared library on Windows.
diff --git a/Help/release/dev/file-GET_RUNTIME_DEPENDENCIES-windows-casing.rst b/Help/release/dev/file-GET_RUNTIME_DEPENDENCIES-windows-casing.rst
new file mode 100644
index 0000000000..858f8b3a99
--- /dev/null
+++ b/Help/release/dev/file-GET_RUNTIME_DEPENDENCIES-windows-casing.rst
@@ -0,0 +1,7 @@
+file-GET_RUNTIME_DEPENDENCIES-windows-casing
+--------------------------------------------
+
+
+* The :command:`file(GET_RUNTIME_DEPENDENCIES)` command now case-preserves
+ DLL names reported on Windows. They are still converted to lowercase
+ for filter matching.
diff --git a/Help/release/dev/find_package-PACKAGENAME_ROOT.rst b/Help/release/dev/find_package-PACKAGENAME_ROOT.rst
new file mode 100644
index 0000000000..03882717fa
--- /dev/null
+++ b/Help/release/dev/find_package-PACKAGENAME_ROOT.rst
@@ -0,0 +1,7 @@
+find_package-PACKAGENAME_ROOT
+-----------------------------
+
+* The :command:`find_package` command now searches prefixes specified by
+ upper-case :variable:`<PACKAGENAME>_ROOT` CMake variables and upper-case
+ :envvar:`<PACKAGENAME>_ROOT` environment variables.
+ See policy :policy:`CMP0144`.
diff --git a/Help/release/dev/install-prefix-genex-install-code-script.rst b/Help/release/dev/install-prefix-genex-install-code-script.rst
new file mode 100644
index 0000000000..810f4483c1
--- /dev/null
+++ b/Help/release/dev/install-prefix-genex-install-code-script.rst
@@ -0,0 +1,5 @@
+install-prefix-genex-install-code-script
+----------------------------------------
+
+* The :command:`install(CODE)` and :command:`install(SCRIPT)` commands
+ now support the :genex:`$<INSTALL_PREFIX>` generator expression.
diff --git a/Help/release/dev/lang-linker-launcher-genex.rst b/Help/release/dev/lang-linker-launcher-genex.rst
new file mode 100644
index 0000000000..b6494eb104
--- /dev/null
+++ b/Help/release/dev/lang-linker-launcher-genex.rst
@@ -0,0 +1,5 @@
+lang-linker-launcher-genex
+--------------------------
+
+* The :prop_tgt:`<LANG>_LINKER_LAUNCHER` target property now supports
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/release/dev/lint-genex.rst b/Help/release/dev/lint-genex.rst
new file mode 100644
index 0000000000..8da30b0333
--- /dev/null
+++ b/Help/release/dev/lint-genex.rst
@@ -0,0 +1,7 @@
+lint-genex
+----------
+
+* The :prop_tgt:`<LANG>_CLANG_TIDY`, :prop_tgt:`<LANG>_CPPCHECK`,
+ :prop_tgt:`<LANG>_CPPLINT`, and :prop_tgt:`<LANG>_INCLUDE_WHAT_YOU_USE`,
+ target properties now support
+ :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/release/dev/ninja-custom-command-depends.rst b/Help/release/dev/ninja-custom-command-depends.rst
new file mode 100644
index 0000000000..0b7840c940
--- /dev/null
+++ b/Help/release/dev/ninja-custom-command-depends.rst
@@ -0,0 +1,11 @@
+ninja-custom-command-depends
+----------------------------
+
+* The :command:`add_custom_command` command gained a new
+ ``DEPENDS_EXPLICIT_ONLY`` option to tell the :ref:`Ninja Generators`
+ not to add any dependencies implied by the target to which it is
+ attached.
+
+* The :variable:`CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY` variable can
+ be set to enable ``DEPENDS_EXPLICIT_ONLY`` in all uses of
+ :command:`add_custom_command` command.
diff --git a/Help/release/dev/remove-FindPythonInterp-FindPythonLibs.rst b/Help/release/dev/remove-FindPythonInterp-FindPythonLibs.rst
new file mode 100644
index 0000000000..426febb50c
--- /dev/null
+++ b/Help/release/dev/remove-FindPythonInterp-FindPythonLibs.rst
@@ -0,0 +1,6 @@
+remove-FindPythonInterp-FindPythonLibs
+--------------------------------------
+
+* The :module:`FindPythonInterp` and :module:`FindPythonLibs` modules have
+ been fully deprecated via policy :policy:`CMP0148`. Port projects to
+ :module:`FindPython3`, :module:`FindPython2`, or :module:`FindPython`.
diff --git a/Help/release/dev/remove-dart-modules.rst b/Help/release/dev/remove-dart-modules.rst
new file mode 100644
index 0000000000..5da2edaeee
--- /dev/null
+++ b/Help/release/dev/remove-dart-modules.rst
@@ -0,0 +1,5 @@
+remove-dart-modules
+-------------------
+
+* The :module:`Dart` and :module:`FindDart` modules have been deprecated via
+ policy :policy:`CMP0145`. Port projects to the :module:`CTest` module.
diff --git a/Help/release/dev/vs-BuildInParallel.rst b/Help/release/dev/vs-BuildInParallel.rst
new file mode 100644
index 0000000000..ef344c7135
--- /dev/null
+++ b/Help/release/dev/vs-BuildInParallel.rst
@@ -0,0 +1,5 @@
+vs-BuildInParallel
+------------------
+
+* :ref:`Visual Studio Generators`, for VS 15.8 (2017) and newer, now
+ build custom commands in parallel. See policy :policy:`CMP0147`.
diff --git a/Help/release/dev/vs-sdk-selection.rst b/Help/release/dev/vs-sdk-selection.rst
new file mode 100644
index 0000000000..856a2033c5
--- /dev/null
+++ b/Help/release/dev/vs-sdk-selection.rst
@@ -0,0 +1,7 @@
+vs-sdk-selection
+----------------
+
+* The :ref:`Visual Studio Generators` for VS 2015 and above learned to
+ select the Windows SDK version explicitly using a ``version=`` field
+ in the :variable:`CMAKE_GENERATOR_PLATFORM` variable.
+ See :ref:`Visual Studio Platform Selection`.
diff --git a/Help/release/dev/vs-windows-min-version.rst b/Help/release/dev/vs-windows-min-version.rst
new file mode 100644
index 0000000000..cb39159441
--- /dev/null
+++ b/Help/release/dev/vs-windows-min-version.rst
@@ -0,0 +1,6 @@
+vs-windows-min-version
+----------------------
+
+* The :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION` variable
+ was added to initialize the :prop_tgt:`VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION`
+ target property on all targets when they are created.
diff --git a/Help/release/dev/vs9-deprecate.rst b/Help/release/dev/vs9-deprecate.rst
new file mode 100644
index 0000000000..46568f8bc5
--- /dev/null
+++ b/Help/release/dev/vs9-deprecate.rst
@@ -0,0 +1,5 @@
+vs9-deprecate
+-------------
+
+* The :generator:`Visual Studio 9 2008` generator is now deprecated
+ and will be removed in a future version of CMake.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index c82889f3a0..d434a3ab05 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -7,6 +7,8 @@ CMake Release Notes
This file should include the adjacent "dev.txt" file
in development versions but not in release versions.
+.. include:: dev.txt
+
Releases
========
diff --git a/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst b/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst
new file mode 100644
index 0000000000..9c9bd2c225
--- /dev/null
+++ b/Help/variable/CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY.rst
@@ -0,0 +1,11 @@
+CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
+----------------------------------------------
+
+.. versionadded:: 3.27
+
+Whether to enable DEPENDS_EXPLICIT_ONLY option by default in
+:command:`add_custom_command`.
+
+This variable affects the default behavior of the :command:`add_custom_command`
+command. Setting this variable to ``ON`` is equivalent to using the ``DEPENDS_EXPLICIT_ONLY``
+option in all uses of that command.
diff --git a/Help/variable/CMAKE_AUTOMOC_EXECUTABLE.rst b/Help/variable/CMAKE_AUTOMOC_EXECUTABLE.rst
new file mode 100644
index 0000000000..150a73ab41
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOMOC_EXECUTABLE.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTOMOC_EXECUTABLE
+------------------------
+
+.. versionadded:: 3.27
+
+This variable is used to initialize the :prop_tgt:`AUTOMOC_EXECUTABLE`
+property on all the targets. See that target property for additional
+information.
+
+By default it is empty.
diff --git a/Help/variable/CMAKE_AUTORCC_EXECUTABLE.rst b/Help/variable/CMAKE_AUTORCC_EXECUTABLE.rst
new file mode 100644
index 0000000000..52d7faa873
--- /dev/null
+++ b/Help/variable/CMAKE_AUTORCC_EXECUTABLE.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTORCC_EXECUTABLE
+------------------------
+
+.. versionadded:: 3.27
+
+This variable is used to initialize the :prop_tgt:`AUTORCC_EXECUTABLE`
+property on all the targets. See that target property for additional
+information.
+
+By default it is empty.
diff --git a/Help/variable/CMAKE_AUTOUIC_EXECUTABLE.rst b/Help/variable/CMAKE_AUTOUIC_EXECUTABLE.rst
new file mode 100644
index 0000000000..b2ebd7fb39
--- /dev/null
+++ b/Help/variable/CMAKE_AUTOUIC_EXECUTABLE.rst
@@ -0,0 +1,10 @@
+CMAKE_AUTOUIC_EXECUTABLE
+------------------------
+
+.. versionadded:: 3.27
+
+This variable is used to initialize the :prop_tgt:`AUTOUIC_EXECUTABLE`
+property on all the targets. See that target property for additional
+information.
+
+By default it is empty.
diff --git a/Help/variable/CMAKE_CROSSCOMPILING.rst b/Help/variable/CMAKE_CROSSCOMPILING.rst
index 7e6ec3371f..16dbfa59c5 100644
--- a/Help/variable/CMAKE_CROSSCOMPILING.rst
+++ b/Help/variable/CMAKE_CROSSCOMPILING.rst
@@ -1,15 +1,15 @@
CMAKE_CROSSCOMPILING
--------------------
-Intended to indicate whether CMake is cross compiling, but note limitations
-discussed below.
+This variable is set by CMake to indicate whether it is cross compiling,
+but note limitations discussed below.
This variable will be set to true by CMake if the :variable:`CMAKE_SYSTEM_NAME`
variable has been set manually (i.e. in a toolchain file or as a cache entry
from the :manual:`cmake <cmake(1)>` command line). In most cases, manually
-setting :variable:`CMAKE_SYSTEM_NAME` will only be done when cross compiling,
-since it will otherwise be given the same value as
-:variable:`CMAKE_HOST_SYSTEM_NAME` if not manually set, which is correct for
+setting :variable:`CMAKE_SYSTEM_NAME` will only be done when cross compiling
+since, if not manually set, it will be given the same value as
+:variable:`CMAKE_HOST_SYSTEM_NAME`, which is correct for
the non-cross-compiling case. In the event that :variable:`CMAKE_SYSTEM_NAME`
is manually set to the same value as :variable:`CMAKE_HOST_SYSTEM_NAME`, then
``CMAKE_CROSSCOMPILING`` will still be set to true.
diff --git a/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst b/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst
new file mode 100644
index 0000000000..5fa49dede4
--- /dev/null
+++ b/Help/variable/CMAKE_DLL_NAME_WITH_SOVERSION.rst
@@ -0,0 +1,14 @@
+CMAKE_DLL_NAME_WITH_SOVERSION
+-----------------------------
+
+.. versionadded:: 3.27
+
+This variable is used to initialize the :prop_tgt:`DLL_NAME_WITH_SOVERSION`
+property on shared library targets for the Windows platform, which is selected
+when the :variable:`WIN32` variable is set.
+
+See this target property for additional information.
+
+Please note that setting this variable has no effect if versioned filenames
+are globally disabled with the :variable:`CMAKE_PLATFORM_NO_VERSIONED_SONAME`
+variable.
diff --git a/Help/variable/CMAKE_EDIT_COMMAND.rst b/Help/variable/CMAKE_EDIT_COMMAND.rst
index 2f4ab1f62a..b21434f7ff 100644
--- a/Help/variable/CMAKE_EDIT_COMMAND.rst
+++ b/Help/variable/CMAKE_EDIT_COMMAND.rst
@@ -2,7 +2,8 @@ CMAKE_EDIT_COMMAND
------------------
Full path to :manual:`cmake-gui(1)` or :manual:`ccmake(1)`. Defined only for
-:ref:`Makefile Generators` when not using an "extra" generator for an IDE.
+:ref:`Makefile Generators` and :ref:`Ninja Generators` when not using any
+:ref:`Extra Generators`.
This is the full path to the CMake executable that can graphically
edit the cache. For example, :manual:`cmake-gui(1)` or :manual:`ccmake(1)`.
diff --git a/Help/variable/CMAKE_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_ENABLE_EXPORTS.rst
index 9f43de3fac..d2c5ed086e 100644
--- a/Help/variable/CMAKE_ENABLE_EXPORTS.rst
+++ b/Help/variable/CMAKE_ENABLE_EXPORTS.rst
@@ -8,3 +8,7 @@ Specify whether executables export symbols for loadable modules.
This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
property for executable targets when they are created by calls to the
:command:`add_executable` command. See the property documentation for details.
+
+This command has been superseded by the
+:variable:`CMAKE_EXECUTABLE_ENABLE_EXPORTS` command. It is provided for
+compatibility with older CMake code.
diff --git a/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst
new file mode 100644
index 0000000000..aa6dda2219
--- /dev/null
+++ b/Help/variable/CMAKE_EXECUTABLE_ENABLE_EXPORTS.rst
@@ -0,0 +1,12 @@
+CMAKE_EXECUTABLE_ENABLE_EXPORTS
+-------------------------------
+
+.. versionadded:: 3.27
+
+Specify whether executables export symbols for loadable modules.
+
+This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
+property for executable targets when they are created by calls to the
+:command:`add_executable` command. See the property documentation for details.
+
+This variable supersede the :variable:`CMAKE_ENABLE_EXPORTS` variable.
diff --git a/Help/variable/CMAKE_EXTRA_GENERATOR.rst b/Help/variable/CMAKE_EXTRA_GENERATOR.rst
index 2c9232345f..0a113a5ae6 100644
--- a/Help/variable/CMAKE_EXTRA_GENERATOR.rst
+++ b/Help/variable/CMAKE_EXTRA_GENERATOR.rst
@@ -1,6 +1,12 @@
CMAKE_EXTRA_GENERATOR
---------------------
+.. deprecated:: 3.27
+
+ Support for :ref:`Extra Generators` is deprecated and will be removed from
+ a future version of CMake. IDEs may use the :manual:`cmake-file-api(7)`
+ to view CMake-generated project build trees.
+
The extra generator used to build the project. See
:manual:`cmake-generators(7)`.
diff --git a/Help/variable/CMAKE_GENERATOR_PLATFORM.rst b/Help/variable/CMAKE_GENERATOR_PLATFORM.rst
index acb7b2e1a2..416ff60f34 100644
--- a/Help/variable/CMAKE_GENERATOR_PLATFORM.rst
+++ b/Help/variable/CMAKE_GENERATOR_PLATFORM.rst
@@ -26,8 +26,46 @@ Platform specification is supported only on specific generators:
See native build system documentation for allowed platform names.
+.. _`Visual Studio Platform Selection`:
+
Visual Studio Platform Selection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-On :ref:`Visual Studio Generators` the selected platform name
-is provided in the :variable:`CMAKE_VS_PLATFORM_NAME` variable.
+The :ref:`Visual Studio Generators` support platform specification
+using one of these forms:
+
+* ``platform``
+* ``platform[,key=value]*``
+* ``key=value[,key=value]*``
+
+The ``platform`` specifies the target platform (VS target architecture),
+such as ``x64``, ``ARM64``, or ``Win32``. The selected platform
+name is provided in the :variable:`CMAKE_VS_PLATFORM_NAME` variable.
+
+The ``key=value`` pairs form a comma-separated list of options to
+specify generator-specific details of the platform selection.
+Supported pairs are:
+
+``version=<version>``
+ .. versionadded:: 3.27
+
+ Specify the Windows SDK version to use. This is supported by VS 2015 and
+ above when targeting Windows 10.0+ or Windows Store. CMake will set the
+ :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION` variable to the
+ selected SDK version.
+
+ The ``<version>`` may be one of:
+
+ ``10.0``
+ Specify that any 10.0 SDK version may be used, and let Visual Studio
+ pick one. This is supported by VS 2019 and above.
+
+ ``10.0.<build>.<increment>``
+ Specify the exact 4-component SDK version, e.g., ``10.0.19041.0``.
+ The specified version of the SDK must be installed. It may not exceed
+ the value of :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM`,
+ if that variable is set.
+
+ If the ``version`` field is not specified, CMake selects a version as
+ described in the :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`
+ variable documentation.
diff --git a/Help/variable/CMAKE_KATE_FILES_MODE.rst b/Help/variable/CMAKE_KATE_FILES_MODE.rst
new file mode 100644
index 0000000000..195c15d3ce
--- /dev/null
+++ b/Help/variable/CMAKE_KATE_FILES_MODE.rst
@@ -0,0 +1,20 @@
+CMAKE_KATE_FILES_MODE
+---------------------
+
+.. versionadded:: 3.27
+
+This cache variable is used by the Kate project generator and controls
+to what mode the ``files`` entry in the project file will be set. See
+:manual:`cmake-generators(7)`.
+
+Possible values are ``AUTO``, ``SVN``, ``GIT``, ``HG``, ``FOSSIL`` and ``LIST``.
+
+When set to ``LIST``, CMake will put the list of source files known to CMake
+in the project file.
+When set to ``SVN``, ``GIT``, ``HG`` or ``FOSSIL``, CMake will set
+the generated project accordingly to Subversion, git, Mercurial
+or Fossil, and Kate will then use the respective command line tool to
+retrieve the list of files in the project.
+When unset or set to ``AUTO``, CMake will try to detect whether the
+source directory is part of a git or svn checkout or not, and put the
+respective entry into the project file.
diff --git a/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst b/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst
new file mode 100644
index 0000000000..c830332557
--- /dev/null
+++ b/Help/variable/CMAKE_KATE_MAKE_ARGUMENTS.rst
@@ -0,0 +1,11 @@
+CMAKE_KATE_MAKE_ARGUMENTS
+-------------------------
+
+.. versionadded:: 3.0
+
+This cache variable is used by the Kate project generator. See
+:manual:`cmake-generators(7)`.
+
+This variable holds arguments which are used when Kate invokes the make
+tool. By default it is initialized to hold flags to enable parallel builds
+(using -j typically).
diff --git a/Help/variable/CMAKE_LANG_FLAGS.rst b/Help/variable/CMAKE_LANG_FLAGS.rst
index 4b39b1deff..909a001a9c 100644
--- a/Help/variable/CMAKE_LANG_FLAGS.rst
+++ b/Help/variable/CMAKE_LANG_FLAGS.rst
@@ -1,9 +1,10 @@
CMAKE_<LANG>_FLAGS
------------------
-Flags for all build types.
-
-``<LANG>`` flags used regardless of the value of :variable:`CMAKE_BUILD_TYPE`.
+Language-wide flags for language ``<LANG>`` used when building for
+all configurations. These flags will be passed to all invocations
+of the compiler. This includes invocations that drive compiling
+and those that drive linking.
For each language, if this variable is not defined, it is initialized
and stored in the cache using values from environment variables in
@@ -27,7 +28,10 @@ combination with CMake's builtin defaults for the toolchain:
This value is a command-line string fragment. Therefore, multiple options
should be separated by spaces, and options with spaces should be quoted.
-The flags in this variable will be passed to the compiler before those
-in the per-configuration :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variant,
-and before flags added by the :command:`add_compile_options` or
-:command:`target_compile_options` commands.
+The flags in this variable will be passed before those in the
+per-configuration :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable.
+On invocations driving compiling, flags from both variables will be passed
+before flags added by commands such as :command:`add_compile_options` and
+:command:`target_compile_options`. On invocations driving linking,
+they will be passed before flags added by commands such as
+:command:`add_link_options` and :command:`target_link_options`.
diff --git a/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst
index f0900fdc8d..5daa4c06a1 100644
--- a/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst
+++ b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst
@@ -1,9 +1,16 @@
CMAKE_<LANG>_FLAGS_<CONFIG>
---------------------------
-Flags for language ``<LANG>`` when building for the ``<CONFIG>`` configuration.
+Language-wide flags for language ``<LANG>`` used when building for
+the ``<CONFIG>`` configuration. These flags will be passed to all
+invocations of the compiler in the corresponding configuration.
+This includes invocations that drive compiling and those that drive
+linking.
-The flags in this variable will be passed to the compiler after those
-in the :variable:`CMAKE_<LANG>_FLAGS` variable, but before flags added
-by the :command:`add_compile_options` or :command:`target_compile_options`
-commands.
+The flags in this variable will be passed after those in the
+:variable:`CMAKE_<LANG>_FLAGS` variable. On invocations driving compiling,
+flags from both variables will be passed before flags added by commands
+such as :command:`add_compile_options` and :command:`target_compile_options`.
+On invocations driving linking, they will be passed before flags added by
+commands such as :command:`add_link_options` and
+:command:`target_link_options`.
diff --git a/Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst b/Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst
index 59c60d3acc..b611967c86 100644
--- a/Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst
+++ b/Help/variable/CMAKE_MAXIMUM_RECURSION_DEPTH.rst
@@ -33,3 +33,5 @@ Calling any of the following commands increases the recursion depth:
depth)
* Reading or writing variables that are being watched by a
:command:`variable_watch`
+
+See also the :envvar:`CMAKE_MAXIMUM_RECURSION_DEPTH` environment variable.
diff --git a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst
index a8c40359a5..79a65b8c9d 100644
--- a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst
+++ b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst
@@ -3,27 +3,38 @@ CMAKE_NINJA_OUTPUT_PATH_PREFIX
.. versionadded:: 3.6
-Set output files path prefix for the :generator:`Ninja` generator.
+Tell the :ref:`Ninja Generators` to add a prefix to every output path in
+``build.ninja``. A trailing slash is appended to the prefix, if missing.
-Every output files listed in the generated ``build.ninja`` will be
-prefixed by the contents of this variable (a trailing slash is
-appended if missing). This is useful when the generated ninja file is
-meant to be embedded as a ``subninja`` file into a *super* ninja
-project. For example, a ninja build file generated with a command
-like::
+This is useful when the generated ninja file is meant to be embedded as a
+``subninja`` file into a *super* ninja project. For example, the command:
- cd top-build-dir/sub &&
- cmake -G Ninja -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ path/to/source
+.. code-block:: shell
-can be embedded in ``top-build-dir/build.ninja`` with a directive like
-this::
+ cd super-build-dir &&
+ cmake -G Ninja -S /path/to/src -B sub -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/
+ # ^^^---------- these match -----------^^^
+
+generates a build directory with its top-level (:variable:`CMAKE_BINARY_DIR`)
+in ``super-build-dir/sub``. The path to the build directory ends in the
+output path prefix. This makes it suitable for use in a separately-written
+``super-build-dir/build.ninja`` file with a directive like this::
subninja sub/build.ninja
-The ``auto-regeneration`` rule in ``top-build-dir/build.ninja`` must have an
-order-only dependency on ``sub/build.ninja``.
+The ``auto-regeneration`` rule in ``super-build-dir/build.ninja`` must
+have an order-only dependency on ``sub/build.ninja``.
+
+.. versionadded:: 3.27
+
+ The :generator:`Ninja Multi-Config` generator supports this variable.
.. note::
When ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` is set, the project generated
by CMake cannot be used as a standalone project. No default targets
are specified.
+
+ The value of ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` must match one or more
+ path components at the *end* of :variable:`CMAKE_BINARY_DIR`, or the
+ behavior is undefined. However, this requirement is not checked
+ automatically.
diff --git a/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst b/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst
new file mode 100644
index 0000000000..3e2c6df8f7
--- /dev/null
+++ b/Help/variable/CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS.rst
@@ -0,0 +1,10 @@
+CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
+-----------------------------------
+
+.. versionadded:: 3.27
+
+Specify whether shared library generates an import file.
+
+This variable is used to initialize the :prop_tgt:`ENABLE_EXPORTS` target
+property for shared library targets when they are created by calls to the
+:command:`add_library` command. See the property documentation for details.
diff --git a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst
new file mode 100644
index 0000000000..8ef54cdb0f
--- /dev/null
+++ b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION.rst
@@ -0,0 +1,12 @@
+CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
+--------------------------------------------
+
+.. versionadded:: 3.27
+
+Tell :ref:`Visual Studio Generators` to use the given
+Windows Target Platform Minimum Version.
+
+This variable is used to initialize the
+:prop_tgt:`VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION` property on all
+targets when they are created. See that target property for
+additional information.
diff --git a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst
index eb7104998a..2c14d3900d 100644
--- a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst
+++ b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION.rst
@@ -5,11 +5,30 @@ CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
Visual Studio Windows Target Platform Version.
-When targeting Windows 10 and above Visual Studio 2015 and above support
-specification of a target Windows version to select a corresponding SDK.
-The :variable:`CMAKE_SYSTEM_VERSION` variable may be set to specify a
-version. Otherwise CMake computes a default version based on the Windows
-SDK versions available. The chosen Windows target version number is provided
+When targeting Windows 10 and above, :ref:`Visual Studio Generators` for
+VS 2015 and above support specification of a Windows SDK version:
+
+* If :variable:`CMAKE_GENERATOR_PLATFORM` specifies a ``version=`` field,
+ as documented by :ref:`Visual Studio Platform Selection`, that SDK
+ version is selected.
+
+* Otherwise, if the ``WindowsSDKVersion`` environment variable
+ is set to an available SDK version, that version is selected.
+ This is intended for use in environments established by ``vcvarsall.bat``
+ or similar scripts.
+
+ .. versionadded:: 3.27
+ This is enabled by policy :policy:`CMP0149`.
+
+* Otherwise, if :variable:`CMAKE_SYSTEM_VERSION` is set to an available
+ SDK version, that version is selected.
+
+ .. versionchanged:: 3.27
+ This is disabled by policy :policy:`CMP0149`.
+
+* Otherwise, CMake uses the latest Windows SDK version available.
+
+The chosen Windows target version number is provided
in ``CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION``. If no Windows 10 SDK
is available this value will be empty.
diff --git a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst
index f1a1977448..727ccc95be 100644
--- a/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst
+++ b/Help/variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM.rst
@@ -10,5 +10,5 @@ be set to a false value (e.g. ``OFF``, ``FALSE``, or ``0``) or the SDK version
to use as the maximum (e.g. ``10.0.14393.0``). If unset, the default depends
on which version of Visual Studio is targeted by the current generator.
-This can be used in conjunction with :variable:`CMAKE_SYSTEM_VERSION`, which
-CMake uses to select :variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`.
+This can be used to exclude Windows SDK versions from consideration for
+:variable:`CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION`.
diff --git a/Help/variable/PackageName_ROOT.rst b/Help/variable/PackageName_ROOT.rst
index 6b17be3bad..8b728ba175 100644
--- a/Help/variable/PackageName_ROOT.rst
+++ b/Help/variable/PackageName_ROOT.rst
@@ -14,3 +14,11 @@ This variable may hold a single prefix or a
:ref:`semicolon-separated list <CMake Language Lists>` of multiple prefixes.
See also the :envvar:`<PackageName>_ROOT` environment variable.
+
+.. variable:: <PACKAGENAME>_ROOT
+
+ .. versionadded:: 3.27
+
+ Calls to :command:`find_package(<PackageName>)` will also search in
+ prefixes specified by the upper-case ``<PACKAGENAME>_ROOT`` CMake
+ variable. See policy :policy:`CMP0144`.
diff --git a/Modules/CMakeASMCompiler.cmake.in b/Modules/CMakeASMCompiler.cmake.in
index 3953b304b0..e300782aee 100644
--- a/Modules/CMakeASMCompiler.cmake.in
+++ b/Modules/CMakeASMCompiler.cmake.in
@@ -6,6 +6,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_ASM@ASM_DIALECT@_COMPILER_RANLIB "@_CMAKE_ASM_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_ASM@ASM_DIALECT@_COMPILER_LOADED 1)
set(CMAKE_ASM@ASM_DIALECT@_COMPILER_ID "@_CMAKE_ASM_COMPILER_ID@")
set(CMAKE_ASM@ASM_DIALECT@_COMPILER_VERSION "@_CMAKE_ASM_COMPILER_VERSION@")
diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in
index 2b24ff2aad..8ae07a34e1 100644
--- a/Modules/CMakeCCompiler.cmake.in
+++ b/Modules/CMakeCCompiler.cmake.in
@@ -27,6 +27,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_C_COMPILER_RANLIB "@CMAKE_C_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_COMPILER_IS_GNUCC @CMAKE_COMPILER_IS_GNUCC@)
set(CMAKE_C_COMPILER_LOADED 1)
set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@)
diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake
index dea721ebb9..e774088811 100644
--- a/Modules/CMakeCUDAInformation.cmake
+++ b/Modules/CMakeCUDAInformation.cmake
@@ -134,7 +134,6 @@ include(CMakeCommonLanguageInclude)
# CMAKE_CUDA_CREATE_SHARED_LIBRARY
# CMAKE_CUDA_CREATE_SHARED_MODULE
# CMAKE_CUDA_COMPILE_WHOLE_COMPILATION
-# CMAKE_CUDA_COMPILE_PTX_COMPILATION
# CMAKE_CUDA_COMPILE_SEPARABLE_COMPILATION
# CMAKE_CUDA_LINK_EXECUTABLE
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index 534e96055f..834c2e6b17 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -28,6 +28,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_CXX_COMPILER_RANLIB "@CMAKE_CXX_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_COMPILER_IS_GNUCXX @CMAKE_COMPILER_IS_GNUCXX@)
set(CMAKE_CXX_COMPILER_LOADED 1)
set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@)
@@ -36,7 +37,7 @@ set(CMAKE_CXX_ABI_COMPILED @CMAKE_CXX_ABI_COMPILED@)
set(CMAKE_CXX_COMPILER_ENV_VAR "CXX")
set(CMAKE_CXX_COMPILER_ID_RUN 1)
-set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm)
+set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm;ccm;cxxm;c++m)
set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
foreach (lang C OBJC OBJCXX)
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index f79e8b8d39..67044fb3b2 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -35,7 +35,7 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src)
else(CMAKE_${lang}_FLAGS_INIT)
set(CMAKE_${lang}_COMPILER_ID_FLAGS ${CMAKE_${lang}_FLAGS_INIT})
endif()
- string(REPLACE " " ";" CMAKE_${lang}_COMPILER_ID_FLAGS_LIST "${CMAKE_${lang}_COMPILER_ID_FLAGS}")
+ separate_arguments(CMAKE_${lang}_COMPILER_ID_FLAGS_LIST NATIVE_COMMAND "${CMAKE_${lang}_COMPILER_ID_FLAGS}")
# Compute the directory in which to run the test.
set(CMAKE_${lang}_COMPILER_ID_DIR ${CMAKE_PLATFORM_INFO_DIR}/CompilerId${lang})
@@ -401,6 +401,9 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS}
endif()
endif()
set(id_toolset_version_props "<Import Project=\"${CMAKE_GENERATOR_INSTANCE}\\VC\\Auxiliary\\Build${id_sep}${CMAKE_VS_PLATFORM_TOOLSET_VERSION}\\Microsoft.VCToolsVersion.${CMAKE_VS_PLATFORM_TOOLSET_VERSION}.props\" />")
+ if(lang STREQUAL CXX OR lang STREQUAL C)
+ set(id_toolset_version_props "<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />${id_toolset_version_props}")
+ endif()
unset(id_sep)
endif()
endif()
diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake
index d4dcc62f99..386be73f68 100644
--- a/Modules/CMakeDetermineSystem.cmake
+++ b/Modules/CMakeDetermineSystem.cmake
@@ -176,6 +176,13 @@ else()
set(CMAKE_SYSTEM_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
endif()
set(CMAKE_SYSTEM_PROCESSOR "${CMAKE_HOST_SYSTEM_PROCESSOR}")
+ if(CMAKE_CROSSCOMPILING)
+ message(AUTHOR_WARNING
+ "CMAKE_CROSSCOMPILING has been set by the project, toolchain file, or user. "
+ "CMake is resetting it to false because CMAKE_SYSTEM_NAME was not set. "
+ "To indicate cross compilation, only CMAKE_SYSTEM_NAME needs to be set."
+ )
+ endif()
set(CMAKE_CROSSCOMPILING FALSE)
set(PRESET_CMAKE_SYSTEM_NAME FALSE)
endif()
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index 2ac8879923..10e8ff347f 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -165,6 +165,7 @@ else()
set(_CMAKE_READELF_NAMES "readelf")
set(_CMAKE_DLLTOOL_NAMES "dlltool")
set(_CMAKE_ADDR2LINE_NAMES "addr2line")
+ set(_CMAKE_TAPI_NAMES "tapi")
# Prepend toolchain-specific names.
if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
@@ -185,7 +186,15 @@ else()
list(PREPEND _CMAKE_RANLIB_NAMES "llvm-ranlib")
if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}" VERSION_GREATER_EQUAL 11)
# llvm-strip versions prior to 11 require additional flags we do not yet add.
- list(PREPEND _CMAKE_STRIP_NAMES "llvm-strip")
+ if(APPLE)
+ # llvm-strip does not seem to support chained fixup format correctly.
+ # FIXME(#23333): We still need to consider 'llvm-strip' as a fallback
+ # because the 'APPLE' definition may be based on the host in this context,
+ # and a cross-compiling toolchain may not have 'strip'.
+ list(APPEND _CMAKE_STRIP_NAMES "llvm-strip")
+ else()
+ list(PREPEND _CMAKE_STRIP_NAMES "llvm-strip")
+ endif()
endif()
list(PREPEND _CMAKE_NM_NAMES "llvm-nm")
if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}" VERSION_GREATER_EQUAL 9)
@@ -201,7 +210,7 @@ else()
list(PREPEND _CMAKE_LINKER_NAMES "armlink")
endif()
- list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE)
+ list(APPEND _CMAKE_TOOL_VARS AR RANLIB STRIP LINKER NM OBJDUMP OBJCOPY READELF DLLTOOL ADDR2LINE TAPI)
endif()
foreach(_CMAKE_TOOL IN LISTS _CMAKE_TOOL_VARS)
@@ -225,6 +234,20 @@ if(NOT CMAKE_RANLIB)
set(CMAKE_RANLIB : CACHE INTERNAL "noop for ranlib")
endif()
+if(NOT CMAKE_TAPI)
+ # try to pick-up from Apple toolchain
+ execute_process(COMMAND xcrun --find tapi
+ OUTPUT_VARIABLE _xcrun_out
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET
+ RESULT_VARIABLE _xcrun_failed)
+ if(NOT _xcrun_failed AND EXISTS "${_xcrun_out}")
+ set_property(CACHE CMAKE_TAPI PROPERTY VALUE "${_xcrun_out}")
+ endif()
+ unset(_xcrun_out)
+ unset(_xcrun_failed)
+endif()
+
if(CMAKE_PLATFORM_HAS_INSTALLNAME)
find_program(CMAKE_INSTALL_NAME_TOOL NAMES ${_CMAKE_TOOLCHAIN_PREFIX}install_name_tool HINTS ${_CMAKE_TOOLCHAIN_LOCATION} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
diff --git a/Modules/CMakeFindKate.cmake b/Modules/CMakeFindKate.cmake
index 9aaf6e5716..521bc5caf8 100644
--- a/Modules/CMakeFindKate.cmake
+++ b/Modules/CMakeFindKate.cmake
@@ -3,7 +3,7 @@
# This file is included in CMakeSystemSpecificInformation.cmake if
-# the Eclipse CDT4 extra generator has been selected.
+# the Kate extra generator has been selected.
# Try to find out how many CPUs we have and set the -j argument for make accordingly
@@ -17,5 +17,9 @@ if("${_CMAKE_KATE_PROCESSOR_COUNT}" GREATER 1 AND CMAKE_HOST_UNIX AND "${CMA
set(_CMAKE_KATE_INITIAL_MAKE_ARGS "-j${_CMAKE_KATE_PROCESSOR_COUNT}")
endif()
-# This variable is used by the Eclipse generator and appended to the make invocation commands.
+# This variable is used by the Kate generator and appended to the make invocation commands.
set(CMAKE_KATE_MAKE_ARGUMENTS "${_CMAKE_KATE_INITIAL_MAKE_ARGS}" CACHE STRING "Additional command line arguments when Kate invokes make. Enter e.g. -j<some_number> to get parallel builds")
+
+
+set(CMAKE_KATE_FILES_MODE "AUTO" CACHE STRING "Option to override the version control detection and force a mode for the Kate project.")
+set_property(CACHE CMAKE_KATE_FILES_MODE PROPERTY STRINGS "AUTO;SVN;GIT;LIST")
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index f52ad02bb2..fc81d0e5f6 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -14,6 +14,7 @@ set(CMAKE_Fortran_SIMULATE_VERSION "@CMAKE_Fortran_SIMULATE_VERSION@")
set(CMAKE_AR "@CMAKE_AR@")
set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
set(CMAKE_RANLIB "@CMAKE_RANLIB@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
set(CMAKE_Fortran_COMPILER_LOADED 1)
@@ -25,7 +26,7 @@ set(CMAKE_Fortran_COMPILER_ENV_VAR "FC")
set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 @CMAKE_Fortran_COMPILER_SUPPORTS_F90@)
set(CMAKE_Fortran_COMPILER_ID_RUN 1)
-set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95@CMAKE_Fortran_VENDOR_SOURCE_FILE_EXTENSIONS@)
+set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95;f03;F03;f08;F08@CMAKE_Fortran_VENDOR_SOURCE_FILE_EXTENSIONS@)
set(CMAKE_Fortran_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
set(CMAKE_Fortran_LINKER_PREFERENCE 20)
if(UNIX)
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in
index ce4e2cf656..8a747c67d8 100644
--- a/Modules/CMakeHIPCompiler.cmake.in
+++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -58,3 +58,4 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_HIP_COMPILER_RANLIB "@CMAKE_HIP_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in
index 36f6ec1505..ea11a7aa36 100644
--- a/Modules/CMakeOBJCCompiler.cmake.in
+++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -25,6 +25,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_OBJC_COMPILER_RANLIB "@CMAKE_OBJC_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_COMPILER_IS_GNUOBJC @CMAKE_COMPILER_IS_GNUOBJC@)
set(CMAKE_OBJC_COMPILER_LOADED 1)
set(CMAKE_OBJC_COMPILER_WORKS @CMAKE_OBJC_COMPILER_WORKS@)
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in
index 4f271003a5..5d0b381257 100644
--- a/Modules/CMakeOBJCXXCompiler.cmake.in
+++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -26,6 +26,7 @@ set(CMAKE_RANLIB "@CMAKE_RANLIB@")
set(CMAKE_OBJCXX_COMPILER_RANLIB "@CMAKE_OBJCXX_COMPILER_RANLIB@")
set(CMAKE_LINKER "@CMAKE_LINKER@")
set(CMAKE_MT "@CMAKE_MT@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
set(CMAKE_COMPILER_IS_GNUOBJCXX @CMAKE_COMPILER_IS_GNUOBJCXX@)
set(CMAKE_OBJCXX_COMPILER_LOADED 1)
set(CMAKE_OBJCXX_COMPILER_WORKS @CMAKE_OBJCXX_COMPILER_WORKS@)
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index f524955861..777c6807c5 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -72,9 +72,9 @@ if(CMAKE_GENERATOR STREQUAL "Xcode")
# these options here will have no effect when compiling with the built-in driver,
# and will explode violently, leaving build products in the source directory, when
# using the old swift driver.
- set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g")
+ set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g ${CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS}")
set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
- set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
+ set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g ${CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS}")
set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
else()
set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g -incremental")
diff --git a/Modules/CPackIFW.cmake b/Modules/CPackIFW.cmake
index d4e02f1227..2338b79f35 100644
--- a/Modules/CPackIFW.cmake
+++ b/Modules/CPackIFW.cmake
@@ -8,7 +8,7 @@ CPackIFW
.. versionadded:: 3.1
This module looks for the location of the command-line utilities supplied with the
-`Qt Installer Framework <http://doc.qt.io/qtinstallerframework/index.html>`_
+`Qt Installer Framework <https://doc.qt.io/qtinstallerframework/index.html>`_
(QtIFW).
The module also defines several commands to control the behavior of the
diff --git a/Modules/CTestTargets.cmake b/Modules/CTestTargets.cmake
index b91b48eec4..99ef8e50c3 100644
--- a/Modules/CTestTargets.cmake
+++ b/Modules/CTestTargets.cmake
@@ -41,7 +41,7 @@ set(__conf_types "")
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
# We need to pass the configuration type on the test command line.
- set(__conf_types -C "${CMAKE_CFG_INTDIR}")
+ set(__conf_types -C "$<CONFIG>")
endif()
# Add convenience targets. Do this at most once in case of nested
diff --git a/Modules/CheckCCompilerFlag.cmake b/Modules/CheckCCompilerFlag.cmake
index 335b437133..cd89a55a92 100644
--- a/Modules/CheckCCompilerFlag.cmake
+++ b/Modules/CheckCCompilerFlag.cmake
@@ -11,29 +11,40 @@ Check whether the C compiler supports a given flag.
.. code-block:: cmake
- check_c_compiler_flag(<flag> <var>)
+ check_c_compiler_flag(<flag> <resultVar>)
Check that the ``<flag>`` is accepted by the compiler without
a diagnostic. Stores the result in an internal cache entry
- named ``<var>``.
-
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_c_source_compiles`` macro from the
-:module:`CheckCSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
+ named ``<resultVar>``.
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
-.. note::
- Since the :command:`try_compile` command forwards flags from variables
- like :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
- in such variables may cause a false negative for this check.
+The check is only performed once, with the result cached in the variable named
+by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+rather than performing the check again, even if the ``<code>`` changes. In
+order to force the check to be re-evaluated, the variable named by
+``<resultVar>`` must be manually removed from the cache.
+
+The compile and link commands can be influenced by setting any of the
+following variables prior to calling ``check_c_compiler_flag()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckCSourceCompiles)
include(Internal/CheckCompilerFlag)
macro (CHECK_C_COMPILER_FLAG _FLAG _RESULT)
diff --git a/Modules/CheckCSourceCompiles.cmake b/Modules/CheckCSourceCompiles.cmake
index b24da49bc0..ce4719a30a 100644
--- a/Modules/CheckCSourceCompiles.cmake
+++ b/Modules/CheckCSourceCompiles.cmake
@@ -22,51 +22,27 @@ Check if given C source compiles and links into an executable.
checking if anything in the output matches any of the specified regular
expressions.
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_c_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
-
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
-
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_c_source_compiles()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckCSourceRuns.cmake b/Modules/CheckCSourceRuns.cmake
index a6081ff46e..d5a8fda423 100644
--- a/Modules/CheckCSourceRuns.cmake
+++ b/Modules/CheckCSourceRuns.cmake
@@ -21,51 +21,27 @@ subsequently be run.
be set to 1, otherwise it will be set to an value that evaluates to boolean
false (e.g. an empty string or an error message).
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_c_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
-
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
-
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_c_source_runs()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckCXXCompilerFlag.cmake b/Modules/CheckCXXCompilerFlag.cmake
index 3bc346362d..a6884f5b83 100644
--- a/Modules/CheckCXXCompilerFlag.cmake
+++ b/Modules/CheckCXXCompilerFlag.cmake
@@ -17,11 +17,6 @@ Check whether the CXX compiler supports a given flag.
a diagnostic. Stores the result in an internal cache entry
named ``<var>``.
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_cxx_source_compiles`` macro from the
-:module:`CheckCXXSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
-
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
@@ -33,7 +28,6 @@ effect or even a specific one is beyond the scope of this module.
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckCXXSourceCompiles)
include(Internal/CheckCompilerFlag)
macro (CHECK_CXX_COMPILER_FLAG _FLAG _RESULT)
diff --git a/Modules/CheckCXXSourceCompiles.cmake b/Modules/CheckCXXSourceCompiles.cmake
index 502bfa76e6..4b33aa8ae4 100644
--- a/Modules/CheckCXXSourceCompiles.cmake
+++ b/Modules/CheckCXXSourceCompiles.cmake
@@ -22,51 +22,27 @@ Check if given C++ source compiles and links into an executable.
checking if anything in the output matches any of the specified regular
expressions.
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_cxx_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_CXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
-
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
-
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_cxx_source_compiles()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckCXXSourceRuns.cmake b/Modules/CheckCXXSourceRuns.cmake
index af03453d98..34027150e9 100644
--- a/Modules/CheckCXXSourceRuns.cmake
+++ b/Modules/CheckCXXSourceRuns.cmake
@@ -21,51 +21,27 @@ subsequently be run.
be set to 1, otherwise it will be set to an value that evaluates to boolean
false (e.g. an empty string or an error message).
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_cxx_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_CXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
-
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
-
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_cxx_source_runs()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index 1fa0898286..569dafdc44 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -41,22 +41,17 @@ Check if a symbol exists as a function, variable, or macro in ``C++``.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
For example:
diff --git a/Modules/CheckCompilerFlag.cmake b/Modules/CheckCompilerFlag.cmake
index 77c07b95da..a18435b3b1 100644
--- a/Modules/CheckCompilerFlag.cmake
+++ b/Modules/CheckCompilerFlag.cmake
@@ -13,24 +13,36 @@ Check whether the compiler supports a given flag.
.. code-block:: cmake
- check_compiler_flag(<lang> <flag> <var>)
+ check_compiler_flag(<lang> <flag> <resultVar>)
Check that the ``<flag>`` is accepted by the compiler without a diagnostic.
-Stores the result in an internal cache entry named ``<var>``.
-
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_source_compiles(<LANG>)`` function from the
-:module:`CheckSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
+Stores the result in an internal cache entry named ``<resultVar>``.
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
-.. note::
- Since the :command:`try_compile` command forwards flags from variables
- like :variable:`CMAKE_<LANG>_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
- in such variables may cause a false negative for this check.
+The check is only performed once, with the result cached in the variable named
+by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+rather than performing the check again, even if the ``<code>`` changes. In
+order to force the check to be re-evaluated, the variable named by
+``<resultVar>`` must be manually removed from the cache.
+
+The compile and link commands can be influenced by setting any of the
+following variables prior to calling ``check_compiler_flag()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckFortranCompilerFlag.cmake b/Modules/CheckFortranCompilerFlag.cmake
index 5b1cd02779..c3cd088873 100644
--- a/Modules/CheckFortranCompilerFlag.cmake
+++ b/Modules/CheckFortranCompilerFlag.cmake
@@ -13,29 +13,40 @@ Check whether the Fortran compiler supports a given flag.
.. code-block:: cmake
- check_fortran_compiler_flag(<flag> <var>)
+ check_fortran_compiler_flag(<flag> <resultVar>)
Check that the ``<flag>`` is accepted by the compiler without
a diagnostic. Stores the result in an internal cache entry
- named ``<var>``.
-
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_fortran_source_compiles`` macro from the
-:module:`CheckFortranSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
+ named ``<resultVar>``.
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
-.. note::
- Since the :command:`try_compile` command forwards flags from variables
- like :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
- in such variables may cause a false negative for this check.
+The check is only performed once, with the result cached in the variable named
+by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+rather than performing the check again, even if the ``<code>`` changes. In
+order to force the check to be re-evaluated, the variable named by
+``<resultVar>`` must be manually removed from the cache.
+
+The compile and link commands can be influenced by setting any of the
+following variables prior to calling ``check_fortran_compiler_flag()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckFortranSourceCompiles)
include(Internal/CheckCompilerFlag)
macro (CHECK_FORTRAN_COMPILER_FLAG _FLAG _RESULT)
diff --git a/Modules/CheckFortranSourceCompiles.cmake b/Modules/CheckFortranSourceCompiles.cmake
index 8dcc1d54a5..5158b7eed4 100644
--- a/Modules/CheckFortranSourceCompiles.cmake
+++ b/Modules/CheckFortranSourceCompiles.cmake
@@ -47,49 +47,27 @@ Check if given Fortran source compiles and links into an executable.
``SRC_EXT`` option can be used to override this with ``.<extension>`` instead--
``.F90`` is a typical choice.
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_fortran_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
-
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_fortran_source_compiles()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckFortranSourceRuns.cmake b/Modules/CheckFortranSourceRuns.cmake
index 985c765599..f9967498c4 100644
--- a/Modules/CheckFortranSourceRuns.cmake
+++ b/Modules/CheckFortranSourceRuns.cmake
@@ -43,47 +43,27 @@ subsequently be run.
By default, the test source file will be given a ``.F90`` file extension. The
``SRC_EXT`` option can be used to override this with ``.<extension>`` instead.
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_fortran_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_fortran_source_runs()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckFunctionExists.cmake b/Modules/CheckFunctionExists.cmake
index e2939ed97d..e7c47a4e7a 100644
--- a/Modules/CheckFunctionExists.cmake
+++ b/Modules/CheckFunctionExists.cmake
@@ -20,22 +20,17 @@ Check if a C function can be linked
The following variables may be set before calling this macro to modify the
way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
.. note::
diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake
index 577130725d..1d8c9f766d 100644
--- a/Modules/CheckIncludeFile.cmake
+++ b/Modules/CheckIncludeFile.cmake
@@ -21,22 +21,17 @@ Provides a macro to check if a header file can be included in ``C``.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
See the :module:`CheckIncludeFiles` module to check for multiple headers
at once. See the :module:`CheckIncludeFileCXX` module to check for headers
diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake
index d27b485e57..53d9a456e0 100644
--- a/Modules/CheckIncludeFileCXX.cmake
+++ b/Modules/CheckIncludeFileCXX.cmake
@@ -21,22 +21,17 @@ Provides a macro to check if a header file can be included in ``CXX``.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFiles`
to check for one or more ``C`` headers.
diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake
index 2f50c61d47..071df0c601 100644
--- a/Modules/CheckIncludeFiles.cmake
+++ b/Modules/CheckIncludeFiles.cmake
@@ -27,22 +27,17 @@ be included together.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
See modules :module:`CheckIncludeFile` and :module:`CheckIncludeFileCXX`
to check for a single header file in ``C`` or ``CXX`` languages.
diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake
index 2e56a195a5..69913a334c 100644
--- a/Modules/CheckLanguage.cmake
+++ b/Modules/CheckLanguage.cmake
@@ -36,7 +36,7 @@ Example:
include_guard(GLOBAL)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0126 NEW)
macro(check_language lang)
@@ -114,4 +114,4 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
endif()
endmacro()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/CheckLibraryExists.cmake b/Modules/CheckLibraryExists.cmake
index 5f1a914b3c..8340500803 100644
--- a/Modules/CheckLibraryExists.cmake
+++ b/Modules/CheckLibraryExists.cmake
@@ -26,18 +26,16 @@ Check if the function exists.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- list of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- list of options to pass to link command.
-``CMAKE_REQUIRED_LIBRARIES``
- list of libraries to link.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckOBJCCompilerFlag.cmake b/Modules/CheckOBJCCompilerFlag.cmake
index d8d874175f..ceb7e1738f 100644
--- a/Modules/CheckOBJCCompilerFlag.cmake
+++ b/Modules/CheckOBJCCompilerFlag.cmake
@@ -13,29 +13,40 @@ Check whether the Objective-C compiler supports a given flag.
.. code-block:: cmake
- check_objc_compiler_flag(<flag> <var>)
+ check_objc_compiler_flag(<flag> <resultVar>)
Check that the ``<flag>`` is accepted by the compiler without
a diagnostic. Stores the result in an internal cache entry
- named ``<var>``.
-
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_objc_source_compiles`` macro from the
-:module:`CheckOBJCSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
+ named ``<resultVar>``.
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
-.. note::
- Since the :command:`try_compile` command forwards flags from variables
- like :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
- in such variables may cause a false negative for this check.
+The check is only performed once, with the result cached in the variable named
+by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+rather than performing the check again, even if the ``<code>`` changes. In
+order to force the check to be re-evaluated, the variable named by
+``<resultVar>`` must be manually removed from the cache.
+
+The compile and link commands can be influenced by setting any of the
+following variables prior to calling ``check_objc_compiler_flag()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckOBJCSourceCompiles)
include(Internal/CheckCompilerFlag)
macro (CHECK_OBJC_COMPILER_FLAG _FLAG _RESULT)
diff --git a/Modules/CheckOBJCSourceCompiles.cmake b/Modules/CheckOBJCSourceCompiles.cmake
index c268ef9050..7663054b62 100644
--- a/Modules/CheckOBJCSourceCompiles.cmake
+++ b/Modules/CheckOBJCSourceCompiles.cmake
@@ -24,47 +24,27 @@ Check if given Objective-C source compiles and links into an executable.
checking if anything in the output matches any of the specified regular
expressions.
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_objc_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_objc_source_compiles()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckOBJCSourceRuns.cmake b/Modules/CheckOBJCSourceRuns.cmake
index dd03309936..a23a0c724e 100644
--- a/Modules/CheckOBJCSourceRuns.cmake
+++ b/Modules/CheckOBJCSourceRuns.cmake
@@ -23,47 +23,27 @@ subsequently be run.
be set to 1, otherwise it will be set to an value that evaluates to boolean
false (e.g. an empty string or an error message).
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_objc_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_OBJC_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_objc_source_runs()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckOBJCXXCompilerFlag.cmake b/Modules/CheckOBJCXXCompilerFlag.cmake
index 3f3f8fe38b..47dacc2a11 100644
--- a/Modules/CheckOBJCXXCompilerFlag.cmake
+++ b/Modules/CheckOBJCXXCompilerFlag.cmake
@@ -13,29 +13,40 @@ Check whether the Objective-C++ compiler supports a given flag.
.. code-block:: cmake
- check_objcxx_compiler_flag(<flag> <var>)
+ check_objcxx_compiler_flag(<flag> <resultVar>)
Check that the ``<flag>`` is accepted by the compiler without
a diagnostic. Stores the result in an internal cache entry
- named ``<var>``.
-
-This command temporarily sets the ``CMAKE_REQUIRED_DEFINITIONS`` variable
-and calls the ``check_objcxx_source_compiles`` macro from the
-:module:`CheckOBJCXXSourceCompiles` module. See documentation of that
-module for a listing of variables that can otherwise modify the build.
+ named ``<resultVar>``.
A positive result from this check indicates only that the compiler did not
issue a diagnostic message when given the flag. Whether the flag has any
effect or even a specific one is beyond the scope of this module.
-.. note::
- Since the :command:`try_compile` command forwards flags from variables
- like :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>`, unknown flags
- in such variables may cause a false negative for this check.
+The check is only performed once, with the result cached in the variable named
+by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+rather than performing the check again, even if the ``<code>`` changes. In
+order to force the check to be re-evaluated, the variable named by
+``<resultVar>`` must be manually removed from the cache.
+
+The compile and link commands can be influenced by setting any of the
+following variables prior to calling ``check_objcxx_compiler_flag()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckOBJCXXSourceCompiles)
include(Internal/CheckCompilerFlag)
macro (CHECK_OBJCXX_COMPILER_FLAG _FLAG _RESULT)
diff --git a/Modules/CheckOBJCXXSourceCompiles.cmake b/Modules/CheckOBJCXXSourceCompiles.cmake
index 11869343a4..cfbda3a3c3 100644
--- a/Modules/CheckOBJCXXSourceCompiles.cmake
+++ b/Modules/CheckOBJCXXSourceCompiles.cmake
@@ -24,47 +24,27 @@ Check if given Objective-C++ source compiles and links into an executable.
checking if anything in the output matches any of the specified regular
expressions.
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_objcxx_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_objcxx_source_compiles()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckOBJCXXSourceRuns.cmake b/Modules/CheckOBJCXXSourceRuns.cmake
index 05a5e4cf59..b2831b6ed3 100644
--- a/Modules/CheckOBJCXXSourceRuns.cmake
+++ b/Modules/CheckOBJCXXSourceRuns.cmake
@@ -23,47 +23,27 @@ subsequently be run.
be set to 1, otherwise it will be set to an value that evaluates to boolean
false (e.g. an empty string or an error message).
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_objcxx_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_OBJCXX_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_objcxx_source_runs()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckPrototypeDefinition.cmake b/Modules/CheckPrototypeDefinition.cmake
index 3d53b930a7..c1a7a1c579 100644
--- a/Modules/CheckPrototypeDefinition.cmake
+++ b/Modules/CheckPrototypeDefinition.cmake
@@ -35,20 +35,18 @@ Check if the prototype we expect is correct.
The following variables may be set before calling this function to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- list of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- list of include directories.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- list of options to pass to link command.
-``CMAKE_REQUIRED_LIBRARIES``
- list of libraries to link.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
#
diff --git a/Modules/CheckSourceCompiles.cmake b/Modules/CheckSourceCompiles.cmake
index 978879815d..041b59c457 100644
--- a/Modules/CheckSourceCompiles.cmake
+++ b/Modules/CheckSourceCompiles.cmake
@@ -47,47 +47,27 @@ Check if given source compiles and links into an executable.
end program"
HAVE_ERROR_STOP)
- The underlying check is performed by the :command:`try_compile` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_source_compiles()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_<LANG>_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_compile()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_compile` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_compile` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
The check is only performed once, with the result cached in the variable
named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
value rather than performing the check again, even if the ``<code>`` changes.
In order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_source_compiles()``:
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckSourceRuns.cmake b/Modules/CheckSourceRuns.cmake
index e2fa579253..822ee07a82 100644
--- a/Modules/CheckSourceRuns.cmake
+++ b/Modules/CheckSourceRuns.cmake
@@ -47,47 +47,27 @@ subsequently be run.
end program"
HAVE_COARRAY)
- The underlying check is performed by the :command:`try_run` command. The
- compile and link commands can be influenced by setting any of the following
- variables prior to calling ``check_source_runs()``:
-
- ``CMAKE_REQUIRED_FLAGS``
- Additional flags to pass to the compiler. Note that the contents of
- :variable:`CMAKE_<LANG>_FLAGS <CMAKE_<LANG>_FLAGS>` and its associated
- configuration-specific variable are automatically added to the compiler
- command before the contents of ``CMAKE_REQUIRED_FLAGS``.
-
- ``CMAKE_REQUIRED_DEFINITIONS``
- A :ref:`;-list <CMake Language Lists>` of compiler definitions of the form
- ``-DFOO`` or ``-DFOO=bar``. A definition for the name specified by
- ``<resultVar>`` will also be added automatically.
-
- ``CMAKE_REQUIRED_INCLUDES``
- A :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler. These will be the only header search paths used by
- ``try_run()``, i.e. the contents of the :prop_dir:`INCLUDE_DIRECTORIES`
- directory property will be ignored.
-
- ``CMAKE_REQUIRED_LINK_OPTIONS``
- A :ref:`;-list <CMake Language Lists>` of options to add to the link
- command (see :command:`try_run` for further details).
-
- ``CMAKE_REQUIRED_LIBRARIES``
- A :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. These can be the name of system libraries or they can be
- :ref:`Imported Targets <Imported Targets>` (see :command:`try_run` for
- further details).
-
- ``CMAKE_REQUIRED_QUIET``
- If this variable evaluates to a boolean true value, all status messages
- associated with the check will be suppressed.
-
- The check is only performed once, with the result cached in the variable
- named by ``<resultVar>``. Every subsequent CMake run will re-use this cached
- value rather than performing the check again, even if the ``<code>`` changes.
- In order to force the check to be re-evaluated, the variable named by
+ The check is only performed once, with the result cached in the variable named
+ by ``<resultVar>``. Every subsequent CMake run will re-use this cached value
+ rather than performing the check again, even if the ``<code>`` changes. In
+ order to force the check to be re-evaluated, the variable named by
``<resultVar>`` must be manually removed from the cache.
+ The compile and link commands can be influenced by setting any of the
+ following variables prior to calling ``check_source_runs()``
+
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/CheckStructHasMember.cmake b/Modules/CheckStructHasMember.cmake
index 8217c84c2a..81ea9fd48b 100644
--- a/Modules/CheckStructHasMember.cmake
+++ b/Modules/CheckStructHasMember.cmake
@@ -26,20 +26,17 @@ Check if the given struct or class has the specified member variable
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- list of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- list of include directories.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- list of options to pass to link command.
-``CMAKE_REQUIRED_LIBRARIES``
- list of libraries to link.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
Example:
@@ -51,8 +48,7 @@ Example:
#]=======================================================================]
include_guard(GLOBAL)
-include(CheckCSourceCompiles)
-include(CheckCXXSourceCompiles)
+include(CheckSourceCompiles)
macro (CHECK_STRUCT_HAS_MEMBER _STRUCT _MEMBER _HEADER _RESULT)
set(_INCLUDE_FILES)
@@ -78,9 +74,9 @@ int main()
")
if("${_lang}" STREQUAL "C")
- CHECK_C_SOURCE_COMPILES("${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
+ CHECK_SOURCE_COMPILES(C "${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
elseif("${_lang}" STREQUAL "CXX")
- CHECK_CXX_SOURCE_COMPILES("${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
+ CHECK_SOURCE_COMPILES(CXX "${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
else()
message(FATAL_ERROR "Unknown language:\n ${_lang}\nSupported languages: C, CXX.\n")
endif()
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake
index c4a15744f4..931ed4a05c 100644
--- a/Modules/CheckSymbolExists.cmake
+++ b/Modules/CheckSymbolExists.cmake
@@ -31,22 +31,17 @@ If the check needs to be done in C++, consider using
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- a :ref:`;-list <CMake Language Lists>` of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- a :ref:`;-list <CMake Language Lists>` of header search paths to pass to
- the compiler.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- a :ref:`;-list <CMake Language Lists>` of options to add to the link command.
-``CMAKE_REQUIRED_LIBRARIES``
- a :ref:`;-list <CMake Language Lists>` of libraries to add to the link
- command. See policy :policy:`CMP0075`.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
For example:
@@ -62,7 +57,7 @@ For example:
include_guard(GLOBAL)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
macro(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE)
@@ -166,4 +161,4 @@ int main(int argc, char** argv)
endif()
endmacro()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/CheckTypeSize.cmake b/Modules/CheckTypeSize.cmake
index 579d189dab..01ce1d202a 100644
--- a/Modules/CheckTypeSize.cmake
+++ b/Modules/CheckTypeSize.cmake
@@ -67,20 +67,18 @@ member you can do something like this:
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- list of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_INCLUDES``
- list of include directories.
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- list of options to pass to link command.
-``CMAKE_REQUIRED_LIBRARIES``
- list of libraries to link.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_INCLUDES.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
``CMAKE_EXTRA_INCLUDE_FILES``
list of extra headers to include.
#]=======================================================================]
@@ -92,7 +90,7 @@ get_filename_component(__check_type_size_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
include_guard(GLOBAL)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW)
#-----------------------------------------------------------------------------
@@ -294,4 +292,4 @@ macro(CHECK_TYPE_SIZE TYPE VARIABLE)
endmacro()
#-----------------------------------------------------------------------------
-cmake_policy(POP)
+endblock()
diff --git a/Modules/CheckVariableExists.cmake b/Modules/CheckVariableExists.cmake
index 3a7a431fdf..9e5d710f33 100644
--- a/Modules/CheckVariableExists.cmake
+++ b/Modules/CheckVariableExists.cmake
@@ -26,18 +26,16 @@ Check if the variable exists.
The following variables may be set before calling this macro to modify
the way the check is run:
-``CMAKE_REQUIRED_FLAGS``
- string of compile command line flags.
-``CMAKE_REQUIRED_DEFINITIONS``
- list of macros to define (-DFOO=bar).
-``CMAKE_REQUIRED_LINK_OPTIONS``
- .. versionadded:: 3.14
- list of options to pass to link command.
-``CMAKE_REQUIRED_LIBRARIES``
- list of libraries to link.
-``CMAKE_REQUIRED_QUIET``
- .. versionadded:: 3.1
- execute quietly without messages.
+.. include:: /module/CMAKE_REQUIRED_FLAGS.txt
+
+.. include:: /module/CMAKE_REQUIRED_DEFINITIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LINK_OPTIONS.txt
+
+.. include:: /module/CMAKE_REQUIRED_LIBRARIES.txt
+
+.. include:: /module/CMAKE_REQUIRED_QUIET.txt
+
#]=======================================================================]
include_guard(GLOBAL)
diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake
index bae0fbd8ca..4c0025c7f9 100644
--- a/Modules/Compiler/IAR-ASM.cmake
+++ b/Modules/Compiler/IAR-ASM.cmake
@@ -2,44 +2,74 @@
include(Compiler/IAR)
-cmake_policy(PUSH)
-cmake_policy(SET CMP0057 NEW) # if IN_LIST
-
-set(_CMAKE_IAR_ITOOLS "ARM" "RH850" "RL78" "RX" "RISC-V" "STM8")
-set(_CMAKE_IAR_XTOOLS "AVR" "MSP430" "V850" "8051")
+# Architecture specific
+if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
+ __compiler_iar_ilink(ASM)
+ __assembler_iar_deps("-y" 9.30)
+ set(_CMAKE_IAR_SILENCER_FLAG " -S")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
-set(_CMAKE_IAR_ASM_SILENT "RH850" "RL78" "RX" "RISC-V" "STM8")
-if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ASM_SILENT)
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
+ __compiler_iar_ilink(ASM)
+ __assembler_iar_deps("--dependencies=ns" 2.50.1)
set(_CMAKE_IAR_SILENCER_FLAG " --silent")
-else()
- set(_CMAKE_IAR_SILENCER_FLAG " -S")
-endif()
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
-string(APPEND CMAKE_ASM_FLAGS_INIT " ")
-string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
-string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
-string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
-string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850")
+ __compiler_iar_ilink(ASM)
+ __assembler_iar_deps("--dependencies=ns" 2)
+ set(_CMAKE_IAR_SILENCER_FLAG " --silent")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
-set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> ${_CMAKE_IAR_SILENCER_FLAG} <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78")
+ __compiler_iar_ilink(ASM)
+ __assembler_iar_deps("--dependencies=ns" 2)
+ set(_CMAKE_IAR_SILENCER_FLAG " --silent")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
-if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ITOOLS)
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" MATCHES "(RISCV|RISC-V)")
__compiler_iar_ilink(ASM)
+ __assembler_iar_deps("--dependencies=ns" 1)
+ set(_CMAKE_IAR_SILENCER_FLAG " --silent")
set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_XTOOLS)
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
+ __compiler_iar_xlink(ASM)
+ __assembler_iar_deps("-y" 8)
+ set(_CMAKE_IAR_SILENCER_FLAG " -S")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;asm;msa)
+
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430")
__compiler_iar_xlink(ASM)
- # AVR=s90, MSP430=s43, V850=s85, 8051=s51
- set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;s43;s85;s51;asm;msa)
+ __assembler_iar_deps("-y" 8)
+ set(_CMAKE_IAR_SILENCER_FLAG " -S")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s43;asm;msa)
-else()
- message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
+ __compiler_iar_xlink(ASM)
+ set(_CMAKE_IAR_SILENCER_FLAG " -S")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s85;asm;msa)
+
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "8051")
+ __compiler_iar_xlink(ASM)
+ set(_CMAKE_IAR_SILENCER_FLAG " -S")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s51;asm;msa)
+
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "STM8")
+ __compiler_iar_ilink(ASM)
+ __assembler_iar_deps("--dependencies=ns" 2)
+ set(_CMAKE_IAR_SILENCER_FLAG " --silent")
+ set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
+else()
+ message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic." )
endif()
-unset(_CMAKE_IAR_ITOOLS)
-unset(_CMAKE_IAR_XTOOLS)
-unset(_CMAKE_IAR_ASM_SILENT)
-unset(_CMAKE_IAR_SILENCER_FLAG)
+string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
+string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
+string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
-cmake_policy(POP)
+set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> ${_CMAKE_IAR_SILENCER_FLAG} <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+
+unset(_CMAKE_IAR_SILENCER_FLAG)
diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake
index 7908f962fd..0aca2838a7 100644
--- a/Modules/Compiler/IAR.cmake
+++ b/Modules/Compiler/IAR.cmake
@@ -9,7 +9,7 @@
include_guard()
macro(__compiler_iar_common lang)
- if (${lang} MATCHES "^(C|CXX)$")
+ if ("x${lang}" MATCHES "^x(C|CXX)$")
set(CMAKE_${lang}_COMPILE_OBJECT "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>")
set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy")
@@ -53,3 +53,9 @@ macro(__compiler_iar_xlink lang)
set(CMAKE_LIBRARY_PATH_FLAG "-I")
endmacro()
+
+macro(__assembler_iar_deps flag min_version)
+ if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL ${min_version})
+ set(CMAKE_DEPFILE_FLAGS_ASM "${flag} <DEP_FILE>")
+ endif()
+endmacro()
diff --git a/Modules/Compiler/IBMClang.cmake b/Modules/Compiler/IBMClang.cmake
index a9d760fa97..169a0f0382 100644
--- a/Modules/Compiler/IBMClang.cmake
+++ b/Modules/Compiler/IBMClang.cmake
@@ -43,7 +43,10 @@ macro(__compiler_ibmclang lang)
set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
- set(_CMAKE_LTO_THIN TRUE)
+ # Thin LTO is not yet supported on AIX.
+ if(NOT (CMAKE_SYSTEM_NAME STREQUAL "AIX"))
+ set(_CMAKE_LTO_THIN TRUE)
+ endif()
if(_CMAKE_LTO_THIN)
set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-flto=thin")
diff --git a/Modules/Compiler/LCC-Fortran.cmake b/Modules/Compiler/LCC-Fortran.cmake
index 8091b29949..2d82ea8063 100644
--- a/Modules/Compiler/LCC-Fortran.cmake
+++ b/Modules/Compiler/LCC-Fortran.cmake
@@ -10,8 +10,11 @@ set(CMAKE_Fortran_PREPROCESS_SOURCE
set(CMAKE_Fortran_FORMAT_FIXED_FLAG "-ffixed-form")
set(CMAKE_Fortran_FORMAT_FREE_FLAG "-ffree-form")
-set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp")
-set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp")
+# LCC < 1.24.00 has a broken Fortran preprocessor
+if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.00")
+ set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON "-cpp")
+ set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp")
+endif()
set(CMAKE_Fortran_POSTPROCESS_FLAG "-fpreprocessed")
diff --git a/Modules/Compiler/NVHPC-Fortran.cmake b/Modules/Compiler/NVHPC-Fortran.cmake
index 59755b3a71..5c0645737d 100644
--- a/Modules/Compiler/NVHPC-Fortran.cmake
+++ b/Modules/Compiler/NVHPC-Fortran.cmake
@@ -1,3 +1,4 @@
include(Compiler/PGI-Fortran)
include(Compiler/NVHPC)
__compiler_nvhpc(Fortran)
+set(CMAKE_Fortran_PREPROCESS_SOURCE_EXCLUDE_FLAGS_REGEX "(^| )-Werror +[a-z][a-z-]+( |$)")
diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake
index 0823954302..c839d1cbbb 100644
--- a/Modules/Compiler/NVIDIA-CUDA.cmake
+++ b/Modules/Compiler/NVIDIA-CUDA.cmake
@@ -8,6 +8,11 @@ set(_CMAKE_COMPILE_AS_CUDA_FLAG "-x cu")
set(_CMAKE_CUDA_WHOLE_FLAG "-c")
set(_CMAKE_CUDA_RDC_FLAG "-rdc=true")
set(_CMAKE_CUDA_PTX_FLAG "-ptx")
+set(_CMAKE_CUDA_CUBIN_FLAG "-cubin")
+set(_CMAKE_CUDA_FATBIN_FLAG "-fatbin")
+if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.7.0")
+ set(_CMAKE_CUDA_OPTIX_FLAG "-optix-ir")
+endif()
if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
# The -forward-unknown-to-host-compiler flag was only
diff --git a/Modules/Dart.cmake b/Modules/Dart.cmake
index 154fe9d7db..361001205e 100644
--- a/Modules/Dart.cmake
+++ b/Modules/Dart.cmake
@@ -5,6 +5,11 @@
Dart
----
+.. deprecated:: 3.27
+ This module is available only if policy :policy:`CMP0145`
+ is not set to ``NEW``. Do not use it in new code.
+ Use the :module:`CTest` module instead.
+
Configure a project for testing with CTest or old Dart Tcl Client
This file is the backwards-compatibility version of the CTest module.
@@ -33,10 +38,24 @@ whether testing support should be enabled. The default is ON.
#
#
+# include(Dart) already warns about CMP0145, but back when this module was in
+# common use, it was often loaded via include(${CMAKE_ROOT}/Modules/Dart.cmake)
+# which will not warn. Warn again just in case.
+cmake_policy(GET CMP0145 cmp0145)
+if(cmp0145 STREQUAL "")
+ cmake_policy(GET_WARNING CMP0145 _cmp0145_warning)
+ message(AUTHOR_WARNING "${_cmp0145_warning}")
+endif()
+
option(BUILD_TESTING "Build the testing tree." ON)
if(BUILD_TESTING)
+ # We only get here if a project already ran include(Dart),
+ # so avoid warning about CMP0145 again.
+ cmake_policy(PUSH)
+ cmake_policy(SET CMP0145 OLD)
find_package(Dart QUIET)
+ cmake_policy(POP)
#
# Section #1:
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index b34a35b23b..9a6cbd6bc4 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -2090,13 +2090,7 @@ function(_ep_get_configuration_subdir_genex suffix_var)
set(suffix "")
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(_isMultiConfig)
- if(CMAKE_GENERATOR STREQUAL "Xcode")
- # The Xcode generator does not support per-config sources,
- # so use the underlying build system's placeholder instead.
- set(suffix "/${CMAKE_CFG_INTDIR}")
- else()
- set(suffix "/$<CONFIG>")
- endif()
+ set(suffix "/$<CONFIG>")
endif()
set(${suffix_var} "${suffix}" PARENT_SCOPE)
endfunction()
diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in
index ff8c659082..bf7f209723 100644
--- a/Modules/ExternalProject/download.cmake.in
+++ b/Modules/ExternalProject/download.cmake.in
@@ -108,7 +108,7 @@ message(STATUS "Downloading...
timeout='@TIMEOUT_MSG@'
inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'"
)
-set(download_retry_codes 7 6 8 15)
+set(download_retry_codes 7 6 8 15 28)
set(skip_url_list)
set(status_code)
foreach(i RANGE ${retry_number})
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index 8afb9bce72..dd5f617acd 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -1536,7 +1536,9 @@ ExternalProject_Add_Step(${contentName}-populate copyfile
if(CMAKE_GENERATOR_TOOLSET)
list(APPEND subCMakeOpts "-T${CMAKE_GENERATOR_TOOLSET}")
endif()
-
+ if(CMAKE_GENERATOR_INSTANCE)
+ list(APPEND subCMakeOpts "-DCMAKE_GENERATOR_INSTANCE:INTERNAL=${CMAKE_GENERATOR_INSTANCE}")
+ endif()
if(CMAKE_MAKE_PROGRAM)
list(APPEND subCMakeOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}")
endif()
@@ -1596,7 +1598,9 @@ set_property(GLOBAL PROPERTY _CMAKE_FindGit_GIT_EXECUTABLE_VERSION
# has this set to something not findable on the PATH. We also ensured above
# that the Debug config will be defined for multi-config generators.
configure_file("${CMAKE_CURRENT_FUNCTION_LIST_DIR}/FetchContent/CMakeLists.cmake.in"
- "${ARG_SUBBUILD_DIR}/CMakeLists.txt")
+ "${ARG_SUBBUILD_DIR}/CMakeLists.txt"
+ @ONLY
+ )
execute_process(
COMMAND ${CMAKE_COMMAND} ${subCMakeOpts} .
RESULT_VARIABLE result
diff --git a/Modules/FetchContent/CMakeLists.cmake.in b/Modules/FetchContent/CMakeLists.cmake.in
index d94b0f415a..8adb533500 100644
--- a/Modules/FetchContent/CMakeLists.cmake.in
+++ b/Modules/FetchContent/CMakeLists.cmake.in
@@ -1,21 +1,27 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
-cmake_minimum_required(VERSION ${CMAKE_VERSION})
+cmake_minimum_required(VERSION @CMAKE_VERSION@)
+
+# Reject any attempt to use a toolchain file. We must not use one because
+# we could be downloading it here. If the CMAKE_TOOLCHAIN_FILE environment
+# variable is set, the cache variable will have been initialized from it.
+unset(CMAKE_TOOLCHAIN_FILE CACHE)
+unset(ENV{CMAKE_TOOLCHAIN_FILE})
# We name the project and the target for the ExternalProject_Add() call
# to something that will highlight to the user what we are working on if
# something goes wrong and an error message is produced.
-project(${contentName}-populate NONE)
+project(@contentName@-populate NONE)
@__FETCHCONTENT_CACHED_INFO@
include(ExternalProject)
-ExternalProject_Add(${contentName}-populate
- ${ARG_EXTRA}
- SOURCE_DIR "${ARG_SOURCE_DIR}"
- BINARY_DIR "${ARG_BINARY_DIR}"
+ExternalProject_Add(@contentName@-populate
+ @ARG_EXTRA@
+ SOURCE_DIR "@ARG_SOURCE_DIR@"
+ BINARY_DIR "@ARG_BINARY_DIR@"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake
index 7af1017006..19bef9471c 100644
--- a/Modules/FindBLAS.cmake
+++ b/Modules/FindBLAS.cmake
@@ -386,10 +386,10 @@ set(BLAS_LINKER_FLAGS)
set(BLAS_LIBRARIES)
set(BLAS95_LIBRARIES)
set(_blas_fphsa_req_var BLAS_LIBRARIES)
-if(NOT $ENV{BLA_VENDOR} STREQUAL "")
- set(BLA_VENDOR $ENV{BLA_VENDOR})
-else()
- if(NOT BLA_VENDOR)
+if(NOT BLA_VENDOR)
+ if(NOT "$ENV{BLA_VENDOR}" STREQUAL "")
+ set(BLA_VENDOR "$ENV{BLA_VENDOR}")
+ else()
set(BLA_VENDOR "All")
endif()
endif()
diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake
index c9281574ba..220b9ab2ae 100644
--- a/Modules/FindCUDA.cmake
+++ b/Modules/FindCUDA.cmake
@@ -2,7 +2,12 @@
FindCUDA
--------
-.. warning:: *Deprecated since version 3.10.*
+.. versionchanged:: 3.27
+ This module is available only if policy :policy:`CMP0146` is not set to ``NEW``.
+ Port projects to CMake's first-class ``CUDA`` language support.
+
+.. deprecated:: 3.10
+ Do not use this module in new code.
It is no longer necessary to use this module or call ``find_package(CUDA)``
for compiling CUDA code. Instead, list ``CUDA`` among the languages named
@@ -555,6 +560,23 @@ The script defines the following variables:
#
###############################################################################
+cmake_policy(GET CMP0146 _FindCUDA_CMP0146)
+if(_FindCUDA_CMP0146 STREQUAL "NEW")
+ message(FATAL_ERROR "The FindCUDA module has been removed by policy CMP0146.")
+endif()
+
+if(CMAKE_GENERATOR MATCHES "Visual Studio")
+ cmake_policy(GET CMP0147 _FindCUDA_CMP0147)
+ if(_FindCUDA_CMP0147 STREQUAL "NEW")
+ message(FATAL_ERROR "The FindCUDA module does not work in Visual Studio with policy CMP0147.")
+ endif()
+endif()
+
+if(_FindCUDA_testing)
+ set(_FindCUDA_included TRUE)
+ return()
+endif()
+
# FindCUDA.cmake
# This macro helps us find the location of helper files we will need the full path to
@@ -1052,6 +1074,7 @@ if(CUDA_USE_STATIC_CUDA_RUNTIME)
if(NOT APPLE AND NOT (CMAKE_SYSTEM_NAME STREQUAL "QNX"))
#On Linux, you must link against librt when using the static cuda runtime.
find_library(CUDA_rt_LIBRARY rt)
+ mark_as_advanced(CUDA_rt_LIBRARY)
if (NOT CUDA_rt_LIBRARY)
message(WARNING "Expecting to find librt for libcudart_static, but didn't find it.")
endif()
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index fcafe65b6e..4423ebb628 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -109,6 +109,7 @@ of the following libraries that are part of the CUDAToolkit:
- :ref:`CUDA Runtime Library<cuda_toolkit_rt_lib>`
- :ref:`CUDA Driver Library<cuda_toolkit_driver_lib>`
- :ref:`cuBLAS<cuda_toolkit_cuBLAS>`
+- :ref:`cuDLA<cuda_toolkit_cuDLA>`
- :ref:`cuFile<cuda_toolkit_cuFile>`
- :ref:`cuFFT<cuda_toolkit_cuFFT>`
- :ref:`cuRAND<cuda_toolkit_cuRAND>`
@@ -166,6 +167,19 @@ Targets Created:
- ``CUDA::cublasLt`` starting in CUDA 10.1
- ``CUDA::cublasLt_static`` starting in CUDA 10.1
+.. _`cuda_toolkit_cuDLA`:
+
+cuDLA
+""""""
+
+.. versionadded:: 3.27
+
+The NVIDIA Tegra Deep Learning Accelerator `cuDLA <https://docs.nvidia.com/cuda/cublas/index.html>`_ library.
+
+Targets Created:
+
+- ``CUDA::cudla`` starting in CUDA 11.6
+
.. _`cuda_toolkit_cuFile`:
cuFile
@@ -173,7 +187,7 @@ cuFile
.. versionadded:: 3.25
-The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/cuda/cufile-api/index.html>`_ library.
+The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/gpudirect-storage/api-reference-guide/index.html>`_ library.
Targets Created:
@@ -236,7 +250,7 @@ Targets Created:
cupti
"""""
-The `NVIDIA CUDA Profiling Tools Interface <https://developer.nvidia.com/CUPTI>`_.
+The `NVIDIA CUDA Profiling Tools Interface <https://developer.nvidia.com/cupti>`_.
Targets Created:
@@ -330,7 +344,7 @@ Targets Created:
nvGRAPH
"""""""
-The `nvGRAPH <https://docs.nvidia.com/cuda/nvgraph/index.html>`_ library.
+The `nvGRAPH <https://web.archive.org/web/20201111171403/https://docs.nvidia.com/cuda/nvgraph/index.html>`_ library.
Removed starting in CUDA 11.0
Targets Created:
@@ -417,7 +431,7 @@ nvToolsExt
.. deprecated:: 3.25 With CUDA 10.0+, use :ref:`nvtx3 <cuda_toolkit_nvtx3>`.
-The `NVIDIA Tools Extension <https://docs.nvidia.com/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm>`_.
+The `NVIDIA Tools Extension <https://docs.nvidia.com/nvtx/>`_.
This is a shared library only.
Targets Created:
@@ -609,8 +623,8 @@ else()
endif()
unset(_CUDA_NVCC_OUT)
- mark_as_advanced(CUDAToolkit_BIN_DIR)
set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
+ mark_as_advanced(CUDAToolkit_BIN_DIR)
endif()
if(CUDAToolkit_SENTINEL_FILE)
@@ -1054,6 +1068,11 @@ if(CUDAToolkit_FOUND)
_CUDAToolkit_find_and_add_import_lib(cuFile_rdma_static DEPS cuFile_static culibos)
endif()
+ if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.6)
+ _CUDAToolkit_find_and_add_import_lib(cudla)
+ endif()
+
+
# cuFFTW depends on cuFFT
_CUDAToolkit_find_and_add_import_lib(cufftw DEPS cufft)
_CUDAToolkit_find_and_add_import_lib(cufftw_static DEPS cufft_static)
@@ -1097,6 +1116,7 @@ if(CUDAToolkit_FOUND)
"${CUDAToolkit_INCLUDE_DIR}/../extras/CUPTI/include"
"${CUDAToolkit_INCLUDE_DIR}"
NO_DEFAULT_PATH)
+ mark_as_advanced(CUDAToolkit_CUPTI_INCLUDE_DIR)
if(CUDAToolkit_CUPTI_INCLUDE_DIR)
_CUDAToolkit_find_and_add_import_lib(cupti
diff --git a/Modules/FindDart.cmake b/Modules/FindDart.cmake
index 04925783b8..fed50e178c 100644
--- a/Modules/FindDart.cmake
+++ b/Modules/FindDart.cmake
@@ -5,12 +5,20 @@
FindDart
--------
+.. deprecated:: 3.27
+ This module is available only if policy :policy:`CMP0145` is not set to ``NEW``.
+
Find DART
This module looks for the dart testing software and sets DART_ROOT to
point to where it found it.
#]=======================================================================]
+if(_FindDart_testing)
+ set(_FindDart_included TRUE)
+ return()
+endif()
+
find_path(DART_ROOT README.INSTALL
HINTS
ENV DART_ROOT
diff --git a/Modules/FindEXPAT.cmake b/Modules/FindEXPAT.cmake
index f9cb432078..3bedc73cfc 100644
--- a/Modules/FindEXPAT.cmake
+++ b/Modules/FindEXPAT.cmake
@@ -39,27 +39,62 @@ pkg_check_modules(PC_EXPAT QUIET expat)
# Look for the header file.
find_path(EXPAT_INCLUDE_DIR NAMES expat.h HINTS ${PC_EXPAT_INCLUDE_DIRS})
-# Look for the library.
-find_library(EXPAT_LIBRARY NAMES expat libexpat NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS})
-
-if (EXPAT_INCLUDE_DIR AND EXISTS "${EXPAT_INCLUDE_DIR}/expat.h")
- file(STRINGS "${EXPAT_INCLUDE_DIR}/expat.h" expat_version_str
- REGEX "^#[\t ]*define[\t ]+XML_(MAJOR|MINOR|MICRO)_VERSION[\t ]+[0-9]+$")
-
- unset(EXPAT_VERSION_STRING)
- foreach(VPART MAJOR MINOR MICRO)
- foreach(VLINE ${expat_version_str})
- if(VLINE MATCHES "^#[\t ]*define[\t ]+XML_${VPART}_VERSION[\t ]+([0-9]+)$")
- set(EXPAT_VERSION_PART "${CMAKE_MATCH_1}")
- if(EXPAT_VERSION_STRING)
- string(APPEND EXPAT_VERSION_STRING ".${EXPAT_VERSION_PART}")
- else()
- set(EXPAT_VERSION_STRING "${EXPAT_VERSION_PART}")
- endif()
- endif()
- endforeach()
+set(EXPAT_NAMES expat expatw)
+set(EXPAT_NAMES_DEBUG expatd expatwd)
+
+if(WIN32)
+ list(APPEND EXPAT_NAMES expatMT expatMD expatwMT expatwMD)
+ list(APPEND EXPAT_NAMES_DEBUG expatdMT expatdMD expatwdMT expatwdMD)
+endif()
+
+# Allow EXPAT_LIBRARY to be set manually, as the location of the expat library
+if(NOT EXPAT_LIBRARY)
+ if(DEFINED CMAKE_FIND_LIBRARY_PREFIXES)
+ set(_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}")
+ else()
+ set(_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES)
+ endif()
+
+ if(WIN32)
+ list(APPEND CMAKE_FIND_LIBRARY_PREFIXES "" "lib")
+ endif()
+
+ # Look for the library.
+ find_library(EXPAT_LIBRARY_RELEASE NAMES ${EXPAT_NAMES} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib)
+ find_library(EXPAT_LIBRARY_DEBUG NAMES ${EXPAT_NAMES_DEBUG} NAMES_PER_DIR HINTS ${PC_EXPAT_LIBRARY_DIRS} PATH_SUFFIXES lib)
+
+ # Restore the original find library ordering
+ if(DEFINED _expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES)
+ set(CMAKE_FIND_LIBRARY_PREFIXES "${_expat_ORIG_CMAKE_FIND_LIBRARY_PREFIXES}")
+ else()
+ set(CMAKE_FIND_LIBRARY_PREFIXES)
+ endif()
+
+ include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
+ select_library_configurations(EXPAT)
+endif()
+
+unset(EXPAT_NAMES)
+unset(EXPAT_NAMES_DEBUG)
+
+if(EXPAT_INCLUDE_DIR AND EXISTS "${EXPAT_INCLUDE_DIR}/expat.h")
+ file(STRINGS "${EXPAT_INCLUDE_DIR}/expat.h" expat_version_str
+ REGEX "^#[\t ]*define[\t ]+XML_(MAJOR|MINOR|MICRO)_VERSION[\t ]+[0-9]+$")
+
+ unset(EXPAT_VERSION_STRING)
+ foreach(VPART MAJOR MINOR MICRO)
+ foreach(VLINE ${expat_version_str})
+ if(VLINE MATCHES "^#[\t ]*define[\t ]+XML_${VPART}_VERSION[\t ]+([0-9]+)$")
+ set(EXPAT_VERSION_PART "${CMAKE_MATCH_1}")
+ if(EXPAT_VERSION_STRING)
+ string(APPEND EXPAT_VERSION_STRING ".${EXPAT_VERSION_PART}")
+ else()
+ set(EXPAT_VERSION_STRING "${EXPAT_VERSION_PART}")
+ endif()
+ endif()
endforeach()
-endif ()
+ endforeach()
+endif()
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(EXPAT
@@ -68,15 +103,36 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(EXPAT
# Copy the results to the output variables and target.
if(EXPAT_FOUND)
- set(EXPAT_LIBRARIES ${EXPAT_LIBRARY})
set(EXPAT_INCLUDE_DIRS ${EXPAT_INCLUDE_DIR})
+ if(NOT EXPAT_LIBRARIES)
+ set(EXPAT_LIBRARIES ${EXPAT_LIBRARY})
+ endif()
+
if(NOT TARGET EXPAT::EXPAT)
add_library(EXPAT::EXPAT UNKNOWN IMPORTED)
set_target_properties(EXPAT::EXPAT PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
- IMPORTED_LOCATION "${EXPAT_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${EXPAT_INCLUDE_DIRS}")
+
+ if(EXPAT_LIBRARY_RELEASE)
+ set_property(TARGET EXPAT::EXPAT APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS RELEASE)
+ set_target_properties(EXPAT::EXPAT PROPERTIES
+ IMPORTED_LOCATION_RELEASE "${EXPAT_LIBRARY_RELEASE}")
+ endif()
+
+ if(EXPAT_LIBRARY_DEBUG)
+ set_property(TARGET EXPAT::EXPAT APPEND PROPERTY
+ IMPORTED_CONFIGURATIONS DEBUG)
+ set_target_properties(EXPAT::EXPAT PROPERTIES
+ IMPORTED_LOCATION_DEBUG "${EXPAT_LIBRARY_DEBUG}")
+ endif()
+
+ if(NOT EXPAT_LIBRARY_RELEASE AND NOT EXPAT_LIBRARY_DEBUG)
+ set_property(TARGET EXPAT::EXPAT APPEND PROPERTY
+ IMPORTED_LOCATION "${EXPAT_LIBRARY}")
+ endif()
endif()
endif()
diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake
index 6fab08ffb2..a44c6f99b9 100644
--- a/Modules/FindHDF5.cmake
+++ b/Modules/FindHDF5.cmake
@@ -219,7 +219,7 @@ endif()
function(_HDF5_test_regular_compiler_C success version is_parallel)
if(NOT ${success} OR
NOT EXISTS ${_HDF5_TEST_DIR}/compiler_has_h5_c)
- file(WRITE "${_HDF5_TEST_SRC}"
+ file(WRITE "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}"
"#include <hdf5.h>\n"
"const char* info_ver = \"INFO\" \":\" H5_VERSION;\n"
"#ifdef H5_HAVE_PARALLEL\n"
@@ -235,7 +235,7 @@ function(_HDF5_test_regular_compiler_C success version is_parallel)
" fid = H5Fcreate(\"foo.h5\",H5F_ACC_TRUNC,H5P_DEFAULT,H5P_DEFAULT);\n"
" return 0;\n"
"}")
- try_compile(${success} SOURCES "${_HDF5_TEST_SRC}"
+ try_compile(${success} SOURCES "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}"
COPY_FILE ${_HDF5_TEST_DIR}/compiler_has_h5_c
)
endif()
@@ -263,7 +263,7 @@ endfunction()
function(_HDF5_test_regular_compiler_CXX success version is_parallel)
if(NOT ${success} OR
NOT EXISTS ${_HDF5_TEST_DIR}/compiler_has_h5_cxx)
- file(WRITE "${_HDF5_TEST_SRC}"
+ file(WRITE "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}"
"#include <H5Cpp.h>\n"
"#ifndef H5_NO_NAMESPACE\n"
"using namespace H5;\n"
@@ -281,7 +281,7 @@ function(_HDF5_test_regular_compiler_CXX success version is_parallel)
" H5File file(\"foo.h5\", H5F_ACC_TRUNC);\n"
" return 0;\n"
"}")
- try_compile(${success} SOURCES "${_HDF5_TEST_SRC}"
+ try_compile(${success} SOURCES "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}"
COPY_FILE ${_HDF5_TEST_DIR}/compiler_has_h5_cxx
)
endif()
@@ -308,22 +308,29 @@ endfunction()
function(_HDF5_test_regular_compiler_Fortran success is_parallel)
if(NOT ${success})
- file(WRITE "${_HDF5_TEST_SRC}"
+ file(WRITE "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}"
"program hdf5_hello\n"
" use hdf5\n"
" integer error\n"
" call h5open_f(error)\n"
" call h5close_f(error)\n"
"end\n")
- try_compile(${success} SOURCES "${_HDF5_TEST_SRC}")
+ try_compile(${success} SOURCES "${_HDF5_TEST_DIR}/${_HDF5_TEST_SRC}")
if(${success})
execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -showconfig
OUTPUT_VARIABLE config_output
ERROR_VARIABLE config_error
RESULT_VARIABLE config_result
)
- if(config_output MATCHES "Parallel HDF5: yes")
- set(${is_parallel} TRUE PARENT_SCOPE)
+ if(config_output MATCHES "Parallel HDF5: ([A-Za-z0-9]+)")
+ # The value may be anything used when HDF5 was configured,
+ # so see if CMake interprets it as "true".
+ set(parallelHDF5 "${CMAKE_MATCH_1}")
+ if(parallelHDF5)
+ set(${is_parallel} TRUE PARENT_SCOPE)
+ else()
+ set(${is_parallel} FALSE PARENT_SCOPE)
+ endif()
else()
set(${is_parallel} FALSE PARENT_SCOPE)
endif()
@@ -349,9 +356,13 @@ function( _HDF5_invoke_compiler language output_var return_value_var version_var
ERROR_VARIABLE output
RESULT_VARIABLE return_value
)
- if(return_value AND NOT HDF5_FIND_QUIETLY)
- message(STATUS
- "HDF5 ${language} compiler wrapper is unable to compile a minimal HDF5 program.")
+ if(NOT return_value EQUAL 0)
+ message(CONFIGURE_LOG
+ "HDF5 ${language} compiler wrapper is unable to compile a minimal HDF5 program.\n\n${output}")
+ if(NOT HDF5_FIND_QUIETLY)
+ message(STATUS
+ "HDF5 ${language} compiler wrapper is unable to compile a minimal HDF5 program.")
+ endif()
else()
execute_process(
COMMAND ${HDF5_${language}_COMPILER_EXECUTABLE} -show ${lib_type_args} "${_HDF5_TEST_SRC}"
@@ -361,9 +372,13 @@ function( _HDF5_invoke_compiler language output_var return_value_var version_var
RESULT_VARIABLE return_value
OUTPUT_STRIP_TRAILING_WHITESPACE
)
- if(return_value AND NOT HDF5_FIND_QUIETLY)
- message(STATUS
- "Unable to determine HDF5 ${language} flags from HDF5 wrapper.")
+ if(NOT return_value EQUAL 0)
+ message(CONFIGURE_LOG
+ "Unable to determine HDF5 ${language} flags from HDF5 wrapper.\n\n${output}")
+ if(NOT HDF5_FIND_QUIETLY)
+ message(STATUS
+ "Unable to determine HDF5 ${language} flags from HDF5 wrapper.")
+ endif()
endif()
execute_process(
COMMAND ${HDF5_${language}_COMPILER_EXECUTABLE} -showconfig
@@ -372,17 +387,26 @@ function( _HDF5_invoke_compiler language output_var return_value_var version_var
RESULT_VARIABLE return_value
OUTPUT_STRIP_TRAILING_WHITESPACE
)
- if(return_value AND NOT HDF5_FIND_QUIETLY)
- message(STATUS
- "Unable to determine HDF5 ${language} version_var from HDF5 wrapper.")
+ if(NOT return_value EQUAL 0)
+ message(CONFIGURE_LOG
+ "Unable to determine HDF5 ${language} version_var from HDF5 wrapper.\n\n${output}")
+ if(NOT HDF5_FIND_QUIETLY)
+ message(STATUS
+ "Unable to determine HDF5 ${language} version_var from HDF5 wrapper.")
+ endif()
endif()
string(REGEX MATCH "HDF5 Version: ([a-zA-Z0-9\\.\\-]*)" version "${config_output}")
if(version)
string(REPLACE "HDF5 Version: " "" version "${version}")
string(REPLACE "-patch" "." version "${version}")
endif()
- if(config_output MATCHES "Parallel HDF5: yes")
- set(is_parallel TRUE)
+ if(config_output MATCHES "Parallel HDF5: ([A-Za-z0-9]+)")
+ # The value may be anything used when HDF5 was configured,
+ # so see if CMake interprets it as "true".
+ set(parallelHDF5 "${CMAKE_MATCH_1}")
+ if(parallelHDF5)
+ set(is_parallel TRUE)
+ endif()
endif()
endif()
foreach(var output return_value version is_parallel)
@@ -576,23 +600,23 @@ if(NOT HDF5_FOUND)
# First check to see if our regular compiler is one of wrappers
if(_lang STREQUAL "C")
- set(_HDF5_TEST_SRC ${_HDF5_TEST_DIR}/cmake_hdf5_test.c)
+ set(_HDF5_TEST_SRC cmake_hdf5_test.c)
if(CMAKE_CXX_COMPILER_LOADED AND NOT CMAKE_C_COMPILER_LOADED)
# CXX project without C enabled
- set(_HDF5_TEST_SRC ${_HDF5_TEST_DIR}/cmake_hdf5_test.cxx)
+ set(_HDF5_TEST_SRC cmake_hdf5_test.cxx)
endif()
_HDF5_test_regular_compiler_C(
HDF5_${_lang}_COMPILER_NO_INTERROGATE
HDF5_${_lang}_VERSION
HDF5_${_lang}_IS_PARALLEL)
elseif(_lang STREQUAL "CXX")
- set(_HDF5_TEST_SRC ${_HDF5_TEST_DIR}/cmake_hdf5_test.cxx)
+ set(_HDF5_TEST_SRC cmake_hdf5_test.cxx)
_HDF5_test_regular_compiler_CXX(
HDF5_${_lang}_COMPILER_NO_INTERROGATE
HDF5_${_lang}_VERSION
HDF5_${_lang}_IS_PARALLEL)
elseif(_lang STREQUAL "Fortran")
- set(_HDF5_TEST_SRC ${_HDF5_TEST_DIR}/cmake_hdf5_test.f90)
+ set(_HDF5_TEST_SRC cmake_hdf5_test.f90)
_HDF5_test_regular_compiler_Fortran(
HDF5_${_lang}_COMPILER_NO_INTERROGATE
HDF5_${_lang}_IS_PARALLEL)
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake
index 1eecb1cbd2..17117bd871 100644
--- a/Modules/FindLAPACK.cmake
+++ b/Modules/FindLAPACK.cmake
@@ -304,10 +304,12 @@ endif()
# Search for different LAPACK distributions if BLAS is found
if(NOT LAPACK_NOT_FOUND_MESSAGE)
set(LAPACK_LINKER_FLAGS ${BLAS_LINKER_FLAGS})
- if(NOT $ENV{BLA_VENDOR} STREQUAL "")
- set(BLA_VENDOR $ENV{BLA_VENDOR})
- elseif(NOT BLA_VENDOR)
- set(BLA_VENDOR "All")
+ if(NOT BLA_VENDOR)
+ if(NOT "$ENV{BLA_VENDOR}" STREQUAL "")
+ set(BLA_VENDOR "$ENV{BLA_VENDOR}")
+ else()
+ set(BLA_VENDOR "All")
+ endif()
endif()
# LAPACK in the Intel MKL 10+ library?
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake
index 34b1c5bf01..e111b790b4 100644
--- a/Modules/FindMatlab.cmake
+++ b/Modules/FindMatlab.cmake
@@ -137,6 +137,12 @@ Result variables
``Matlab_FOUND``
``TRUE`` if the Matlab installation is found, ``FALSE``
otherwise. All variable below are defined if Matlab is found.
+``Matlab_VERSION``
+ .. versionadded:: 3.27
+
+ the numerical version (e.g. 9.13) of Matlab found. Not to be confused with
+ Matlab release name (e.g. R2022b) that can be obtained with
+ :command:`matlab_get_release_name_from_version`.
``Matlab_ROOT_DIR``
the final root of the Matlab installation determined by the FindMatlab
module.
@@ -337,7 +343,14 @@ endif()
#[=======================================================================[.rst:
.. command:: matlab_get_version_from_release_name
- Returns the version of Matlab (17.58) from a release name (R2017k)
+ .. code-block:: cmake
+
+ matlab_get_version_from_release_name(release version)
+
+ * Input: ``release`` is the release name (R2022b)
+ * Output: ``version`` is the version of Matlab (9.13)
+
+ Returns the version of Matlab from a release name
#]=======================================================================]
macro(matlab_get_version_from_release_name release_name version_name)
@@ -354,13 +367,17 @@ macro(matlab_get_version_from_release_name release_name version_name)
endmacro()
+#[=======================================================================[.rst:
+.. command:: matlab_get_release_name_from_version
+ .. code-block:: cmake
+ matlab_get_release_name_from_version(version release_name)
-#[=======================================================================[.rst:
-.. command:: matlab_get_release_name_from_version
+ * Input: ``version`` is the version of Matlab (9.13)
+ * Output: ``release_name`` is the release name (R2022b)
- Returns the release name (R2017k) from the version of Matlab (17.58)
+ Returns the release name from the version of Matlab
#]=======================================================================]
macro(matlab_get_release_name_from_version version release_name)
@@ -371,7 +388,7 @@ macro(matlab_get_release_name_from_version version release_name)
set(${release_name} ${CMAKE_MATCH_1})
break()
endif()
- endforeach(_var)
+ endforeach()
unset(_var)
unset(_matched)
@@ -382,10 +399,7 @@ macro(matlab_get_release_name_from_version version release_name)
endmacro()
-
-
-
-# extracts all the supported release names (R2017k...) of Matlab
+# extracts all the supported release names (R2022b...) of Matlab
# internal use
macro(matlab_get_supported_releases list_releases)
set(${list_releases})
@@ -396,7 +410,7 @@ macro(matlab_get_supported_releases list_releases)
endif()
unset(_matched)
unset(CMAKE_MATCH_1)
- endforeach(_var)
+ endforeach()
unset(_var)
endmacro()
@@ -413,7 +427,7 @@ macro(matlab_get_supported_versions list_versions)
endif()
unset(_matched)
unset(CMAKE_MATCH_1)
- endforeach(_var)
+ endforeach()
unset(_var)
endmacro()
@@ -421,8 +435,15 @@ endmacro()
#[=======================================================================[.rst:
.. command:: matlab_extract_all_installed_versions_from_registry
- This function parses the registry and founds the Matlab versions that are
- installed. The found versions are returned in `matlab_versions`.
+ .. code-block:: cmake
+
+ matlab_extract_all_installed_versions_from_registry(win64 matlab_versions)
+
+ * Input: ``win64`` is a boolean to search for the 64 bit version of Matlab
+ * Output: ``matlab_versions`` is a list of all the versions of Matlab found
+
+ This function parses the Windows registry and founds the Matlab versions that
+ are installed. The found versions are returned in `matlab_versions`.
Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for
The returned list contains all versions under
``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` and
@@ -505,31 +526,6 @@ macro(extract_matlab_versions_from_registry_brute_force matlab_versions)
set(matlab_supported_versions)
matlab_get_supported_versions(matlab_supported_versions)
-
- # this is a manual population of the versions we want to look for
- # this can be done as is, but preferably with the call to
- # matlab_get_supported_versions and variable
-
- # populating the versions we want to look for
- # set(matlab_supported_versions)
-
- # # Matlab 7
- # set(matlab_major 7)
- # foreach(current_matlab_minor RANGE 4 20)
- # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
- # endforeach(current_matlab_minor)
-
- # # Matlab 8
- # set(matlab_major 8)
- # foreach(current_matlab_minor RANGE 0 5)
- # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
- # endforeach(current_matlab_minor)
-
- # # taking into account the possible additional versions provided by the user
- # if(DEFINED MATLAB_ADDITIONAL_VERSIONS)
- # list(APPEND matlab_supported_versions MATLAB_ADDITIONAL_VERSIONS)
- # endif()
-
# we order from more recent to older
if(matlab_supported_versions)
list(REMOVE_DUPLICATES matlab_supported_versions)
@@ -541,8 +537,6 @@ macro(extract_matlab_versions_from_registry_brute_force matlab_versions)
endmacro()
-
-
#[=======================================================================[.rst:
.. command:: matlab_get_all_valid_matlab_roots_from_registry
@@ -552,16 +546,12 @@ endmacro()
``(type,version_number,matlab_root_path)``, where ``type``
indicates either ``MATLAB`` or ``MCR``.
- ::
+ .. code-block:: cmake
- matlab_get_all_valid_matlab_roots_from_registry(
- matlab_versions
- matlab_roots)
+ matlab_get_all_valid_matlab_roots_from_registry(matlab_versions matlab_roots)
- ``matlab_versions``
- the versions of each of the Matlab or MCR installations
- ``matlab_roots``
- the location of each of the Matlab or MCR installations
+ * Input: ``matlab_versions`` of each of the Matlab or MCR installations
+ * Output: ``matlab_roots`` location of each of the Matlab or MCR installations
#]=======================================================================]
function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots)
@@ -571,7 +561,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_
set(_matlab_roots_list )
# check for Matlab installations
- foreach(_matlab_current_version ${matlab_versions})
+ foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component(
current_MATLAB_ROOT
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]"
@@ -584,7 +574,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_
endforeach()
# Check for MCR installations
- foreach(_matlab_current_version ${matlab_versions})
+ foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component(
current_MATLAB_ROOT
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Runtime\\${_matlab_current_version};MATLABROOT]"
@@ -600,7 +590,7 @@ function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_
endforeach()
# Check for old MCR installations
- foreach(_matlab_current_version ${matlab_versions})
+ foreach(_matlab_current_version IN LISTS matlab_versions)
get_filename_component(
current_MATLAB_ROOT
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Compiler Runtime\\${_matlab_current_version};MATLABROOT]"
@@ -624,16 +614,12 @@ endfunction()
This function should not be called before the appropriate Matlab root has
been found.
- ::
+ .. code-block:: cmake
- matlab_get_mex_suffix(
- matlab_root
- mex_suffix)
+ matlab_get_mex_suffix(matlab_root mex_suffix)
- ``matlab_root``
- the root of the Matlab/MCR installation
- ``mex_suffix``
- the variable name in which the suffix will be returned.
+ * Input: ``matlab_root`` root of Matlab/MCR install e.g. ``Matlab_ROOT_DIR``
+ * Output: ``mex_suffix`` variable name in which the suffix will be returned.
#]=======================================================================]
function(matlab_get_mex_suffix matlab_root mex_suffix)
@@ -711,8 +697,6 @@ function(matlab_get_mex_suffix matlab_root mex_suffix)
endfunction()
-
-
#[=======================================================================[.rst:
.. command:: matlab_get_version_from_matlab_run
@@ -720,16 +704,12 @@ endfunction()
version. If the path provided for the Matlab installation points to an MCR
installation, the version is extracted from the installed files.
- ::
+ .. code-block:: cmake
- matlab_get_version_from_matlab_run(
- matlab_binary_path
- matlab_list_versions)
+ matlab_get_version_from_matlab_run(matlab_binary_path matlab_list_versions)
- ``matlab_binary_path``
- the location of the `matlab` binary executable
- ``matlab_list_versions``
- the version extracted from Matlab
+ * Input: ``matlab_binary_path`` path of the `matlab` binary executable
+ * Output: ``matlab_list_versions`` the version extracted from Matlab
#]=======================================================================]
function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_versions)
@@ -899,7 +879,7 @@ endfunction()
non 0 failure). Additional arguments accepted by :command:`add_test` can be
passed through ``TEST_ARGS`` (eg. ``CONFIGURATION <config> ...``).
- ::
+ .. code-block:: cmake
matlab_add_unit_test(
NAME <name>
@@ -913,7 +893,7 @@ endfunction()
[NO_UNITTEST_FRAMEWORK]
)
- The function arguments are:
+ Function Parameters:
``NAME``
name of the unittest in ctest.
@@ -1011,7 +991,7 @@ endfunction()
for the MEX file. Remaining arguments of the call are passed to the
:command:`add_library` or :command:`add_executable` command.
- ::
+ .. code-block:: cmake
matlab_add_mex(
NAME <name>
@@ -1026,6 +1006,8 @@ endfunction()
[...]
)
+ Function Parameters:
+
``NAME``
name of the target.
``SRC``
@@ -1210,18 +1192,17 @@ function(matlab_add_mex)
if (MSVC)
- set(_link_flags "${_link_flags} /EXPORT:mexFunction")
+ string(APPEND _link_flags " /EXPORT:mexFunction")
if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version
- set(_link_flags "${_link_flags} /EXPORT:mexfilerequiredapiversion")
+ string(APPEND _link_flags " /EXPORT:mexfilerequiredapiversion")
endif()
set_property(TARGET ${${prefix}_NAME} APPEND PROPERTY LINK_FLAGS ${_link_flags})
endif() # No other compiler currently supported on Windows.
- set_target_properties(${${prefix}_NAME}
- PROPERTIES
- DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)")
+ set_property(TARGET ${${prefix}_NAME} PROPERTY
+ DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)")
else()
@@ -1247,7 +1228,7 @@ function(matlab_add_mex)
if(Matlab_HAS_CPP_API)
list(APPEND _ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/cppMexFunction.map) # This one doesn't exist on Linux
- set(_link_flags "${_link_flags} -Wl,-U,_mexCreateMexFunction -Wl,-U,_mexDestroyMexFunction -Wl,-U,_mexFunctionAdapter")
+ string(APPEND _link_flags " -Wl,-U,_mexCreateMexFunction -Wl,-U,_mexDestroyMexFunction -Wl,-U,_mexFunctionAdapter")
# On MacOS, the MEX command adds the above, without it the link breaks
# because we indiscriminately use "cppMexFunction.map" even for C API MEX-files.
endif()
@@ -1262,14 +1243,14 @@ function(matlab_add_mex)
target_compile_options(${${prefix}_NAME} PRIVATE "-pthread")
endif()
- set(_link_flags "${_link_flags} -Wl,--as-needed")
+ string(APPEND _link_flags " -Wl,--as-needed")
set(_export_flag_name --version-script)
endif()
- foreach(_file ${_ver_map_files})
- set(_link_flags "${_link_flags} -Wl,${_export_flag_name},${_file}")
+ foreach(_file IN LISTS _ver_map_files)
+ string(APPEND _link_flags " -Wl,${_export_flag_name},${_file}")
endforeach()
# The `mex` command doesn't add this define. It is specified here in order
@@ -2021,11 +2002,13 @@ _Matlab_add_imported_target(MAT mat)
_Matlab_add_imported_target(ENGINE MatlabEngine)
_Matlab_add_imported_target(DATAARRAY MatlabDataArray)
+set(Matlab_VERSION ${Matlab_VERSION_STRING})
+
find_package_handle_standard_args(
Matlab
FOUND_VAR Matlab_FOUND
REQUIRED_VARS ${_matlab_required_variables}
- VERSION_VAR Matlab_VERSION_STRING
+ VERSION_VAR Matlab_VERSION
HANDLE_COMPONENTS)
unset(_matlab_required_variables)
diff --git a/Modules/FindOpenAL.cmake b/Modules/FindOpenAL.cmake
index 53aafdc0c3..3d585694de 100644
--- a/Modules/FindOpenAL.cmake
+++ b/Modules/FindOpenAL.cmake
@@ -105,18 +105,16 @@ find_package_handle_standard_args(
mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
-if(OPENAL_INCLUDE_DIR AND OPENAL_LIBRARY)
- if(NOT TARGET OpenAL::OpenAL)
- if(EXISTS "${OPENAL_LIBRARY}")
- add_library(OpenAL::OpenAL UNKNOWN IMPORTED)
- set_target_properties(OpenAL::OpenAL PROPERTIES
- IMPORTED_LOCATION "${OPENAL_LIBRARY}")
- else()
- add_library(OpenAL::OpenAL INTERFACE IMPORTED)
- set_target_properties(OpenAL::OpenAL PROPERTIES
- IMPORTED_LIBNAME "${OPENAL_LIBRARY}")
- endif()
+if(OPENAL_FOUND AND NOT TARGET OpenAL::OpenAL)
+ if(OPENAL_LIBRARY MATCHES "/([^/]+)\\.framework$")
+ add_library(OpenAL::OpenAL INTERFACE IMPORTED)
set_target_properties(OpenAL::OpenAL PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}")
+ INTERFACE_LINK_LIBRARIES "${OPENAL_LIBRARY}")
+ else()
+ add_library(OpenAL::OpenAL UNKNOWN IMPORTED)
+ set_target_properties(OpenAL::OpenAL PROPERTIES
+ IMPORTED_LOCATION "${OPENAL_LIBRARY}")
endif()
+ set_target_properties(OpenAL::OpenAL PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}")
endif()
diff --git a/Modules/FindOpenCL.cmake b/Modules/FindOpenCL.cmake
index 2b700ffd37..55be667a86 100644
--- a/Modules/FindOpenCL.cmake
+++ b/Modules/FindOpenCL.cmake
@@ -39,6 +39,8 @@ The module will also define two cache variables::
#]=======================================================================]
+set(_OPENCL_x86 "(x86)")
+
function(_FIND_OPENCL_VERSION)
include(CheckSymbolExists)
include(CMakePushCheckState)
@@ -79,6 +81,9 @@ find_path(OpenCL_INCLUDE_DIR
CL/cl.h OpenCL/cl.h
PATHS
ENV "PROGRAMFILES(X86)"
+ ENV "PROGRAMFILES"
+ $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCLHeaders
+ $ENV{PROGRAMFILES}/OpenCLHeaders
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV NVSDKCOMPUTE_ROOT
@@ -100,6 +105,9 @@ if(WIN32)
NAMES OpenCL
PATHS
ENV "PROGRAMFILES(X86)"
+ ENV "PROGRAMFILES"
+ $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCL-ICD-Loader
+ $ENV{PROGRAMFILES}/OpenCL-ICD-Loader
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV CUDA_PATH
@@ -116,6 +124,9 @@ if(WIN32)
NAMES OpenCL
PATHS
ENV "PROGRAMFILES(X86)"
+ ENV "PROGRAMFILES"
+ $ENV{PROGRAMFILES${_OPENCL_x86}}/OpenCL-ICD-Loader
+ $ENV{PROGRAMFILES}/OpenCL-ICD-Loader
ENV AMDAPPSDKROOT
ENV INTELOCLSDKROOT
ENV CUDA_PATH
@@ -126,6 +137,7 @@ if(WIN32)
"AMD APP/lib/x86_64"
lib/x86_64
lib/x64
+ lib
OpenCL/common/lib/x64)
endif()
else()
@@ -156,6 +168,8 @@ else()
endif()
endif()
+unset(_OPENCL_x86)
+
set(OpenCL_LIBRARIES ${OpenCL_LIBRARY})
set(OpenCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIR})
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake
index a9a1b2aec2..a7736015ad 100644
--- a/Modules/FindOpenGL.cmake
+++ b/Modules/FindOpenGL.cmake
@@ -18,8 +18,26 @@ Optional COMPONENTS
.. versionadded:: 3.10
-This module respects several optional COMPONENTS: ``EGL``, ``GLX``, and
-``OpenGL``. There are corresponding import targets for each of these flags.
+This module respects several optional COMPONENTS:
+
+``EGL``
+ The EGL interface between OpenGL, OpenGL ES and the underlying windowing system.
+
+``GLX``
+ An extension to X that interfaces OpenGL, OpenGL ES with X window system.
+
+``OpenGL``
+ The cross platform API for 3D graphics.
+
+``GLES2``
+ .. versionadded:: 3.27
+
+ A subset of OpenGL API for embedded systems with limited capabilities.
+
+``GLES3``
+ .. versionadded:: 3.27
+
+ A subset of OpenGL API for embedded systems with more capabilities.
IMPORTED Targets
^^^^^^^^^^^^^^^^
@@ -42,6 +60,14 @@ This module defines the :prop_tgt:`IMPORTED` targets:
Defined if the system has OpenGL Extension to the X Window System (GLX).
``OpenGL::EGL``
Defined if the system has EGL.
+``OpenGL::GLES2``
+ .. versionadded:: 3.27
+
+ Defined if the system has GLES2.
+``OpenGL::GLES3``
+ .. versionadded:: 3.27
+
+ Defined if the system has GLES3.
Result Variables
^^^^^^^^^^^^^^^^
@@ -60,6 +86,10 @@ This module sets the following variables:
True, if the system has GLX.
``OpenGL_EGL_FOUND``
True, if the system has EGL.
+``OpenGL::GLES2``
+ Defined if the system has GLES2.
+``OpenGL::GLES3``
+ Defined if the system has GLES3.
``OPENGL_INCLUDE_DIR``
Path to the OpenGL include directory.
``OPENGL_EGL_INCLUDE_DIRS``
@@ -88,6 +118,14 @@ The following cache variables may also be set:
``OPENGL_gl_LIBRARY``
Path to the OpenGL library. New code should prefer the ``OpenGL::*`` import
targets.
+``OPENGL_gles2_LIBRARY``
+ .. versionadded:: 3.27
+
+ Path to the OpenGL GLES2 library.
+``OPENGL_gles3_LIBRARY``
+ .. versionadded:: 3.27
+
+ Path to the OpenGL GLES3 library.
.. versionadded:: 3.10
Variables for GLVND-specific libraries ``OpenGL``, ``EGL`` and ``GLX``.
@@ -182,7 +220,10 @@ elseif (APPLE)
OPENGL_glu_LIBRARY
)
else()
- if (CMAKE_SYSTEM_NAME MATCHES "HP-UX")
+ if (CMAKE_ANDROID_NDK)
+ set(_OPENGL_INCLUDE_PATH ${CMAKE_ANDROID_NDK}/sysroot/usr/include)
+ set(_OPENGL_LIB_PATH ${CMAKE_ANDROID_NDK}/platforms/android-${CMAKE_SYSTEM_VERSION}/arch-${CMAKE_ANDROID_ARCH}/usr/lib)
+ elseif (CMAKE_SYSTEM_NAME MATCHES "HP-UX")
# Handle HP-UX cases where we only want to find OpenGL in either hpux64
# or hpux32 depending on if we're doing a 64 bit build.
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
@@ -198,6 +239,13 @@ else()
/boot/develop/lib/x86)
set(_OPENGL_INCLUDE_PATH
/boot/develop/headers/os/opengl)
+ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ # CMake doesn't support arbitrary globs in search paths.
+ file(GLOB _OPENGL_LIB_PATH
+ # The NVidia driver installation tool on Linux installs libraries to a
+ # `nvidia-<version>` subdirectory.
+ "/usr/lib/nvidia-*"
+ "/usr/lib32/nvidia-*")
endif()
# The first line below is to make sure that the proper headers
@@ -215,15 +263,20 @@ else()
)
find_path(OPENGL_GLX_INCLUDE_DIR GL/glx.h ${_OPENGL_INCLUDE_PATH})
find_path(OPENGL_EGL_INCLUDE_DIR EGL/egl.h ${_OPENGL_INCLUDE_PATH})
+ find_path(OPENGL_GLES2_INCLUDE_DIR GLES2/gl2.h ${_OPENGL_INCLUDE_PATH})
+ find_path(OPENGL_GLES3_INCLUDE_DIR GLES3/gl3.h ${_OPENGL_INCLUDE_PATH})
find_path(OPENGL_xmesa_INCLUDE_DIR GL/xmesa.h
/usr/share/doc/NVIDIA_GLX-1.0/include
/usr/openwin/share/include
/opt/graphics/OpenGL/include
)
+
list(APPEND _OpenGL_CACHE_VARS
OPENGL_INCLUDE_DIR
OPENGL_GLX_INCLUDE_DIR
OPENGL_EGL_INCLUDE_DIR
+ OPENGL_GLES2_INCLUDE_DIR
+ OPENGL_GLES3_INCLUDE_DIR
OPENGL_xmesa_INCLUDE_DIR
)
@@ -246,6 +299,17 @@ else()
PATH_SUFFIXES libglvnd
)
+ find_library(OPENGL_gles2_LIBRARY
+ NAMES GLESv2
+ PATHS ${_OPENGL_LIB_PATH}
+ )
+
+ find_library(OPENGL_gles3_LIBRARY
+ NAMES GLESv3
+ GLESv2 # mesa provides only libGLESv2
+ PATHS ${_OPENGL_LIB_PATH}
+ )
+
find_library(OPENGL_glu_LIBRARY
NAMES GLU MesaGLU
PATHS ${OPENGL_gl_LIBRARY}
@@ -258,6 +322,8 @@ else()
OPENGL_opengl_LIBRARY
OPENGL_glx_LIBRARY
OPENGL_egl_LIBRARY
+ OPENGL_gles2_LIBRARY
+ OPENGL_gles3_LIBRARY
OPENGL_glu_LIBRARY
)
@@ -338,12 +404,16 @@ else()
OPENGL_glx_LIBRARY AND
NOT OPENGL_gl_LIBRARY) OR
(NOT OPENGL_USE_EGL AND
+ NOT OPENGL_USE_GLES3 AND
+ NOT OPENGL_USE_GLES2 AND
NOT OPENGL_glx_LIBRARY AND
NOT OPENGL_gl_LIBRARY) OR
(NOT OPENGL_USE_EGL AND
OPENGL_opengl_LIBRARY AND
OPENGL_glx_LIBRARY) OR
- ( OPENGL_USE_EGL))
+ (NOT OPENGL_USE_GLES3 AND
+ NOT OPENGL_USE_GLES2 AND
+ OPENGL_USE_EGL))
list(APPEND _OpenGL_REQUIRED_VARS OPENGL_opengl_LIBRARY)
endif()
@@ -351,13 +421,19 @@ else()
if((NOT OPENGL_USE_OPENGL AND
NOT OPENGL_USE_GLX AND
NOT OPENGL_USE_EGL AND
+ NOT OPENGL_USE_GLES3 AND
+ NOT OPENGL_USE_GLES2 AND
NOT OPENGL_glx_LIBRARY AND
NOT OPENGL_gl_LIBRARY) OR
( OPENGL_USE_GLX AND
NOT OPENGL_USE_EGL AND
+ NOT OPENGL_USE_GLES3 AND
+ NOT OPENGL_USE_GLES2 AND
NOT OPENGL_glx_LIBRARY AND
NOT OPENGL_gl_LIBRARY) OR
(NOT OPENGL_USE_EGL AND
+ NOT OPENGL_USE_GLES3 AND
+ NOT OPENGL_USE_GLES2 AND
OPENGL_opengl_LIBRARY AND
OPENGL_glx_LIBRARY) OR
(OPENGL_USE_GLX AND OPENGL_USE_EGL))
@@ -369,6 +445,16 @@ else()
list(APPEND _OpenGL_REQUIRED_VARS OPENGL_egl_LIBRARY)
endif()
+ # GLVND GLES2 library.
+ if(OPENGL_USE_GLES2)
+ list(APPEND _OpenGL_REQUIRED_VARS OPENGL_gles2_LIBRARY)
+ endif()
+
+ # GLVND GLES3 library.
+ if(OPENGL_USE_GLES3)
+ list(APPEND _OpenGL_REQUIRED_VARS OPENGL_gles3_LIBRARY)
+ endif()
+
# Old-style "libGL" library: used as a fallback when GLVND isn't available.
if((NOT OPENGL_USE_EGL AND
NOT OPENGL_opengl_LIBRARY AND
@@ -381,7 +467,11 @@ else()
endif()
# We always need the 'gl.h' include dir.
- list(APPEND _OpenGL_REQUIRED_VARS OPENGL_INCLUDE_DIR)
+ if(OPENGL_USE_EGL)
+ list(APPEND _OpenGL_REQUIRED_VARS OPENGL_EGL_INCLUDE_DIR)
+ else()
+ list(APPEND _OpenGL_REQUIRED_VARS OPENGL_INCLUDE_DIR)
+ endif()
unset(_OPENGL_INCLUDE_PATH)
unset(_OPENGL_LIB_PATH)
@@ -428,6 +518,18 @@ else()
set(OpenGL_EGL_FOUND FALSE)
endif()
+if(OPENGL_gles2_LIBRARY AND OPENGL_GLES2_INCLUDE_DIR)
+ set(OpenGL_GLES2_FOUND TRUE)
+else()
+ set(OpenGL_GLES2_FOUND FALSE)
+endif()
+
+if(OPENGL_gles3_LIBRARY AND OPENGL_GLES3_INCLUDE_DIR)
+ set(OpenGL_GLES3_FOUND TRUE)
+else()
+ set(OpenGL_GLES3_FOUND FALSE)
+endif()
+
# User-visible names should be plural.
if(OPENGL_EGL_INCLUDE_DIR)
set(OPENGL_EGL_INCLUDE_DIRS ${OPENGL_EGL_INCLUDE_DIR})
@@ -461,6 +563,7 @@ if(OPENGL_FOUND)
endif()
set_target_properties(OpenGL::OpenGL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${OPENGL_INCLUDE_DIR}")
+ set(_OpenGL_EGL_IMPL OpenGL::OpenGL)
endif()
# ::GLX is a GLVND library, and thus Linux-only: we don't bother checking
@@ -481,6 +584,73 @@ if(OPENGL_FOUND)
"${OPENGL_GLX_INCLUDE_DIR}")
endif()
+ # ::GLES2 is a GLVND library, and thus Linux-only: we don't bother checking
+ # for a framework version of this library.
+ if(OpenGL_GLES2_FOUND AND NOT TARGET OpenGL::GLES2)
+
+ # Initialize target
+ if(NOT OPENGL_gles2_LIBRARY)
+ add_library(OpenGL::GLES2 INTERFACE IMPORTED)
+ else()
+ if(IS_ABSOLUTE "${OPENGL_gles2_LIBRARY}")
+ add_library(OpenGL::GLES2 UNKNOWN IMPORTED)
+ set_target_properties(OpenGL::GLES2 PROPERTIES
+ IMPORTED_LOCATION "${OPENGL_gles2_LIBRARY}"
+ )
+ else()
+ add_library(OpenGL::GLES2 INTERFACE IMPORTED)
+ set_target_properties(OpenGL::GLES2 PROPERTIES
+ IMPORTED_LIBNAME "${OPENGL_gles2_LIBRARY}"
+ )
+ endif()
+ endif()
+
+ # Attach target properties
+ set_target_properties(OpenGL::GLES2
+ PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES
+ "${OPENGL_GLES2_INCLUDE_DIR}"
+ )
+
+ if (OPENGL_USE_GLES2)
+ set(_OpenGL_EGL_IMPL OpenGL::GLES2)
+ endif ()
+
+ endif()
+
+ # ::GLES3 is a GLVND library, and thus Linux-only: we don't bother checking
+ # for a framework version of this library.
+ if(OpenGL_GLES3_FOUND AND NOT TARGET OpenGL::GLES3)
+
+ # Initialize target
+ if(NOT OPENGL_gles3_LIBRARY)
+ add_library(OpenGL::GLES3 INTERFACE IMPORTED)
+ else()
+ if(IS_ABSOLUTE "${OPENGL_gles3_LIBRARY}")
+ add_library(OpenGL::GLES3 UNKNOWN IMPORTED)
+ set_target_properties(OpenGL::GLES3 PROPERTIES
+ IMPORTED_LOCATION "${OPENGL_gles3_LIBRARY}"
+ )
+ else()
+ add_library(OpenGL::GLES3 INTERFACE IMPORTED)
+ set_target_properties(OpenGL::GLES3 PROPERTIES
+ IMPORTED_LIBNAME "${OPENGL_gles3_LIBRARY}"
+ )
+ endif()
+ endif()
+
+ # Attach target properties
+ set_target_properties(OpenGL::GLES3 PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES
+ "${OPENGL_GLES3_INCLUDE_DIR}"
+ )
+
+ if (OPENGL_USE_GLES3)
+ set(_OpenGL_EGL_IMPL OpenGL::GLES3)
+ endif ()
+
+ endif()
+
if(OPENGL_gl_LIBRARY AND NOT TARGET OpenGL::GL)
# A legacy GL library is available, so use it for the legacy GL target.
if(IS_ABSOLUTE "${OPENGL_gl_LIBRARY}")
@@ -517,10 +687,9 @@ if(OPENGL_FOUND)
# ::EGL is a GLVND library, and thus Linux-only: we don't bother checking
# for a framework version of this library.
- # Note we test for OpenGL::OpenGL as a target. When this module is updated to
- # support GLES, we would additionally want to check for the hypothetical GLES
- # target and enable EGL if either ::GLES or ::OpenGL is created.
- if(TARGET OpenGL::OpenGL AND OpenGL_EGL_FOUND AND NOT TARGET OpenGL::EGL)
+ # Note we test whether _OpenGL_EGL_IMPL is set. Based on the OpenGL implementation,
+ # _OpenGL_EGL_IMPL will be one of OpenGL::OpenGL, OpenGL::GLES2, OpenGL::GLES3
+ if(_OpenGL_EGL_IMPL AND OpenGL_EGL_FOUND AND NOT TARGET OpenGL::EGL)
if(IS_ABSOLUTE "${OPENGL_egl_LIBRARY}")
add_library(OpenGL::EGL UNKNOWN IMPORTED)
set_target_properties(OpenGL::EGL PROPERTIES IMPORTED_LOCATION
@@ -531,7 +700,7 @@ if(OPENGL_FOUND)
"${OPENGL_egl_LIBRARY}")
endif()
set_target_properties(OpenGL::EGL PROPERTIES INTERFACE_LINK_LIBRARIES
- OpenGL::OpenGL)
+ "${_OpenGL_EGL_IMPL}")
# Note that EGL's include directory is different from OpenGL/GLX's!
set_target_properties(OpenGL::EGL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
"${OPENGL_EGL_INCLUDE_DIR}")
diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake
index 4e8374c68e..45dc9ac809 100644
--- a/Modules/FindOpenSSL.cmake
+++ b/Modules/FindOpenSSL.cmake
@@ -210,7 +210,7 @@ endif ()
# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
if(OPENSSL_USE_STATIC_LIBS)
set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
- if(WIN32)
+ if(MSVC)
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES .a )
@@ -230,7 +230,7 @@ else()
set(_OPENSSL_FIND_PATH_SUFFIX "include")
endif()
-if (WIN32)
+if (MSVC)
# http://www.slproweb.com/products/Win32OpenSSL.html
set(_OPENSSL_ROOT_HINTS
${OPENSSL_ROOT_DIR}
diff --git a/Modules/FindPNG.cmake b/Modules/FindPNG.cmake
index 94d15db5fc..043b69ca02 100644
--- a/Modules/FindPNG.cmake
+++ b/Modules/FindPNG.cmake
@@ -48,18 +48,35 @@ Since PNG depends on the ZLib compression library, none of the above
will be defined unless ZLib can be found.
#]=======================================================================]
+# Default install location on windows when installing from included cmake build
+# From FindZLIB.cmake
+set(_PNG_x86 "(x86)")
+set(_PNG_INCLUDE_SEARCH_NORMAL
+ "$ENV{ProgramFiles}/libpng"
+ "$ENV{ProgramFiles${_PNG_x86}}/libpng")
+set(_PNG_LIB_SEARCH_NORMAL
+ "$ENV{ProgramFiles}/libpng/lib"
+ "$ENV{ProgramFiles${_PNG_x86}}/libpng/lib")
+unset(_PNG_x86)
+
if(PNG_FIND_QUIETLY)
set(_FIND_ZLIB_ARG QUIET)
endif()
find_package(ZLIB ${_FIND_ZLIB_ARG})
if(ZLIB_FOUND)
- find_path(PNG_PNG_INCLUDE_DIR png.h PATH_SUFFIXES include/libpng)
+ set(_PNG_VERSION_SUFFIXES 17 16 15 14 12)
+
+ list(APPEND _PNG_INCLUDE_PATH_SUFFIXES include/libpng)
+ foreach(v IN LISTS _PNG_VERSION_SUFFIXES)
+ list(APPEND _PNG_INCLUDE_PATH_SUFFIXES include/libpng${v})
+ endforeach()
+
+ find_path(PNG_PNG_INCLUDE_DIR png.h PATH_SUFFIXES ${_PNG_INCLUDE_PATH_SUFFIXES} PATHS ${_PNG_INCLUDE_SEARCH_NORMAL} )
mark_as_advanced(PNG_PNG_INCLUDE_DIR)
list(APPEND PNG_NAMES png libpng)
unset(PNG_NAMES_DEBUG)
- set(_PNG_VERSION_SUFFIXES 17 16 15 14 12)
if (PNG_FIND_VERSION MATCHES "^([0-9]+)\\.([0-9]+)(\\..*)?$")
set(_PNG_VERSION_SUFFIX_MIN "${CMAKE_MATCH_1}${CMAKE_MATCH_2}")
if (PNG_FIND_VERSION_EXACT)
@@ -79,14 +96,15 @@ if(ZLIB_FOUND)
# For compatibility with versions prior to this multi-config search, honor
# any PNG_LIBRARY that is already specified and skip the search.
if(NOT PNG_LIBRARY)
- find_library(PNG_LIBRARY_RELEASE NAMES ${PNG_NAMES} NAMES_PER_DIR)
- find_library(PNG_LIBRARY_DEBUG NAMES ${PNG_NAMES_DEBUG} NAMES_PER_DIR)
+ find_library(PNG_LIBRARY_RELEASE NAMES ${PNG_NAMES} NAMES_PER_DIR PATHS ${_PNG_LIB_SEARCH_NORMAL})
+ find_library(PNG_LIBRARY_DEBUG NAMES ${PNG_NAMES_DEBUG} NAMES_PER_DIR PATHS ${_PNG_LIB_SEARCH_NORMAL})
include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
select_library_configurations(PNG)
mark_as_advanced(PNG_LIBRARY_RELEASE PNG_LIBRARY_DEBUG)
endif()
unset(PNG_NAMES)
unset(PNG_NAMES_DEBUG)
+ unset(_PNG_INCLUDE_PATH_SUFFIXES)
# Set by select_library_configurations(), but we want the one from
# find_package_handle_standard_args() below.
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 517ac21eb7..60d229645e 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -192,28 +192,25 @@ function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS)
if (implementation STREQUAL "CPython")
foreach (version IN LISTS _PGR_VERSION)
string (REPLACE "." "" version_no_dots ${version})
- list (APPEND registries
- [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath]
- [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath])
+ list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths)
+ list (APPEND registries ${reg_paths})
if (version VERSION_GREATER_EQUAL "3.5")
# cmake_host_system_information is not usable in bootstrap
get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME)
- if (arch MATCHES "(${_${_PYTHON_PREFIX}_ARCH}|${_${_PYTHON_PREFIX}_ARCH2})bit")
- list (APPEND registries
- [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath])
+ string (REPLACE "bit" "" arch "${arch}")
+ if (arch IN_LIST _${_PYTHON_PREFIX}_ARCH)
+ list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath])
endif()
else()
- list (APPEND registries
- [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath])
- endif()
- list (APPEND registries
- [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath]
- [HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]
- [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath]
- [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath]
- [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}/InstallPath]
- [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath]
- [HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath])
+ list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath])
+ endif()
+ list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths)
+ list (APPEND registries ${reg_paths})
+ list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths)
+ list (APPEND registries ${reg_paths})
+ list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}/InstallPath])
+ list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths)
+ list (APPEND registries ${reg_paths})
endforeach()
elseif (implementation STREQUAL "IronPython")
foreach (version IN LISTS _PGR_VERSION)
@@ -927,6 +924,33 @@ function (_PYTHON_VALIDATE_INTERPRETER)
set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
return()
endif()
+
+ if (WIN32)
+ # In this case, check if the interpreter is compatible with the target processor architecture
+ if (NOT CMAKE_GENERATOR_PLATFORM AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM" OR CMAKE_GENERATOR_PLATFORM MATCHES "ARM")
+ set(target_arm TRUE)
+ else()
+ set(target_arm FALSE)
+ endif()
+ execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+ "import sys, sysconfig; sys.stdout.write(sysconfig.get_platform())"
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE platform
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ string(TOUPPER "${platform}" platform)
+ if (result OR ((target_arm AND NOT platform MATCHES "ARM") OR
+ (NOT target_arm AND platform MATCHES "ARM")))
+ # interpreter not usable or has wrong architecture
+ if (result)
+ set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
+ else()
+ set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
+ endif()
+ set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
+ return()
+ endif()
+ endif()
endif()
endfunction()
@@ -1419,19 +1443,37 @@ if (CMAKE_SIZEOF_VOID_P)
OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
# In this case, search only for 64bit or 32bit
- set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH})
set (_${_PYTHON_PREFIX}_REGISTRY_VIEW REGISTRY_VIEW ${_${_PYTHON_PREFIX}_ARCH})
+ if (WIN32 AND (NOT CMAKE_GENERATOR_PLATFORM AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM"
+ OR CMAKE_GENERATOR_PLATFORM MATCHES "ARM"))
+ # search exclusively ARM architecture: 64bit or 32bit
+ if (_${_PYTHON_PREFIX}_ARCH EQUAL 64)
+ set (_${_PYTHON_PREFIX}_ARCH ARM64)
+ else()
+ set (_${_PYTHON_PREFIX}_ARCH ARM)
+ endif()
+ endif()
else()
if (_${_PYTHON_PREFIX}_ARCH EQUAL "32")
- set (_${_PYTHON_PREFIX}_ARCH2 64)
+ if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM")
+ # search first ARM architectures: 32bit and then 64bit
+ list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM ARM64)
+ endif()
+ list (APPEND _${_PYTHON_PREFIX}_ARCH 64)
else()
- set (_${_PYTHON_PREFIX}_ARCH2 32)
+ if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM")
+ # search first ARM architectures: 64bit and then 32bit
+ list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM64 ARM)
+ endif()
+ list (APPEND _${_PYTHON_PREFIX}_ARCH 32)
endif()
endif()
else()
# architecture unknown, search for both 64bit and 32bit
- set (_${_PYTHON_PREFIX}_ARCH 64)
- set (_${_PYTHON_PREFIX}_ARCH2 32)
+ set (_${_PYTHON_PREFIX}_ARCH 64 32)
+ if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM")
+ list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM64 ARM)
+ endif()
endif()
# IronPython support
@@ -1439,7 +1481,7 @@ unset (_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES)
unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES)
unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS)
if (CMAKE_SIZEOF_VOID_P)
- if (_${_PYTHON_PREFIX}_ARCH EQUAL "32")
+ if (CMAKE_SIZEOF_VOID_P EQUAL "4")
set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x86")
else()
set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x64")
@@ -2048,7 +2090,6 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATCH)
list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 4 _${_PYTHON_PREFIX}_ARCH)
- set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH})
list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 5 _${_PYTHON_PREFIX}_ABIFLAGS)
list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 6 ${_PYTHON_PREFIX}_SOABI)
@@ -2098,10 +2139,27 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
if (NOT _${_PYTHON_PREFIX}_RESULT)
if (${_PYTHON_PREFIX}_IS64BIT)
set (_${_PYTHON_PREFIX}_ARCH 64)
- set (_${_PYTHON_PREFIX}_ARCH2 64)
else()
set (_${_PYTHON_PREFIX}_ARCH 32)
- set (_${_PYTHON_PREFIX}_ARCH2 32)
+ endif()
+ endif()
+
+ if (WIN32)
+ # check if architecture is Intel or ARM
+ execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+ "import sys; import sysconfig; sys.stdout.write(sysconfig.get_platform())"
+ RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
+ OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PLATFORM
+ ERROR_VARIABLE ${_PYTHON_PREFIX}_PLATFORM)
+ if (NOT _${_PYTHON_PREFIX}_RESULT)
+ string(TOUPPER "${_${_PYTHON_PREFIX}_PLATFORM}" _${_PYTHON_PREFIX}_PLATFORM)
+ if (_${_PYTHON_PREFIX}_PLATFORM MATCHES "ARM")
+ if (${_PYTHON_PREFIX}_IS64BIT)
+ set (_${_PYTHON_PREFIX}_ARCH ARM64)
+ else()
+ set (_${_PYTHON_PREFIX}_ARCH ARM)
+ endif()
+ endif()
endif()
endif()
endif()
diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake
index 7ad358782f..b5e759de43 100644
--- a/Modules/FindPythonInterp.cmake
+++ b/Modules/FindPythonInterp.cmake
@@ -5,6 +5,9 @@
FindPythonInterp
----------------
+.. versionchanged:: 3.27
+ This module is available only if policy :policy:`CMP0148` is not set to ``NEW``.
+
.. deprecated:: 3.12
Use :module:`FindPython3`, :module:`FindPython2` or :module:`FindPython` instead.
@@ -50,6 +53,16 @@ of PYTHON_LIBRARIES.
#]=======================================================================]
+cmake_policy(GET CMP0148 _FindPythonInterp_CMP0148)
+if(_FindPythonInterp_CMP0148 STREQUAL "NEW")
+ message(FATAL_ERROR "The FindPythonInterp module has been removed by policy CMP0148.")
+endif()
+
+if(_FindPythonInterp_testing)
+ set(_FindPythonInterp_included TRUE)
+ return()
+endif()
+
unset(_Python_NAMES)
set(_PYTHON1_VERSIONS 1.6 1.5)
diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake
index 43a84dd735..06004ee468 100644
--- a/Modules/FindPythonLibs.cmake
+++ b/Modules/FindPythonLibs.cmake
@@ -5,6 +5,9 @@
FindPythonLibs
--------------
+.. versionchanged:: 3.27
+ This module is available only if policy :policy:`CMP0148` is not set to ``NEW``.
+
.. deprecated:: 3.12
Use :module:`FindPython3`, :module:`FindPython2` or :module:`FindPython` instead.
@@ -45,6 +48,16 @@ get the currently active Python version by default with a consistent version
of PYTHON_LIBRARIES.
#]=======================================================================]
+cmake_policy(GET CMP0148 _FindPythonLibs_CMP0148)
+if(_FindPythonLibs_CMP0148 STREQUAL "NEW")
+ message(FATAL_ERROR "The FindPythonLibs module has been removed by policy CMP0148.")
+endif()
+
+if(_FindPythonLibs_testing)
+ set(_FindPythonLibs_included TRUE)
+ return()
+endif()
+
# Use the executable's path as a hint
set(_Python_LIBRARY_PATH_HINT)
if(IS_ABSOLUTE "${PYTHON_EXECUTABLE}")
diff --git a/Modules/FindVulkan.cmake b/Modules/FindVulkan.cmake
index 38179878c2..581763de00 100644
--- a/Modules/FindVulkan.cmake
+++ b/Modules/FindVulkan.cmake
@@ -244,23 +244,26 @@ endif()
if(WIN32)
set(_Vulkan_library_name vulkan-1)
set(_Vulkan_hint_include_search_paths
- "$ENV{VULKAN_SDK}/Include"
+ "$ENV{VULKAN_SDK}/include"
)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_Vulkan_hint_executable_search_paths
- "$ENV{VULKAN_SDK}/Bin"
+ "$ENV{VULKAN_SDK}/bin"
)
set(_Vulkan_hint_library_search_paths
- "$ENV{VULKAN_SDK}/Lib"
- "$ENV{VULKAN_SDK}/Bin"
+ "$ENV{VULKAN_SDK}/lib"
+ "$ENV{VULKAN_SDK}/bin"
)
else()
set(_Vulkan_hint_executable_search_paths
- "$ENV{VULKAN_SDK}/Bin32"
+ "$ENV{VULKAN_SDK}/bin32"
+ "$ENV{VULKAN_SDK}/bin"
)
set(_Vulkan_hint_library_search_paths
- "$ENV{VULKAN_SDK}/Lib32"
- "$ENV{VULKAN_SDK}/Bin32"
+ "$ENV{VULKAN_SDK}/lib32"
+ "$ENV{VULKAN_SDK}/bin32"
+ "$ENV{VULKAN_SDK}/lib"
+ "$ENV{VULKAN_SDK}/bin"
)
endif()
else()
diff --git a/Modules/FindX11.cmake b/Modules/FindX11.cmake
index 8e5a5f1709..6f6483ab73 100644
--- a/Modules/FindX11.cmake
+++ b/Modules/FindX11.cmake
@@ -30,10 +30,13 @@ and also the following more fine grained variables and targets:
X11_Xau_INCLUDE_PATH, X11_Xau_LIB, X11_Xau_FOUND, X11::Xau
X11_xcb_INCLUDE_PATH, X11_xcb_LIB, X11_xcb_FOUND, X11::xcb
X11_X11_xcb_INCLUDE_PATH, X11_X11_xcb_LIB, X11_X11_xcb_FOUND, X11::X11_xcb
+ X11_xcb_cursor_INCLUDE_PATH, X11_xcb_cursor_LIB, X11_xcb_cursor_FOUND, X11::xcb_cursor
X11_xcb_icccm_INCLUDE_PATH, X11_xcb_icccm_LIB, X11_xcb_icccm_FOUND, X11::xcb_icccm
X11_xcb_randr_INCLUDE_PATH, X11_xcb_randr_LIB, X11_xcb_randr_FOUND, X11::xcb_randr
+ X11_xcb_shape_INCLUDE_PATH, X11_xcb_shape_LIB, X11_xcb_shape_FOUND, X11::xcb_shape
X11_xcb_util_INCLUDE_PATH, X11_xcb_util_LIB, X11_xcb_util_FOUND, X11::xcb_util
X11_xcb_xfixes_INCLUDE_PATH, X11_xcb_xfixes_LIB, X11_xcb_xfixes_FOUND, X11::xcb_xfixes
+ X11_xcb_xrm_INCLUDE_PATH, X11_xcb_xrm_LIB, X11_xcb_xrm_FOUND, X11::xcb_xrm
X11_xcb_xtest_INCLUDE_PATH, X11_xcb_xtest_LIB, X11_xcb_xtest_FOUND, X11::xcb_xtest
X11_xcb_keysyms_INCLUDE_PATH, X11_xcb_keysyms_LIB,X11_xcb_keysyms_FOUND,X11::xcb_keysyms
X11_xcb_xkb_INCLUDE_PATH, X11_xcb_xkb_LIB, X11_xcb_xkb_FOUND, X11::xcb_xkb
@@ -88,6 +91,9 @@ and also the following more fine grained variables and targets:
.. versionadded:: 3.24
Added the ``xcb_randr``, ``xcb_xtext``, and ``xcb_keysyms`` libraries.
+.. versionadded:: 3.27
+ Added the ``xcb_cursor``, ``xcb_shape``, and ``xcb_xrm`` libraries.
+
#]=======================================================================]
if (UNIX)
@@ -132,10 +138,13 @@ if (UNIX)
find_path(X11_Xaw_INCLUDE_PATH X11/Xaw/Intrinsic.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_INCLUDE_PATH xcb/xcb.h ${X11_INC_SEARCH_PATH})
find_path(X11_X11_xcb_INCLUDE_PATH X11/Xlib-xcb.h ${X11_INC_SEARCH_PATH})
+ find_path(X11_xcb_cursor_INCLUDE_PATH xcb/xcb_cursor.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_icccm_INCLUDE_PATH xcb/xcb_icccm.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_randr_INCLUDE_PATH xcb/randr.h ${X11_INC_SEARCH_PATH})
+ find_path(X11_xcb_shape_INCLUDE_PATH xcb/shape.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_util_INCLUDE_PATH xcb/xcb_aux.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_xfixes_INCLUDE_PATH xcb/xfixes.h ${X11_INC_SEARCH_PATH})
+ find_path(X11_xcb_xrm_INCLUDE_PATH xcb/xcb_xrm.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_xtest_INCLUDE_PATH xcb/xtest.h ${X11_INC_SEARCH_PATH})
find_path(X11_xcb_keysyms_INCLUDE_PATH xcb/xcb_keysyms.h ${X11_INC_SEARCH_PATH})
find_path(X11_Xcomposite_INCLUDE_PATH X11/extensions/Xcomposite.h ${X11_INC_SEARCH_PATH})
@@ -188,10 +197,13 @@ if (UNIX)
find_library(X11_Xaw_LIB Xaw ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_LIB xcb ${X11_LIB_SEARCH_PATH})
find_library(X11_X11_xcb_LIB X11-xcb ${X11_LIB_SEARCH_PATH})
+ find_library(X11_xcb_cursor_LIB xcb-cursor ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_icccm_LIB xcb-icccm ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_randr_LIB xcb-randr ${X11_LIB_SEARCH_PATH})
+ find_library(X11_xcb_shape_LIB xcb-shape ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_util_LIB xcb-util ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_xfixes_LIB xcb-xfixes ${X11_LIB_SEARCH_PATH})
+ find_library(X11_xcb_xrm_LIB xcb-xrm ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_xtest_LIB xcb-xtest ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_keysyms_LIB xcb-keysyms ${X11_LIB_SEARCH_PATH})
find_library(X11_xcb_xkb_LIB xcb-xkb ${X11_LIB_SEARCH_PATH})
@@ -289,6 +301,10 @@ if (UNIX)
set(X11_X11_xcb_FOUND TRUE)
endif ()
+ if (X11_xcb_cursor_LIB AND X11_xcb_cursor_INCLUDE_PATH)
+ set(X11_xcb_cursor_FOUND TRUE)
+ endif ()
+
if (X11_xcb_icccm_LIB AND X11_xcb_icccm_INCLUDE_PATH)
set(X11_xcb_icccm_FOUND TRUE)
endif ()
@@ -297,6 +313,10 @@ if (UNIX)
set(X11_xcb_randr_FOUND TRUE)
endif ()
+ if (X11_xcb_shape_LIB AND X11_xcb_shape_INCLUDE_PATH)
+ set(X11_xcb_shape_FOUND TRUE)
+ endif ()
+
if (X11_xcb_util_LIB AND X11_xcb_util_INCLUDE_PATH)
set(X11_xcb_util_FOUND TRUE)
endif ()
@@ -305,6 +325,10 @@ if (UNIX)
set(X11_xcb_xfixes_FOUND TRUE)
endif ()
+ if (X11_xcb_xrm_LIB AND X11_xcb_xrm_INCLUDE_PATH)
+ set(X11_xcb_xrm_FOUND TRUE)
+ endif ()
+
if (X11_xcb_xtest_LIB)
set(X11_xcb_xtest_FOUND TRUE)
endif ()
@@ -617,6 +641,13 @@ if (UNIX)
INTERFACE_LINK_LIBRARIES "X11::xcb;X11::X11")
endif ()
+ if (X11_xcb_cursor_FOUND AND NOT TARGET X11::xcb_cursor)
+ add_library(X11::xcb_cursor UNKNOWN IMPORTED)
+ set_target_properties(X11::xcb_cursor PROPERTIES
+ IMPORTED_LOCATION "${X11_xcb_cursor_LIB}"
+ INTERFACE_LINK_LIBRARIES "X11::xcb")
+ endif ()
+
if (X11_xcb_icccm_FOUND AND NOT TARGET X11::xcb_icccm)
add_library(X11::xcb_icccm UNKNOWN IMPORTED)
set_target_properties(X11::xcb_icccm PROPERTIES
@@ -631,6 +662,13 @@ if (UNIX)
INTERFACE_LINK_LIBRARIES "X11::xcb")
endif ()
+ if (X11_xcb_shape_FOUND AND NOT TARGET X11::xcb_shape)
+ add_library(X11::xcb_shape UNKNOWN IMPORTED)
+ set_target_properties(X11::xcb_shape PROPERTIES
+ IMPORTED_LOCATION "${X11_xcb_shape_LIB}"
+ INTERFACE_LINK_LIBRARIES "X11::xcb")
+ endif ()
+
if (X11_xcb_util_FOUND AND NOT TARGET X11::xcb_util)
add_library(X11::xcb_util UNKNOWN IMPORTED)
set_target_properties(X11::xcb_util PROPERTIES
@@ -645,6 +683,13 @@ if (UNIX)
INTERFACE_LINK_LIBRARIES "X11::xcb")
endif ()
+ if (X11_xcb_xrm_FOUND AND NOT TARGET X11::xcb_xrm)
+ add_library(X11::xcb_xrm UNKNOWN IMPORTED)
+ set_target_properties(X11::xcb_xrm PROPERTIES
+ IMPORTED_LOCATION "${X11_xcb_xrm_LIB}"
+ INTERFACE_LINK_LIBRARIES "X11::xcb")
+ endif ()
+
if (X11_xcb_xtest_FOUND AND NOT TARGET X11::xcb_xtest)
add_library(X11::xcb_xtest UNKNOWN IMPORTED)
set_target_properties(X11::xcb_xtest PROPERTIES
@@ -873,14 +918,20 @@ if (UNIX)
X11_Xau_INCLUDE_PATH
X11_xcb_LIB
X11_xcb_INCLUDE_PATH
+ X11_xcb_cursor_LIB
+ X11_xcb_cursor_INCLUDE_PATH
X11_xcb_icccm_LIB
X11_xcb_icccm_INCLUDE_PATH
X11_xcb_randr_LIB
X11_xcb_randr_INCLUDE_PATH
+ X11_xcb_shape_LIB
+ X11_xcb_shape_INCLUDE_PATH
X11_xcb_util_LIB
X11_xcb_util_INCLUDE_PATH
X11_xcb_xfixes_LIB
X11_xcb_xfixes_INCLUDE_PATH
+ X11_xcb_xrm_LIB
+ X11_xcb_xrm_INCLUDE_PATH
X11_xcb_xtest_LIB
X11_xcb_xtest_INCLUDE_PATH
X11_xcb_keysyms_LIB
diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake
index 7118df200f..40e767b54d 100644
--- a/Modules/FindXCTest.cmake
+++ b/Modules/FindXCTest.cmake
@@ -13,7 +13,7 @@ An XCTest bundle is a CFBundle with a special product-type
and bundle extension. The Mac Developer Library provides more
information in the `Testing with Xcode`_ document.
-.. _Testing with Xcode: https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/
+.. _Testing with Xcode: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/
Module Functions
^^^^^^^^^^^^^^^^
diff --git a/Modules/FindwxWidgets.cmake b/Modules/FindwxWidgets.cmake
index 3159ff7654..cc76b350c9 100644
--- a/Modules/FindwxWidgets.cmake
+++ b/Modules/FindwxWidgets.cmake
@@ -113,6 +113,16 @@ If wxWidgets is required (i.e., not an optional part):
include(${wxWidgets_USE_FILE})
# and for each of your dependent executable/library targets:
target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
+
+Imported targets
+^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.27
+
+This module defines the following :prop_tgt:`IMPORTED` targets:
+
+``wxWidgets::wxWidgets``
+ An interface library providing usage requirements for the found components.
#]=======================================================================]
#
@@ -981,6 +991,17 @@ find_package_handle_standard_args(wxWidgets
)
unset(wxWidgets_HANDLE_COMPONENTS)
+if(wxWidgets_FOUND AND NOT TARGET wxWidgets::wxWidgets)
+ add_library(wxWidgets::wxWidgets INTERFACE IMPORTED)
+ target_link_libraries(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARIES})
+ target_link_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_LIBRARY_DIRS})
+ target_include_directories(wxWidgets::wxWidgets INTERFACE ${wxWidgets_INCLUDE_DIRS})
+ target_compile_options(wxWidgets::wxWidgets INTERFACE ${wxWidgets_CXX_FLAGS})
+ target_compile_definitions(wxWidgets::wxWidgets INTERFACE ${wxWidgets_DEFINITIONS})
+ # FIXME: Add "$<$<CONFIG:Debug>:${wxWidgets_DEFINITIONS_DEBUG}>"
+ # if the debug library variant is available.
+endif()
+
#=====================================================================
# Macros for use in wxWidgets apps.
# - This module will not fail to find wxWidgets based on the code
diff --git a/Modules/FortranCInterface/CMakeLists.txt b/Modules/FortranCInterface/CMakeLists.txt
index 4bd7006efb..a0f186250b 100644
--- a/Modules/FortranCInterface/CMakeLists.txt
+++ b/Modules/FortranCInterface/CMakeLists.txt
@@ -6,11 +6,11 @@ project(FortranCInterface C Fortran)
include(${FortranCInterface_BINARY_DIR}/Input.cmake OPTIONAL)
# Check if the C compiler supports '$' in identifiers.
-include(CheckCSourceCompiles)
-check_c_source_compiles("
-extern int dollar$(void);
-int main() { return 0; }
-" C_SUPPORTS_DOLLAR)
+include(CheckSourceCompiles)
+check_source_compiles(C
+"extern int dollar$(void);
+int main() { return 0; }"
+C_SUPPORTS_DOLLAR)
# List manglings of global symbol names to try.
set(global_symbols
diff --git a/Modules/GenerateExportHeader.cmake b/Modules/GenerateExportHeader.cmake
index 7461a3e5d0..01a6e4f5f0 100644
--- a/Modules/GenerateExportHeader.cmake
+++ b/Modules/GenerateExportHeader.cmake
@@ -198,19 +198,19 @@ that will be populated with the ``CXX_FLAGS`` required to enable visibility
support for the compiler/architecture in use.
#]=======================================================================]
-include(CheckCCompilerFlag)
-include(CheckCXXCompilerFlag)
+include(CheckCompilerFlag)
+include(CheckSourceCompiles)
# TODO: Install this macro separately?
macro(_check_cxx_compiler_attribute _ATTRIBUTE _RESULT)
- check_cxx_source_compiles("${_ATTRIBUTE} int somefunc() { return 0; }
+ check_source_compiles(CXX "${_ATTRIBUTE} int somefunc() { return 0; }
int main() { return somefunc();}" ${_RESULT}
)
endmacro()
# TODO: Install this macro separately?
macro(_check_c_compiler_attribute _ATTRIBUTE _RESULT)
- check_c_source_compiles("${_ATTRIBUTE} int somefunc() { return 0; }
+ check_source_compiles(C "${_ATTRIBUTE} int somefunc() { return 0; }
int main() { return somefunc();}" ${_RESULT}
)
endmacro()
@@ -226,7 +226,7 @@ macro(_test_compiler_hidden_visibility)
endif()
# Exclude XL here because it misinterprets -fvisibility=hidden even though
- # the check_cxx_compiler_flag passes
+ # the check_compiler_flag passes
if(NOT GCC_TOO_OLD
AND NOT _INTEL_TOO_OLD
AND NOT WIN32
@@ -235,12 +235,12 @@ macro(_test_compiler_hidden_visibility)
AND NOT CMAKE_CXX_COMPILER_ID MATCHES "^(PGI|NVHPC)$"
AND NOT CMAKE_CXX_COMPILER_ID MATCHES Watcom)
if (CMAKE_CXX_COMPILER_LOADED)
- check_cxx_compiler_flag(-fvisibility=hidden COMPILER_HAS_HIDDEN_VISIBILITY)
- check_cxx_compiler_flag(-fvisibility-inlines-hidden
+ check_compiler_flag(CXX -fvisibility=hidden COMPILER_HAS_HIDDEN_VISIBILITY)
+ check_compiler_flag(CXX -fvisibility-inlines-hidden
COMPILER_HAS_HIDDEN_INLINE_VISIBILITY)
else()
- check_c_compiler_flag(-fvisibility=hidden COMPILER_HAS_HIDDEN_VISIBILITY)
- check_c_compiler_flag(-fvisibility-inlines-hidden
+ check_compiler_flag(C -fvisibility=hidden COMPILER_HAS_HIDDEN_VISIBILITY)
+ check_compiler_flag(C -fvisibility-inlines-hidden
COMPILER_HAS_HIDDEN_INLINE_VISIBILITY)
endif()
endif()
@@ -293,7 +293,7 @@ macro(_DO_SET_MACRO_VALUES TARGET_LIBRARY)
set(DEFINE_IMPORT)
set(DEFINE_NO_EXPORT)
- if (COMPILER_HAS_DEPRECATED_ATTR)
+ if (COMPILER_HAS_DEPRECATED_ATTR AND NOT WIN32)
set(DEFINE_DEPRECATED "__attribute__ ((__deprecated__))")
elseif(COMPILER_HAS_DEPRECATED)
set(DEFINE_DEPRECATED "__declspec(deprecated)")
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index 0ba35b6b79..0cd49ab4ba 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -730,7 +730,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
if(gp_tool MATCHES "ldd$")
set(gp_cmd_args "")
- set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+([^\t\(]+)( \(.+\))?${eol_char}$")
+ set(gp_regex "^[\t ]*[^\t ]+ =>[\t ]+(/[^\t\(]+)( \(.+\))?${eol_char}$")
set(gp_regex_error "not found${eol_char}$")
set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
set(gp_regex_cmp_count 1)
diff --git a/Modules/Internal/CheckFlagCommonConfig.cmake b/Modules/Internal/CheckFlagCommonConfig.cmake
index f8481cdd2b..8c5703db19 100644
--- a/Modules/Internal/CheckFlagCommonConfig.cmake
+++ b/Modules/Internal/CheckFlagCommonConfig.cmake
@@ -7,7 +7,8 @@
# It's content may change in any way between releases.
include_guard(GLOBAL)
-cmake_policy(PUSH)
+
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
@@ -74,4 +75,4 @@ macro(CMAKE_CHECK_FLAG_COMMON_FINISH)
endforeach()
endmacro()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/Internal/CheckLinkerFlag.cmake b/Modules/Internal/CheckLinkerFlag.cmake
index 7613105500..b872b5121e 100644
--- a/Modules/Internal/CheckLinkerFlag.cmake
+++ b/Modules/Internal/CheckLinkerFlag.cmake
@@ -6,7 +6,7 @@ include(Internal/CheckFlagCommonConfig)
include(Internal/CheckSourceCompiles)
include(CMakeCheckCompilerFlagCommonPatterns)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
function(CMAKE_CHECK_LINKER_FLAG _lang _flag _var)
@@ -49,4 +49,4 @@ function(CMAKE_CHECK_LINKER_FLAG _lang _flag _var)
cmake_check_flag_common_finish()
endfunction()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/Internal/CheckSourceCompiles.cmake b/Modules/Internal/CheckSourceCompiles.cmake
index 83d7020df5..14a9a61a87 100644
--- a/Modules/Internal/CheckSourceCompiles.cmake
+++ b/Modules/Internal/CheckSourceCompiles.cmake
@@ -3,7 +3,7 @@
include_guard(GLOBAL)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
@@ -131,4 +131,4 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var)
endif()
endfunction()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/Internal/CheckSourceRuns.cmake b/Modules/Internal/CheckSourceRuns.cmake
index 805d98d89c..c01081eb98 100644
--- a/Modules/Internal/CheckSourceRuns.cmake
+++ b/Modules/Internal/CheckSourceRuns.cmake
@@ -3,7 +3,7 @@
include_guard(GLOBAL)
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
@@ -124,4 +124,4 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var)
endif()
endfunction()
-cmake_policy(POP)
+endblock()
diff --git a/Modules/Internal/HeaderpadWorkaround.cmake b/Modules/Internal/HeaderpadWorkaround.cmake
index 9a7f9f57e4..fccfaae4a6 100644
--- a/Modules/Internal/HeaderpadWorkaround.cmake
+++ b/Modules/Internal/HeaderpadWorkaround.cmake
@@ -9,7 +9,7 @@ if(NOT APPLE)
return()
endif()
-cmake_policy(PUSH)
+block(SCOPE_FOR POLICIES)
cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
function(__cmake_internal_workaround_headerpad_flag_conflict _LANG)
@@ -66,4 +66,4 @@ endforeach()
unset(__lang)
unset(__enabled_languages)
-cmake_policy(POP)
+endblock()
diff --git a/Modules/MatlabTestsRedirect.cmake b/Modules/MatlabTestsRedirect.cmake
index d651cddd53..8973231317 100644
--- a/Modules/MatlabTestsRedirect.cmake
+++ b/Modules/MatlabTestsRedirect.cmake
@@ -19,8 +19,8 @@
# -P FindMatlab_TestsRedirect.cmake
set(Matlab_UNIT_TESTS_CMD -nosplash -nodesktop -nodisplay ${Matlab_ADDITIONAL_STARTUP_OPTIONS})
-if(WIN32)
- set(Matlab_UNIT_TESTS_CMD ${Matlab_UNIT_TESTS_CMD} -wait)
+if(WIN32 AND maut_BATCH_OPTION STREQUAL "-r")
+ list(APPEND Matlab_UNIT_TESTS_CMD -wait)
endif()
if(NOT test_timeout)
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index ac2478bc77..a6c86f1303 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -45,6 +45,8 @@ set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
set(CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES ".tbd" ".so")
set(CMAKE_SHARED_MODULE_PREFIX "lib")
set(CMAKE_SHARED_MODULE_SUFFIX ".so")
+set(CMAKE_APPLE_IMPORT_FILE_PREFIX "lib")
+set(CMAKE_APPLE_IMPORT_FILE_SUFFIX ".tbd")
set(CMAKE_MODULE_EXISTS 1)
set(CMAKE_DL_LIBS "")
if(NOT "${_CURRENT_OSX_VERSION}" VERSION_LESS "10.5")
@@ -108,6 +110,9 @@ foreach(lang C CXX Fortran OBJC OBJCXX)
set(CMAKE_${lang}_FRAMEWORK_SEARCH_FLAG -F)
endforeach()
+# To generate text-based stubs
+set(CMAKE_CREATE_TEXT_STUBS "<CMAKE_TAPI> stubify -isysroot <CMAKE_OSX_SYSROOT> -o <TARGET_IMPLIB> <TARGET>")
+
# Defines LINK_LIBRARY features for frameworks
set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
diff --git a/Modules/Platform/Linux-LCC-Fortran.cmake b/Modules/Platform/Linux-LCC-Fortran.cmake
index bf2a1c2d68..308c771c54 100644
--- a/Modules/Platform/Linux-LCC-Fortran.cmake
+++ b/Modules/Platform/Linux-LCC-Fortran.cmake
@@ -1,7 +1,9 @@
include(Platform/Linux-LCC)
__linux_compiler_lcc(Fortran)
-if (CMAKE_Fortran_COMPILER_VERSION VERSION_LESS "1.26.03")
+if (CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.26.03")
+ set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-lgfortran")
+elseif (CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.01")
set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-llfortran")
else()
- set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS "-lgfortran")
+ unset(CMAKE_Fortran_CREATE_PREPROCESSED_SOURCE)
endif()
diff --git a/Modules/Platform/Windows-Apple-Swift.cmake b/Modules/Platform/Windows-Apple-Swift.cmake
index 1177755474..3f754fda09 100644
--- a/Modules/Platform/Windows-Apple-Swift.cmake
+++ b/Modules/Platform/Windows-Apple-Swift.cmake
@@ -1 +1,3 @@
set(CMAKE_Swift_IMPLIB_LINKER_FLAGS "-Xlinker -implib:<TARGET_IMPLIB>")
+set(CMAKE_Swift_FLAGS_DEBUG_LINKER_FLAGS "-Xlinker -debug")
+set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_LINKER_FLAGS "-Xlinker -debug")
diff --git a/Modules/UseJava.cmake b/Modules/UseJava.cmake
index 54a8cf741c..99fd6176b0 100644
--- a/Modules/UseJava.cmake
+++ b/Modules/UseJava.cmake
@@ -294,7 +294,7 @@ Header Generation
.. deprecated:: 3.11
This command will no longer be supported starting with version 10 of the JDK
- due to the `suppression of javah tool <https://openjdk.java.net/jeps/313>`_.
+ due to the `suppression of javah tool <https://openjdk.org/jeps/313>`_.
The :ref:`add_jar(GENERATE_NATIVE_HEADERS) <add_jar>` command should be
used instead.
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index e99da49296..4a7d9bc591 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -11,18 +11,6 @@ endif()
include(CheckIncludeFile)
-if(NOT CMake_DEFAULT_RECURSION_LIMIT)
- if(DEFINED ENV{DASHBOARD_TEST_FROM_CTEST})
- set(CMake_DEFAULT_RECURSION_LIMIT 100)
- elseif(MINGW OR MSYS)
- set(CMake_DEFAULT_RECURSION_LIMIT 400)
- elseif(WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM")
- set(CMake_DEFAULT_RECURSION_LIMIT 600)
- else()
- set(CMake_DEFAULT_RECURSION_LIMIT 1000)
- endif()
-endif()
-
if(APPLE)
set(CMake_USE_MACH_PARSER 1)
endif()
@@ -140,6 +128,7 @@ add_library(
cmCLocaleEnvironmentScope.cxx
cmCMakePath.h
cmCMakePath.cxx
+ cmCMakePresetErrors.h
cmCMakePresetsGraph.cxx
cmCMakePresetsGraph.h
cmCMakePresetsGraphInternal.h
@@ -204,6 +193,8 @@ add_library(
cmDyndepCollation.h
cmELF.h
cmELF.cxx
+ cmEvaluatedTargetProperty.cxx
+ cmEvaluatedTargetProperty.h
cmExprParserHelper.cxx
cmExportBuildAndroidMKGenerator.h
cmExportBuildAndroidMKGenerator.cxx
@@ -331,6 +322,8 @@ add_library(
cmInstallDirectoryGenerator.h
cmInstallDirectoryGenerator.cxx
cmJSONHelpers.h
+ cmJSONState.cxx
+ cmJSONState.h
cmLDConfigLDConfigTool.cxx
cmLDConfigLDConfigTool.h
cmLDConfigTool.cxx
@@ -342,6 +335,8 @@ add_library(
cmLinkLineComputer.h
cmLinkLineDeviceComputer.cxx
cmLinkLineDeviceComputer.h
+ cmList.h
+ cmList.cxx
cmListFileCache.cxx
cmListFileCache.h
cmLocalCommonGenerator.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 423b145caa..5de756be83 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 26)
-set(CMake_VERSION_PATCH 3)
+set(CMake_VERSION_PATCH 20230414)
#set(CMake_VERSION_RC 0)
set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx
index 162dfc7346..ea7b24b488 100644
--- a/Source/CPack/cmCPackFreeBSDGenerator.cxx
+++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx
@@ -263,7 +263,7 @@ cmGeneratedFileStream& operator<<(cmGeneratedFileStream& s,
}
// Look up variable; if no value is set, returns an empty string;
-// basically a wrapper that handles the NULL-ptr return from GetOption().
+// basically a wrapper that handles the nullptr return from GetOption().
std::string cmCPackFreeBSDGenerator::var_lookup(const char* var_name)
{
cmValue pv = this->GetOption(var_name);
@@ -402,7 +402,7 @@ int cmCPackFreeBSDGenerator::PackageFiles()
this->packageFileNames.emplace_back(actualPackage);
}
- if (!pkg_initialized() && pkg_init(NULL, NULL) != EPKG_OK) {
+ if (!pkg_initialized() && pkg_init(nullptr, nullptr) != EPKG_OK) {
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Can not initialize FreeBSD libpkg." << std::endl);
return 0;
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index 225711808c..234bc59352 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -28,6 +28,7 @@
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
#include "cmGlobalGenerator.h"
+#include "cmJSONState.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
@@ -265,11 +266,11 @@ int main(int argc, char const* const* argv)
cmCMakePresetsGraph presetsGraph;
auto result = presetsGraph.ReadProjectPresets(workingDirectory);
- if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
+ if (result != true) {
cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
"Could not read presets from "
- << workingDirectory << ": "
- << cmCMakePresetsGraph::ResultToString(result) << '\n');
+ << workingDirectory << ":"
+ << presetsGraph.parseState.GetErrorMessage() << '\n');
return 1;
}
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index 643bc6ff4c..cece98ea19 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -72,11 +72,6 @@ int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring,
if (!this->CTest->GetConfigType().empty()) {
config = this->CTest->GetConfigType().c_str();
}
-#ifdef CMAKE_INTDIR
- if (!config) {
- config = CMAKE_INTDIR;
- }
-#endif
if (config) {
args.push_back("-DCMAKE_BUILD_TYPE:STRING=" + std::string(config));
@@ -256,11 +251,6 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
if (!this->CTest->GetConfigType().empty()) {
config = this->CTest->GetConfigType().c_str();
}
-#ifdef CMAKE_INTDIR
- if (!config) {
- config = CMAKE_INTDIR;
- }
-#endif
if (!config) {
config = "Debug";
}
diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx
index 71787ea9ec..50d69df446 100644
--- a/Source/CTest/cmCTestBuildCommand.cxx
+++ b/Source/CTest/cmCTestBuildCommand.cxx
@@ -89,11 +89,7 @@ cmCTestGenericHandler* cmCTestBuildCommand::InitializeHandler()
}
}
if (cmakeBuildConfiguration.empty()) {
-#ifdef CMAKE_INTDIR
- cmakeBuildConfiguration = CMAKE_INTDIR;
-#else
cmakeBuildConfiguration = "Debug";
-#endif
}
std::string dir = this->CTest->GetCTestConfiguration("BuildDirectory");
diff --git a/Source/CTest/cmCTestCurl.cxx b/Source/CTest/cmCTestCurl.cxx
index 13b0278b2a..8f7d5811bf 100644
--- a/Source/CTest/cmCTestCurl.cxx
+++ b/Source/CTest/cmCTestCurl.cxx
@@ -157,7 +157,7 @@ bool cmCTestCurl::UploadFile(std::string const& local_file,
// Now run off and do what you've been told!
::curl_easy_perform(this->Curl);
::fclose(ftpfile);
- ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, NULL);
+ ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, nullptr);
::curl_slist_free_all(headers);
if (!responseData.empty()) {
diff --git a/Source/CTest/cmCTestResourceSpec.cxx b/Source/CTest/cmCTestResourceSpec.cxx
index 142b07de1a..0e81fa9423 100644
--- a/Source/CTest/cmCTestResourceSpec.cxx
+++ b/Source/CTest/cmCTestResourceSpec.cxx
@@ -10,17 +10,14 @@
#include <cmext/string_view>
-#include <cm3p/json/reader.h>
#include <cm3p/json/value.h>
-#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmJSONHelpers.h"
namespace {
-using JSONHelperBuilder =
- cmJSONHelperBuilder<cmCTestResourceSpec::ReadFileResult>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
@@ -36,165 +33,104 @@ struct TopVersion
};
auto const VersionFieldHelper =
- JSONHelperBuilder::Int(cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_VERSION);
+ JSONHelperBuilder::Int(cmCTestResourceSpecErrors::INVALID_VERSION);
auto const VersionHelper = JSONHelperBuilder::Required<Version>(
- cmCTestResourceSpec::ReadFileResult::NO_VERSION,
- JSONHelperBuilder::Object<Version>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_VERSION)
+ cmCTestResourceSpecErrors::NO_VERSION,
+ JSONHelperBuilder::Object<Version>()
.Bind("major"_s, &Version::Major, VersionFieldHelper)
.Bind("minor"_s, &Version::Minor, VersionFieldHelper));
-auto const RootVersionHelper =
- JSONHelperBuilder::Object<TopVersion>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
- .Bind("version"_s, &TopVersion::Version, VersionHelper, false);
+auto const RootVersionHelper = JSONHelperBuilder::Object<TopVersion>().Bind(
+ "version"_s, &TopVersion::Version, VersionHelper, false);
-cmCTestResourceSpec::ReadFileResult ResourceIdHelper(std::string& out,
- const Json::Value* value)
+bool ResourceIdHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state)
{
- auto result = JSONHelperBuilder::String(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)(out, value);
- if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
- return result;
+ if (!JSONHelperBuilder::String(cmCTestResourceSpecErrors::INVALID_RESOURCE)(
+ out, value, state)) {
+ return false;
}
cmsys::RegularExpressionMatch match;
if (!IdRegex.find(out.c_str(), match)) {
- return cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE;
+ cmCTestResourceSpecErrors::INVALID_RESOURCE(value, state);
+ return false;
}
- return cmCTestResourceSpec::ReadFileResult::READ_OK;
+ return true;
}
auto const ResourceHelper =
- JSONHelperBuilder::Object<cmCTestResourceSpec::Resource>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)
+ JSONHelperBuilder::Object<cmCTestResourceSpec::Resource>()
.Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper)
- .Bind("slots"_s, &cmCTestResourceSpec::Resource::Capacity,
- JSONHelperBuilder::UInt(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, 1),
- false);
+ .Bind(
+ "slots"_s, &cmCTestResourceSpec::Resource::Capacity,
+ JSONHelperBuilder::UInt(cmCTestResourceSpecErrors::INVALID_RESOURCE, 1),
+ false);
auto const ResourceListHelper =
JSONHelperBuilder::Vector<cmCTestResourceSpec::Resource>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE,
- ResourceHelper);
+ cmCTestResourceSpecErrors::INVALID_RESOURCE_TYPE, ResourceHelper);
auto const ResourceMapHelper =
JSONHelperBuilder::MapFilter<std::vector<cmCTestResourceSpec::Resource>>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
- ResourceListHelper, [](const std::string& key) -> bool {
+ cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, ResourceListHelper,
+ [](const std::string& key) -> bool {
cmsys::RegularExpressionMatch match;
return IdentifierRegex.find(key.c_str(), match);
});
auto const SocketSetHelper = JSONHelperBuilder::Vector<
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper);
+ cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, ResourceMapHelper);
-cmCTestResourceSpec::ReadFileResult SocketHelper(
- cmCTestResourceSpec::Socket& out, const Json::Value* value)
+bool SocketHelper(cmCTestResourceSpec::Socket& out, const Json::Value* value,
+ cmJSONState* state)
{
std::vector<
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>
sockets;
- cmCTestResourceSpec::ReadFileResult result = SocketSetHelper(sockets, value);
- if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
- return result;
+ if (!SocketSetHelper(sockets, value, state)) {
+ return false;
}
if (sockets.size() > 1) {
- return cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC;
+ cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC(value, state);
+ return false;
}
if (sockets.empty()) {
out.Resources.clear();
} else {
out.Resources = std::move(sockets[0]);
}
- return cmCTestResourceSpec::ReadFileResult::READ_OK;
+ return true;
}
auto const LocalRequiredHelper =
JSONHelperBuilder::Required<cmCTestResourceSpec::Socket>(
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper);
+ cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, SocketHelper);
-auto const RootHelper = JSONHelperBuilder::Object<cmCTestResourceSpec>(
- cmCTestResourceSpec::ReadFileResult::READ_OK,
- cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
- .Bind("local", &cmCTestResourceSpec::LocalSocket,
- LocalRequiredHelper, false);
+auto const RootHelper = JSONHelperBuilder::Object<cmCTestResourceSpec>().Bind(
+ "local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper, false);
}
-cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile(
- const std::string& filename)
+bool cmCTestResourceSpec::ReadFromJSONFile(const std::string& filename)
{
- cmsys::ifstream fin(filename.c_str());
- if (!fin) {
- return ReadFileResult::FILE_NOT_FOUND;
- }
-
Json::Value root;
- Json::CharReaderBuilder builder;
- if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
- return ReadFileResult::JSON_PARSE_ERROR;
+
+ this->parseState = cmJSONState(filename, &root);
+ if (!this->parseState.errors.empty()) {
+ return false;
}
TopVersion version;
- ReadFileResult result;
- if ((result = RootVersionHelper(version, &root)) !=
- ReadFileResult::READ_OK) {
+ bool result;
+ if ((result = RootVersionHelper(version, &root, &parseState)) != true) {
return result;
}
if (version.Version.Major != 1 || version.Version.Minor != 0) {
- return ReadFileResult::UNSUPPORTED_VERSION;
+ return false;
}
- return RootHelper(*this, &root);
-}
-
-const char* cmCTestResourceSpec::ResultToString(ReadFileResult result)
-{
- switch (result) {
- case ReadFileResult::READ_OK:
- return "OK";
-
- case ReadFileResult::FILE_NOT_FOUND:
- return "File not found";
-
- case ReadFileResult::JSON_PARSE_ERROR:
- return "JSON parse error";
-
- case ReadFileResult::INVALID_ROOT:
- return "Invalid root object";
-
- case ReadFileResult::NO_VERSION:
- return "No version specified";
-
- case ReadFileResult::INVALID_VERSION:
- return "Invalid version object";
-
- case ReadFileResult::UNSUPPORTED_VERSION:
- return "Unsupported version";
-
- case ReadFileResult::INVALID_SOCKET_SPEC:
- return "Invalid socket object";
-
- case ReadFileResult::INVALID_RESOURCE_TYPE:
- return "Invalid resource type object";
-
- case ReadFileResult::INVALID_RESOURCE:
- return "Invalid resource object";
-
- default:
- return "Unknown";
- }
+ return RootHelper(*this, &root, &parseState);
}
bool cmCTestResourceSpec::operator==(const cmCTestResourceSpec& other) const
diff --git a/Source/CTest/cmCTestResourceSpec.h b/Source/CTest/cmCTestResourceSpec.h
index 72628a346a..37ccd72fee 100644
--- a/Source/CTest/cmCTestResourceSpec.h
+++ b/Source/CTest/cmCTestResourceSpec.h
@@ -8,6 +8,12 @@
#include <string>
#include <vector>
+#include "cmJSONState.h"
+
+namespace Json {
+class Value;
+}
+
class cmCTestResourceSpec
{
public:
@@ -31,24 +37,45 @@ public:
};
Socket LocalSocket;
+ cmJSONState parseState;
- enum class ReadFileResult
- {
- READ_OK,
- FILE_NOT_FOUND,
- JSON_PARSE_ERROR,
- INVALID_ROOT,
- NO_VERSION,
- INVALID_VERSION,
- UNSUPPORTED_VERSION,
- INVALID_SOCKET_SPEC, // Can't be INVALID_SOCKET due to a Windows macro
- INVALID_RESOURCE_TYPE,
- INVALID_RESOURCE,
- };
-
- ReadFileResult ReadFromJSONFile(const std::string& filename);
- static const char* ResultToString(ReadFileResult result);
+ bool ReadFromJSONFile(const std::string& filename);
bool operator==(const cmCTestResourceSpec& other) const;
bool operator!=(const cmCTestResourceSpec& other) const;
};
+
+namespace cmCTestResourceSpecErrors {
+const auto FILE_NOT_FOUND = [](const Json::Value*, cmJSONState* state) {
+ state->AddError("File not found");
+};
+const auto JSON_PARSE_ERROR = [](const Json::Value* value,
+ cmJSONState* state) {
+ state->AddErrorAtValue("JSON parse error", value);
+};
+const auto INVALID_ROOT = [](const Json::Value* value, cmJSONState* state) {
+ state->AddErrorAtValue("Invalid root object", value);
+};
+const auto NO_VERSION = [](const Json::Value* value, cmJSONState* state) {
+ state->AddErrorAtValue("No version specified", value);
+};
+const auto INVALID_VERSION = [](const Json::Value* value, cmJSONState* state) {
+ state->AddErrorAtValue("Invalid version object", value);
+};
+const auto UNSUPPORTED_VERSION = [](const Json::Value* value,
+ cmJSONState* state) {
+ state->AddErrorAtValue("Unsupported version", value);
+};
+const auto INVALID_SOCKET_SPEC = [](const Json::Value* value,
+ cmJSONState* state) {
+ state->AddErrorAtValue("Invalid socket object", value);
+};
+const auto INVALID_RESOURCE_TYPE = [](const Json::Value* value,
+ cmJSONState* state) {
+ state->AddErrorAtValue("Invalid resource type object", value);
+};
+const auto INVALID_RESOURCE = [](const Json::Value* value,
+ cmJSONState* state) {
+ state->AddErrorAtValue("Invalid resource object", value);
+};
+}
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 1c8c713345..f693ace4b5 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -37,6 +37,7 @@
#include "cmExecutionStatus.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
+#include "cmJSONState.h"
#include "cmMakefile.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
@@ -1346,12 +1347,11 @@ bool cmCTestTestHandler::ProcessDirectory(std::vector<std::string>& passed,
}
if (!this->ResourceSpecFile.empty()) {
this->UseResourceSpec = true;
- auto result = this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile);
- if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
+ if (!this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile)) {
cmCTestLog(this->CTest, ERROR_MESSAGE,
"Could not read/parse resource spec file "
<< this->ResourceSpecFile << ": "
- << cmCTestResourceSpec::ResultToString(result)
+ << this->ResourceSpec.parseState.GetErrorMessage()
<< std::endl);
return false;
}
@@ -2569,7 +2569,7 @@ bool cmCTestTestHandler::WriteJUnitXML()
xml.EndElement(); // </skipped>
} else if (status == "fail") {
xml.StartElement("failure");
- xml.Attribute("message", result.Reason);
+ xml.Attribute("message", this->GetTestStatus(result));
xml.EndElement(); // </failure>
}
diff --git a/Source/Checks/cm_cxx_features.cmake b/Source/Checks/cm_cxx_features.cmake
index 73e7ef2442..f5b7587593 100644
--- a/Source/Checks/cm_cxx_features.cmake
+++ b/Source/Checks/cm_cxx_features.cmake
@@ -38,6 +38,8 @@ function(cm_check_cxx_feature name)
string(REGEX REPLACE " +0 Warning\\(s\\)" "" check_output "${check_output}")
# Filter out MSBuild output that looks like a warning.
string(REGEX REPLACE "[^\n]*warning MSB[0-9][0-9][0-9][0-9][^\n]*" "" check_output "${check_output}")
+ # Filter out MSVC output that looks like a command-line warning.
+ string(REGEX REPLACE "[^\n]*warning D[0-9][0-9][0-9][0-9][^\n]*" "" check_output "${check_output}")
# Filter out warnings caused by user flags.
string(REGEX REPLACE "[^\n]*warning:[^\n]*-Winvalid-command-line-argument[^\n]*" "" check_output "${check_output}")
# Filter out warnings caused by local configuration.
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake
index 5cfb0e76ea..3dc099f7c8 100644
--- a/Source/Modules/CMakeBuildUtilities.cmake
+++ b/Source/Modules/CMakeBuildUtilities.cmake
@@ -54,7 +54,6 @@ if(BUILD_TESTING)
CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestProcess "${kwsys_folder}")
CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsC "${kwsys_folder}")
CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestsCxx "${kwsys_folder}")
- CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestSharedForward "${kwsys_folder}")
endif()
#---------------------------------------------------------------------
diff --git a/Source/QtDialog/AddCacheEntry.cxx b/Source/QtDialog/AddCacheEntry.cxx
index 1075895b92..7c34c6001c 100644
--- a/Source/QtDialog/AddCacheEntry.cxx
+++ b/Source/QtDialog/AddCacheEntry.cxx
@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "AddCacheEntry.h"
+#include "QCMakeSizeType.h"
#include <QCompleter>
#include <QMetaProperty>
@@ -88,7 +89,7 @@ QString AddCacheEntry::typeString() const
void AddCacheEntry::onCompletionActivated(const QString& text)
{
- int idx = this->VarNames.indexOf(text);
+ cm_qsizetype idx = this->VarNames.indexOf(text);
if (idx != -1) {
QString vartype = this->VarTypes[idx];
for (int i = 0; i < NumTypes; i++) {
diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt
index c90b7ee722..e6e41ec4e9 100644
--- a/Source/QtDialog/CMakeLists.txt
+++ b/Source/QtDialog/CMakeLists.txt
@@ -175,6 +175,7 @@ add_library(
QCMakePresetComboBox.h
QCMakePresetItemModel.cxx
QCMakePresetItemModel.h
+ QCMakeSizeType.h
QCMakeWidgets.cxx
QCMakeWidgets.h
RegexExplorer.cxx
diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx
index 50e8e3aa5a..21ed8c8431 100644
--- a/Source/QtDialog/CMakeSetup.cxx
+++ b/Source/QtDialog/CMakeSetup.cxx
@@ -34,7 +34,7 @@ const cmDocumentationEntry cmDocumentationUsage = {
" cmake-gui [options] <path-to-source>\n"
" cmake-gui [options] <path-to-existing-build>\n"
" cmake-gui [options] -S <path-to-source> -B <path-to-build>\n"
- " cmake-gui [options] --browse-manual"
+ " cmake-gui [options] --browse-manual [<filename>]"
};
const cmDocumentationEntry cmDocumentationOptions[3] = {
@@ -62,7 +62,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin);
int CMakeGUIExec(CMakeSetupDialog* window);
void SetupDefaultQSettings();
-void OpenReferenceManual();
+void OpenReferenceManual(const QString& filename);
int main(int argc, char** argv)
{
@@ -199,7 +199,12 @@ int main(int argc, char** argv)
}
presetName = preset.toStdString();
} else if (arg == "--browse-manual") {
- OpenReferenceManual();
+ ++i;
+ if (i >= args.size()) {
+ OpenReferenceManual("index.html");
+ } else {
+ OpenReferenceManual(args[i]);
+ }
return 0;
}
}
diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx
index 3d4d7265f7..ab778183a3 100644
--- a/Source/QtDialog/CMakeSetupDialog.cxx
+++ b/Source/QtDialog/CMakeSetupDialog.cxx
@@ -32,6 +32,7 @@
#include "QCMake.h"
#include "QCMakeCacheView.h"
+#include "QCMakeSizeType.h"
#include "cmSystemTools.h"
#include "cmVersion.h"
@@ -42,7 +43,7 @@
#include "RegexExplorer.h"
#include "WarningMessagesDialog.h"
-void OpenReferenceManual()
+void OpenReferenceManual(const QString& filename)
{
QString urlFormat("https://cmake.org/cmake/help/v%1.%2/");
QUrl url(urlFormat.arg(QString::number(cmVersion::GetMajorVersion()),
@@ -51,7 +52,7 @@ void OpenReferenceManual()
if (!cmSystemTools::GetHTMLDoc().empty()) {
url = QUrl::fromLocalFile(
QDir(QString::fromStdString(cmSystemTools::GetHTMLDoc()))
- .filePath("index.html"));
+ .filePath(filename));
}
QDesktopServices::openUrl(url);
@@ -212,7 +213,8 @@ CMakeSetupDialog::CMakeSetupDialog()
QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doHelp);
a->setShortcut(QKeySequence::HelpContents);
a = HelpMenu->addAction(tr("CMake Reference Manual"));
- QObject::connect(a, &QAction::triggered, this, OpenReferenceManual);
+ QObject::connect(a, &QAction::triggered, this,
+ [] { OpenReferenceManual("index.html"); });
a = HelpMenu->addAction(tr("About"));
QObject::connect(a, &QAction::triggered, this, &CMakeSetupDialog::doAbout);
@@ -730,13 +732,12 @@ void CMakeSetupDialog::updatePreset(const QString& name)
}
}
-void CMakeSetupDialog::showPresetLoadError(
- const QString& dir, cmCMakePresetsGraph::ReadFileResult result)
+void CMakeSetupDialog::showPresetLoadError(const QString& dir,
+ const QString& message)
{
QMessageBox::warning(
this, "Error Reading CMake Presets",
- QString("Could not read presets from %1: %2")
- .arg(dir, cmCMakePresetsGraph::ResultToString(result)));
+ QString("Could not read presets from %1: %2").arg(dir, message));
}
void CMakeSetupDialog::doBinaryBrowse()
@@ -1119,12 +1120,12 @@ void CMakeSetupDialog::saveBuildPaths(const QStringList& paths)
QSettings settings;
settings.beginGroup("Settings/StartPath");
- int num = paths.count();
+ cm_qsizetype num = paths.count();
if (num > 10) {
num = 10;
}
- for (int i = 0; i < num; i++) {
+ for (cm_qsizetype i = 0; i < num; i++) {
settings.setValue(QString("WhereBuild%1").arg(i), paths[i]);
}
}
diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h
index 8aee70de3c..d4c72cb17b 100644
--- a/Source/QtDialog/CMakeSetupDialog.h
+++ b/Source/QtDialog/CMakeSetupDialog.h
@@ -59,8 +59,7 @@ protected slots:
void updateBinaryDirectory(const QString& dir);
void updatePresets(const QVector<QCMakePreset>& presets);
void updatePreset(const QString& name);
- void showPresetLoadError(const QString& dir,
- cmCMakePresetsGraph::ReadFileResult result);
+ void showPresetLoadError(const QString& dir, const QString& message);
void showProgress(const QString& msg, float percent);
void setEnabledState(bool);
bool setupFirstConfigure();
diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx
index 707eaae2aa..a454cb664a 100644
--- a/Source/QtDialog/FirstConfigure.cxx
+++ b/Source/QtDialog/FirstConfigure.cxx
@@ -1,6 +1,7 @@
#include "FirstConfigure.h"
+#include "QCMakeSizeType.h"
#include <QComboBox>
#include <QRadioButton>
#include <QSettings>
@@ -242,10 +243,12 @@ void StartCompilerSetup::onGeneratorChanged(int index)
// Default to generator platform from environment
if (!DefaultGeneratorPlatform.isEmpty()) {
- int platform_index = platforms.indexOf(DefaultGeneratorPlatform);
+ cm_qsizetype platform_index =
+ platforms.indexOf(DefaultGeneratorPlatform);
if (platform_index != -1) {
// The index is off-by-one due to the first empty item added above.
- this->PlatformOptions->setCurrentIndex(platform_index + 1);
+ this->PlatformOptions->setCurrentIndex(
+ static_cast<int>(platform_index + 1));
}
}
} else {
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index 6b3cb9fca8..6ed24ca501 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -6,6 +6,7 @@
#include <cm/memory>
+#include "QCMakeSizeType.h"
#include <QCoreApplication>
#include <QDir>
#include <QString>
@@ -32,7 +33,6 @@ QCMake::QCMake(QObject* p)
qRegisterMetaType<QCMakePropertyList>();
qRegisterMetaType<QProcessEnvironment>();
qRegisterMetaType<QVector<QCMakePreset>>();
- qRegisterMetaType<cmCMakePresetsGraph::ReadFileResult>();
cmSystemTools::DisableRunCommandOutput();
cmSystemTools::SetRunCommandHideConsole(true);
@@ -326,7 +326,7 @@ void QCMake::setProperties(const QCMakePropertyList& newProps)
QCMakeProperty prop;
prop.Key = QString::fromStdString(key);
- int idx = props.indexOf(prop);
+ cm_qsizetype idx = props.indexOf(prop);
if (idx == -1) {
toremove.append(QString::fromStdString(key));
} else {
@@ -529,9 +529,11 @@ void QCMake::loadPresets()
{
auto result = this->CMakePresetsGraph.ReadProjectPresets(
this->SourceDirectory.toStdString(), true);
- if (result != this->LastLoadPresetsResult &&
- result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
- emit this->presetLoadError(this->SourceDirectory, result);
+ if (result != this->LastLoadPresetsResult && !result) {
+ emit this->presetLoadError(
+ this->SourceDirectory,
+ QString::fromStdString(
+ this->CMakePresetsGraph.parseState.GetErrorMessage(false)));
}
this->LastLoadPresetsResult = result;
diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h
index 8a7e4cb8aa..0890558127 100644
--- a/Source/QtDialog/QCMake.h
+++ b/Source/QtDialog/QCMake.h
@@ -60,7 +60,6 @@ using QCMakePropertyList = QList<QCMakeProperty>;
Q_DECLARE_METATYPE(QCMakeProperty)
Q_DECLARE_METATYPE(QCMakePropertyList)
Q_DECLARE_METATYPE(QProcessEnvironment)
-Q_DECLARE_METATYPE(cmCMakePresetsGraph::ReadFileResult)
/// Qt API for CMake library.
/// Wrapper like class allows for easier integration with
@@ -158,8 +157,7 @@ signals:
/// signal when the selected preset changes
void presetChanged(const QString& name);
/// signal when there's an error reading the presets files
- void presetLoadError(const QString& dir,
- cmCMakePresetsGraph::ReadFileResult error);
+ void presetLoadError(const QString& dir, const QString& error);
/// signal when uninitialized warning changes
void warnUninitializedModeChanged(bool value);
/// signal for progress events
@@ -203,8 +201,7 @@ protected:
QString Toolset;
std::vector<cmake::GeneratorInfo> AvailableGenerators;
cmCMakePresetsGraph CMakePresetsGraph;
- cmCMakePresetsGraph::ReadFileResult LastLoadPresetsResult =
- cmCMakePresetsGraph::ReadFileResult::READ_OK;
+ bool LastLoadPresetsResult = true;
QString PresetName;
QString CMakeExecutable;
QAtomicInt InterruptFlag;
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx
index 6f19b67948..e67e0c29e2 100644
--- a/Source/QtDialog/QCMakeCacheView.cxx
+++ b/Source/QtDialog/QCMakeCacheView.cxx
@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "QCMakeCacheView.h"
+#include "QCMakeSizeType.h"
#include "QCMakeWidgets.h"
#include <QApplication>
#include <QEvent>
@@ -188,7 +189,7 @@ QCMakeCacheModel::~QCMakeCacheModel() = default;
static uint qHash(const QCMakeProperty& p)
{
- return qHash(p.Key);
+ return static_cast<uint>(qHash(p.Key));
}
void QCMakeCacheModel::setShowNewProperties(bool f)
@@ -241,7 +242,7 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props)
bool b = this->blockSignals(true);
this->clear();
- this->NewPropertyCount = newProps.size();
+ this->NewPropertyCount = static_cast<int>(newProps.size());
if (View == FlatView) {
QCMakePropertyList newP = newProps.values();
@@ -297,8 +298,8 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props)
parentItems[1]->setData(1, GroupRole);
root->appendRow(parentItems);
- int num = props2.size();
- for (int i = 0; i < num; i++) {
+ cm_qsizetype num = props2.size();
+ for (cm_qsizetype i = 0; i < num; i++) {
QCMakeProperty const& prop = props2[i];
QList<QStandardItem*> items;
items.append(new QStandardItem());
@@ -319,8 +320,8 @@ void QCMakeCacheModel::setProperties(const QCMakePropertyList& props)
root->appendRow(parentItem);
parentItem->setData(1, GroupRole);
- int num = props2.size();
- for (int i = 0; i < num; i++) {
+ cm_qsizetype num = props2.size();
+ for (cm_qsizetype i = 0; i < num; i++) {
QCMakeProperty const& prop = props2[i];
QList<QStandardItem*> items;
items.append(new QStandardItem());
@@ -349,8 +350,8 @@ void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t)
QCMakePropertyList props = this->properties();
QCMakePropertyList oldProps;
int numNew = this->NewPropertyCount;
- int numTotal = props.count();
- for (int i = numNew; i < numTotal; i++) {
+ cm_qsizetype numTotal = props.count();
+ for (cm_qsizetype i = numNew; i < numTotal; i++) {
oldProps.append(props[i]);
}
diff --git a/Source/QtDialog/QCMakePresetItemModel.cxx b/Source/QtDialog/QCMakePresetItemModel.cxx
index 7ada2a50ac..31a60008ce 100644
--- a/Source/QtDialog/QCMakePresetItemModel.cxx
+++ b/Source/QtDialog/QCMakePresetItemModel.cxx
@@ -2,6 +2,7 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "QCMakePresetItemModel.h"
+#include "QCMakeSizeType.h"
#include <QFont>
QCMakePresetItemModel::QCMakePresetItemModel(QObject* parent)
@@ -27,7 +28,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const
if (index.internalId() == SEPARATOR_INDEX) {
return QVariant{};
}
- auto const& preset = this->m_presets[index.internalId()];
+ auto const& preset =
+ this->m_presets[static_cast<cm_qsizetype>(index.internalId())];
return preset.displayName.isEmpty() ? preset.name : preset.displayName;
}
case Qt::ToolTipRole:
@@ -37,7 +39,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const
if (index.internalId() == SEPARATOR_INDEX) {
return QVariant{};
}
- return this->m_presets[index.internalId()].description;
+ return this->m_presets[static_cast<cm_qsizetype>(index.internalId())]
+ .description;
case Qt::UserRole:
if (index.internalId() == CUSTOM_INDEX) {
return QVariant{};
@@ -45,7 +48,8 @@ QVariant QCMakePresetItemModel::data(const QModelIndex& index, int role) const
if (index.internalId() == SEPARATOR_INDEX) {
return QVariant{};
}
- return QVariant::fromValue(this->m_presets[index.internalId()]);
+ return QVariant::fromValue(
+ this->m_presets[static_cast<cm_qsizetype>(index.internalId())]);
case Qt::FontRole:
if (index.internalId() == CUSTOM_INDEX) {
QFont font;
@@ -64,7 +68,8 @@ Qt::ItemFlags QCMakePresetItemModel::flags(const QModelIndex& index) const
Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
if (index.internalId() != SEPARATOR_INDEX &&
(index.internalId() == CUSTOM_INDEX ||
- this->m_presets[index.internalId()].enabled)) {
+ this->m_presets[static_cast<cm_qsizetype>(index.internalId())]
+ .enabled)) {
flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
return flags;
@@ -78,7 +83,7 @@ int QCMakePresetItemModel::rowCount(const QModelIndex& parent) const
if (this->m_presets.empty()) {
return 1;
}
- return this->m_presets.size() + 2;
+ return static_cast<int>(this->m_presets.size() + 2);
}
int QCMakePresetItemModel::columnCount(const QModelIndex& parent) const
@@ -139,5 +144,5 @@ int QCMakePresetItemModel::presetNameToRow(const QString& name) const
index++;
}
- return this->m_presets.size() + 1;
+ return static_cast<int>(this->m_presets.size() + 1);
}
diff --git a/Source/QtDialog/QCMakeSizeType.h b/Source/QtDialog/QCMakeSizeType.h
new file mode 100644
index 0000000000..b5cac17cbe
--- /dev/null
+++ b/Source/QtDialog/QCMakeSizeType.h
@@ -0,0 +1,12 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <QtGlobal>
+
+// The signed integer type that Qt uses for indexing.
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+using cm_qsizetype = qsizetype;
+#else
+using cm_qsizetype = int;
+#endif
diff --git a/Source/bindexplib.cxx b/Source/bindexplib.cxx
index 52e200c24f..3495aed2f2 100644
--- a/Source/bindexplib.cxx
+++ b/Source/bindexplib.cxx
@@ -281,6 +281,7 @@ public:
// the symbol
const char* scalarPrefix = "??_G";
const char* vectorPrefix = "??_E";
+ const char* vftablePrefix = "??_7";
// The original code had a check for
// symbol.find("real@") == std::string::npos)
// but this disallows member functions with the name "real".
@@ -302,7 +303,8 @@ public:
this->DataSymbols.insert(symbol);
} else {
if (pSymbolTable->Type || !(SectChar & IMAGE_SCN_MEM_READ) ||
- (SectChar & IMAGE_SCN_MEM_EXECUTE)) {
+ (SectChar & IMAGE_SCN_MEM_EXECUTE) ||
+ (symbol.compare(0, 4, vftablePrefix) == 0)) {
this->Symbols.insert(symbol);
}
}
@@ -406,7 +408,7 @@ static bool DumpFile(std::string const& nmPath, const char* filename,
LPVOID lpFileBase;
hFile = CreateFileW(cmsys::Encoding::ToWide(filename).c_str(), GENERIC_READ,
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -414,7 +416,8 @@ static bool DumpFile(std::string const& nmPath, const char* filename,
return false;
}
- hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
+ hFileMapping =
+ CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (hFileMapping == 0) {
CloseHandle(hFile);
fprintf(stderr, "Couldn't open file mapping with CreateFileMapping()\n");
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index 831e9c7d59..b1398db142 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -49,6 +49,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
bool append = false;
bool uses_terminal = false;
bool command_expand_lists = false;
+ bool depends_explicit_only =
+ mf.IsOn("CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY");
std::string implicit_depends_lang;
cmImplicitDependsList implicit_depends;
@@ -104,6 +106,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
MAKE_STATIC_KEYWORD(USES_TERMINAL);
MAKE_STATIC_KEYWORD(VERBATIM);
MAKE_STATIC_KEYWORD(WORKING_DIRECTORY);
+ MAKE_STATIC_KEYWORD(DEPENDS_EXPLICIT_ONLY);
#undef MAKE_STATIC_KEYWORD
static std::unordered_set<std::string> const keywords{
keyAPPEND,
@@ -126,7 +129,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
keyTARGET,
keyUSES_TERMINAL,
keyVERBATIM,
- keyWORKING_DIRECTORY
+ keyWORKING_DIRECTORY,
+ keyDEPENDS_EXPLICIT_ONLY
};
for (std::string const& copy : args) {
@@ -155,6 +159,8 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
uses_terminal = true;
} else if (copy == keyCOMMAND_EXPAND_LISTS) {
command_expand_lists = true;
+ } else if (copy == keyDEPENDS_EXPLICIT_ONLY) {
+ depends_explicit_only = true;
} else if (copy == keyTARGET) {
doing = doing_target;
} else if (copy == keyARGS) {
@@ -329,6 +335,7 @@ bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
cc->SetDepfile(depfile);
cc->SetJobPool(job_pool);
cc->SetCommandExpandLists(command_expand_lists);
+ cc->SetDependsExplicitOnly(depends_explicit_only);
if (source.empty() && output.empty()) {
// Source is empty, use the target.
mf.AddCustomCommandToTarget(target, cctype, std::move(cc));
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index a1830f9f8e..2c3ee9b5ef 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -95,7 +95,7 @@ typename Range::const_iterator cmRemoveIndices(Range& r, InputRange const& rem)
}
template <typename Range, typename MatchRange>
-typename Range::const_iterator cmRemoveMatching(Range& r, MatchRange const& m)
+auto cmRemoveMatching(Range& r, MatchRange const& m) -> decltype(r.begin())
{
return std::remove_if(r.begin(), r.end(),
ContainerAlgorithms::BinarySearcher<MatchRange>(m));
diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx
index 79e39e9c52..918f563387 100644
--- a/Source/cmBinUtilsWindowsPELinker.cxx
+++ b/Source/cmBinUtilsWindowsPELinker.cxx
@@ -3,7 +3,10 @@
#include "cmBinUtilsWindowsPELinker.h"
+#include <algorithm>
+#include <iterator>
#include <sstream>
+#include <utility>
#include <vector>
#include <cm/memory>
@@ -16,6 +19,27 @@
#ifdef _WIN32
# include <windows.h>
+
+# include "cmsys/Encoding.hxx"
+#endif
+
+#ifdef _WIN32
+namespace {
+
+void ReplaceWithActualNameCasing(std::string& path)
+{
+ WIN32_FIND_DATAW findData;
+ HANDLE hFind = ::FindFirstFileW(
+ cmsys::Encoding::ToWindowsExtendedPath(path).c_str(), &findData);
+
+ if (hFind != INVALID_HANDLE_VALUE) {
+ auto onDiskName = cmsys::Encoding::ToNarrow(findData.cFileName);
+ ::FindClose(hFind);
+ path.replace(path.end() - onDiskName.size(), path.end(), onDiskName);
+ }
+}
+
+}
#endif
cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
@@ -60,29 +84,47 @@ bool cmBinUtilsWindowsPELinker::ScanDependencies(
if (!this->Tool->GetFileInfo(file, needed)) {
return false;
}
- for (auto& n : needed) {
- n = cmSystemTools::LowerCase(n);
- }
+
+ struct WinPEDependency
+ {
+ WinPEDependency(std::string o)
+ : Original(std::move(o))
+ , LowerCase(cmSystemTools::LowerCase(Original))
+ {
+ }
+ std::string const Original;
+ std::string const LowerCase;
+ };
+
+ std::vector<WinPEDependency> depends;
+ depends.reserve(needed.size());
+ std::move(needed.begin(), needed.end(), std::back_inserter(depends));
std::string origin = cmSystemTools::GetFilenamePath(file);
- for (auto const& lib : needed) {
- if (!this->Archive->IsPreExcluded(lib)) {
+ for (auto const& lib : depends) {
+ if (!this->Archive->IsPreExcluded(lib.LowerCase)) {
std::string path;
bool resolved = false;
- if (!this->ResolveDependency(lib, origin, path, resolved)) {
+ if (!this->ResolveDependency(lib.LowerCase, origin, path, resolved)) {
return false;
}
if (resolved) {
if (!this->Archive->IsPostExcluded(path)) {
+#ifdef _WIN32
+ ReplaceWithActualNameCasing(path);
+#else
+ path.replace(path.end() - lib.Original.size(), path.end(),
+ lib.Original);
+#endif
bool unique;
- this->Archive->AddResolvedPath(lib, path, unique);
+ this->Archive->AddResolvedPath(lib.Original, path, unique);
if (unique &&
!this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
return false;
}
}
} else {
- this->Archive->AddUnresolvedPath(lib);
+ this->Archive->AddUnresolvedPath(lib.Original);
}
}
}
diff --git a/Source/cmCMakePresetErrors.h b/Source/cmCMakePresetErrors.h
new file mode 100644
index 0000000000..0db391e1ad
--- /dev/null
+++ b/Source/cmCMakePresetErrors.h
@@ -0,0 +1,245 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
+#include "cmStringAlgorithms.h"
+
+namespace cmCMakePresetErrors {
+const auto getPreset = [](cmJSONState* state) -> const Json::Value* {
+ if (state->parseStack.size() < 2) {
+ return nullptr;
+ }
+ std::string firstKey = state->parseStack[0].first;
+ if (firstKey == "configurePresets" || firstKey == "packagePresets" ||
+ firstKey == "buildPresets" || firstKey == "testPresets") {
+ return state->parseStack[1].second;
+ }
+ return nullptr;
+};
+const auto getPresetName = [](cmJSONState* state) -> std::string {
+#if !defined(CMAKE_BOOTSTRAP)
+ const Json::Value* preset = getPreset(state);
+ if (preset != nullptr && preset->isMember("name")) {
+ return preset->operator[]("name").asString();
+ }
+#endif
+ return "";
+};
+const auto getVariableName = [](cmJSONState* state) -> std::string {
+ std::string var = state->key_after("cacheVariables");
+ std::string errMsg = cmStrCat("variable \"", var, "\"");
+ errMsg = cmStrCat(errMsg, " for preset \"", getPresetName(state), "\"");
+ return errMsg;
+};
+const auto FILE_NOT_FOUND = [](const std::string& filename,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("File not found: ", filename));
+};
+const auto INVALID_ROOT = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid root object", value);
+};
+const auto NO_VERSION = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("No \"version\" field", value);
+};
+const auto INVALID_VERSION = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid \"version\" field", value);
+};
+const auto UNRECOGNIZED_VERSION = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Unrecognized \"version\" field", value);
+};
+const auto INVALID_PRESETS = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid \"configurePresets\" field", value);
+};
+const auto INVALID_PRESET = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid preset", value);
+};
+const auto INVALID_PRESET_NAMED = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Invalid preset: \"", presetName, "\""));
+};
+const auto INVALID_VARIABLE = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ std::string var = cmCMakePresetErrors::getVariableName(state);
+ state->AddErrorAtValue(cmStrCat("Invalid CMake ", var), value);
+};
+const auto DUPLICATE_PRESETS = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Duplicate preset: \"", presetName, "\""));
+};
+const auto CYCLIC_PRESET_INHERITANCE = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(
+ cmStrCat("Cyclic preset inheritance for preset \"", presetName, "\""));
+};
+const auto INHERITED_PRESET_UNREACHABLE_FROM_FILE =
+ [](const std::string& presetName, cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Inherited preset \"", presetName,
+ "\" is unreachable from preset's file"));
+};
+const auto CONFIGURE_PRESET_UNREACHABLE_FROM_FILE =
+ [](const std::string& presetName, cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Configure preset \"", presetName,
+ "\" is unreachable from preset's file"));
+};
+const auto INVALID_MACRO_EXPANSION = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Invalid macro expansion in \"", presetName, "\""));
+};
+const auto BUILD_TEST_PRESETS_UNSUPPORTED = [](const Json::Value*,
+ cmJSONState* state) -> void {
+ state->AddError("File version must be 2 or higher for build and test preset "
+ "support");
+};
+const auto PACKAGE_PRESETS_UNSUPPORTED = [](const Json::Value*,
+ cmJSONState* state) -> void {
+ state->AddError(
+ "File version must be 6 or higher for package preset support");
+};
+const auto WORKFLOW_PRESETS_UNSUPPORTED = [](const Json::Value*,
+ cmJSONState* state) -> void {
+ state->AddError(
+ "File version must be 6 or higher for workflow preset support");
+};
+const auto INCLUDE_UNSUPPORTED = [](const Json::Value*,
+ cmJSONState* state) -> void {
+ state->AddError("File version must be 4 or higher for include support");
+};
+const auto INVALID_INCLUDE = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid \"include\" field", value);
+};
+const auto INVALID_CONFIGURE_PRESET = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(
+ cmStrCat(R"(Invalid "configurePreset": ")", presetName, "\""));
+};
+const auto INSTALL_PREFIX_UNSUPPORTED = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue(
+ "File version must be 3 or higher for installDir preset "
+ "support",
+ value);
+};
+const auto CONDITION_UNSUPPORTED = [](cmJSONState* state) -> void {
+ state->AddError("File version must be 3 or higher for condition support");
+};
+const auto TOOLCHAIN_FILE_UNSUPPORTED = [](cmJSONState* state) -> void {
+ state->AddError("File version must be 3 or higher for toolchainFile preset "
+ "support");
+};
+const auto CYCLIC_INCLUDE = [](const std::string& file,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Cyclic include among preset files: ", file));
+};
+const auto TEST_OUTPUT_TRUNCATION_UNSUPPORTED =
+ [](cmJSONState* state) -> void {
+ state->AddError("File version must be 5 or higher for testOutputTruncation "
+ "preset support");
+};
+const auto INVALID_WORKFLOW_STEPS = [](const std::string& workflowStep,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Invalid workflow step \"", workflowStep, "\""));
+};
+const auto NO_WORKFLOW_STEPS = [](const std::string& presetName,
+ cmJSONState* state) -> void {
+ state->AddError(
+ cmStrCat("No workflow steps specified for \"", presetName, "\""));
+};
+const auto FIRST_WORKFLOW_STEP_NOT_CONFIGURE = [](const std::string& stepName,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("First workflow step \"", stepName,
+ "\" must be a configure step"));
+};
+const auto CONFIGURE_WORKFLOW_STEP_NOT_FIRST = [](const std::string& stepName,
+ cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Configure workflow step \"", stepName,
+ "\" must be the first step"));
+};
+const auto WORKFLOW_STEP_UNREACHABLE_FROM_FILE =
+ [](const std::string& workflowStep, cmJSONState* state) -> void {
+ state->AddError(cmStrCat("Workflow step \"", workflowStep,
+ "\" is unreachable from preset's file"));
+};
+const auto CTEST_JUNIT_UNSUPPORTED = [](cmJSONState* state) -> void {
+ state->AddError(
+ "File version must be 6 or higher for CTest JUnit output support");
+};
+const auto TRACE_UNSUPPORTED = [](cmJSONState* state) -> void {
+ state->AddError("File version must be 7 or higher for trace preset support");
+};
+const auto UNRECOGNIZED_CMAKE_VERSION = [](const std::string& version,
+ int current, int required) {
+ return [version, current, required](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue(cmStrCat("\"cmakeMinimumRequired\" ", version,
+ " version ", required,
+ " must be less than ", current),
+ value);
+ };
+};
+const auto INVALID_PRESET_NAME = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ std::string errMsg = "Invalid Preset Name";
+ if (value && value->isConvertibleTo(Json::ValueType::stringValue) &&
+ !value->asString().empty()) {
+ errMsg = cmStrCat(errMsg, ": ", value->asString());
+ }
+ state->AddErrorAtValue(errMsg, value);
+};
+const auto INVALID_CONDITION = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ state->AddErrorAtValue(
+ cmStrCat("Invalid condition for preset \"", getPresetName(state), "\""),
+ value);
+};
+const auto INVALID_CONDITION_OBJECT =
+ [](JsonErrors::ObjectError errorType,
+ const Json::Value::Members& extraFields) {
+ return JsonErrors::INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState* state) -> std::string {
+ return cmStrCat(" condition for preset \"", getPresetName(state),
+ "\"");
+ })(errorType, extraFields);
+ };
+const auto INVALID_VARIABLE_OBJECT =
+ [](JsonErrors::ObjectError errorType,
+ const Json::Value::Members& extraFields) {
+ return JsonErrors::INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState* state) -> std::string {
+ return getVariableName(state);
+ })(errorType, extraFields);
+ };
+const auto INVALID_PRESET_OBJECT =
+ [](JsonErrors::ObjectError errorType,
+ const Json::Value::Members& extraFields) {
+ return JsonErrors::INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState*) -> std::string {
+ return "Preset";
+ })(errorType, extraFields);
+ };
+const auto INVALID_ROOT_OBJECT = [](JsonErrors::ObjectError errorType,
+ const Json::Value::Members& extraFields) {
+ return JsonErrors::INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState*) -> std::string {
+ return "root object";
+ })(errorType, extraFields);
+};
+const auto PRESET_MISSING_FIELD = [](const std::string& presetName,
+ const std::string& missingField,
+ cmJSONState* state) {
+ state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"",
+ missingField, "\""));
+};
+}
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx
index 7325e447df..13e8bad1f1 100644
--- a/Source/cmCMakePresetsGraph.cxx
+++ b/Source/cmCMakePresetsGraph.cxx
@@ -14,6 +14,7 @@
#include "cmsys/RegularExpression.hxx"
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
@@ -39,7 +40,6 @@ enum class CycleStatus
Verified,
};
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
using BuildPreset = cmCMakePresetsGraph::BuildPreset;
using TestPreset = cmCMakePresetsGraph::TestPreset;
@@ -81,17 +81,18 @@ void InheritVector(std::vector<T>& child, const std::vector<T>& parent)
* inheritance.
*/
template <class T>
-ReadFileResult VisitPreset(
+bool VisitPreset(
T& preset,
std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
- std::map<std::string, CycleStatus> cycleStatus,
- const cmCMakePresetsGraph& graph)
+ std::map<std::string, CycleStatus> cycleStatus, cmCMakePresetsGraph& graph)
{
switch (cycleStatus[preset.Name]) {
case CycleStatus::InProgress:
- return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
+ cmCMakePresetErrors::CYCLIC_PRESET_INHERITANCE(preset.Name,
+ &graph.parseState);
+ return false;
case CycleStatus::Verified:
- return ReadFileResult::READ_OK;
+ return true;
default:
break;
}
@@ -99,28 +100,41 @@ ReadFileResult VisitPreset(
cycleStatus[preset.Name] = CycleStatus::InProgress;
if (preset.Environment.count("") != 0) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+ return false;
}
- CHECK_OK(preset.VisitPresetBeforeInherit());
+ bool result = preset.VisitPresetBeforeInherit();
+ if (!result) {
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+ return false;
+ }
for (auto const& i : preset.Inherits) {
auto parent = presets.find(i);
if (parent == presets.end()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
+ &graph.parseState);
+ return false;
}
auto& parentPreset = parent->second.Unexpanded;
if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) {
- return ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE;
+ cmCMakePresetErrors::INHERITED_PRESET_UNREACHABLE_FROM_FILE(
+ preset.Name, &graph.parseState);
+ return false;
}
- auto result = VisitPreset(parentPreset, presets, cycleStatus, graph);
- if (result != ReadFileResult::READ_OK) {
- return result;
+ if (!VisitPreset(parentPreset, presets, cycleStatus, graph)) {
+ return false;
}
- CHECK_OK(preset.VisitPresetInherit(parentPreset));
+ result = preset.VisitPresetInherit(parentPreset);
+ if (!result) {
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
+ &graph.parseState);
+ return false;
+ }
for (auto const& v : parentPreset.Environment) {
preset.Environment.insert(v);
@@ -135,16 +149,21 @@ ReadFileResult VisitPreset(
preset.ConditionEvaluator.reset();
}
- CHECK_OK(preset.VisitPresetAfterInherit(graph.GetVersion(preset)));
+ result = preset.VisitPresetAfterInherit(graph.GetVersion(preset),
+ &graph.parseState);
+ if (!result) {
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+ return false;
+ }
cycleStatus[preset.Name] = CycleStatus::Verified;
- return ReadFileResult::READ_OK;
+ return true;
}
template <class T>
-ReadFileResult ComputePresetInheritance(
+bool ComputePresetInheritance(
std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
- const cmCMakePresetsGraph& graph)
+ cmCMakePresetsGraph& graph)
{
std::map<std::string, CycleStatus> cycleStatus;
for (auto const& it : presets) {
@@ -153,13 +172,12 @@ ReadFileResult ComputePresetInheritance(
for (auto& it : presets) {
auto& preset = it.second.Unexpanded;
- auto result = VisitPreset<T>(preset, presets, cycleStatus, graph);
- if (result != ReadFileResult::READ_OK) {
- return result;
+ if (!VisitPreset<T>(preset, presets, cycleStatus, graph)) {
+ return false;
}
}
- return ReadFileResult::READ_OK;
+ return true;
}
constexpr const char* ValidPrefixes[] = {
@@ -338,7 +356,7 @@ bool ExpandMacros(const cmCMakePresetsGraph& /*graph*/,
}
template <class T>
-bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
+bool ExpandMacros(cmCMakePresetsGraph& graph, const T& preset,
cm::optional<T>& out)
{
out.emplace(preset);
@@ -448,6 +466,8 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
graph.GetVersion(preset))) {
case ExpandMacroResult::Error:
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
+ &graph.parseState);
return false;
case ExpandMacroResult::Ignore:
out.reset();
@@ -462,6 +482,8 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
cm::optional<bool> result;
if (!preset.ConditionEvaluator->Evaluate(
macroExpanders, graph.GetVersion(preset), result)) {
+ cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
+ &graph.parseState);
return false;
}
if (!result) {
@@ -594,39 +616,44 @@ ExpandMacroResult ExpandMacro(std::string& out,
}
template <typename T>
-ReadFileResult SetupWorkflowConfigurePreset(
- const T& preset, const ConfigurePreset*& configurePreset)
+bool SetupWorkflowConfigurePreset(const T& preset,
+ const ConfigurePreset*& configurePreset,
+ cmJSONState* state)
{
if (preset.ConfigurePreset != configurePreset->Name) {
- return ReadFileResult::INVALID_WORKFLOW_STEPS;
+ cmCMakePresetErrors::INVALID_WORKFLOW_STEPS(configurePreset->Name, state);
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
template <>
-ReadFileResult SetupWorkflowConfigurePreset<ConfigurePreset>(
- const ConfigurePreset& preset, const ConfigurePreset*& configurePreset)
+bool SetupWorkflowConfigurePreset<ConfigurePreset>(
+ const ConfigurePreset& preset, const ConfigurePreset*& configurePreset,
+ cmJSONState*)
{
configurePreset = &preset;
- return ReadFileResult::READ_OK;
+ return true;
}
template <typename T>
-ReadFileResult TryReachPresetFromWorkflow(
+bool TryReachPresetFromWorkflow(
const WorkflowPreset& origin,
const std::map<std::string, PresetPair<T>>& presets, const std::string& name,
- const ConfigurePreset*& configurePreset)
+ const ConfigurePreset*& configurePreset, cmJSONState* state)
{
auto it = presets.find(name);
if (it == presets.end()) {
- return ReadFileResult::INVALID_WORKFLOW_STEPS;
+ cmCMakePresetErrors::INVALID_WORKFLOW_STEPS(name, state);
+ return false;
}
if (!origin.OriginFile->ReachableFiles.count(
it->second.Unexpanded.OriginFile)) {
- return ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE;
+ cmCMakePresetErrors::WORKFLOW_STEP_UNREACHABLE_FROM_FILE(name, state);
+ return false;
}
return SetupWorkflowConfigurePreset<T>(it->second.Unexpanded,
- configurePreset);
+ configurePreset, state);
}
}
@@ -722,8 +749,7 @@ bool cmCMakePresetsGraphInternal::NotCondition::Evaluate(
return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit(
+bool cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit(
const cmCMakePresetsGraph::Preset& parentPreset)
{
auto& preset = *this;
@@ -753,50 +779,52 @@ cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit(
preset.CacheVariables.insert(v);
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::ConfigurePreset::VisitPresetBeforeInherit()
+bool cmCMakePresetsGraph::ConfigurePreset::VisitPresetBeforeInherit()
{
auto& preset = *this;
if (preset.Environment.count("") != 0) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::ConfigurePreset::VisitPresetAfterInherit(int version)
+bool cmCMakePresetsGraph::ConfigurePreset::VisitPresetAfterInherit(
+ int version, cmJSONState* state)
{
auto& preset = *this;
if (!preset.Hidden) {
if (version < 3) {
if (preset.Generator.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::PRESET_MISSING_FIELD(preset.Name, "generator",
+ state);
+ return false;
}
if (preset.BinaryDir.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::PRESET_MISSING_FIELD(preset.Name, "binaryDir",
+ state);
+ return false;
}
}
if (preset.WarnDev == false && preset.ErrorDev == true) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
if (preset.CacheVariables.count("") != 0) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::BuildPreset::VisitPresetInherit(
+bool cmCMakePresetsGraph::BuildPreset::VisitPresetInherit(
const cmCMakePresetsGraph::Preset& parentPreset)
{
auto& preset = *this;
@@ -815,21 +843,20 @@ cmCMakePresetsGraph::BuildPreset::VisitPresetInherit(
preset.ResolvePackageReferences = parent.ResolvePackageReferences;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit(int /* version */)
+bool cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit(
+ int /* version */, cmJSONState* /*stat*/)
{
auto& preset = *this;
if (!preset.Hidden && preset.ConfigurePreset.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
+bool cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
const cmCMakePresetsGraph::Preset& parentPreset)
{
auto& preset = *this;
@@ -928,21 +955,20 @@ cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
}
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(int /* version */)
+bool cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(
+ int /* version */, cmJSONState* /*state*/)
{
auto& preset = *this;
if (!preset.Hidden && preset.ConfigurePreset.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
+bool cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
const cmCMakePresetsGraph::Preset& parentPreset)
{
auto& preset = *this;
@@ -966,30 +992,29 @@ cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
InheritString(preset.PackageDirectory, parent.PackageDirectory);
InheritString(preset.VendorName, parent.VendorName);
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(int /* version */)
+bool cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(
+ int /* version */, cmJSONState* /*state*/)
{
auto& preset = *this;
if (!preset.Hidden && preset.ConfigurePreset.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit(
+bool cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit(
const cmCMakePresetsGraph::Preset& /*parentPreset*/)
{
- return ReadFileResult::READ_OK;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::WorkflowPreset::VisitPresetAfterInherit(int /* version */)
+bool cmCMakePresetsGraph::WorkflowPreset::VisitPresetAfterInherit(
+ int /* version */, cmJSONState* /*state*/)
{
- return ReadFileResult::READ_OK;
+ return true;
}
std::string cmCMakePresetsGraph::GetFilename(const std::string& sourceDir)
@@ -1002,22 +1027,21 @@ std::string cmCMakePresetsGraph::GetUserFilename(const std::string& sourceDir)
return cmStrCat(sourceDir, "/CMakeUserPresets.json");
}
-cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadProjectPresets(
- const std::string& sourceDir, bool allowNoFiles)
+bool cmCMakePresetsGraph::ReadProjectPresets(const std::string& sourceDir,
+ bool allowNoFiles)
{
this->SourceDir = sourceDir;
this->ClearPresets();
- auto result = this->ReadProjectPresetsInternal(allowNoFiles);
- if (result != ReadFileResult::READ_OK) {
+ if (!this->ReadProjectPresetsInternal(allowNoFiles)) {
this->ClearPresets();
+ return false;
}
- return result;
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult
-cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
+bool cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
{
bool haveOneFile = false;
@@ -1025,21 +1049,17 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
std::string filename = GetUserFilename(this->SourceDir);
std::vector<File*> inProgressFiles;
if (cmSystemTools::FileExists(filename)) {
- auto result =
- this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
- inProgressFiles, file, this->errors);
- if (result != ReadFileResult::READ_OK) {
- return result;
+ if (!this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
+ inProgressFiles, file, this->errors)) {
+ return false;
}
haveOneFile = true;
} else {
filename = GetFilename(this->SourceDir);
if (cmSystemTools::FileExists(filename)) {
- auto result =
- this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
- inProgressFiles, file, this->errors);
- if (result != ReadFileResult::READ_OK) {
- return result;
+ if (!this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
+ inProgressFiles, file, this->errors)) {
+ return false;
}
haveOneFile = true;
}
@@ -1047,19 +1067,28 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
assert(inProgressFiles.empty());
if (!haveOneFile) {
- return allowNoFiles ? ReadFileResult::READ_OK
- : ReadFileResult::FILE_NOT_FOUND;
+ if (allowNoFiles) {
+ return true;
+ }
+ cmCMakePresetErrors::FILE_NOT_FOUND(filename, &this->parseState);
+ return false;
}
- CHECK_OK(ComputePresetInheritance(this->ConfigurePresets, *this));
- CHECK_OK(ComputePresetInheritance(this->BuildPresets, *this));
- CHECK_OK(ComputePresetInheritance(this->TestPresets, *this));
- CHECK_OK(ComputePresetInheritance(this->PackagePresets, *this));
- CHECK_OK(ComputePresetInheritance(this->WorkflowPresets, *this));
+ bool result = ComputePresetInheritance(this->ConfigurePresets, *this) &&
+ ComputePresetInheritance(this->ConfigurePresets, *this) &&
+ ComputePresetInheritance(this->BuildPresets, *this) &&
+ ComputePresetInheritance(this->TestPresets, *this) &&
+ ComputePresetInheritance(this->PackagePresets, *this) &&
+ ComputePresetInheritance(this->WorkflowPresets, *this);
+ if (!result) {
+ return false;
+ }
for (auto& it : this->ConfigurePresets) {
if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
- return ReadFileResult::INVALID_MACRO_EXPANSION;
+ cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
+ &this->parseState);
+ return false;
}
}
@@ -1068,11 +1097,15 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
const auto configurePreset =
this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
if (configurePreset == this->ConfigurePresets.end()) {
- return ReadFileResult::INVALID_CONFIGURE_PRESET;
+ cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
+ &this->parseState);
+ return false;
}
if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
configurePreset->second.Unexpanded.OriginFile)) {
- return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
+ cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+ it.first, &this->parseState);
+ return false;
}
if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
@@ -1083,7 +1116,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
}
if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
- return ReadFileResult::INVALID_MACRO_EXPANSION;
+ cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
+ &this->parseState);
+ return false;
}
}
@@ -1092,11 +1127,15 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
const auto configurePreset =
this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
if (configurePreset == this->ConfigurePresets.end()) {
- return ReadFileResult::INVALID_CONFIGURE_PRESET;
+ cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
+ &this->parseState);
+ return false;
}
if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
configurePreset->second.Unexpanded.OriginFile)) {
- return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
+ cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+ it.first, &this->parseState);
+ return false;
}
if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
@@ -1107,7 +1146,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
}
if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
- return ReadFileResult::INVALID_MACRO_EXPANSION;
+ cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
+ &this->parseState);
+ return false;
}
}
@@ -1116,11 +1157,15 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
const auto configurePreset =
this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
if (configurePreset == this->ConfigurePresets.end()) {
- return ReadFileResult::INVALID_CONFIGURE_PRESET;
+ cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
+ &this->parseState);
+ return false;
}
if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
configurePreset->second.Unexpanded.OriginFile)) {
- return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
+ cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+ it.first, &this->parseState);
+ return false;
}
if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
@@ -1131,7 +1176,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
}
if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
- return ReadFileResult::INVALID_MACRO_EXPANSION;
+ cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
+ &this->parseState);
+ return false;
}
}
@@ -1141,126 +1188,56 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
const ConfigurePreset* configurePreset = nullptr;
for (auto const& step : it.second.Unexpanded.Steps) {
if (configurePreset == nullptr && step.PresetType != Type::Configure) {
- return ReadFileResult::INVALID_WORKFLOW_STEPS;
+ cmCMakePresetErrors::FIRST_WORKFLOW_STEP_NOT_CONFIGURE(
+ step.PresetName, &this->parseState);
+ return false;
}
if (configurePreset != nullptr && step.PresetType == Type::Configure) {
- return ReadFileResult::INVALID_WORKFLOW_STEPS;
+ cmCMakePresetErrors::CONFIGURE_WORKFLOW_STEP_NOT_FIRST(
+ step.PresetName, &this->parseState);
+ return false;
}
- ReadFileResult result;
switch (step.PresetType) {
case Type::Configure:
result = TryReachPresetFromWorkflow(
it.second.Unexpanded, this->ConfigurePresets, step.PresetName,
- configurePreset);
+ configurePreset, &this->parseState);
break;
case Type::Build:
result = TryReachPresetFromWorkflow(
it.second.Unexpanded, this->BuildPresets, step.PresetName,
- configurePreset);
+ configurePreset, &this->parseState);
break;
case Type::Test:
- result =
- TryReachPresetFromWorkflow(it.second.Unexpanded, this->TestPresets,
- step.PresetName, configurePreset);
+ result = TryReachPresetFromWorkflow(
+ it.second.Unexpanded, this->TestPresets, step.PresetName,
+ configurePreset, &this->parseState);
break;
case Type::Package:
result = TryReachPresetFromWorkflow(
it.second.Unexpanded, this->PackagePresets, step.PresetName,
- configurePreset);
+ configurePreset, &this->parseState);
break;
}
- if (result != ReadFileResult::READ_OK) {
- return result;
+ if (!result) {
+ return false;
}
}
if (configurePreset == nullptr) {
- return ReadFileResult::INVALID_WORKFLOW_STEPS;
+ cmCMakePresetErrors::NO_WORKFLOW_STEPS(it.first, &this->parseState);
+ return false;
}
if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
- return ReadFileResult::INVALID_MACRO_EXPANSION;
+ cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
+ &this->parseState);
+ return false;
}
}
- return ReadFileResult::READ_OK;
-}
-
-const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
-{
- switch (result) {
- case ReadFileResult::READ_OK:
- return "OK";
- case ReadFileResult::FILE_NOT_FOUND:
- return "File not found";
- case ReadFileResult::JSON_PARSE_ERROR:
- return "JSON parse error";
- case ReadFileResult::INVALID_ROOT:
- return "Invalid root object";
- case ReadFileResult::NO_VERSION:
- return "No \"version\" field";
- case ReadFileResult::INVALID_VERSION:
- return "Invalid \"version\" field";
- case ReadFileResult::UNRECOGNIZED_VERSION:
- return "Unrecognized \"version\" field";
- case ReadFileResult::INVALID_CMAKE_VERSION:
- return "Invalid \"cmakeMinimumRequired\" field";
- case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
- return "\"cmakeMinimumRequired\" version too new";
- case ReadFileResult::INVALID_PRESETS:
- return "Invalid \"configurePresets\" field";
- case ReadFileResult::INVALID_PRESET:
- return "Invalid preset";
- case ReadFileResult::INVALID_VARIABLE:
- return "Invalid CMake variable definition";
- case ReadFileResult::DUPLICATE_PRESETS:
- return "Duplicate presets";
- case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
- return "Cyclic preset inheritance";
- case ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE:
- return "Inherited preset is unreachable from preset's file";
- case ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE:
- return "Configure preset is unreachable from preset's file";
- case ReadFileResult::INVALID_MACRO_EXPANSION:
- return "Invalid macro expansion";
- case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED:
- return "File version must be 2 or higher for build and test preset "
- "support.";
- case ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED:
- return "File version must be 6 or higher for package preset support";
- case ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED:
- return "File version must be 6 or higher for workflow preset support";
- case ReadFileResult::INCLUDE_UNSUPPORTED:
- return "File version must be 4 or higher for include support";
- case ReadFileResult::INVALID_INCLUDE:
- return "Invalid \"include\" field";
- case ReadFileResult::INVALID_CONFIGURE_PRESET:
- return "Invalid \"configurePreset\" field";
- case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
- return "File version must be 3 or higher for installDir preset "
- "support.";
- case ReadFileResult::INVALID_CONDITION:
- return "Invalid preset condition";
- case ReadFileResult::CONDITION_UNSUPPORTED:
- return "File version must be 3 or higher for condition support";
- case ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED:
- return "File version must be 3 or higher for toolchainFile preset "
- "support.";
- case ReadFileResult::CYCLIC_INCLUDE:
- return "Cyclic include among preset files";
- case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED:
- return "File version must be 5 or higher for testOutputTruncation "
- "preset support.";
- case ReadFileResult::INVALID_WORKFLOW_STEPS:
- return "Invalid workflow steps";
- case ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE:
- return "Workflow step is unreachable from preset's file";
- case ReadFileResult::CTEST_JUNIT_UNSUPPORTED:
- return "File version must be 6 or higher for CTest JUnit output support";
- }
-
- return "Unknown error";
+ return true;
}
void cmCMakePresetsGraph::ClearPresets()
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h
index 17c902ba62..7844624509 100644
--- a/Source/cmCMakePresetsGraph.h
+++ b/Source/cmCMakePresetsGraph.h
@@ -14,6 +14,9 @@
#include <cm/optional>
+#include "cmJSONState.h"
+#include "cmStateTypes.h"
+
#include "CTest/cmCTestTypes.h"
enum class PackageResolveMode;
@@ -21,49 +24,22 @@ enum class PackageResolveMode;
class cmCMakePresetsGraph
{
public:
- enum class ReadFileResult
- {
- READ_OK,
- FILE_NOT_FOUND,
- JSON_PARSE_ERROR,
- INVALID_ROOT,
- NO_VERSION,
- INVALID_VERSION,
- UNRECOGNIZED_VERSION,
- INVALID_CMAKE_VERSION,
- UNRECOGNIZED_CMAKE_VERSION,
- INVALID_PRESETS,
- INVALID_PRESET,
- INVALID_VARIABLE,
- DUPLICATE_PRESETS,
- CYCLIC_PRESET_INHERITANCE,
- INHERITED_PRESET_UNREACHABLE_FROM_FILE,
- CONFIGURE_PRESET_UNREACHABLE_FROM_FILE,
- INVALID_MACRO_EXPANSION,
- BUILD_TEST_PRESETS_UNSUPPORTED,
- PACKAGE_PRESETS_UNSUPPORTED,
- WORKFLOW_PRESETS_UNSUPPORTED,
- INCLUDE_UNSUPPORTED,
- INVALID_INCLUDE,
- INVALID_CONFIGURE_PRESET,
- INSTALL_PREFIX_UNSUPPORTED,
- INVALID_CONDITION,
- CONDITION_UNSUPPORTED,
- TOOLCHAIN_FILE_UNSUPPORTED,
- CYCLIC_INCLUDE,
- TEST_OUTPUT_TRUNCATION_UNSUPPORTED,
- INVALID_WORKFLOW_STEPS,
- WORKFLOW_STEP_UNREACHABLE_FROM_FILE,
- CTEST_JUNIT_UNSUPPORTED,
- };
-
std::string errors;
+ cmJSONState parseState;
+
enum class ArchToolsetStrategy
{
Set,
External,
};
+ enum class TraceEnableMode
+ {
+ Disable,
+ Default,
+ Expand,
+ };
+
class CacheVariable
{
public:
@@ -111,15 +87,13 @@ public:
std::map<std::string, cm::optional<std::string>> Environment;
- virtual ReadFileResult VisitPresetInherit(const Preset& parent) = 0;
- virtual ReadFileResult VisitPresetBeforeInherit()
- {
- return ReadFileResult::READ_OK;
- }
+ virtual bool VisitPresetInherit(const Preset& parent) = 0;
+ virtual bool VisitPresetBeforeInherit() { return true; }
- virtual ReadFileResult VisitPresetAfterInherit(int /* version */)
+ virtual bool VisitPresetAfterInherit(int /* version */,
+ cmJSONState* /*state*/)
{
- return ReadFileResult::READ_OK;
+ return true;
}
};
@@ -163,9 +137,14 @@ public:
cm::optional<bool> DebugTryCompile;
cm::optional<bool> DebugFind;
- ReadFileResult VisitPresetInherit(const Preset& parent) override;
- ReadFileResult VisitPresetBeforeInherit() override;
- ReadFileResult VisitPresetAfterInherit(int version) override;
+ cm::optional<TraceEnableMode> TraceMode;
+ cm::optional<cmTraceEnums::TraceOutputFormat> TraceFormat;
+ std::vector<std::string> TraceSource;
+ std::string TraceRedirect;
+
+ bool VisitPresetInherit(const Preset& parent) override;
+ bool VisitPresetBeforeInherit() override;
+ bool VisitPresetAfterInherit(int version, cmJSONState* state) override;
};
class BuildPreset : public Preset
@@ -195,8 +174,9 @@ public:
std::vector<std::string> NativeToolOptions;
cm::optional<PackageResolveMode> ResolvePackageReferences;
- ReadFileResult VisitPresetInherit(const Preset& parent) override;
- ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+ bool VisitPresetInherit(const Preset& parent) override;
+ bool VisitPresetAfterInherit(int /* version */,
+ cmJSONState* /*state*/) override;
};
class TestPreset : public Preset
@@ -328,8 +308,9 @@ public:
cm::optional<FilterOptions> Filter;
cm::optional<ExecutionOptions> Execution;
- ReadFileResult VisitPresetInherit(const Preset& parent) override;
- ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+ bool VisitPresetInherit(const Preset& parent) override;
+ bool VisitPresetAfterInherit(int /* version */,
+ cmJSONState* /*state*/) override;
};
class PackagePreset : public Preset
@@ -364,8 +345,9 @@ public:
std::string PackageDirectory;
std::string VendorName;
- ReadFileResult VisitPresetInherit(const Preset& parent) override;
- ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+ bool VisitPresetInherit(const Preset& parent) override;
+ bool VisitPresetAfterInherit(int /* version */,
+ cmJSONState* /*state*/) override;
};
class WorkflowPreset : public Preset
@@ -401,8 +383,9 @@ public:
std::vector<WorkflowStep> Steps;
- ReadFileResult VisitPresetInherit(const Preset& parent) override;
- ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+ bool VisitPresetInherit(const Preset& parent) override;
+ bool VisitPresetAfterInherit(int /* version */,
+ cmJSONState* /* state */) override;
};
template <class T>
@@ -435,9 +418,8 @@ public:
static std::string GetFilename(const std::string& sourceDir);
static std::string GetUserFilename(const std::string& sourceDir);
- ReadFileResult ReadProjectPresets(const std::string& sourceDir,
- bool allowNoFiles = false);
- static const char* ResultToString(ReadFileResult result);
+ bool ReadProjectPresets(const std::string& sourceDir,
+ bool allowNoFiles = false);
std::string GetGeneratorForPreset(const std::string& presetName) const
{
@@ -502,10 +484,9 @@ private:
Included,
};
- ReadFileResult ReadProjectPresetsInternal(bool allowNoFiles);
- ReadFileResult ReadJSONFile(const std::string& filename, RootType rootType,
- ReadReason readReason,
- std::vector<File*>& inProgressFiles, File*& file,
- std::string& errMsg);
+ bool ReadProjectPresetsInternal(bool allowNoFiles);
+ bool ReadJSONFile(const std::string& filename, RootType rootType,
+ ReadReason readReason, std::vector<File*>& inProgressFiles,
+ File*& file, std::string& errMsg);
void ClearPresets();
};
diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h
index 2726e92212..db784c341a 100644
--- a/Source/cmCMakePresetsGraphInternal.h
+++ b/Source/cmCMakePresetsGraphInternal.h
@@ -14,7 +14,7 @@
#define CHECK_OK(expr) \
do { \
auto _result = expr; \
- if (_result != ReadFileResult::READ_OK) \
+ if (_result != true) \
return _result; \
} while (false)
@@ -117,57 +117,56 @@ public:
std::unique_ptr<Condition> SubCondition;
};
-cmCMakePresetsGraph::ReadFileResult PresetStringHelper(
- std::string& out, const Json::Value* value);
+bool PresetStringHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetVectorStringHelper(
- std::vector<std::string>& out, const Json::Value* value);
+bool PresetNameHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetBoolHelper(bool& out,
- const Json::Value* value);
+bool PresetVectorStringHelper(std::vector<std::string>& out,
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetOptionalBoolHelper(
- cm::optional<bool>& out, const Json::Value* value);
+bool PresetBoolHelper(bool& out, const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetIntHelper(int& out,
- const Json::Value* value);
+bool PresetOptionalBoolHelper(cm::optional<bool>& out,
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetOptionalIntHelper(
- cm::optional<int>& out, const Json::Value* value);
+bool PresetIntHelper(int& out, const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetVectorIntHelper(
- std::vector<int>& out, const Json::Value* value);
+bool PresetOptionalIntHelper(cm::optional<int>& out, const Json::Value* value,
+ cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult ConfigurePresetsHelper(
+bool PresetVectorIntHelper(std::vector<int>& out, const Json::Value* value,
+ cmJSONState* state);
+
+bool ConfigurePresetsHelper(
std::vector<cmCMakePresetsGraph::ConfigurePreset>& out,
- const Json::Value* value);
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult BuildPresetsHelper(
- std::vector<cmCMakePresetsGraph::BuildPreset>& out,
- const Json::Value* value);
+bool BuildPresetsHelper(std::vector<cmCMakePresetsGraph::BuildPreset>& out,
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult TestPresetsHelper(
- std::vector<cmCMakePresetsGraph::TestPreset>& out, const Json::Value* value);
+bool TestPresetsHelper(std::vector<cmCMakePresetsGraph::TestPreset>& out,
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper(
- std::vector<cmCMakePresetsGraph::PackagePreset>& out,
- const Json::Value* value);
+bool PackagePresetsHelper(std::vector<cmCMakePresetsGraph::PackagePreset>& out,
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper(
+bool WorkflowPresetsHelper(
std::vector<cmCMakePresetsGraph::WorkflowPreset>& out,
- const Json::Value* value);
+ const Json::Value* value, cmJSONState* state);
-cmJSONHelper<std::nullptr_t, cmCMakePresetsGraph::ReadFileResult> VendorHelper(
- cmCMakePresetsGraph::ReadFileResult error);
+cmJSONHelper<std::nullptr_t> VendorHelper(const ErrorGenerator& error);
-cmCMakePresetsGraph::ReadFileResult PresetConditionHelper(
+bool PresetConditionHelper(
std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
- const Json::Value* value);
+ const Json::Value* value, cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult PresetVectorOneOrMoreStringHelper(
- std::vector<std::string>& out, const Json::Value* value);
+bool PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
+ const Json::Value* value,
+ cmJSONState* state);
-cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper(
+bool EnvironmentMapHelper(
std::map<std::string, cm::optional<std::string>>& out,
- const Json::Value* value);
+ const Json::Value* value, cmJSONState* state);
}
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx
index a96ab584de..bc829f3e44 100644
--- a/Source/cmCMakePresetsGraphReadJSON.cxx
+++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -1,6 +1,7 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include <algorithm>
+#include <fstream>
#include <functional>
#include <map>
#include <string>
@@ -12,20 +13,18 @@
#include <cm/optional>
#include <cmext/string_view>
-#include <cm3p/json/reader.h>
#include <cm3p/json/value.h>
-#include "cmsys/FStream.hxx"
-
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmVersion.h"
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using CacheVariable = cmCMakePresetsGraph::CacheVariable;
using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
using BuildPreset = cmCMakePresetsGraph::BuildPreset;
@@ -33,10 +32,10 @@ using TestPreset = cmCMakePresetsGraph::TestPreset;
using PackagePreset = cmCMakePresetsGraph::PackagePreset;
using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
-using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
constexpr int MIN_VERSION = 1;
-constexpr int MAX_VERSION = 6;
+constexpr int MAX_VERSION = 7;
struct CMakeVersion
{
@@ -64,26 +63,23 @@ std::unique_ptr<cmCMakePresetsGraphInternal::NotCondition> InvertCondition(
return retval;
}
-auto const ConditionStringHelper = JSONHelperBuilder::String(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
+auto const ConditionStringHelper = JSONHelperBuilder::String();
-auto const ConditionBoolHelper = JSONHelperBuilder::Bool(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
+auto const ConditionBoolHelper = JSONHelperBuilder::Bool();
auto const ConditionStringListHelper = JSONHelperBuilder::Vector<std::string>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
- ConditionStringHelper);
+ cmCMakePresetErrors::INVALID_CONDITION, ConditionStringHelper);
auto const ConstConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::ConstCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
ConditionBoolHelper, true);
auto const EqualsConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::EqualsCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
ConditionStringHelper, true)
@@ -92,7 +88,7 @@ auto const EqualsConditionHelper =
auto const InListConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::InListCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
ConditionStringHelper, true)
@@ -101,24 +97,22 @@ auto const InListConditionHelper =
auto const MatchesConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::MatchesCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
ConditionStringHelper, true)
.Bind("regex"_s, &cmCMakePresetsGraphInternal::MatchesCondition::Regex,
ConditionStringHelper, true);
-ReadFileResult SubConditionHelper(
- std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
- const Json::Value* value);
+bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
+ const Json::Value* value, cmJSONState* state);
auto const ListConditionVectorHelper =
JSONHelperBuilder::Vector<std::unique_ptr<cmCMakePresetsGraph::Condition>>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
- SubConditionHelper);
+ cmCMakePresetErrors::INVALID_CONDITION, SubConditionHelper);
auto const AnyAllOfConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::AnyAllOfCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("conditions"_s,
&cmCMakePresetsGraphInternal::AnyAllOfCondition::Conditions,
@@ -126,158 +120,160 @@ auto const AnyAllOfConditionHelper =
auto const NotConditionHelper =
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::NotCondition>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+ cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
.Bind("condition"_s,
&cmCMakePresetsGraphInternal::NotCondition::SubCondition,
SubConditionHelper);
-ReadFileResult ConditionHelper(
- std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
- const Json::Value* value)
+bool ConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
out.reset();
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isBool()) {
auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
c->Value = value->asBool();
out = std::move(c);
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isNull()) {
out = cm::make_unique<cmCMakePresetsGraphInternal::NullCondition>();
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isObject()) {
if (!value->isMember("type")) {
- return ReadFileResult::INVALID_CONDITION;
+ cmCMakePresetErrors::INVALID_CONDITION(value, state);
+ return false;
}
if (!(*value)["type"].isString()) {
- return ReadFileResult::INVALID_CONDITION;
+ cmCMakePresetErrors::INVALID_CONDITION(value, state);
+ return false;
}
auto type = (*value)["type"].asString();
if (type == "const") {
auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
- CHECK_OK(ConstConditionHelper(*c, value));
+ CHECK_OK(ConstConditionHelper(*c, value, state));
out = std::move(c);
- return ReadFileResult::READ_OK;
+ return true;
}
if (type == "equals" || type == "notEquals") {
auto c = cm::make_unique<cmCMakePresetsGraphInternal::EqualsCondition>();
- CHECK_OK(EqualsConditionHelper(*c, value));
+ CHECK_OK(EqualsConditionHelper(*c, value, state));
out = std::move(c);
if (type == "notEquals") {
out = InvertCondition(std::move(out));
}
- return ReadFileResult::READ_OK;
+ return true;
}
if (type == "inList" || type == "notInList") {
auto c = cm::make_unique<cmCMakePresetsGraphInternal::InListCondition>();
- CHECK_OK(InListConditionHelper(*c, value));
+ CHECK_OK(InListConditionHelper(*c, value, state));
out = std::move(c);
if (type == "notInList") {
out = InvertCondition(std::move(out));
}
- return ReadFileResult::READ_OK;
+ return true;
}
if (type == "matches" || type == "notMatches") {
auto c =
cm::make_unique<cmCMakePresetsGraphInternal::MatchesCondition>();
- CHECK_OK(MatchesConditionHelper(*c, value));
+ CHECK_OK(MatchesConditionHelper(*c, value, state));
out = std::move(c);
if (type == "notMatches") {
out = InvertCondition(std::move(out));
}
- return ReadFileResult::READ_OK;
+ return true;
}
if (type == "anyOf" || type == "allOf") {
auto c =
cm::make_unique<cmCMakePresetsGraphInternal::AnyAllOfCondition>();
c->StopValue = (type == "anyOf");
- CHECK_OK(AnyAllOfConditionHelper(*c, value));
+ CHECK_OK(AnyAllOfConditionHelper(*c, value, state));
out = std::move(c);
- return ReadFileResult::READ_OK;
+ return true;
}
if (type == "not") {
auto c = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
- CHECK_OK(NotConditionHelper(*c, value));
+ CHECK_OK(NotConditionHelper(*c, value, state));
out = std::move(c);
- return ReadFileResult::READ_OK;
+ return true;
}
}
- return ReadFileResult::INVALID_CONDITION;
+ cmCMakePresetErrors::INVALID_CONDITION(value, state);
+ return false;
}
-ReadFileResult SubConditionHelper(
- std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
- const Json::Value* value)
+bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
+ const Json::Value* value, cmJSONState* state)
{
std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
- auto result = ConditionHelper(ptr, value);
+ auto result = ConditionHelper(ptr, value, state);
if (ptr && ptr->IsNull()) {
- return ReadFileResult::INVALID_CONDITION;
+ cmCMakePresetErrors::INVALID_CONDITION(value, state);
+ return false;
}
out = std::move(ptr);
return result;
}
-ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
- const Json::Value* value)
+bool EnvironmentHelper(cm::optional<std::string>& out,
+ const Json::Value* value, cmJSONState* state)
{
if (!value || value->isNull()) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isString()) {
out = value->asString();
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
-auto const VersionIntHelper = JSONHelperBuilder::Int(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+auto const VersionIntHelper =
+ JSONHelperBuilder::Int(cmCMakePresetErrors::INVALID_VERSION);
auto const VersionHelper = JSONHelperBuilder::Required<int>(
- ReadFileResult::NO_VERSION, VersionIntHelper);
+ cmCMakePresetErrors::NO_VERSION, VersionIntHelper);
auto const RootVersionHelper =
- JSONHelperBuilder::Object<int>(ReadFileResult::READ_OK,
- ReadFileResult::INVALID_ROOT)
+ JSONHelperBuilder::Object<int>(cmCMakePresetErrors::INVALID_ROOT_OBJECT)
.Bind("version"_s, VersionHelper, false);
-auto const CMakeVersionUIntHelper = JSONHelperBuilder::UInt(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+auto const CMakeVersionUIntHelper =
+ JSONHelperBuilder::UInt(cmCMakePresetErrors::INVALID_VERSION);
auto const CMakeVersionHelper =
- JSONHelperBuilder::Object<CMakeVersion>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
+ JSONHelperBuilder::Object<CMakeVersion>(JsonErrors::INVALID_NAMED_OBJECT_KEY,
+ false)
.Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
.Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
.Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
-auto const IncludeHelper = JSONHelperBuilder::String(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_INCLUDE);
+auto const IncludeHelper =
+ JSONHelperBuilder::String(cmCMakePresetErrors::INVALID_INCLUDE);
auto const IncludeVectorHelper = JSONHelperBuilder::Vector<std::string>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_INCLUDE, IncludeHelper);
+ cmCMakePresetErrors::INVALID_INCLUDE, IncludeHelper);
auto const RootPresetsHelper =
- JSONHelperBuilder::Object<RootPresets>(ReadFileResult::READ_OK,
- ReadFileResult::INVALID_ROOT, false)
+ JSONHelperBuilder::Object<RootPresets>(
+ cmCMakePresetErrors::INVALID_ROOT_OBJECT, false)
.Bind<int>("version"_s, nullptr, VersionHelper)
.Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
@@ -292,136 +288,137 @@ auto const RootPresetsHelper =
.Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
CMakeVersionHelper, false)
.Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
- .Bind<std::nullptr_t>(
- "vendor"_s, nullptr,
- cmCMakePresetsGraphInternal::VendorHelper(ReadFileResult::INVALID_ROOT),
- false);
+ .Bind<std::nullptr_t>("vendor"_s, nullptr,
+ cmCMakePresetsGraphInternal::VendorHelper(
+ cmCMakePresetErrors::INVALID_ROOT),
+ false);
}
namespace cmCMakePresetsGraphInternal {
-cmCMakePresetsGraph::ReadFileResult PresetStringHelper(
- std::string& out, const Json::Value* value)
+bool PresetStringHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state)
{
- static auto const helper = JSONHelperBuilder::String(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+ static auto const helper = JSONHelperBuilder::String();
+ return helper(out, value, state);
+}
- return helper(out, value);
+bool PresetNameHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state)
+{
+ if (!value || !value->isString() || value->asString().empty()) {
+ cmCMakePresetErrors::INVALID_PRESET_NAME(value, state);
+ return false;
+ }
+ out = value->asString();
+ return true;
}
-cmCMakePresetsGraph::ReadFileResult PresetVectorStringHelper(
- std::vector<std::string>& out, const Json::Value* value)
+bool PresetVectorStringHelper(std::vector<std::string>& out,
+ const Json::Value* value, cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Vector<std::string>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
+ cmCMakePresetErrors::INVALID_PRESET,
cmCMakePresetsGraphInternal::PresetStringHelper);
-
- return helper(out, value);
+ return helper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult PresetBoolHelper(bool& out,
- const Json::Value* value)
+bool PresetBoolHelper(bool& out, const Json::Value* value, cmJSONState* state)
{
- static auto const helper = JSONHelperBuilder::Bool(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
-
- return helper(out, value);
+ static auto const helper = JSONHelperBuilder::Bool();
+ return helper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult PresetOptionalBoolHelper(
- cm::optional<bool>& out, const Json::Value* value)
+bool PresetOptionalBoolHelper(cm::optional<bool>& out,
+ const Json::Value* value, cmJSONState* state)
{
- static auto const helper = JSONHelperBuilder::Optional<bool>(
- ReadFileResult::READ_OK, PresetBoolHelper);
-
- return helper(out, value);
+ static auto const helper =
+ JSONHelperBuilder::Optional<bool>(PresetBoolHelper);
+ return helper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult PresetIntHelper(int& out,
- const Json::Value* value)
+bool PresetIntHelper(int& out, const Json::Value* value, cmJSONState* state)
{
- static auto const helper = JSONHelperBuilder::Int(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
-
- return helper(out, value);
+ static auto const helper = JSONHelperBuilder::Int();
+ return helper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult PresetOptionalIntHelper(
- cm::optional<int>& out, const Json::Value* value)
+bool PresetOptionalIntHelper(cm::optional<int>& out, const Json::Value* value,
+ cmJSONState* state)
{
- static auto const helper =
- JSONHelperBuilder::Optional<int>(ReadFileResult::READ_OK, PresetIntHelper);
-
- return helper(out, value);
+ static auto const helper = JSONHelperBuilder::Optional<int>(PresetIntHelper);
+ return helper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult PresetVectorIntHelper(
- std::vector<int>& out, const Json::Value* value)
+bool PresetVectorIntHelper(std::vector<int>& out, const Json::Value* value,
+ cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Vector<int>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
-
- return helper(out, value);
+ cmCMakePresetErrors::INVALID_PRESET, PresetIntHelper);
+ return helper(out, value, state);
}
-cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
+cmJSONHelper<std::nullptr_t> VendorHelper(const ErrorGenerator& error)
{
- return [error](std::nullptr_t& /*out*/,
- const Json::Value* value) -> ReadFileResult {
+ return [error](std::nullptr_t& /*out*/, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isObject()) {
- return error;
+ error(value, state);
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
};
}
-ReadFileResult PresetConditionHelper(
+bool PresetConditionHelper(
std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
- auto result = ConditionHelper(ptr, value);
+ auto result = ConditionHelper(ptr, value, state);
out = std::move(ptr);
return result;
}
-ReadFileResult PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
- const Json::Value* value)
+bool PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
+ const Json::Value* value,
+ cmJSONState* state)
{
out.clear();
if (!value) {
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isString()) {
out.push_back(value->asString());
- return ReadFileResult::READ_OK;
+ return true;
}
- return PresetVectorStringHelper(out, value);
+ return PresetVectorStringHelper(out, value, state);
}
-cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper(
+bool EnvironmentMapHelper(
std::map<std::string, cm::optional<std::string>>& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Map<cm::optional<std::string>>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
- EnvironmentHelper);
+ cmCMakePresetErrors::INVALID_PRESET, EnvironmentHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
-cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
- const std::string& filename, RootType rootType, ReadReason readReason,
- std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg)
+bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename,
+ RootType rootType,
+ ReadReason readReason,
+ std::vector<File*>& inProgressFiles,
+ File*& file, std::string& errMsg)
{
- ReadFileResult result;
+ bool result;
for (auto const& f : this->Files) {
if (cmSystemTools::SameFile(filename, f->Filename)) {
@@ -429,61 +426,67 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
auto fileIt =
std::find(inProgressFiles.begin(), inProgressFiles.end(), file);
if (fileIt != inProgressFiles.end()) {
- return cmCMakePresetsGraph::ReadFileResult::CYCLIC_INCLUDE;
+ cmCMakePresetErrors::CYCLIC_INCLUDE(filename, &this->parseState);
+ return false;
}
- return cmCMakePresetsGraph::ReadFileResult::READ_OK;
+ return true;
}
}
- cmsys::ifstream fin(filename.c_str());
- if (!fin) {
- errMsg = cmStrCat(filename, ": Failed to read file\n", errMsg);
- return ReadFileResult::FILE_NOT_FOUND;
- }
- // If there's a BOM, toss it.
- cmsys::FStream::ReadBOM(fin);
-
Json::Value root;
- Json::CharReaderBuilder builder;
- Json::CharReaderBuilder::strictMode(&builder.settings_);
- if (!Json::parseFromStream(builder, fin, &root, &errMsg)) {
- errMsg = cmStrCat(filename, ":\n", errMsg);
- return ReadFileResult::JSON_PARSE_ERROR;
+ this->parseState = cmJSONState(filename, &root);
+ if (!this->parseState.errors.empty()) {
+ return false;
}
int v = 0;
- if ((result = RootVersionHelper(v, &root)) != ReadFileResult::READ_OK) {
+ if ((result = RootVersionHelper(v, &root, &parseState)) != true) {
return result;
}
if (v < MIN_VERSION || v > MAX_VERSION) {
- return ReadFileResult::UNRECOGNIZED_VERSION;
+ cmCMakePresetErrors::UNRECOGNIZED_VERSION(&root["version"],
+ &this->parseState);
+ return false;
}
// Support for build and test presets added in version 2.
- if (v < 2 &&
- (root.isMember("buildPresets") || root.isMember("testPresets"))) {
- return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
+ if (v < 2) {
+ if (root.isMember("buildPresets")) {
+ cmCMakePresetErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
+ &root["buildPresets"], &this->parseState);
+ return false;
+ }
+ if (root.isMember("testPresets")) {
+ cmCMakePresetErrors::BUILD_TEST_PRESETS_UNSUPPORTED(&root["testPresets"],
+ &this->parseState);
+ return false;
+ }
}
// Support for package presets added in version 6.
if (v < 6 && root.isMember("packagePresets")) {
- return ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED;
+ cmCMakePresetErrors::PACKAGE_PRESETS_UNSUPPORTED(&root["packagePresets"],
+ &this->parseState);
+ return false;
}
// Support for workflow presets added in version 6.
if (v < 6 && root.isMember("workflowPresets")) {
- return ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED;
+ cmCMakePresetErrors::WORKFLOW_PRESETS_UNSUPPORTED(&root["workflowPresets"],
+ &this->parseState);
+ return false;
}
// Support for include added in version 4.
if (v < 4 && root.isMember("include")) {
- return ReadFileResult::INCLUDE_UNSUPPORTED;
+ cmCMakePresetErrors::INCLUDE_UNSUPPORTED(&root["include"],
+ &this->parseState);
+ return false;
}
RootPresets presets;
- if ((result = RootPresetsHelper(presets, &root)) !=
- ReadFileResult::READ_OK) {
+ if ((result = RootPresetsHelper(presets, &root, &parseState)) != true) {
return result;
}
@@ -491,12 +494,25 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
unsigned int currentMinor = cmVersion::GetMinorVersion();
unsigned int currentPatch = cmVersion::GetPatchVersion();
auto const& required = presets.CMakeMinimumRequired;
- if (required.Major > currentMajor ||
- (required.Major == currentMajor &&
- (required.Minor > currentMinor ||
- (required.Minor == currentMinor &&
- (required.Patch > currentPatch))))) {
- return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
+ if (required.Major > currentMajor) {
+ ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+ "major", currentMajor, required.Major);
+ error(&root["cmakeMinimumRequired"]["major"], &this->parseState);
+ return false;
+ }
+ if (required.Major == currentMajor) {
+ if (required.Minor > currentMinor) {
+ ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+ "minor", currentMinor, required.Minor);
+ error(&root["cmakeMinimumRequired"]["minor"], &this->parseState);
+ return false;
+ }
+ if (required.Minor == currentMinor && required.Patch > currentPatch) {
+ ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+ "patch", currentPatch, required.Patch);
+ error(&root["cmakeMinimumRequired"]["patch"], &this->parseState);
+ return false;
+ }
}
auto filePtr = cm::make_unique<File>();
@@ -510,31 +526,43 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
for (auto& preset : presets.ConfigurePresets) {
preset.OriginFile = file;
if (preset.Name.empty()) {
- errMsg += R"(\n\t)";
- errMsg += filename;
- return ReadFileResult::INVALID_PRESET;
+ // No error, already handled by PresetNameHelper
+ return false;
}
PresetPair<ConfigurePreset> presetPair;
presetPair.Unexpanded = preset;
presetPair.Expanded = cm::nullopt;
if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) {
- return ReadFileResult::DUPLICATE_PRESETS;
+ cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+ return false;
}
// Support for installDir presets added in version 3.
if (v < 3 && !preset.InstallDir.empty()) {
- return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
+ cmCMakePresetErrors::INSTALL_PREFIX_UNSUPPORTED(&root["installDir"],
+ &this->parseState);
+ return false;
}
// Support for conditions added in version 3.
if (v < 3 && preset.ConditionEvaluator) {
- return ReadFileResult::CONDITION_UNSUPPORTED;
+ cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+ return false;
}
// Support for toolchainFile presets added in version 3.
if (v < 3 && !preset.ToolchainFile.empty()) {
- return ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED;
+ cmCMakePresetErrors::TOOLCHAIN_FILE_UNSUPPORTED(&this->parseState);
+ return false;
+ }
+
+ // Support for trace presets added in version 7.
+ if (v < 7 &&
+ (preset.TraceMode.has_value() || preset.TraceFormat.has_value() ||
+ !preset.TraceRedirect.empty() || !preset.TraceSource.empty())) {
+ cmCMakePresetErrors::TRACE_UNSUPPORTED(&this->parseState);
+ return false;
}
this->ConfigurePresetOrder.push_back(preset.Name);
@@ -543,21 +571,22 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
for (auto& preset : presets.BuildPresets) {
preset.OriginFile = file;
if (preset.Name.empty()) {
- errMsg += R"(\n\t)";
- errMsg += filename;
- return ReadFileResult::INVALID_PRESET;
+ // No error, already handled by PresetNameHelper
+ return false;
}
PresetPair<BuildPreset> presetPair;
presetPair.Unexpanded = preset;
presetPair.Expanded = cm::nullopt;
if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
- return ReadFileResult::DUPLICATE_PRESETS;
+ cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+ return false;
}
// Support for conditions added in version 3.
if (v < 3 && preset.ConditionEvaluator) {
- return ReadFileResult::CONDITION_UNSUPPORTED;
+ cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+ return false;
}
this->BuildPresetOrder.push_back(preset.Name);
@@ -566,29 +595,35 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
for (auto& preset : presets.TestPresets) {
preset.OriginFile = file;
if (preset.Name.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ // No error, already handled by PresetNameHelper
+ return false;
}
PresetPair<TestPreset> presetPair;
presetPair.Unexpanded = preset;
presetPair.Expanded = cm::nullopt;
if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
- return ReadFileResult::DUPLICATE_PRESETS;
+ cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+ return false;
}
// Support for conditions added in version 3.
if (v < 3 && preset.ConditionEvaluator) {
- return ReadFileResult::CONDITION_UNSUPPORTED;
+ cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+ return false;
}
// Support for TestOutputTruncation added in version 5.
if (v < 5 && preset.Output && preset.Output->TestOutputTruncation) {
- return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED;
+ cmCMakePresetErrors::TEST_OUTPUT_TRUNCATION_UNSUPPORTED(
+ &this->parseState);
+ return false;
}
// Support for outputJUnitFile added in version 6.
if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
- return ReadFileResult::CTEST_JUNIT_UNSUPPORTED;
+ cmCMakePresetErrors::CTEST_JUNIT_UNSUPPORTED(&this->parseState);
+ return false;
}
this->TestPresetOrder.push_back(preset.Name);
@@ -597,14 +632,16 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
for (auto& preset : presets.PackagePresets) {
preset.OriginFile = file;
if (preset.Name.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ // No error, already handled by PresetNameHelper
+ return false;
}
PresetPair<PackagePreset> presetPair;
presetPair.Unexpanded = preset;
presetPair.Expanded = cm::nullopt;
if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
- return ReadFileResult::DUPLICATE_PRESETS;
+ cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+ return false;
}
// Support for conditions added in version 3, but this requires version 5
@@ -616,14 +653,16 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
for (auto& preset : presets.WorkflowPresets) {
preset.OriginFile = file;
if (preset.Name.empty()) {
- return ReadFileResult::INVALID_PRESET;
+ // No error, already handled by PresetNameHelper
+ return false;
}
PresetPair<WorkflowPreset> presetPair;
presetPair.Unexpanded = preset;
presetPair.Expanded = cm::nullopt;
if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
- return ReadFileResult::DUPLICATE_PRESETS;
+ cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+ return false;
}
// Support for conditions added in version 3, but this requires version 6
@@ -632,21 +671,21 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
this->WorkflowPresetOrder.push_back(preset.Name);
}
- auto const includeFile = [this, &inProgressFiles, file](
- const std::string& include, RootType rootType2,
- ReadReason readReason2,
- std::string& FailureMessage) -> ReadFileResult {
- ReadFileResult r;
+ auto const includeFile = [this, &inProgressFiles,
+ file](const std::string& include,
+ RootType rootType2, ReadReason readReason2,
+ std::string& FailureMessage) -> bool {
+ bool r;
File* includedFile;
- if ((r = this->ReadJSONFile(include, rootType2, readReason2,
- inProgressFiles, includedFile,
- FailureMessage)) != ReadFileResult::READ_OK) {
+ if ((r =
+ this->ReadJSONFile(include, rootType2, readReason2, inProgressFiles,
+ includedFile, FailureMessage)) != true) {
return r;
}
file->ReachableFiles.insert(includedFile->ReachableFiles.begin(),
includedFile->ReachableFiles.end());
- return ReadFileResult::READ_OK;
+ return true;
};
for (auto include : presets.Include) {
@@ -656,7 +695,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
}
if ((result = includeFile(include, rootType, ReadReason::Included,
- errMsg)) != ReadFileResult::READ_OK) {
+ errMsg)) != true) {
return result;
}
}
@@ -665,13 +704,12 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
auto cmakePresetsFilename = GetFilename(this->SourceDir);
if (cmSystemTools::FileExists(cmakePresetsFilename)) {
if ((result = includeFile(cmakePresetsFilename, RootType::Project,
- ReadReason::Root, errMsg)) !=
- ReadFileResult::READ_OK) {
+ ReadReason::Root, errMsg)) != true) {
return result;
}
}
}
inProgressFiles.pop_back();
- return ReadFileResult::READ_OK;
+ return true;
}
diff --git a/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx b/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
index 430d7ee3a2..07f2bc34e0 100644
--- a/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
@@ -13,25 +13,27 @@
#include <cm3p/json/value.h>
#include "cmBuildOptions.h"
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+class cmJSONState;
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using BuildPreset = cmCMakePresetsGraph::BuildPreset;
-using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
-ReadFileResult PackageResolveModeHelper(cm::optional<PackageResolveMode>& out,
- const Json::Value* value)
+bool PackageResolveModeHelper(cm::optional<PackageResolveMode>& out,
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "on") {
@@ -41,23 +43,25 @@ ReadFileResult PackageResolveModeHelper(cm::optional<PackageResolveMode>& out,
} else if (value->asString() == "only") {
out = PackageResolveMode::OnlyResolve;
} else {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
- return ReadFileResult::READ_OK;
+ return true;
}
-std::function<ReadFileResult(BuildPreset&, const Json::Value*)> const
- ResolvePackageReferencesHelper =
- [](BuildPreset& out, const Json::Value* value) -> ReadFileResult {
- return PackageResolveModeHelper(out.ResolvePackageReferences, value);
+std::function<bool(BuildPreset&, const Json::Value*, cmJSONState*)> const
+ ResolvePackageReferencesHelper = [](BuildPreset& out,
+ const Json::Value* value,
+ cmJSONState* state) -> bool {
+ return PackageResolveModeHelper(out.ResolvePackageReferences, value, state);
};
auto const BuildPresetHelper =
- JSONHelperBuilder::Object<BuildPreset>(ReadFileResult::READ_OK,
- ReadFileResult::INVALID_PRESET, false)
+ JSONHelperBuilder::Object<BuildPreset>(
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
.Bind("name"_s, &BuildPreset::Name,
- cmCMakePresetsGraphInternal::PresetStringHelper)
+ cmCMakePresetsGraphInternal::PresetNameHelper)
.Bind("inherits"_s, &BuildPreset::Inherits,
cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
false)
@@ -65,7 +69,7 @@ auto const BuildPresetHelper =
cmCMakePresetsGraphInternal::PresetBoolHelper, false)
.Bind<std::nullptr_t>("vendor"_s, nullptr,
cmCMakePresetsGraphInternal::VendorHelper(
- ReadFileResult::INVALID_PRESET),
+ cmCMakePresetErrors::INVALID_PRESET),
false)
.Bind("displayName"_s, &BuildPreset::DisplayName,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -97,13 +101,12 @@ auto const BuildPresetHelper =
}
namespace cmCMakePresetsGraphInternal {
-ReadFileResult BuildPresetsHelper(std::vector<BuildPreset>& out,
- const Json::Value* value)
+bool BuildPresetsHelper(std::vector<BuildPreset>& out,
+ const Json::Value* value, cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Vector<BuildPreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
- BuildPresetHelper);
+ cmCMakePresetErrors::INVALID_PRESETS, BuildPresetHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
diff --git a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
index 7cff55a01b..66ec6a4930 100644
--- a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
@@ -12,72 +12,80 @@
#include <cm3p/json/value.h>
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
+#include "cmStateTypes.h"
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using CacheVariable = cmCMakePresetsGraph::CacheVariable;
using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
-using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
+using TraceEnableMode = cmCMakePresetsGraph::TraceEnableMode;
+using TraceOutputFormat = cmTraceEnums::TraceOutputFormat;
-ReadFileResult ArchToolsetStrategyHelper(
- cm::optional<ArchToolsetStrategy>& out, const Json::Value* value)
+bool ArchToolsetStrategyHelper(cm::optional<ArchToolsetStrategy>& out,
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "set") {
out = ArchToolsetStrategy::Set;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "external") {
out = ArchToolsetStrategy::External;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
-std::function<ReadFileResult(ConfigurePreset&, const Json::Value*)>
+std::function<bool(ConfigurePreset&, const Json::Value*, cmJSONState*)>
ArchToolsetHelper(
std::string ConfigurePreset::*valueField,
cm::optional<ArchToolsetStrategy> ConfigurePreset::*strategyField)
{
auto const objectHelper =
- JSONHelperBuilder::Object<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ JSONHelperBuilder::Object<ConfigurePreset>(JsonErrors::INVALID_OBJECT,
+ false)
.Bind("value", valueField,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
.Bind("strategy", strategyField, ArchToolsetStrategyHelper, false);
- return [valueField, strategyField, objectHelper](
- ConfigurePreset& out, const Json::Value* value) -> ReadFileResult {
+ return [valueField, strategyField,
+ objectHelper](ConfigurePreset& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
(out.*valueField).clear();
out.*strategyField = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isString()) {
out.*valueField = value->asString();
out.*strategyField = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isObject()) {
- return objectHelper(out, value);
+ return objectHelper(out, value, state);
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
};
}
@@ -86,65 +94,118 @@ auto const ArchitectureHelper = ArchToolsetHelper(
auto const ToolsetHelper = ArchToolsetHelper(
&ConfigurePreset::Toolset, &ConfigurePreset::ToolsetStrategy);
-auto const VariableStringHelper = JSONHelperBuilder::String(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
+bool TraceEnableModeHelper(cm::optional<TraceEnableMode>& out,
+ const Json::Value* value, cmJSONState* state)
+{
+ if (!value) {
+ out = cm::nullopt;
+ return true;
+ }
-ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value)
+ if (!value->isString()) {
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
+ }
+
+ if (value->asString() == "on") {
+ out = TraceEnableMode::Default;
+ } else if (value->asString() == "off") {
+ out = TraceEnableMode::Disable;
+ } else if (value->asString() == "expand") {
+ out = TraceEnableMode::Expand;
+ } else {
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
+ }
+
+ return true;
+}
+
+bool TraceOutputFormatHelper(cm::optional<TraceOutputFormat>& out,
+ const Json::Value* value, cmJSONState* state)
+{
+ if (!value) {
+ out = cm::nullopt;
+ return true;
+ }
+
+ if (!value->isString()) {
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
+ }
+
+ if (value->asString() == "human") {
+ out = TraceOutputFormat::Human;
+ } else if (value->asString() == "json-v1") {
+ out = TraceOutputFormat::JSONv1;
+ } else {
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
+ }
+
+ return true;
+}
+
+auto const VariableStringHelper = JSONHelperBuilder::String();
+
+bool VariableValueHelper(std::string& out, const Json::Value* value,
+ cmJSONState* state)
{
if (!value) {
out.clear();
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isBool()) {
out = value->asBool() ? "TRUE" : "FALSE";
- return ReadFileResult::READ_OK;
+ return true;
}
- return VariableStringHelper(out, value);
+ return VariableStringHelper(out, value, state);
}
auto const VariableObjectHelper =
JSONHelperBuilder::Object<CacheVariable>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
+ cmCMakePresetErrors::INVALID_VARIABLE_OBJECT, false)
.Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
.Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
-ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
- const Json::Value* value)
+bool VariableHelper(cm::optional<CacheVariable>& out, const Json::Value* value,
+ cmJSONState* state)
{
if (value->isBool()) {
out = CacheVariable{
/*Type=*/"BOOL",
/*Value=*/value->asBool() ? "TRUE" : "FALSE",
};
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isString()) {
out = CacheVariable{
/*Type=*/"",
/*Value=*/value->asString(),
};
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isObject()) {
out.emplace();
- return VariableObjectHelper(*out, value);
+ return VariableObjectHelper(*out, value, state);
}
if (value->isNull()) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_VARIABLE;
+ cmCMakePresetErrors::INVALID_VARIABLE(value, state);
+ return false;
}
auto const VariablesHelper =
JSONHelperBuilder::Map<cm::optional<CacheVariable>>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
+ cmCMakePresetErrors::INVALID_PRESET, VariableHelper);
auto const PresetWarningsHelper =
JSONHelperBuilder::Object<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ JsonErrors::INVALID_NAMED_OBJECT_KEY, false)
.Bind("dev"_s, &ConfigurePreset::WarnDev,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated,
@@ -158,7 +219,7 @@ auto const PresetWarningsHelper =
auto const PresetErrorsHelper =
JSONHelperBuilder::Object<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ JsonErrors::INVALID_NAMED_OBJECT_KEY, false)
.Bind("dev"_s, &ConfigurePreset::ErrorDev,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated,
@@ -166,7 +227,7 @@ auto const PresetErrorsHelper =
auto const PresetDebugHelper =
JSONHelperBuilder::Object<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ JsonErrors::INVALID_NAMED_OBJECT_KEY, false)
.Bind("output"_s, &ConfigurePreset::DebugOutput,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("tryCompile"_s, &ConfigurePreset::DebugTryCompile,
@@ -174,11 +235,23 @@ auto const PresetDebugHelper =
.Bind("find"_s, &ConfigurePreset::DebugFind,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false);
+auto const PresetTraceHelper =
+ JSONHelperBuilder::Object<ConfigurePreset>(
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+ .Bind("mode"_s, &ConfigurePreset::TraceMode, TraceEnableModeHelper, false)
+ .Bind("format"_s, &ConfigurePreset::TraceFormat, TraceOutputFormatHelper,
+ false)
+ .Bind("source"_s, &ConfigurePreset::TraceSource,
+ cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
+ false)
+ .Bind("redirect"_s, &ConfigurePreset::TraceRedirect,
+ cmCMakePresetsGraphInternal::PresetStringHelper, false);
+
auto const ConfigurePresetHelper =
JSONHelperBuilder::Object<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
.Bind("name"_s, &ConfigurePreset::Name,
- cmCMakePresetsGraphInternal::PresetStringHelper)
+ cmCMakePresetsGraphInternal::PresetNameHelper)
.Bind("inherits"_s, &ConfigurePreset::Inherits,
cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
false)
@@ -186,7 +259,7 @@ auto const ConfigurePresetHelper =
cmCMakePresetsGraphInternal::PresetBoolHelper, false)
.Bind<std::nullptr_t>("vendor"_s, nullptr,
cmCMakePresetsGraphInternal::VendorHelper(
- ReadFileResult::INVALID_PRESET),
+ cmCMakePresetErrors::INVALID_PRESET),
false)
.Bind("displayName"_s, &ConfigurePreset::DisplayName,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -211,18 +284,18 @@ auto const ConfigurePresetHelper =
.Bind("warnings"_s, PresetWarningsHelper, false)
.Bind("errors"_s, PresetErrorsHelper, false)
.Bind("debug"_s, PresetDebugHelper, false)
+ .Bind("trace"_s, PresetTraceHelper, false)
.Bind("condition"_s, &ConfigurePreset::ConditionEvaluator,
cmCMakePresetsGraphInternal::PresetConditionHelper, false);
}
namespace cmCMakePresetsGraphInternal {
-ReadFileResult ConfigurePresetsHelper(std::vector<ConfigurePreset>& out,
- const Json::Value* value)
+bool ConfigurePresetsHelper(std::vector<ConfigurePreset>& out,
+ const Json::Value* value, cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Vector<ConfigurePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
- ConfigurePresetHelper);
+ cmCMakePresetErrors::INVALID_PRESETS, ConfigurePresetHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
diff --git a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
index 4ae51b1fc6..7290d4defa 100644
--- a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
@@ -12,34 +12,34 @@
#include <cm3p/json/value.h>
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+class cmJSONState;
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using PackagePreset = cmCMakePresetsGraph::PackagePreset;
auto const OutputHelper =
- cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ cmJSONHelperBuilder::Object<PackagePreset>(
+ JsonErrors::INVALID_NAMED_OBJECT_KEY, false)
.Bind("debug"_s, &PackagePreset::DebugOutput,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("verbose"_s, &PackagePreset::VerboseOutput,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false);
-auto const VariableHelper = cmJSONHelperBuilder<ReadFileResult>::String(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
+auto const VariableHelper =
+ cmJSONHelperBuilder::String(cmCMakePresetErrors::INVALID_VARIABLE);
-auto const VariablesHelper =
- cmJSONHelperBuilder<ReadFileResult>::Map<std::string>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
+auto const VariablesHelper = cmJSONHelperBuilder::Map<std::string>(
+ cmCMakePresetErrors::INVALID_VARIABLE, VariableHelper);
auto const PackagePresetHelper =
- cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ cmJSONHelperBuilder::Object<PackagePreset>(
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
.Bind("name"_s, &PackagePreset::Name,
- cmCMakePresetsGraphInternal::PresetStringHelper)
+ cmCMakePresetsGraphInternal::PresetNameHelper)
.Bind("inherits"_s, &PackagePreset::Inherits,
cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
false)
@@ -47,7 +47,7 @@ auto const PackagePresetHelper =
cmCMakePresetsGraphInternal::PresetBoolHelper, false)
.Bind<std::nullptr_t>("vendor"_s, nullptr,
cmCMakePresetsGraphInternal::VendorHelper(
- ReadFileResult::INVALID_PRESET),
+ cmCMakePresetErrors::INVALID_PRESET),
false)
.Bind("displayName"_s, &PackagePreset::DisplayName,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -81,15 +81,12 @@ auto const PackagePresetHelper =
}
namespace cmCMakePresetsGraphInternal {
-cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper(
- std::vector<cmCMakePresetsGraph::PackagePreset>& out,
- const Json::Value* value)
+bool PackagePresetsHelper(std::vector<cmCMakePresetsGraph::PackagePreset>& out,
+ const Json::Value* value, cmJSONState* state)
{
- static auto const helper =
- cmJSONHelperBuilder<ReadFileResult>::Vector<PackagePreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
- PackagePresetHelper);
+ static auto const helper = cmJSONHelperBuilder::Vector<PackagePreset>(
+ cmCMakePresetErrors::INVALID_PRESETS, PackagePresetHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
index 3856f630c5..791be04a58 100644
--- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
@@ -12,86 +12,93 @@
#include <cm3p/json/value.h>
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
#include "CTest/cmCTestTypes.h"
+class cmJSONState;
+
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using TestPreset = cmCMakePresetsGraph::TestPreset;
-using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
-ReadFileResult TestPresetOutputVerbosityHelper(
- TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value)
+bool TestPresetOutputVerbosityHelper(
+ TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value,
+ cmJSONState* state)
{
if (!value) {
out = TestPreset::OutputOptions::VerbosityEnum::Default;
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "default") {
out = TestPreset::OutputOptions::VerbosityEnum::Default;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "verbose") {
out = TestPreset::OutputOptions::VerbosityEnum::Verbose;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "extra") {
out = TestPreset::OutputOptions::VerbosityEnum::Extra;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const TestPresetOptionalOutputVerbosityHelper =
JSONHelperBuilder::Optional<TestPreset::OutputOptions::VerbosityEnum>(
- ReadFileResult::READ_OK, TestPresetOutputVerbosityHelper);
+ TestPresetOutputVerbosityHelper);
-ReadFileResult TestPresetOutputTruncationHelper(
- cm::optional<cmCTestTypes::TruncationMode>& out, const Json::Value* value)
+bool TestPresetOutputTruncationHelper(
+ cm::optional<cmCTestTypes::TruncationMode>& out, const Json::Value* value,
+ cmJSONState* state)
{
if (!value) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "tail") {
out = cmCTestTypes::TruncationMode::Tail;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "middle") {
out = cmCTestTypes::TruncationMode::Middle;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "head") {
out = cmCTestTypes::TruncationMode::Head;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const TestPresetOptionalOutputHelper =
JSONHelperBuilder::Optional<TestPreset::OutputOptions>(
- ReadFileResult::READ_OK,
JSONHelperBuilder::Object<TestPreset::OutputOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ JsonErrors::INVALID_OBJECT, false)
.Bind("shortProgress"_s, &TestPreset::OutputOptions::ShortProgress,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("verbosity"_s, &TestPreset::OutputOptions::Verbosity,
@@ -125,9 +132,7 @@ auto const TestPresetOptionalOutputHelper =
auto const TestPresetOptionalFilterIncludeIndexObjectHelper =
JSONHelperBuilder::Optional<TestPreset::IncludeOptions::IndexOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::IncludeOptions::IndexOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::IncludeOptions::IndexOptions>()
.Bind("start"_s, &TestPreset::IncludeOptions::IndexOptions::Start,
cmCMakePresetsGraphInternal::PresetOptionalIntHelper, false)
.Bind("end"_s, &TestPreset::IncludeOptions::IndexOptions::End,
@@ -138,33 +143,31 @@ auto const TestPresetOptionalFilterIncludeIndexObjectHelper =
&TestPreset::IncludeOptions::IndexOptions::SpecificTests,
cmCMakePresetsGraphInternal::PresetVectorIntHelper, false));
-ReadFileResult TestPresetOptionalFilterIncludeIndexHelper(
+bool TestPresetOptionalFilterIncludeIndexHelper(
cm::optional<TestPreset::IncludeOptions::IndexOptions>& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
out = cm::nullopt;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isString()) {
out.emplace();
out->IndexFile = value->asString();
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->isObject()) {
- return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value);
+ return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value, state);
}
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
auto const TestPresetOptionalFilterIncludeHelper =
JSONHelperBuilder::Optional<TestPreset::IncludeOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::IncludeOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::IncludeOptions>()
.Bind("name"_s, &TestPreset::IncludeOptions::Name,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
.Bind("label"_s, &TestPreset::IncludeOptions::Label,
@@ -176,9 +179,7 @@ auto const TestPresetOptionalFilterIncludeHelper =
auto const TestPresetOptionalFilterExcludeFixturesHelper =
JSONHelperBuilder::Optional<TestPreset::ExcludeOptions::FixturesOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::ExcludeOptions::FixturesOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::ExcludeOptions::FixturesOptions>()
.Bind("any"_s, &TestPreset::ExcludeOptions::FixturesOptions::Any,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
.Bind("setup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Setup,
@@ -188,9 +189,7 @@ auto const TestPresetOptionalFilterExcludeFixturesHelper =
auto const TestPresetOptionalFilterExcludeHelper =
JSONHelperBuilder::Optional<TestPreset::ExcludeOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::ExcludeOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::ExcludeOptions>()
.Bind("name"_s, &TestPreset::ExcludeOptions::Name,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
.Bind("label"_s, &TestPreset::ExcludeOptions::Label,
@@ -198,110 +197,113 @@ auto const TestPresetOptionalFilterExcludeHelper =
.Bind("fixtures"_s, &TestPreset::ExcludeOptions::Fixtures,
TestPresetOptionalFilterExcludeFixturesHelper, false));
-ReadFileResult TestPresetExecutionShowOnlyHelper(
- TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value)
+bool TestPresetExecutionShowOnlyHelper(
+ TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value,
+ cmJSONState* state)
{
if (!value || !value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "human") {
out = TestPreset::ExecutionOptions::ShowOnlyEnum::Human;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "json-v1") {
out = TestPreset::ExecutionOptions::ShowOnlyEnum::JsonV1;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const TestPresetOptionalExecutionShowOnlyHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions::ShowOnlyEnum>(
- ReadFileResult::READ_OK, TestPresetExecutionShowOnlyHelper);
+ TestPresetExecutionShowOnlyHelper);
-ReadFileResult TestPresetExecutionModeHelper(
+bool TestPresetExecutionModeHelper(
TestPreset::ExecutionOptions::RepeatOptions::ModeEnum& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "until-fail") {
out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilFail;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "until-pass") {
out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilPass;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "after-timeout") {
out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::AfterTimeout;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const TestPresetOptionalExecutionRepeatHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions::RepeatOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::ExecutionOptions::RepeatOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::ExecutionOptions::RepeatOptions>()
.Bind("mode"_s, &TestPreset::ExecutionOptions::RepeatOptions::Mode,
TestPresetExecutionModeHelper, true)
.Bind("count"_s, &TestPreset::ExecutionOptions::RepeatOptions::Count,
cmCMakePresetsGraphInternal::PresetIntHelper, true));
-ReadFileResult TestPresetExecutionNoTestsActionHelper(
+bool TestPresetExecutionNoTestsActionHelper(
TestPreset::ExecutionOptions::NoTestsActionEnum& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
- return ReadFileResult::READ_OK;
+ return true;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (value->asString() == "default") {
out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "error") {
out = TestPreset::ExecutionOptions::NoTestsActionEnum::Error;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "ignore") {
out = TestPreset::ExecutionOptions::NoTestsActionEnum::Ignore;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const TestPresetOptionalExecutionNoTestsActionHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions::NoTestsActionEnum>(
- ReadFileResult::READ_OK, TestPresetExecutionNoTestsActionHelper);
+ TestPresetExecutionNoTestsActionHelper);
auto const TestPresetExecutionHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::ExecutionOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::ExecutionOptions>()
.Bind("stopOnFailure"_s, &TestPreset::ExecutionOptions::StopOnFailure,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover,
@@ -329,19 +331,17 @@ auto const TestPresetExecutionHelper =
auto const TestPresetFilterHelper =
JSONHelperBuilder::Optional<TestPreset::FilterOptions>(
- ReadFileResult::READ_OK,
- JSONHelperBuilder::Object<TestPreset::FilterOptions>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+ JSONHelperBuilder::Object<TestPreset::FilterOptions>()
.Bind("include"_s, &TestPreset::FilterOptions::Include,
TestPresetOptionalFilterIncludeHelper, false)
.Bind("exclude"_s, &TestPreset::FilterOptions::Exclude,
TestPresetOptionalFilterExcludeHelper, false));
auto const TestPresetHelper =
- JSONHelperBuilder::Object<TestPreset>(ReadFileResult::READ_OK,
- ReadFileResult::INVALID_PRESET, false)
+ JSONHelperBuilder::Object<TestPreset>(
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
.Bind("name"_s, &TestPreset::Name,
- cmCMakePresetsGraphInternal::PresetStringHelper)
+ cmCMakePresetsGraphInternal::PresetNameHelper)
.Bind("inherits"_s, &TestPreset::Inherits,
cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
false)
@@ -349,7 +349,7 @@ auto const TestPresetHelper =
cmCMakePresetsGraphInternal::PresetBoolHelper, false)
.Bind<std::nullptr_t>("vendor"_s, nullptr,
cmCMakePresetsGraphInternal::VendorHelper(
- ReadFileResult::INVALID_PRESET),
+ cmCMakePresetErrors::INVALID_PRESET),
false)
.Bind("displayName"_s, &TestPreset::DisplayName,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -377,13 +377,12 @@ auto const TestPresetHelper =
}
namespace cmCMakePresetsGraphInternal {
-cmCMakePresetsGraph::ReadFileResult TestPresetsHelper(
- std::vector<cmCMakePresetsGraph::TestPreset>& out, const Json::Value* value)
+bool TestPresetsHelper(std::vector<cmCMakePresetsGraph::TestPreset>& out,
+ const Json::Value* value, cmJSONState* state)
{
static auto const helper = JSONHelperBuilder::Vector<TestPreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
- TestPresetHelper);
+ cmCMakePresetErrors::INVALID_PRESETS, TestPresetHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
diff --git a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
index 33680a1829..7224e17d60 100644
--- a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
@@ -9,69 +9,72 @@
#include <cm3p/json/value.h>
+#include "cmCMakePresetErrors.h"
#include "cmCMakePresetsGraph.h"
#include "cmCMakePresetsGraphInternal.h"
#include "cmJSONHelpers.h"
+class cmJSONState;
+
namespace {
-using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
-ReadFileResult WorkflowStepTypeHelper(WorkflowPreset::WorkflowStep::Type& out,
- const Json::Value* value)
+bool WorkflowStepTypeHelper(WorkflowPreset::WorkflowStep::Type& out,
+ const Json::Value* value, cmJSONState* state)
{
if (!value) {
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
if (!value->isString()) {
- return ReadFileResult::INVALID_PRESET;
+ return false;
}
if (value->asString() == "configure") {
out = WorkflowPreset::WorkflowStep::Type::Configure;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "build") {
out = WorkflowPreset::WorkflowStep::Type::Build;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "test") {
out = WorkflowPreset::WorkflowStep::Type::Test;
- return ReadFileResult::READ_OK;
+ return true;
}
if (value->asString() == "package") {
out = WorkflowPreset::WorkflowStep::Type::Package;
- return ReadFileResult::READ_OK;
+ return true;
}
- return ReadFileResult::INVALID_PRESET;
+ cmCMakePresetErrors::INVALID_PRESET(value, state);
+ return false;
}
auto const WorkflowStepHelper =
- cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset::WorkflowStep>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ cmJSONHelperBuilder::Object<WorkflowPreset::WorkflowStep>(
+ JsonErrors::INVALID_OBJECT, false)
.Bind("type"_s, &WorkflowPreset::WorkflowStep::PresetType,
WorkflowStepTypeHelper)
.Bind("name"_s, &WorkflowPreset::WorkflowStep::PresetName,
cmCMakePresetsGraphInternal::PresetStringHelper);
auto const WorkflowStepsHelper =
- cmJSONHelperBuilder<ReadFileResult>::Vector<WorkflowPreset::WorkflowStep>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
- WorkflowStepHelper);
+ cmJSONHelperBuilder::Vector<WorkflowPreset::WorkflowStep>(
+ cmCMakePresetErrors::INVALID_PRESET, WorkflowStepHelper);
auto const WorkflowPresetHelper =
- cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+ cmJSONHelperBuilder::Object<WorkflowPreset>(
+ cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
.Bind("name"_s, &WorkflowPreset::Name,
- cmCMakePresetsGraphInternal::PresetStringHelper)
+ cmCMakePresetsGraphInternal::PresetNameHelper)
.Bind<std::nullptr_t>("vendor"_s, nullptr,
cmCMakePresetsGraphInternal::VendorHelper(
- ReadFileResult::INVALID_PRESET),
+ cmCMakePresetErrors::INVALID_PRESET),
false)
.Bind("displayName"_s, &WorkflowPreset::DisplayName,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -81,15 +84,13 @@ auto const WorkflowPresetHelper =
}
namespace cmCMakePresetsGraphInternal {
-cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper(
+bool WorkflowPresetsHelper(
std::vector<cmCMakePresetsGraph::WorkflowPreset>& out,
- const Json::Value* value)
+ const Json::Value* value, cmJSONState* state)
{
- static auto const helper =
- cmJSONHelperBuilder<ReadFileResult>::Vector<WorkflowPreset>(
- ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
- WorkflowPresetHelper);
+ static auto const helper = cmJSONHelperBuilder::Vector<WorkflowPreset>(
+ cmCMakePresetErrors::INVALID_PRESETS, WorkflowPresetHelper);
- return helper(out, value);
+ return helper(out, value, state);
}
}
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 5899a61b43..c8eea38b75 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -54,6 +54,7 @@
#include "cmDynamicLoader.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
+#include "cmJSONState.h"
#include "cmMakefile.h"
#include "cmProcessOutput.h"
#include "cmState.h"
@@ -2336,10 +2337,10 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName,
cmCMakePresetsGraph settingsFile;
auto result = settingsFile.ReadProjectPresets(workingDirectory);
- if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
- cmSystemTools::Error(
- cmStrCat("Could not read presets from ", workingDirectory, ": ",
- cmCMakePresetsGraph::ResultToString(result)));
+ if (result != true) {
+ cmSystemTools::Error(cmStrCat("Could not read presets from ",
+ workingDirectory, ":",
+ settingsFile.parseState.GetErrorMessage()));
return false;
}
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 6be0fa329c..f6fdd48969 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -10,6 +10,8 @@
#include <cmext/string_view>
#include "cmComputeLinkInformation.h"
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalCommonGenerator.h"
#include "cmGlobalGenerator.h"
@@ -23,7 +25,6 @@
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
-#include "cmTarget.h"
#include "cmValue.h"
cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
@@ -177,7 +178,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories(
// We can ignore the INTERFACE_LIBRARY items because
// Target->GetLinkInformation already processed their
// link interface and they don't have any output themselves.
- && linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
+ && (linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY
+ // Synthesized targets may have relevant rules.
+ || linkee->IsSynthetic()) &&
((lang == "CXX"_s && linkee->HaveCxx20ModuleSources()) ||
(lang == "Fortran"_s && linkee->HaveFortranSources(config))) &&
emitted.insert(linkee).second) {
@@ -247,7 +250,7 @@ std::string cmCommonTargetGenerator::GetManifests(const std::string& config)
std::string cmCommonTargetGenerator::GetAIXExports(std::string const&)
{
std::string aixExports;
- if (this->GeneratorTarget->Target->IsAIX()) {
+ if (this->GeneratorTarget->IsAIX()) {
if (cmValue exportAll =
this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) {
if (cmIsOff(*exportAll)) {
@@ -291,11 +294,17 @@ std::string cmCommonTargetGenerator::GetLinkerLauncher(
const std::string& config)
{
std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
- cmValue launcherProp =
- this->GeneratorTarget->GetProperty(lang + "_LINKER_LAUNCHER");
+ std::string propName = lang + "_LINKER_LAUNCHER";
+ cmValue launcherProp = this->GeneratorTarget->GetProperty(propName);
if (cmNonempty(launcherProp)) {
+ cmGeneratorExpressionDAGChecker dagChecker(this->GeneratorTarget, propName,
+ nullptr, nullptr);
+ std::string evaluatedLinklauncher = cmGeneratorExpression::Evaluate(
+ *launcherProp, this->LocalCommonGenerator, config, this->GeneratorTarget,
+ &dagChecker, this->GeneratorTarget, lang);
// Convert ;-delimited list to single string
- std::vector<std::string> args = cmExpandedList(*launcherProp, true);
+ std::vector<std::string> args =
+ cmExpandedList(evaluatedLinklauncher, true);
if (!args.empty()) {
args[0] = this->LocalCommonGenerator->ConvertToOutputFormat(
args[0], cmOutputConverter::SHELL);
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index ad8fb8b6f6..a93477b924 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -289,28 +289,28 @@ cmComputeLinkInformation::cmComputeLinkInformation(
// Get options needed to link libraries.
if (cmValue flag = this->Makefile->GetDefinition(
- "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) {
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FLAG"))) {
this->LibLinkFlag = *flag;
} else {
this->LibLinkFlag =
this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
}
if (cmValue flag = this->Makefile->GetDefinition(
- "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) {
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_FILE_FLAG"))) {
this->LibLinkFileFlag = *flag;
} else {
this->LibLinkFileFlag =
this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG");
}
if (cmValue suffix = this->Makefile->GetDefinition(
- "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) {
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_LIBRARY_SUFFIX"))) {
this->LibLinkSuffix = *suffix;
} else {
this->LibLinkSuffix =
this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
}
if (cmValue flag = this->Makefile->GetDefinition(
- "CMAKE_" + this->LinkLanguage + "_LINK_OBJECT_FILE_FLAG")) {
+ cmStrCat("CMAKE_", this->LinkLanguage, "_LINK_OBJECT_FILE_FLAG"))) {
this->ObjLinkFileFlag = *flag;
} else {
this->ObjLinkFileFlag =
@@ -325,7 +325,7 @@ cmComputeLinkInformation::cmComputeLinkInformation(
: "SHARED_LIBRARY");
std::string rtVar =
cmStrCat("CMAKE_", tType, "_RUNTIME_", this->LinkLanguage, "_FLAG");
- std::string rtSepVar = rtVar + "_SEP";
+ std::string rtSepVar = cmStrCat(rtVar, "_SEP");
this->RuntimeFlag = this->Makefile->GetSafeDefinition(rtVar);
this->RuntimeSep = this->Makefile->GetSafeDefinition(rtSepVar);
this->RuntimeAlways = (this->Makefile->GetSafeDefinition(
@@ -1070,8 +1070,8 @@ void cmComputeLinkInformation::AddRuntimeLinkLibrary(std::string const& lang)
if (runtimeLibrary.empty()) {
return;
}
- if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(
- "CMAKE_" + lang + "_RUNTIME_LIBRARY_LINK_OPTIONS_" + runtimeLibrary)) {
+ if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(cmStrCat(
+ "CMAKE_", lang, "_RUNTIME_LIBRARY_LINK_OPTIONS_", runtimeLibrary))) {
std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
for (std::string const& i : libsVec) {
if (!cm::contains(this->ImplicitLinkLibs, i)) {
@@ -1157,7 +1157,7 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
// Pass the full path to the target file.
BT<std::string> lib = BT<std::string>(
tgt->GetFullPath(config, artifact, true), item.Backtrace);
- if (tgt->Target->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") &&
+ if (tgt->IsAIX() && cmHasLiteralSuffix(lib.Value, "-NOTFOUND") &&
artifact == cmStateEnums::ImportLibraryArtifact) {
// This is an imported executable on AIX that has ENABLE_EXPORTS
// but not IMPORTED_IMPLIB. CMake used to produce and accept such
@@ -1185,7 +1185,7 @@ void cmComputeLinkInformation::AddItem(LinkEntry const& entry)
if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) ||
(entry.Feature == DEFAULT &&
cmSystemTools::IsPathToFramework(item.Value) &&
- this->Makefile->IsOn("APPLE"))) {
+ this->Target->IsApple())) {
// This is a framework.
this->AddFrameworkItem(entry);
} else if (cmSystemTools::FileIsFullPath(item.Value)) {
@@ -1399,10 +1399,9 @@ void cmComputeLinkInformation::ComputeItemParserInfo()
reg = "^(";
for (std::string const& p : this->LinkPrefixes) {
reg += p;
- reg += "|";
+ reg += '|';
}
- reg += ")";
- reg += "([^/:]*)";
+ reg += ")([^/:]*)";
// Create a regex to match any library name.
std::string reg_any = cmStrCat(reg, libext);
@@ -1479,14 +1478,14 @@ std::string cmComputeLinkInformation::CreateExtensionRegex(
}
// Finish the list.
- libext += ")";
+ libext += ')';
// Add an optional OpenBSD-style version or major.minor.version component.
if (this->OpenBSD || type == LinkShared) {
libext += "(\\.[0-9]+)*";
}
- libext += "$";
+ libext += '$';
return libext;
}
@@ -1564,7 +1563,7 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
this->OldLinkDirItems.push_back(item.Value);
}
- if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) {
+ if (target->IsFrameworkOnApple()) {
// Add the framework directory and the framework item itself
auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath(
item.Value, cmGlobalGenerator::FrameworkFormat::Extended);
@@ -1580,26 +1579,32 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
// Add the directory portion to the framework search path.
this->AddFrameworkPath(fwDescriptor->Directory);
}
- if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) {
- this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes,
- target,
- this->FindLibraryFeature(entry.Feature));
- } else {
+
+ if (this->GlobalGenerator->IsXcode()) {
this->Items.emplace_back(
item, ItemIsPath::Yes, target,
- this->FindLibraryFeature(
- entry.Feature == DEFAULT ? "__CMAKE_LINK_LIBRARY" : entry.Feature));
+ this->FindLibraryFeature(entry.Feature == DEFAULT
+ ? "__CMAKE_LINK_FRAMEWORK"
+ : entry.Feature));
+ } else {
+ if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) {
+ this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes,
+ target,
+ this->FindLibraryFeature(entry.Feature));
+ } else {
+ this->Items.emplace_back(
+ item, ItemIsPath::Yes, target,
+ this->FindLibraryFeature(entry.Feature == DEFAULT
+ ? "__CMAKE_LINK_LIBRARY"
+ : entry.Feature));
+ }
}
} else {
// Now add the full path to the library.
this->Items.emplace_back(
item, ItemIsPath::Yes, target,
this->FindLibraryFeature(
- entry.Feature == DEFAULT
- ? (target->IsFrameworkOnApple() && this->GlobalGenerator->IsXcode()
- ? "__CMAKE_LINK_FRAMEWORK"
- : "__CMAKE_LINK_LIBRARY")
- : entry.Feature));
+ entry.Feature == DEFAULT ? "__CMAKE_LINK_LIBRARY" : entry.Feature));
}
}
@@ -1697,7 +1702,8 @@ bool cmComputeLinkInformation::CheckImplicitDirItem(LinkEntry const& entry)
case cmPolicies::WARN:
if (this->CMP0060Warn) {
// Print the warning at most once for this item.
- std::string const& wid = "CMP0060-WARNING-GIVEN-" + item.Value;
+ std::string const& wid =
+ cmStrCat("CMP0060-WARNING-GIVEN-", item.Value);
if (!this->CMakeInstance->GetPropertyAsBool(wid)) {
this->CMakeInstance->SetProperty(wid, "1");
this->CMP0060WarnItems.insert(item.Value);
@@ -1859,8 +1865,8 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
: cmGlobalGenerator::FrameworkFormat::Extended);
if (!fwDescriptor) {
std::ostringstream e;
- e << "Could not parse framework path \"" << item << "\" "
- << "linked by target " << this->Target->GetName() << ".";
+ e << "Could not parse framework path \"" << item << "\" linked by target "
+ << this->Target->GetName() << '.';
cmSystemTools::Error(e.str());
return;
}
@@ -1994,9 +2000,9 @@ void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry,
std::ostringstream w;
/* clang-format off */
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0008) << "\n"
- << "Target \"" << this->Target->GetName() << "\" links to item\n"
- << " " << item << "\n"
- << "which is a full-path but not a valid library file name.";
+ "Target \"" << this->Target->GetName() << "\" links to item\n"
+ " " << item << "\n"
+ "which is a full-path but not a valid library file name.";
/* clang-format on */
this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
this->Target->GetBacktrace());
@@ -2014,9 +2020,9 @@ void cmComputeLinkInformation::HandleBadFullItem(LinkEntry const& entry,
std::ostringstream e;
/* clang-format off */
e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0008) << "\n"
- << "Target \"" << this->Target->GetName() << "\" links to item\n"
- << " " << item << "\n"
- << "which is a full-path but not a valid library file name.";
+ "Target \"" << this->Target->GetName() << "\" links to item\n"
+ " " << item << "\n"
+ "which is a full-path but not a valid library file name.";
/* clang-format on */
this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
@@ -2055,7 +2061,7 @@ bool cmComputeLinkInformation::FinishLinkerSearchDirectories()
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS: {
std::ostringstream e;
- e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << "\n";
+ e << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0003) << '\n';
this->PrintLinkPolicyDiagnosis(e);
this->CMakeInstance->IssueMessage(MessageType::FATAL_ERROR, e.str(),
this->Target->GetBacktrace());
@@ -2075,18 +2081,17 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
// Tell the user what to do.
/* clang-format off */
os << "Policy CMP0003 should be set before this line. "
- << "Add code such as\n"
- << " if(COMMAND cmake_policy)\n"
- << " cmake_policy(SET CMP0003 NEW)\n"
- << " endif(COMMAND cmake_policy)\n"
- << "as early as possible but after the most recent call to "
- << "cmake_minimum_required or cmake_policy(VERSION). ";
+ "Add code such as\n"
+ " if(COMMAND cmake_policy)\n"
+ " cmake_policy(SET CMP0003 NEW)\n"
+ " endif(COMMAND cmake_policy)\n"
+ "as early as possible but after the most recent call to "
+ "cmake_minimum_required or cmake_policy(VERSION). ";
/* clang-format on */
// List the items that might need the old-style paths.
os << "This warning appears because target \"" << this->Target->GetName()
- << "\" "
- << "links to some libraries for which the linker must search:\n";
+ << "\" links to some libraries for which the linker must search:\n";
{
// Format the list of unknown items to be as short as possible while
// still fitting in the allowed width (a true solution would be the
@@ -2099,7 +2104,7 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
// output the current line and reset it. Note that the separator
// is either " " or ", " which is always 2 characters.
if (!line.empty() && (line.size() + i.size() + 2) > max_size) {
- os << line << "\n";
+ os << line << '\n';
sep = " ";
line.clear();
}
@@ -2109,7 +2114,7 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
sep = ", ";
}
if (!line.empty()) {
- os << line << "\n";
+ os << line << '\n';
}
}
@@ -2118,17 +2123,17 @@ void cmComputeLinkInformation::PrintLinkPolicyDiagnosis(std::ostream& os)
std::set<std::string> emitted;
for (std::string const& i : this->OldLinkDirItems) {
if (emitted.insert(cmSystemTools::GetFilenamePath(i)).second) {
- os << " " << i << "\n";
+ os << " " << i << '\n';
}
}
// Explain.
os << "CMake is adding directories in the second list to the linker "
- << "search path in case they are needed to find libraries from the "
- << "first list (for backwards compatibility with CMake 2.4). "
- << "Set policy CMP0003 to OLD or NEW to enable or disable this "
- << "behavior explicitly. "
- << "Run \"cmake --help-policy CMP0003\" for more information.";
+ "search path in case they are needed to find libraries from the "
+ "first list (for backwards compatibility with CMake 2.4). "
+ "Set policy CMP0003 to OLD or NEW to enable or disable this "
+ "behavior explicitly. "
+ "Run \"cmake --help-policy CMP0003\" for more information.";
}
void cmComputeLinkInformation::LoadImplicitLinkInfo()
@@ -2144,7 +2149,7 @@ void cmComputeLinkInformation::LoadImplicitLinkInfo()
if (cmValue libraryArch =
this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
for (std::string const& i : implicitDirVec) {
- this->ImplicitLinkDirs.insert(i + "/" + *libraryArch);
+ this->ImplicitLinkDirs.insert(cmStrCat(i, '/', *libraryArch));
}
}
@@ -2400,10 +2405,11 @@ void cmComputeLinkInformation::GetRPath(std::vector<std::string>& runtimeDirs,
cmGeneratorTarget::LinkClosure const* lc =
this->Target->GetLinkClosure(this->Config);
for (std::string const& li : lc->Languages) {
- std::string useVar =
- "CMAKE_" + li + "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH";
+ std::string useVar = cmStrCat(
+ "CMAKE_", li, "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH");
if (this->Makefile->IsOn(useVar)) {
- std::string dirVar = "CMAKE_" + li + "_IMPLICIT_LINK_DIRECTORIES";
+ std::string dirVar =
+ cmStrCat("CMAKE_", li, "_IMPLICIT_LINK_DIRECTORIES");
if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) {
cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted);
}
diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in
index 90f3de0a1d..3f19a11aa2 100644
--- a/Source/cmConfigure.cmake.h.in
+++ b/Source/cmConfigure.cmake.h.in
@@ -23,7 +23,7 @@
#cmakedefine CMake_USE_MACH_PARSER
#cmakedefine CMake_USE_XCOFF_PARSER
#cmakedefine CMAKE_USE_WMAKE
-#define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
+#cmakedefine CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
#define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@"
#define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@"
#define CMAKE_DOC_DIR "/@CMAKE_DOC_DIR@"
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index acf1c206be..3149ccfa9c 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -72,6 +72,10 @@ SETUP_LANGUAGE(swift_properties, Swift);
std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
+std::string const kCMAKE_EXECUTABLE_ENABLE_EXPORTS =
+ "CMAKE_EXECUTABLE_ENABLE_EXPORTS";
+std::string const kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS =
+ "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS";
std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
@@ -997,6 +1001,8 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
vars.insert(kCMAKE_CUDA_ARCHITECTURES);
vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
vars.insert(kCMAKE_ENABLE_EXPORTS);
+ vars.insert(kCMAKE_EXECUTABLE_ENABLE_EXPORTS);
+ vars.insert(kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS);
vars.insert(kCMAKE_HIP_ARCHITECTURES);
vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx
index fd6aee1f38..24ba3687f7 100644
--- a/Source/cmCurl.cxx
+++ b/Source/cmCurl.cxx
@@ -131,12 +131,12 @@ std::string cmCurlFixFileURL(std::string url)
// Convert string from UTF-8 to ACP if this is a file:// URL.
std::wstring wurl = cmsys::Encoding::ToWide(url);
if (!wurl.empty()) {
- int mblen =
- WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, NULL, 0, NULL, NULL);
+ int mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, nullptr, 0,
+ nullptr, nullptr);
if (mblen > 0) {
std::vector<char> chars(mblen);
mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
- mblen, NULL, NULL);
+ mblen, nullptr, nullptr);
if (mblen > 0) {
url = &chars[0];
}
diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx
index 68c65bbd91..e12cf708e1 100644
--- a/Source/cmCustomCommand.cxx
+++ b/Source/cmCustomCommand.cxx
@@ -7,6 +7,8 @@
#include <cmext/algorithm>
+#include "cmStateSnapshot.h"
+
const std::vector<std::string>& cmCustomCommand::GetOutputs() const
{
return this->Outputs;
@@ -162,6 +164,16 @@ void cmCustomCommand::SetCommandExpandLists(bool b)
this->CommandExpandLists = b;
}
+bool cmCustomCommand::GetDependsExplicitOnly() const
+{
+ return this->DependsExplicitOnly;
+}
+
+void cmCustomCommand::SetDependsExplicitOnly(bool b)
+{
+ this->DependsExplicitOnly = b;
+}
+
const std::string& cmCustomCommand::GetDepfile() const
{
return this->Depfile;
@@ -182,14 +194,19 @@ void cmCustomCommand::SetJobPool(const std::string& job_pool)
this->JobPool = job_pool;
}
-cmPolicies::PolicyStatus cmCustomCommand::GetCMP0116Status() const
-{
- return this->CMP0116Status;
-}
+#define DEFINE_CC_POLICY_ACCESSOR(P) \
+ cmPolicies::PolicyStatus cmCustomCommand::Get##P##Status() const \
+ { \
+ return this->P##Status; \
+ }
+CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DEFINE_CC_POLICY_ACCESSOR)
+#undef DEFINE_CC_POLICY_ACCESSOR
-void cmCustomCommand::SetCMP0116Status(cmPolicies::PolicyStatus cmp0116)
+void cmCustomCommand::RecordPolicyValues(const cmStateSnapshot& snapshot)
{
- this->CMP0116Status = cmp0116;
+#define SET_CC_POLICY(P) this->P##Status = snapshot.GetPolicy(cmPolicies::P);
+ CM_FOR_EACH_CUSTOM_COMMAND_POLICY(SET_CC_POLICY)
+#undef SET_CC_POLICY
}
const std::string& cmCustomCommand::GetTarget() const
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index 5533847ae1..1e68dbfdda 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -17,6 +17,8 @@ class cmImplicitDependsList
{
};
+class cmStateSnapshot;
+
/** \class cmCustomCommand
* \brief A class to encapsulate a custom command
*
@@ -100,6 +102,11 @@ public:
bool GetCommandExpandLists() const;
void SetCommandExpandLists(bool b);
+ /** Set/Get whether to use additional dependencies coming from
+ users of OUTPUT of the custom command. */
+ bool GetDependsExplicitOnly() const;
+ void SetDependsExplicitOnly(bool b);
+
/** Set/Get the depfile (used by the Ninja generator) */
const std::string& GetDepfile() const;
void SetDepfile(const std::string& depfile);
@@ -108,9 +115,13 @@ public:
const std::string& GetJobPool() const;
void SetJobPool(const std::string& job_pool);
- /** Set/Get the CMP0116 status (used by the Ninja generator) */
- cmPolicies::PolicyStatus GetCMP0116Status() const;
- void SetCMP0116Status(cmPolicies::PolicyStatus cmp0116);
+#define DECLARE_CC_POLICY_ACCESSOR(P) \
+ cmPolicies::PolicyStatus Get##P##Status() const;
+ CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DECLARE_CC_POLICY_ACCESSOR)
+#undef DECLARE_CC_POLICY_ACCESSOR
+
+ /** Record policy values from state snapshot */
+ void RecordPolicyValues(const cmStateSnapshot& snapshot);
/** Set/Get the associated target */
const std::string& GetTarget() const;
@@ -135,5 +146,12 @@ private:
bool CommandExpandLists = false;
bool StdPipesUTF8 = false;
bool HasMainDependency_ = false;
- cmPolicies::PolicyStatus CMP0116Status = cmPolicies::WARN;
+ bool DependsExplicitOnly = false;
+
+// Policies are NEW for synthesized custom commands, and set by cmMakefile for
+// user-created custom commands.
+#define DECLARE_CC_POLICY_FIELD(P) \
+ cmPolicies::PolicyStatus P##Status = cmPolicies::NEW;
+ CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DECLARE_CC_POLICY_FIELD)
+#undef DECLARE_CC_POLICY_FIELD
};
diff --git a/Source/cmEvaluatedTargetProperty.cxx b/Source/cmEvaluatedTargetProperty.cxx
new file mode 100644
index 0000000000..1173690318
--- /dev/null
+++ b/Source/cmEvaluatedTargetProperty.cxx
@@ -0,0 +1,111 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmEvaluatedTargetProperty.h"
+
+#include <unordered_map>
+#include <utility>
+
+#include "cmGeneratorExpressionContext.h"
+#include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmStringAlgorithms.h"
+
+struct cmGeneratorExpressionDAGChecker;
+
+EvaluatedTargetPropertyEntry::EvaluatedTargetPropertyEntry(
+ cmLinkImplItem const& item, cmListFileBacktrace bt)
+ : LinkImplItem(item)
+ , Backtrace(std::move(bt))
+{
+}
+
+EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
+ cmGeneratorTarget const* thisTarget, std::string const& config,
+ std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+ cmGeneratorTarget::TargetPropertyEntry& entry)
+{
+ EvaluatedTargetPropertyEntry ee(entry.LinkImplItem, entry.GetBacktrace());
+ cmExpandList(entry.Evaluate(thisTarget->GetLocalGenerator(), config,
+ thisTarget, dagChecker, lang),
+ ee.Values);
+ if (entry.GetHadContextSensitiveCondition()) {
+ ee.ContextDependent = true;
+ }
+ return ee;
+}
+
+EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
+ cmGeneratorTarget const* thisTarget, std::string const& config,
+ std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+ std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
+ in)
+{
+ EvaluatedTargetPropertyEntries out;
+ out.Entries.reserve(in.size());
+ for (auto const& entry : in) {
+ out.Entries.emplace_back(EvaluateTargetPropertyEntry(
+ thisTarget, config, lang, dagChecker, *entry));
+ }
+ return out;
+}
+
+namespace {
+void addInterfaceEntry(cmGeneratorTarget const* headTarget,
+ std::string const& config, std::string const& prop,
+ std::string const& lang,
+ cmGeneratorExpressionDAGChecker* dagChecker,
+ EvaluatedTargetPropertyEntries& entries,
+ cmGeneratorTarget::LinkInterfaceFor interfaceFor,
+ std::vector<cmLinkImplItem> const& libraries)
+{
+ for (cmLinkImplItem const& lib : libraries) {
+ if (lib.Target) {
+ EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
+ // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
+ // caller's property and hand-evaluate it as if it were compiled.
+ // Create a context as cmCompiledGeneratorExpression::Evaluate does.
+ cmGeneratorExpressionContext context(
+ headTarget->GetLocalGenerator(), config, false, headTarget, headTarget,
+ true, lib.Backtrace, lang);
+ cmExpandList(lib.Target->EvaluateInterfaceProperty(
+ prop, &context, dagChecker, interfaceFor),
+ ee.Values);
+ ee.ContextDependent = context.HadContextSensitiveCondition;
+ entries.Entries.emplace_back(std::move(ee));
+ }
+ }
+}
+}
+
+void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
+ std::string const& config, std::string const& prop,
+ std::string const& lang,
+ cmGeneratorExpressionDAGChecker* dagChecker,
+ EvaluatedTargetPropertyEntries& entries,
+ IncludeRuntimeInterface searchRuntime,
+ cmGeneratorTarget::LinkInterfaceFor interfaceFor)
+{
+ if (searchRuntime == IncludeRuntimeInterface::Yes) {
+ if (cmLinkImplementation const* impl =
+ headTarget->GetLinkImplementation(config, interfaceFor)) {
+ entries.HadContextSensitiveCondition =
+ impl->HadContextSensitiveCondition;
+
+ auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang);
+ if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
+ addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+ interfaceFor, runtimeLibIt->second);
+ }
+ addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+ interfaceFor, impl->Libraries);
+ }
+ } else {
+ if (cmLinkImplementationLibraries const* impl =
+ headTarget->GetLinkImplementationLibraries(config, interfaceFor)) {
+ entries.HadContextSensitiveCondition =
+ impl->HadContextSensitiveCondition;
+ addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+ interfaceFor, impl->Libraries);
+ }
+ }
+}
diff --git a/Source/cmEvaluatedTargetProperty.h b/Source/cmEvaluatedTargetProperty.h
new file mode 100644
index 0000000000..e413ab0a21
--- /dev/null
+++ b/Source/cmEvaluatedTargetProperty.h
@@ -0,0 +1,80 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmGeneratorTarget.h"
+#include "cmListFileCache.h"
+
+class cmLinkImplItem;
+struct cmGeneratorExpressionDAGChecker;
+
+// Represent a target property entry after evaluating generator expressions
+// and splitting up lists.
+struct EvaluatedTargetPropertyEntry
+{
+ EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
+ cmListFileBacktrace bt);
+
+ // Move-only.
+ EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
+ EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
+ EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
+ delete;
+ EvaluatedTargetPropertyEntry& operator=(
+ EvaluatedTargetPropertyEntry const&) = delete;
+
+ cmLinkImplItem const& LinkImplItem;
+ cmListFileBacktrace Backtrace;
+ std::vector<std::string> Values;
+ bool ContextDependent = false;
+};
+
+EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
+ cmGeneratorTarget const* thisTarget, std::string const& config,
+ std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+ cmGeneratorTarget::TargetPropertyEntry& entry);
+
+struct EvaluatedTargetPropertyEntries
+{
+ std::vector<EvaluatedTargetPropertyEntry> Entries;
+ bool HadContextSensitiveCondition = false;
+};
+
+EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
+ cmGeneratorTarget const* thisTarget, std::string const& config,
+ std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+ std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
+ in);
+
+// IncludeRuntimeInterface is used to break the cycle in computing
+// the necessary transitive dependencies of targets that can occur
+// now that we have implicit language runtime targets.
+//
+// To determine the set of languages that a target has we need to iterate
+// all the sources which includes transitive INTERFACE sources.
+// Therefore we can't determine what language runtimes are needed
+// for a target until after all sources are computed.
+//
+// Therefore while computing the applicable INTERFACE_SOURCES we
+// must ignore anything in LanguageRuntimeLibraries or we would
+// create a cycle ( INTERFACE_SOURCES requires LanguageRuntimeLibraries,
+// LanguageRuntimeLibraries requires INTERFACE_SOURCES).
+//
+enum class IncludeRuntimeInterface
+{
+ Yes,
+ No
+};
+
+void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
+ std::string const& config, std::string const& prop,
+ std::string const& lang,
+ cmGeneratorExpressionDAGChecker* dagChecker,
+ EvaluatedTargetPropertyEntries& entries,
+ IncludeRuntimeInterface searchRuntime,
+ cmGeneratorTarget::LinkInterfaceFor interfaceFor =
+ cmGeneratorTarget::LinkInterfaceFor::Usage);
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index ed199ea5d3..437ae69501 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -106,6 +106,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
cmGeneratorExpression::BuildInterface,
properties);
+ this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte,
+ cmGeneratorExpression::BuildInterface,
+ properties);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
cmGeneratorExpression::BuildInterface,
properties);
@@ -254,7 +257,7 @@ void cmExportBuildFileGenerator::SetImportLocationProperty(
if (target->HasImportLibrary(config)) {
std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
std::string value =
- target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact);
+ target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true);
if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
target->GetImplibGNUtoMS(config, value, value,
"${CMAKE_IMPORT_LIBRARY_SUFFIX}");
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index c8e2cb8bcb..6e7ef4e735 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -938,13 +938,13 @@ void cmExportFileGenerator::GeneratePolicyHeaderCode(std::ostream& os)
// Isolate the file policy level.
// Support CMake versions as far back as 2.6 but also support using NEW
- // policy settings for up to CMake 3.24 (this upper limit may be reviewed
+ // policy settings for up to CMake 3.25 (this upper limit may be reviewed
// and increased from time to time). This reduces the opportunity for CMake
// warnings when an older export file is later used with newer CMake
// versions.
/* clang-format off */
os << "cmake_policy(PUSH)\n"
- << "cmake_policy(VERSION 2.8.3...3.24)\n";
+ << "cmake_policy(VERSION 2.8.3...3.25)\n";
/* clang-format on */
}
@@ -1063,7 +1063,8 @@ void cmExportFileGenerator::GenerateImportTargetCode(
}
// Mark the imported executable if it has exports.
- if (target->IsExecutableWithExports()) {
+ if (target->IsExecutableWithExports() ||
+ (target->IsSharedLibraryWithExports() && target->HasImportLibrary(""))) {
os << "set_property(TARGET " << targetName
<< " PROPERTY ENABLE_EXPORTS 1)\n";
}
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 5e190f490c..51c91f3658 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -110,6 +110,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
cmGeneratorExpression::InstallInterface,
properties);
+ this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gt,
+ cmGeneratorExpression::InstallInterface,
+ properties);
this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
cmGeneratorExpression::InstallInterface,
properties);
@@ -409,7 +412,7 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
// Append the installed file name.
value += cmInstallTargetGenerator::GetInstallFilename(
- target, config, cmInstallTargetGenerator::NameImplib);
+ target, config, cmInstallTargetGenerator::NameImplibReal);
// Store the property.
properties[prop] = value;
@@ -430,6 +433,19 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
properties[prop] = cmJoin(objects, ";");
importedLocations.insert(prop);
} else {
+ if (target->IsFrameworkOnApple() && target->HasImportLibrary(config)) {
+ // store as well IMPLIB value
+ auto importProp = cmStrCat("IMPORTED_IMPLIB", suffix);
+ auto importValue =
+ cmStrCat(value,
+ cmInstallTargetGenerator::GetInstallFilename(
+ target, config, cmInstallTargetGenerator::NameImplibReal));
+
+ // Store the property.
+ properties[importProp] = importValue;
+ importedLocations.insert(importProp);
+ }
+
// Construct the property name.
std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index 33c057dab0..c6ebad5c2d 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -85,7 +85,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*prop);
cmTarget dummyHead("try_compile_dummy_exe", cmStateEnums::EXECUTABLE,
- cmTarget::VisibilityNormal, tgt->Target->GetMakefile(),
+ cmTarget::Visibility::Normal, tgt->Target->GetMakefile(),
cmTarget::PerConfig::Yes);
cmGeneratorTarget gDummyHead(&dummyHead, tgt->GetLocalGenerator());
diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx
index 988c5c3d55..e9e2921316 100644
--- a/Source/cmExtraCodeBlocksGenerator.cxx
+++ b/Source/cmExtraCodeBlocksGenerator.cxx
@@ -45,7 +45,7 @@ cmExtraCodeBlocksGenerator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<
cmExtraCodeBlocksGenerator>
- factory("CodeBlocks", "Generates CodeBlocks project files.");
+ factory("CodeBlocks", "Generates CodeBlocks project files (deprecated).");
if (factory.GetSupportedGlobalGenerators().empty()) {
#if defined(_WIN32)
diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx
index 9e8ac5cf0e..7538a7f469 100644
--- a/Source/cmExtraCodeLiteGenerator.cxx
+++ b/Source/cmExtraCodeLiteGenerator.cxx
@@ -33,7 +33,7 @@ cmExtraCodeLiteGenerator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<
cmExtraCodeLiteGenerator>
- factory("CodeLite", "Generates CodeLite project files.");
+ factory("CodeLite", "Generates CodeLite project files (deprecated).");
if (factory.GetSupportedGlobalGenerators().empty()) {
#if defined(_WIN32)
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx
index a07acdc9ae..6201889f22 100644
--- a/Source/cmExtraEclipseCDT4Generator.cxx
+++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -63,7 +63,8 @@ cmExtraEclipseCDT4Generator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<
cmExtraEclipseCDT4Generator>
- factory("Eclipse CDT4", "Generates Eclipse CDT 4.0 project files.");
+ factory("Eclipse CDT4",
+ "Generates Eclipse CDT 4.0 project files (deprecated).");
if (factory.GetSupportedGlobalGenerators().empty()) {
// TODO: Verify if __CYGWIN__ should be checked.
diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx
index eec43c4736..6e6d009826 100644
--- a/Source/cmExtraKateGenerator.cxx
+++ b/Source/cmExtraKateGenerator.cxx
@@ -8,6 +8,7 @@
#include <set>
#include <vector>
+#include "cmCMakePath.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -24,7 +25,7 @@ cmExtraKateGenerator::cmExtraKateGenerator() = default;
cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<cmExtraKateGenerator>
- factory("Kate", "Generates Kate project files.");
+ factory("Kate", "Generates Kate project files (deprecated).");
if (factory.GetSupportedGlobalGenerators().empty()) {
#if defined(_WIN32)
@@ -34,6 +35,7 @@ cmExternalMakefileProjectGeneratorFactory* cmExtraKateGenerator::GetFactory()
// factory.AddSupportedGlobalGenerator("MSYS Makefiles");
#endif
factory.AddSupportedGlobalGenerator("Ninja");
+ factory.AddSupportedGlobalGenerator("Ninja Multi-Config");
factory.AddSupportedGlobalGenerator("Unix Makefiles");
}
@@ -47,7 +49,9 @@ void cmExtraKateGenerator::Generate()
this->ProjectName = this->GenerateProjectName(
lg->GetProjectName(), mf->GetSafeDefinition("CMAKE_BUILD_TYPE"),
this->GetPathBasename(lg->GetBinaryDirectory()));
- this->UseNinja = (this->GlobalGenerator->GetName() == "Ninja");
+ this->UseNinja =
+ ((this->GlobalGenerator->GetName() == "Ninja") ||
+ (this->GlobalGenerator->GetName() == "Ninja Multi-Config"));
this->CreateKateProjectFile(*lg);
this->CreateDummyKateProjectFile(*lg);
@@ -81,6 +85,7 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
const std::string& makeArgs =
mf->GetSafeDefinition("CMAKE_KATE_MAKE_ARGUMENTS");
std::string const& homeOutputDir = lg.GetBinaryDirectory();
+ const auto configs = mf->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
/* clang-format off */
fout <<
@@ -104,16 +109,16 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
// this is for kate >= 4.13:
fout << "\t\t\"targets\":[\n";
- this->AppendTarget(fout, "all", make, makeArgs, homeOutputDir,
+ this->AppendTarget(fout, "all", configs, make, makeArgs, homeOutputDir,
homeOutputDir);
- this->AppendTarget(fout, "clean", make, makeArgs, homeOutputDir,
+ this->AppendTarget(fout, "clean", configs, make, makeArgs, homeOutputDir,
homeOutputDir);
// add all executable and library targets and some of the GLOBAL
// and UTILITY targets
for (const auto& localGen : this->GlobalGenerator->GetLocalGenerators()) {
const auto& targets = localGen->GetGeneratorTargets();
- std::string currentDir = localGen->GetCurrentBinaryDirectory();
+ const std::string currentDir = localGen->GetCurrentBinaryDirectory();
bool topLevel = (currentDir == localGen->GetBinaryDirectory());
for (const auto& target : targets) {
@@ -137,8 +142,8 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
}
}
if (insertTarget) {
- this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
- homeOutputDir);
+ this->AppendTarget(fout, targetName, configs, make, makeArgs,
+ currentDir, homeOutputDir);
}
} break;
case cmStateEnums::UTILITY:
@@ -153,19 +158,21 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
break;
}
- this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
- homeOutputDir);
+ this->AppendTarget(fout, targetName, configs, make, makeArgs,
+ currentDir, homeOutputDir);
break;
case cmStateEnums::EXECUTABLE:
case cmStateEnums::STATIC_LIBRARY:
case cmStateEnums::SHARED_LIBRARY:
case cmStateEnums::MODULE_LIBRARY:
case cmStateEnums::OBJECT_LIBRARY: {
- this->AppendTarget(fout, targetName, make, makeArgs, currentDir,
- homeOutputDir);
- std::string fastTarget = cmStrCat(targetName, "/fast");
- this->AppendTarget(fout, fastTarget, make, makeArgs, currentDir,
- homeOutputDir);
+ this->AppendTarget(fout, targetName, configs, make, makeArgs,
+ currentDir, homeOutputDir);
+ if (!this->UseNinja) {
+ std::string fastTarget = cmStrCat(targetName, "/fast");
+ this->AppendTarget(fout, fastTarget, configs, make, makeArgs,
+ currentDir, homeOutputDir);
+ }
} break;
default:
@@ -178,29 +185,36 @@ void cmExtraKateGenerator::WriteTargets(const cmLocalGenerator& lg,
std::vector<std::string> objectFileTargets;
localGen->GetIndividualFileTargets(objectFileTargets);
for (std::string const& f : objectFileTargets) {
- this->AppendTarget(fout, f, make, makeArgs, currentDir, homeOutputDir);
+ this->AppendTarget(fout, f, configs, make, makeArgs, currentDir,
+ homeOutputDir);
}
}
fout << "\t] }\n";
}
-void cmExtraKateGenerator::AppendTarget(cmGeneratedFileStream& fout,
- const std::string& target,
- const std::string& make,
- const std::string& makeArgs,
- const std::string& path,
- const std::string& homeOutputDir) const
+void cmExtraKateGenerator::AppendTarget(
+ cmGeneratedFileStream& fout, const std::string& target,
+ const std::vector<std::string>& configs, const std::string& make,
+ const std::string& makeArgs, const std::string& path,
+ const std::string& homeOutputDir) const
{
static char JsonSep = ' ';
- fout << "\t\t\t" << JsonSep << R"({"name":")" << target
- << "\", "
- "\"build_cmd\":\""
- << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path)
- << "\\\" " << makeArgs << " " << target << "\"}\n";
-
- JsonSep = ',';
+ for (const std::string& conf : configs) {
+ fout << "\t\t\t" << JsonSep << R"({"name":")" << target
+ << ((configs.size() > 1) ? (std::string(":") + conf) : std::string())
+ << "\", "
+ "\"build_cmd\":\""
+ << make << " -C \\\"" << (this->UseNinja ? homeOutputDir : path)
+ << "\\\" "
+ << ((this->UseNinja && configs.size() > 1)
+ ? std::string(" -f build-") + conf + ".ninja"
+ : std::string())
+ << makeArgs << " " << target << "\"}\n";
+
+ JsonSep = ',';
+ }
}
void cmExtraKateGenerator::CreateDummyKateProjectFile(
@@ -220,17 +234,58 @@ void cmExtraKateGenerator::CreateDummyKateProjectFile(
std::string cmExtraKateGenerator::GenerateFilesString(
const cmLocalGenerator& lg) const
{
- std::string s = cmStrCat(lg.GetSourceDirectory(), "/.git");
- if (cmSystemTools::FileExists(s)) {
- return "\"git\": 1 ";
+ const cmMakefile* mf = lg.GetMakefile();
+ std::string mode =
+ cmSystemTools::UpperCase(mf->GetSafeDefinition("CMAKE_KATE_FILES_MODE"));
+ static const std::string gitString = "\"git\": 1 ";
+ static const std::string svnString = "\"svn\": 1 ";
+ static const std::string hgString = "\"hg\": 1 ";
+ static const std::string fossilString = "\"fossil\": 1 ";
+
+ if (mode == "SVN") {
+ return svnString;
}
-
- s = cmStrCat(lg.GetSourceDirectory(), "/.svn");
- if (cmSystemTools::FileExists(s)) {
- return "\"svn\": 1 ";
+ if (mode == "GIT") {
+ return gitString;
}
+ if (mode == "HG") {
+ return hgString;
+ }
+ if (mode == "FOSSIL") {
+ return fossilString;
+ }
+
+ // check for the VCS files except when "forced" to "FILES" mode:
+ if (mode != "LIST") {
+ cmCMakePath startDir(lg.GetSourceDirectory(), cmCMakePath::auto_format);
+ // move the directories up to the root directory to see whether we are in
+ // a subdir of a svn, git, hg or fossil checkout
+ for (;;) {
+ std::string s = startDir.String() + "/.git";
+ if (cmSystemTools::FileExists(s)) {
+ return gitString;
+ }
- s = cmStrCat(lg.GetSourceDirectory(), '/');
+ s = startDir.String() + "/.svn";
+ if (cmSystemTools::FileExists(s)) {
+ return svnString;
+ }
+
+ s = startDir.String() + "/.hg";
+ if (cmSystemTools::FileExists(s)) {
+ return hgString;
+ }
+ s = startDir.String() + "/.fslckout";
+ if (cmSystemTools::FileExists(s)) {
+ return fossilString;
+ }
+
+ if (!startDir.HasRelativePath()) { // have we reached the root dir ?
+ break;
+ }
+ startDir = startDir.GetParentPath();
+ }
+ }
std::set<std::string> files;
std::string tmp;
@@ -240,9 +295,8 @@ std::string cmExtraKateGenerator::GenerateFilesString(
cmMakefile* makefile = lgen->GetMakefile();
const std::vector<std::string>& listFiles = makefile->GetListFiles();
for (std::string const& listFile : listFiles) {
- tmp = listFile;
- {
- files.insert(tmp);
+ if (listFile.find("/CMakeFiles/") == std::string::npos) {
+ files.insert(listFile);
}
}
diff --git a/Source/cmExtraKateGenerator.h b/Source/cmExtraKateGenerator.h
index c66ddbf0c3..203fa2f11c 100644
--- a/Source/cmExtraKateGenerator.h
+++ b/Source/cmExtraKateGenerator.h
@@ -5,6 +5,7 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <string>
+#include <vector>
#include "cmExternalMakefileProjectGenerator.h"
@@ -29,6 +30,7 @@ private:
void WriteTargets(const cmLocalGenerator& lg,
cmGeneratedFileStream& fout) const;
void AppendTarget(cmGeneratedFileStream& fout, const std::string& target,
+ const std::vector<std::string>& configs,
const std::string& make, const std::string& makeArgs,
const std::string& path,
const std::string& homeOutputDir) const;
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
index 19e87d52f8..33901ac594 100644
--- a/Source/cmExtraSublimeTextGenerator.cxx
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -43,7 +43,8 @@ cmExtraSublimeTextGenerator::GetFactory()
{
static cmExternalMakefileProjectGeneratorSimpleFactory<
cmExtraSublimeTextGenerator>
- factory("Sublime Text 2", "Generates Sublime Text 2 project files.");
+ factory("Sublime Text 2",
+ "Generates Sublime Text 2 project files (deprecated).");
if (factory.GetSupportedGlobalGenerators().empty()) {
#if defined(_WIN32)
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index d3683d404a..4a8716f8ed 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -1355,8 +1355,8 @@ CompileData Target::BuildCompileData(cmSourceFile* sf)
}
// Add precompile headers compile options.
- std::vector<std::string> architectures;
- this->GT->GetAppleArchs(this->Config, architectures);
+ std::vector<std::string> architectures =
+ this->GT->GetAppleArchs(this->Config, fd.Language);
if (architectures.empty()) {
architectures.emplace_back();
}
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 00a68a833e..45fba8bffb 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -30,7 +30,6 @@
#include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h"
-#include "cmCMakePath.h"
#include "cmCryptoHash.h"
#include "cmELF.h"
#include "cmExecutionStatus.h"
@@ -1278,9 +1277,9 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
}
}
- cmCMakePath path(input, cmCMakePath::auto_format);
- path = path.Absolute(*arguments.BaseDirectory).Normal();
- auto realPath = cmSystemTools::GetRealPath(path.GenericString());
+ auto realPath =
+ cmSystemTools::CollapseFullPath(input, *arguments.BaseDirectory);
+ realPath = cmSystemTools::GetRealPath(realPath);
status.GetMakefile().AddDefinition(args[2], realPath);
diff --git a/Source/cmFileLockResult.cxx b/Source/cmFileLockResult.cxx
index 70b8cdbdd6..b7f7f38b24 100644
--- a/Source/cmFileLockResult.cxx
+++ b/Source/cmFileLockResult.cxx
@@ -56,9 +56,9 @@ std::string cmFileLockResult::GetOutputMessage() const
# define WINMSG_BUF_LEN (1024)
char winmsg[WINMSG_BUF_LEN];
DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
- if (FormatMessageA(flags, NULL, this->ErrorValue,
+ if (FormatMessageA(flags, nullptr, this->ErrorValue,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR)winmsg, WINMSG_BUF_LEN, NULL)) {
+ (LPSTR)winmsg, WINMSG_BUF_LEN, nullptr)) {
const std::string message = winmsg;
return message;
} else {
diff --git a/Source/cmFileLockWin32.cxx b/Source/cmFileLockWin32.cxx
index b8e435aa53..7bee5f22bd 100644
--- a/Source/cmFileLockWin32.cxx
+++ b/Source/cmFileLockWin32.cxx
@@ -36,9 +36,9 @@ cmFileLockResult cmFileLock::OpenFile()
{
const DWORD access = GENERIC_READ | GENERIC_WRITE;
const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
- const PSECURITY_ATTRIBUTES security = NULL;
+ const PSECURITY_ATTRIBUTES security = nullptr;
const DWORD attr = 0;
- const HANDLE templ = NULL;
+ const HANDLE templ = nullptr;
this->File = CreateFileW(
cmSystemTools::ConvertToWindowsExtendedPath(this->Filename).c_str(),
access, shareMode, security, OPEN_EXISTING, attr, templ);
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index c1b82abd09..b1029e62c8 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -456,6 +456,51 @@ int parseVersion(const std::string& version, unsigned int& major,
} // anonymous namespace
+class cmFindPackageCommand::FlushDebugBufferOnExit
+{
+ cmFindPackageCommand& Command;
+
+public:
+ FlushDebugBufferOnExit(cmFindPackageCommand& command)
+ : Command(command)
+ {
+ }
+ ~FlushDebugBufferOnExit()
+ {
+ if (!Command.DebugBuffer.empty()) {
+ Command.DebugMessage(Command.DebugBuffer);
+ }
+ }
+};
+
+class cmFindPackageCommand::PushPopRootPathStack
+{
+ cmFindPackageCommand& Command;
+
+public:
+ PushPopRootPathStack(cmFindPackageCommand& command)
+ : Command(command)
+ {
+ Command.PushFindPackageRootPathStack();
+ }
+ ~PushPopRootPathStack() { Command.PopFindPackageRootPathStack(); }
+};
+
+class cmFindPackageCommand::SetRestoreFindDefinitions
+{
+ cmFindPackageCommand& Command;
+
+public:
+ SetRestoreFindDefinitions(
+ cmFindPackageCommand& command, const std::string& components,
+ const std::vector<std::pair<std::string, const char*>>& componentVarDefs)
+ : Command(command)
+ {
+ Command.SetModuleVariables(components, componentVarDefs);
+ }
+ ~SetRestoreFindDefinitions() { Command.RestoreFindDefinitions(); }
+};
+
cmFindPackageCommand::PathLabel
cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT");
cmFindPackageCommand::PathLabel cmFindPackageCommand::PathLabel::UserRegistry(
@@ -502,6 +547,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status)
this->DebugMode = false;
this->AppendSearchPathGroups();
+ this->DeprecatedFindModules["CUDA"] = cmPolicies::CMP0146;
+ this->DeprecatedFindModules["Dart"] = cmPolicies::CMP0145;
+ this->DeprecatedFindModules["PythonInterp"] = cmPolicies::CMP0148;
+ this->DeprecatedFindModules["PythonLibs"] = cmPolicies::CMP0148;
this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084;
}
@@ -975,37 +1024,26 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
}
}
+ // Limit package nesting depth well below the recursion depth limit because
+ // find_package nesting uses more stack space than normal recursion.
{
- // Allocate a PACKAGE_ROOT_PATH for the current find_package call.
- this->Makefile->FindPackageRootPathStack.emplace_back();
- std::vector<std::string>& rootPaths =
- this->Makefile->FindPackageRootPathStack.back();
-
- // Add root paths from <PackageName>_ROOT CMake and environment variables,
- // subject to CMP0074.
- switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) {
- case cmPolicies::WARN:
- this->Makefile->MaybeWarnCMP0074(this->Name);
- CM_FALLTHROUGH;
- case cmPolicies::OLD:
- // OLD behavior is to ignore the <pkg>_ROOT variables.
- break;
- case cmPolicies::REQUIRED_IF_USED:
- case cmPolicies::REQUIRED_ALWAYS:
- this->Makefile->IssueMessage(
- MessageType::FATAL_ERROR,
- cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074));
- break;
- case cmPolicies::NEW: {
- // NEW behavior is to honor the <pkg>_ROOT variables.
- std::string const rootVar = this->Name + "_ROOT";
- this->Makefile->GetDefExpandList(rootVar, rootPaths, false);
- cmSystemTools::GetPath(rootPaths, rootVar.c_str());
- } break;
- }
- }
-
- this->SetModuleVariables(components, componentVarDefs);
+ static std::size_t const findPackageDepthMinMax = 100;
+ std::size_t const findPackageDepthMax = std::max(
+ this->Makefile->GetRecursionDepthLimit() / 2, findPackageDepthMinMax);
+ std::size_t const findPackageDepth =
+ this->Makefile->FindPackageRootPathStack.size() + 1;
+ if (findPackageDepth > findPackageDepthMax) {
+ this->SetError(cmStrCat("maximum nesting depth of ", findPackageDepthMax,
+ " exceeded."));
+ return false;
+ }
+ }
+
+ // RAII objects to ensure we leave this function with consistent state
+ FlushDebugBufferOnExit flushDebugBufferOnExit(*this);
+ PushPopRootPathStack pushPopRootPathStack(*this);
+ SetRestoreFindDefinitions setRestoreFindDefinitions(*this, components,
+ componentVarDefs);
// See if we have been told to delegate to FetchContent or some other
// redirected config package first. We have to check all names that
@@ -1126,16 +1164,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
this->AppendSuccessInformation();
- // Restore original state of "_FIND_" variables set in SetModuleVariables()
- this->RestoreFindDefinitions();
-
- // Pop the package stack
- this->Makefile->FindPackageRootPathStack.pop_back();
-
- if (!this->DebugBuffer.empty()) {
- this->DebugMessage(this->DebugBuffer);
- }
-
return loadedPackage;
}
@@ -1834,6 +1862,99 @@ void cmFindPackageCommand::AppendSuccessInformation()
}
}
+void cmFindPackageCommand::PushFindPackageRootPathStack()
+{
+ // Allocate a PACKAGE_ROOT_PATH for the current find_package call.
+ this->Makefile->FindPackageRootPathStack.emplace_back();
+ std::vector<std::string>& rootPaths =
+ this->Makefile->FindPackageRootPathStack.back();
+
+ // Add root paths from <PackageName>_ROOT CMake and environment variables,
+ // subject to CMP0074.
+ std::string const rootVar = this->Name + "_ROOT";
+ cmValue rootDef = this->Makefile->GetDefinition(rootVar);
+ if (rootDef && rootDef.IsEmpty()) {
+ rootDef = nullptr;
+ }
+ cm::optional<std::string> rootEnv = cmSystemTools::GetEnvVar(rootVar);
+ if (rootEnv && rootEnv->empty()) {
+ rootEnv = cm::nullopt;
+ }
+ switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0074)) {
+ case cmPolicies::WARN:
+ this->Makefile->MaybeWarnCMP0074(rootVar, rootDef, rootEnv);
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // OLD behavior is to ignore the <PackageName>_ROOT variables.
+ return;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0074));
+ return;
+ case cmPolicies::NEW: {
+ // NEW behavior is to honor the <PackageName>_ROOT variables.
+ } break;
+ }
+
+ // Add root paths from <PACKAGENAME>_ROOT CMake and environment variables,
+ // if they are different than <PackageName>_ROOT, and subject to CMP0144.
+ std::string const rootVAR = cmSystemTools::UpperCase(rootVar);
+ cmValue rootDEF;
+ cm::optional<std::string> rootENV;
+ if (rootVAR != rootVar) {
+ rootDEF = this->Makefile->GetDefinition(rootVAR);
+ if (rootDEF && (rootDEF.IsEmpty() || rootDEF == rootDef)) {
+ rootDEF = nullptr;
+ }
+ rootENV = cmSystemTools::GetEnvVar(rootVAR);
+ if (rootENV && (rootENV->empty() || rootENV == rootEnv)) {
+ rootENV = cm::nullopt;
+ }
+ }
+
+ switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0144)) {
+ case cmPolicies::WARN:
+ this->Makefile->MaybeWarnCMP0144(rootVAR, rootDEF, rootENV);
+ CM_FALLTHROUGH;
+ case cmPolicies::OLD:
+ // OLD behavior is to ignore the <PACKAGENAME>_ROOT variables.
+ rootDEF = nullptr;
+ rootENV = cm::nullopt;
+ break;
+ case cmPolicies::REQUIRED_IF_USED:
+ case cmPolicies::REQUIRED_ALWAYS:
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0144));
+ return;
+ case cmPolicies::NEW: {
+ // NEW behavior is to honor the <PACKAGENAME>_ROOT variables.
+ } break;
+ }
+
+ if (rootDef) {
+ cmExpandList(*rootDef, rootPaths);
+ }
+ if (rootDEF) {
+ cmExpandList(*rootDEF, rootPaths);
+ }
+ if (rootEnv) {
+ std::vector<std::string> p = cmSystemTools::SplitEnvPath(*rootEnv);
+ std::move(p.begin(), p.end(), std::back_inserter(rootPaths));
+ }
+ if (rootENV) {
+ std::vector<std::string> p = cmSystemTools::SplitEnvPath(*rootENV);
+ std::move(p.begin(), p.end(), std::back_inserter(rootPaths));
+ }
+}
+
+void cmFindPackageCommand::PopFindPackageRootPathStack()
+{
+ this->Makefile->FindPackageRootPathStack.pop_back();
+}
+
void cmFindPackageCommand::ComputePrefixes()
{
this->FillPrefixesPackageRedirect();
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 28e00a109d..653e15f51f 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -105,6 +105,8 @@ private:
void AddFindDefinition(const std::string& var, cm::string_view value);
void RestoreFindDefinitions();
+ class SetRestoreFindDefinitions;
+
enum /*class*/ HandlePackageModeType
{
Module,
@@ -125,6 +127,10 @@ private:
void StoreVersionFound();
void SetConfigDirCacheVariable(const std::string& value);
+ void PushFindPackageRootPathStack();
+ void PopFindPackageRootPathStack();
+ class PushPopRootPathStack;
+
void ComputePrefixes();
void FillPrefixesPackageRedirect();
void FillPrefixesPackageRoot();
@@ -212,6 +218,8 @@ private:
std::set<std::string> IgnoredPrefixPaths;
std::string DebugBuffer;
+ class FlushDebugBufferOnExit;
+
/*! the selected sortOrder (None by default)*/
SortOrderType SortOrder = None;
/*! the selected sortDirection (Asc by default)*/
diff --git a/Source/cmGeneratorExpressionDAGChecker.cxx b/Source/cmGeneratorExpressionDAGChecker.cxx
index 6be5153c22..82a6c57dc0 100644
--- a/Source/cmGeneratorExpressionDAGChecker.cxx
+++ b/Source/cmGeneratorExpressionDAGChecker.cxx
@@ -177,6 +177,15 @@ bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
return property == "LINK_OPTIONS"_s;
}
+bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const
+{
+ cm::string_view property(this->Top()->Property);
+
+ return property.length() > cmStrLen("_LINKER_LAUNCHER") &&
+ property.substr(property.length() - cmStrLen("_LINKER_LAUNCHER")) ==
+ "_LINKER_LAUNCHER"_s;
+}
+
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
cmGeneratorTarget const* tgt, ForGenex genex) const
{
diff --git a/Source/cmGeneratorExpressionDAGChecker.h b/Source/cmGeneratorExpressionDAGChecker.h
index 55d131f845..df1e0050c2 100644
--- a/Source/cmGeneratorExpressionDAGChecker.h
+++ b/Source/cmGeneratorExpressionDAGChecker.h
@@ -70,6 +70,7 @@ struct cmGeneratorExpressionDAGChecker
bool EvaluatingCompileExpression() const;
bool EvaluatingLinkExpression() const;
bool EvaluatingLinkOptionsExpression() const;
+ bool EvaluatingLinkerLauncher() const;
enum class ForGenex
{
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 6595323b9a..a47366b4a1 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -12,6 +12,7 @@
#include <memory>
#include <set>
#include <sstream>
+#include <stdexcept>
#include <unordered_map>
#include <utility>
@@ -25,7 +26,6 @@
#include "cmsys/RegularExpression.hxx"
#include "cmsys/String.h"
-#include "cmAlgorithms.h"
#include "cmCMakePath.h"
#include "cmComputeLinkInformation.h"
#include "cmGeneratorExpression.h"
@@ -35,6 +35,7 @@
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
+#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
@@ -291,18 +292,18 @@ static const struct InListNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::vector<std::string> values;
- std::vector<std::string> checkValues;
+ cmList values;
+ cmList checkValues;
bool check = false;
switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
case cmPolicies::WARN:
if (parameters.front().empty()) {
check = true;
- cmExpandList(parameters[1], checkValues, true);
+ checkValues.assign(parameters[1], cmList::EmptyElements::Yes);
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
- cmExpandList(parameters[1], values);
+ values.assign(parameters[1]);
if (check && values != checkValues) {
std::ostringstream e;
e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
@@ -319,11 +320,11 @@ static const struct InListNode : public cmGeneratorExpressionNode
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW:
- cmExpandList(parameters[1], values, true);
+ values.assign(parameters[1], cmList::EmptyElements::Yes);
break;
}
- return cm::contains(values, parameters.front()) ? "1" : "0";
+ return values.find(parameters.front()) != cmList::npos ? "1" : "0";
}
} inListNode;
@@ -352,24 +353,17 @@ static const struct FilterNode : public cmGeneratorExpressionNode
return {};
}
- const bool exclude = parameters[1] == "EXCLUDE";
-
- cmsys::RegularExpression re;
- if (!re.compile(parameters[2])) {
+ try {
+ return cmList{ parameters.front(), cmList::EmptyElements::Yes }
+ .filter(parameters[2],
+ parameters[1] == "EXCLUDE" ? cmList::FilterMode::EXCLUDE
+ : cmList::FilterMode::INCLUDE)
+ .to_string();
+ } catch (std::invalid_argument&) {
reportError(context, content->GetOriginalExpression(),
"$<FILTER:...> failed to compile regex");
return {};
}
-
- std::vector<std::string> values;
- std::vector<std::string> result;
- cmExpandList(parameters.front(), values, true);
-
- std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
- [&re, exclude](std::string const& input) {
- return exclude ^ re.find(input);
- });
- return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";");
}
} filterNode;
@@ -391,11 +385,7 @@ static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode
"$<REMOVE_DUPLICATES:...> expression requires one parameter");
}
- std::vector<std::string> values = cmExpandedList(parameters.front(), true);
-
- auto valuesEnd = cmRemoveDuplicates(values);
- auto valuesBegin = values.cbegin();
- return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";");
+ return cmList{ parameters.front() }.remove_duplicates().to_string();
}
} removeDuplicatesNode;
@@ -688,6 +678,14 @@ static const struct PathNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
+ static auto processList =
+ [](std::string const& arg,
+ std::function<void(std::string&)> transform) -> std::string {
+ auto list = cmExpandedList(arg);
+ std::for_each(list.begin(), list.end(), std::move(transform));
+ return cmJoin(list, ";");
+ };
+
static std::unordered_map<
cm::string_view,
std::function<std::string(cmGeneratorExpressionContext*,
@@ -698,38 +696,49 @@ static const struct PathNode : public cmGeneratorExpressionNode
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.GetRootName().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetRootName().String();
+ });
+ }
+ return std::string{};
} },
{ "GET_ROOT_DIRECTORY"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s,
- args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.GetRootDirectory().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetRootDirectory().String();
+ });
+ }
+ return std::string{};
} },
{ "GET_ROOT_PATH"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.GetRootPath().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetRootPath().String();
+ });
+ }
+ return std::string{};
} },
{ "GET_FILENAME"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.GetFileName().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetFileName().String();
+ });
+ }
+ return std::string{};
} },
{ "GET_EXTENSION"_s,
[](cmGeneratorExpressionContext* ctx,
@@ -746,9 +755,14 @@ static const struct PathNode : public cmGeneratorExpressionNode
if (args.front().empty()) {
return std::string{};
}
- return lastOnly
- ? cmCMakePath{ args.front() }.GetExtension().String()
- : cmCMakePath{ args.front() }.GetWideExtension().String();
+ if (lastOnly) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetExtension().String();
+ });
+ }
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetWideExtension().String();
+ });
}
return std::string{};
} },
@@ -766,9 +780,14 @@ static const struct PathNode : public cmGeneratorExpressionNode
if (args.front().empty()) {
return std::string{};
}
- return lastOnly
- ? cmCMakePath{ args.front() }.GetStem().String()
- : cmCMakePath{ args.front() }.GetNarrowStem().String();
+ if (lastOnly) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetStem().String();
+ });
+ }
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetNarrowStem().String();
+ });
}
return std::string{};
} },
@@ -776,19 +795,24 @@ static const struct PathNode : public cmGeneratorExpressionNode
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s,
- args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.GetRelativePath().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetRelativePath().String();
+ });
+ }
+ return std::string{};
} },
{ "GET_PARENT_PATH"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)
- ? cmCMakePath{ args.front() }.GetParentPath().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.GetParentPath().String();
+ });
+ }
+ return std::string{};
} },
{ "HAS_ROOT_NAME"_s,
[](cmGeneratorExpressionContext* ctx,
@@ -904,10 +928,12 @@ static const struct PathNode : public cmGeneratorExpressionNode
normalize ? "CMAKE_PATH,NORMALIZE"_s
: "CMAKE_PATH"_s,
args.size(), 1)) {
- auto path =
- cmCMakePath{ args.front(), cmCMakePath::auto_format };
- return normalize ? path.Normal().GenericString()
- : path.GenericString();
+ return processList(
+ args.front(), [normalize](std::string& value) {
+ auto path = cmCMakePath{ value, cmCMakePath::auto_format };
+ value = normalize ? path.Normal().GenericString()
+ : path.GenericString();
+ });
}
return std::string{};
} },
@@ -917,11 +943,16 @@ static const struct PathNode : public cmGeneratorExpressionNode
Arguments& args) -> std::string {
if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1,
false)) {
- cmCMakePath path;
- for (const auto& p : args) {
- path /= p;
- }
- return path.String();
+ auto const& list = args.front();
+ args.advance(1);
+
+ return processList(list, [&args](std::string& value) {
+ cmCMakePath path{ value };
+ for (const auto& p : args) {
+ path /= p;
+ }
+ value = path.String();
+ });
}
return std::string{};
} },
@@ -929,20 +960,26 @@ static const struct PathNode : public cmGeneratorExpressionNode
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.RemoveFileName().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.RemoveFileName().String();
+ });
+ }
+ return std::string{};
} },
{ "REPLACE_FILENAME"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)
- ? cmCMakePath{ args[0] }
- .ReplaceFileName(cmCMakePath{ args[1] })
- .String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)) {
+ return processList(args.front(), [&args](std::string& value) {
+ value = cmCMakePath{ value }
+ .ReplaceFileName(cmCMakePath{ args[1] })
+ .String();
+ });
+ }
+ return std::string{};
} },
{ "REMOVE_EXTENSION"_s,
[](cmGeneratorExpressionContext* ctx,
@@ -959,9 +996,14 @@ static const struct PathNode : public cmGeneratorExpressionNode
if (args.front().empty()) {
return std::string{};
}
- return lastOnly
- ? cmCMakePath{ args.front() }.RemoveExtension().String()
- : cmCMakePath{ args.front() }.RemoveWideExtension().String();
+ if (lastOnly) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.RemoveExtension().String();
+ });
+ }
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.RemoveWideExtension().String();
+ });
}
return std::string{};
} },
@@ -979,13 +1021,17 @@ static const struct PathNode : public cmGeneratorExpressionNode
: "REPLACE_EXTENSION"_s,
args.size(), 2)) {
if (lastOnly) {
- return cmCMakePath{ args[0] }
- .ReplaceExtension(cmCMakePath{ args[1] })
- .String();
+ return processList(args.front(), [&args](std::string& value) {
+ value = cmCMakePath{ value }
+ .ReplaceExtension(cmCMakePath{ args[1] })
+ .String();
+ });
}
- return cmCMakePath{ args[0] }
- .ReplaceWideExtension(cmCMakePath{ args[1] })
- .String();
+ return processList(args.front(), [&args](std::string& value) {
+ value = cmCMakePath{ value }
+ .ReplaceWideExtension(cmCMakePath{ args[1] })
+ .String();
+ });
}
return std::string{};
} },
@@ -993,18 +1039,24 @@ static const struct PathNode : public cmGeneratorExpressionNode
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
- !args.front().empty()
- ? cmCMakePath{ args.front() }.Normal().String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
+ !args.front().empty()) {
+ return processList(args.front(), [](std::string& value) {
+ value = cmCMakePath{ value }.Normal().String();
+ });
+ }
+ return std::string{};
} },
{ "RELATIVE_PATH"_s,
[](cmGeneratorExpressionContext* ctx,
const GeneratorExpressionContent* cnt,
Arguments& args) -> std::string {
- return CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)
- ? cmCMakePath{ args[0] }.Relative(args[1]).String()
- : std::string{};
+ if (CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)) {
+ return processList(args.front(), [&args](std::string& value) {
+ value = cmCMakePath{ value }.Relative(args[1]).String();
+ });
+ }
+ return std::string{};
} },
{ "ABSOLUTE_PATH"_s,
[](cmGeneratorExpressionContext* ctx,
@@ -1018,8 +1070,11 @@ static const struct PathNode : public cmGeneratorExpressionNode
normalize ? "ABSOLUTE_PATH,NORMALIZE"_s
: "ABSOLUTE_PATH"_s,
args.size(), 2)) {
- auto path = cmCMakePath{ args[0] }.Absolute(args[1]);
- return normalize ? path.Normal().String() : path.String();
+ return processList(
+ args.front(), [&args, normalize](std::string& value) {
+ auto path = cmCMakePath{ value }.Absolute(args[1]);
+ value = normalize ? path.Normal().String() : path.String();
+ });
}
return std::string{};
} }
@@ -1435,8 +1490,7 @@ static const struct JoinNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::vector<std::string> list = cmExpandedList(parameters.front());
- return cmJoin(list, parameters[1]);
+ return cmList{ parameters.front() }.join(parameters[1]);
}
} joinNode;
@@ -1468,7 +1522,8 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
- genName.find("Watcom WMake") == std::string::npos) {
+ genName.find("Watcom WMake") == std::string::npos &&
+ genName.find("Green Hills MULTI") == std::string::npos) {
reportError(context, content->GetOriginalExpression(),
"$<COMPILE_LANGUAGE:...> not supported for this generator.");
return std::string();
@@ -1516,7 +1571,8 @@ static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
- genName.find("Watcom WMake") == std::string::npos) {
+ genName.find("Watcom WMake") == std::string::npos &&
+ genName.find("Green Hills MULTI") == std::string::npos) {
reportError(
context, content->GetOriginalExpression(),
"$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator.");
@@ -1548,7 +1604,8 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode
{
if (!context->HeadTarget || !dagChecker ||
!(dagChecker->EvaluatingLinkExpression() ||
- dagChecker->EvaluatingLinkLibraries())) {
+ dagChecker->EvaluatingLinkLibraries() ||
+ dagChecker->EvaluatingLinkerLauncher())) {
reportError(context, content->GetOriginalExpression(),
"$<LINK_LANGUAGE:...> may only be used with binary targets "
"to specify link libraries, link directories, link options "
@@ -1568,7 +1625,8 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
- genName.find("Watcom WMake") == std::string::npos) {
+ genName.find("Watcom WMake") == std::string::npos &&
+ genName.find("Green Hills MULTI") == std::string::npos) {
reportError(context, content->GetOriginalExpression(),
"$<LINK_LANGUAGE:...> not supported for this generator.");
return std::string();
@@ -1641,7 +1699,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
{
if (!context->HeadTarget || !dagChecker ||
!(dagChecker->EvaluatingLinkExpression() ||
- dagChecker->EvaluatingLinkLibraries())) {
+ dagChecker->EvaluatingLinkLibraries() ||
+ dagChecker->EvaluatingLinkerLauncher())) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
@@ -1656,7 +1715,8 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
genName.find("Ninja") == std::string::npos &&
genName.find("Visual Studio") == std::string::npos &&
genName.find("Xcode") == std::string::npos &&
- genName.find("Watcom WMake") == std::string::npos) {
+ genName.find("Watcom WMake") == std::string::npos &&
+ genName.find("Green Hills MULTI") == std::string::npos) {
reportError(
context, content->GetOriginalExpression(),
"$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
@@ -2098,7 +2158,8 @@ static const struct TargetPropertyNode : public cmGeneratorExpressionNode
if (dagCheckerParent) {
if (dagCheckerParent->EvaluatingGenexExpression() ||
- dagCheckerParent->EvaluatingPICExpression()) {
+ dagCheckerParent->EvaluatingPICExpression() ||
+ dagCheckerParent->EvaluatingLinkerLauncher()) {
// No check required.
} else if (dagCheckerParent->EvaluatingLinkLibraries()) {
evaluatingLinkLibraries = true;
@@ -2330,15 +2391,12 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
}
} targetObjectsNode;
-static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
+struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode
{
- TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
-
- std::string Evaluate(
+ std::vector<std::string> CollectDlls(
const std::vector<std::string>& parameters,
cmGeneratorExpressionContext* context,
- const GeneratorExpressionContent* content,
- cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ const GeneratorExpressionContent* content) const
{
std::string const& tgtName = parameters.front();
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
@@ -2347,7 +2405,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
e << "Objects of target \"" << tgtName
<< "\" referenced but no such target exists.";
reportError(context, content->GetOriginalExpression(), e.str());
- return std::string();
+ return std::vector<std::string>();
}
cmStateEnums::TargetType type = gt->GetType();
if (type != cmStateEnums::EXECUTABLE &&
@@ -2358,7 +2416,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
<< "\" referenced but is not one of the allowed target types "
<< "(EXECUTABLE, SHARED, MODULE).";
reportError(context, content->GetOriginalExpression(), e.str());
- return std::string();
+ return std::vector<std::string>();
}
if (auto* cli = gt->GetLinkInformation(context->Config)) {
@@ -2371,13 +2429,51 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
}
}
- return cmJoin(dllPaths, ";");
+ return dllPaths;
}
- return "";
+ return std::vector<std::string>();
+ }
+};
+
+static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode
+{
+ TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ std::vector<std::string> dlls = CollectDlls(parameters, context, content);
+ return cmJoin(dlls, ";");
}
} targetRuntimeDllsNode;
+static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode
+{
+ TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default)
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ std::vector<std::string> dlls = CollectDlls(parameters, context, content);
+ std::vector<std::string> dllDirs;
+ for (const std::string& dll : dlls) {
+ std::string directory = cmSystemTools::GetFilenamePath(dll);
+ if (std::find(dllDirs.begin(), dllDirs.end(), directory) ==
+ dllDirs.end()) {
+ dllDirs.push_back(directory);
+ }
+ }
+ return cmJoin(dllDirs, ";");
+ }
+} targetRuntimeDllDirsNode;
+
static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
{
CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
@@ -2586,10 +2682,14 @@ static const struct InstallPrefixNode : public cmGeneratorExpressionNode
class ArtifactDirTag;
class ArtifactLinkerTag;
+class ArtifactLinkerLibraryTag;
+class ArtifactLinkerImportTag;
class ArtifactNameTag;
+class ArtifactImportTag;
class ArtifactPathTag;
class ArtifactPdbTag;
class ArtifactSonameTag;
+class ArtifactSonameImportTag;
class ArtifactBundleDirTag;
class ArtifactBundleDirNameTag;
class ArtifactBundleContentDirTag;
@@ -2699,6 +2799,38 @@ struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
};
template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactSonameImportTag>
+{
+ static std::string Create(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ // The target soname file (.so.1).
+ if (target->IsDLLPlatform()) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_SONAME_IMPORT_FILE is not allowed "
+ "for DLL target platforms.");
+ return std::string();
+ }
+ if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_SONAME_IMPORT_FILE is allowed only for "
+ "SHARED libraries.");
+ return std::string();
+ }
+
+ if (target->HasImportLibrary(context->Config)) {
+ return cmStrCat(target->GetDirectory(
+ context->Config, cmStateEnums::ImportLibraryArtifact),
+ '/',
+ target->GetSOName(context->Config,
+ cmStateEnums::ImportLibraryArtifact));
+ }
+ return std::string{};
+ }
+};
+
+template <>
struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
{
static std::string Create(cmGeneratorTarget* target,
@@ -2745,7 +2877,8 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content)
{
- // The file used to link to the target (.so, .lib, .a).
+ // The file used to link to the target (.so, .lib, .a) or import file
+ // (.lib, .tbd).
if (!target->IsLinkable()) {
::reportError(context, content->GetOriginalExpression(),
"TARGET_LINKER_FILE is allowed only for libraries and "
@@ -2761,6 +2894,55 @@ struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
};
template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactLinkerLibraryTag>
+{
+ static std::string Create(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ // The file used to link to the target (.dylib, .so, .a).
+ if (!target->IsLinkable() ||
+ target->GetType() == cmStateEnums::EXECUTABLE) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_LINKER_LIBRARY_FILE is allowed only for libraries "
+ "with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (!target->IsDLLPlatform() ||
+ target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ return target->GetFullPath(context->Config,
+ cmStateEnums::RuntimeBinaryArtifact);
+ }
+ return std::string{};
+ }
+};
+
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactLinkerImportTag>
+{
+ static std::string Create(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ // The file used to link to the target (.lib, .tbd).
+ if (!target->IsLinkable()) {
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_IMPORT_FILE is allowed only for libraries and "
+ "executables with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFullPath(context->Config,
+ cmStateEnums::ImportLibraryArtifact);
+ }
+ return std::string{};
+ }
+};
+
+template <>
struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
{
static std::string Create(cmGeneratorTarget* target,
@@ -2857,6 +3039,21 @@ struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
}
};
+template <>
+struct TargetFilesystemArtifactResultCreator<ArtifactImportTag>
+{
+ static std::string Create(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* /*unused*/)
+ {
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFullPath(context->Config,
+ cmStateEnums::ImportLibraryArtifact, true);
+ }
+ return std::string{};
+ }
+};
+
template <typename ArtifactT>
struct TargetFilesystemArtifactResultGetter
{
@@ -2982,12 +3179,24 @@ struct TargetFilesystemArtifactNodeGroup
static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
targetNodeGroup;
+static const TargetFilesystemArtifactNodeGroup<ArtifactImportTag>
+ targetImportNodeGroup;
+
static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
targetLinkerNodeGroup;
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag>
+ targetLinkerLibraryNodeGroup;
+
+static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag>
+ targetLinkerImportNodeGroup;
+
static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
targetSoNameNodeGroup;
+static const TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag>
+ targetSoNameImportNodeGroup;
+
static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
targetPdbNodeGroup;
@@ -3027,13 +3236,30 @@ struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
};
template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactImportTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* /*unused*/)
+ {
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetOutputName(context->Config,
+ cmStateEnums::ImportLibraryArtifact) +
+ target->GetFilePostfix(context->Config);
+ }
+ return std::string{};
+ }
+};
+
+template <>
struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
{
static std::string Get(cmGeneratorTarget* target,
cmGeneratorExpressionContext* context,
const GeneratorExpressionContent* content)
{
- // The file used to link to the target (.so, .lib, .a).
+ // The library file used to link to the target (.so, .lib, .a) or import
+ // file (.lin, .tbd).
if (!target->IsLinkable()) {
::reportError(context, content->GetOriginalExpression(),
"TARGET_LINKER_FILE_BASE_NAME is allowed only for "
@@ -3050,6 +3276,56 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
};
template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactLinkerLibraryTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ // The library file used to link to the target (.so, .lib, .a).
+ if (!target->IsLinkable() ||
+ target->GetType() == cmStateEnums::EXECUTABLE) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for "
+ "libraries with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (!target->IsDLLPlatform() ||
+ target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ return target->GetOutputName(context->Config,
+ cmStateEnums::ImportLibraryArtifact) +
+ target->GetFilePostfix(context->Config);
+ }
+ return std::string{};
+ }
+};
+
+template <>
+struct TargetOutputNameArtifactResultGetter<ArtifactLinkerImportTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ // The import file used to link to the target (.lib, .tbd).
+ if (!target->IsLinkable()) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for "
+ "libraries and executables with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetOutputName(context->Config,
+ cmStateEnums::ImportLibraryArtifact) +
+ target->GetFilePostfix(context->Config);
+ }
+ return std::string{};
+ }
+};
+
+template <>
struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
{
static std::string Get(cmGeneratorTarget* target,
@@ -3120,15 +3396,27 @@ struct TargetFileBaseNameArtifact : public TargetArtifactBase
static const TargetFileBaseNameArtifact<ArtifactNameTag>
targetFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactImportTag>
+ targetImportFileBaseNameNode;
static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
targetLinkerFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag>
+ targetLinkerLibraryFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerImportTag>
+ targetLinkerImportFileBaseNameNode;
static const TargetFileBaseNameArtifact<ArtifactPdbTag>
targetPdbFileBaseNameNode;
class ArtifactFilePrefixTag;
+class ArtifactImportFilePrefixTag;
class ArtifactLinkerFilePrefixTag;
+class ArtifactLinkerLibraryFilePrefixTag;
+class ArtifactLinkerImportFilePrefixTag;
class ArtifactFileSuffixTag;
+class ArtifactImportFileSuffixTag;
class ArtifactLinkerFileSuffixTag;
+class ArtifactLinkerLibraryFileSuffixTag;
+class ArtifactLinkerImportFileSuffixTag;
template <typename ArtifactT>
struct TargetFileArtifactResultGetter
@@ -3149,6 +3437,20 @@ struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
}
};
template <>
+struct TargetFileArtifactResultGetter<ArtifactImportFilePrefixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent*)
+ {
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFilePrefix(context->Config,
+ cmStateEnums::ImportLibraryArtifact);
+ }
+ return std::string{};
+ }
+};
+template <>
struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
{
static std::string Get(cmGeneratorTarget* target,
@@ -3156,9 +3458,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
const GeneratorExpressionContent* content)
{
if (!target->IsLinkable()) {
- ::reportError(context, content->GetOriginalExpression(),
- "TARGET_LINKER_PREFIX is allowed only for libraries and "
- "executables with ENABLE_EXPORTS.");
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_FILE_PREFIX is allowed only for libraries and "
+ "executables with ENABLE_EXPORTS.");
return std::string();
}
@@ -3171,6 +3474,52 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
}
};
template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFilePrefixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ if (!target->IsLinkable() ||
+ target->GetType() == cmStateEnums::EXECUTABLE) {
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries "
+ "with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (!target->IsDLLPlatform() ||
+ target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ return target->GetFilePrefix(context->Config,
+ cmStateEnums::RuntimeBinaryArtifact);
+ }
+ return std::string{};
+ }
+};
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerImportFilePrefixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ if (!target->IsLinkable()) {
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and "
+ "executables with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFilePrefix(context->Config,
+ cmStateEnums::ImportLibraryArtifact);
+ }
+ return std::string{};
+ }
+};
+template <>
struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
{
static std::string Get(cmGeneratorTarget* target,
@@ -3181,6 +3530,20 @@ struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
}
};
template <>
+struct TargetFileArtifactResultGetter<ArtifactImportFileSuffixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent*)
+ {
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFileSuffix(context->Config,
+ cmStateEnums::ImportLibraryArtifact);
+ }
+ return std::string{};
+ }
+};
+template <>
struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
{
static std::string Get(cmGeneratorTarget* target,
@@ -3188,9 +3551,10 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
const GeneratorExpressionContent* content)
{
if (!target->IsLinkable()) {
- ::reportError(context, content->GetOriginalExpression(),
- "TARGET_LINKER_SUFFIX is allowed only for libraries and "
- "executables with ENABLE_EXPORTS.");
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_FILE_SUFFIX is allowed only for libraries and "
+ "executables with ENABLE_EXPORTS.");
return std::string();
}
@@ -3202,6 +3566,51 @@ struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
return target->GetFileSuffix(context->Config, artifact);
}
};
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFileSuffixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ if (!target->IsLinkable() ||
+ target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for "
+ "libraries with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (!target->IsDLLPlatform() ||
+ target->GetType() == cmStateEnums::STATIC_LIBRARY) {
+ return target->GetFileSuffix(context->Config,
+ cmStateEnums::RuntimeBinaryArtifact);
+ }
+ return std::string{};
+ }
+};
+template <>
+struct TargetFileArtifactResultGetter<ArtifactLinkerImportFileSuffixTag>
+{
+ static std::string Get(cmGeneratorTarget* target,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content)
+ {
+ if (!target->IsLinkable()) {
+ ::reportError(
+ context, content->GetOriginalExpression(),
+ "TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and "
+ "executables with ENABLE_EXPORTS.");
+ return std::string();
+ }
+
+ if (target->HasImportLibrary(context->Config)) {
+ return target->GetFileSuffix(context->Config,
+ cmStateEnums::ImportLibraryArtifact);
+ }
+ return std::string{};
+ }
+};
template <typename ArtifactT>
struct TargetFileArtifact : public TargetArtifactBase
@@ -3232,11 +3641,23 @@ struct TargetFileArtifact : public TargetArtifactBase
};
static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
+static const TargetFileArtifact<ArtifactImportFilePrefixTag>
+ targetImportFilePrefixNode;
static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
targetLinkerFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag>
+ targetLinkerLibraryFilePrefixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFilePrefixTag>
+ targetLinkerImportFilePrefixNode;
static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
+static const TargetFileArtifact<ArtifactImportFileSuffixTag>
+ targetImportFileSuffixNode;
static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
targetLinkerFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag>
+ targetLinkerLibraryFileSuffixNode;
+static const TargetFileArtifact<ArtifactLinkerImportFileSuffixTag>
+ targetLinkerImportFileSuffixNode;
static const struct ShellPathNode : public cmGeneratorExpressionNode
{
@@ -3304,23 +3725,52 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "CONFIGURATION", &configurationNode },
{ "CONFIG", &configurationTestNode },
{ "TARGET_FILE", &targetNodeGroup.File },
+ { "TARGET_IMPORT_FILE", &targetImportNodeGroup.File },
{ "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
+ { "TARGET_LINKER_LIBRARY_FILE", &targetLinkerLibraryNodeGroup.File },
+ { "TARGET_LINKER_IMPORT_FILE", &targetLinkerImportNodeGroup.File },
{ "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
+ { "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File },
{ "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
{ "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
+ { "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode },
{ "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
+ { "TARGET_LINKER_LIBRARY_FILE_BASE_NAME",
+ &targetLinkerLibraryFileBaseNameNode },
+ { "TARGET_LINKER_IMPORT_FILE_BASE_NAME",
+ &targetLinkerImportFileBaseNameNode },
{ "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
{ "TARGET_FILE_PREFIX", &targetFilePrefixNode },
+ { "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode },
{ "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
+ { "TARGET_LINKER_LIBRARY_FILE_PREFIX",
+ &targetLinkerLibraryFilePrefixNode },
+ { "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode },
{ "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
+ { "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode },
{ "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
+ { "TARGET_LINKER_LIBRARY_FILE_SUFFIX",
+ &targetLinkerLibraryFileSuffixNode },
+ { "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode },
{ "TARGET_FILE_NAME", &targetNodeGroup.FileName },
+ { "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName },
{ "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
+ { "TARGET_LINKER_LIBRARY_FILE_NAME",
+ &targetLinkerLibraryNodeGroup.FileName },
+ { "TARGET_LINKER_IMPORT_FILE_NAME",
+ &targetLinkerImportNodeGroup.FileName },
{ "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
+ { "TARGET_SONAME_IMPORT_FILE_NAME",
+ &targetSoNameImportNodeGroup.FileName },
{ "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
{ "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
+ { "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir },
{ "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
+ { "TARGET_LINKER_LIBRARY_FILE_DIR",
+ &targetLinkerLibraryNodeGroup.FileDir },
+ { "TARGET_LINKER_IMPORT_FILE_DIR", &targetLinkerImportNodeGroup.FileDir },
{ "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
+ { "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir },
{ "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
{ "TARGET_BUNDLE_DIR", &targetBundleDirNode },
{ "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
@@ -3348,6 +3798,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
{ "TARGET_GENEX_EVAL", &targetGenexEvalNode },
{ "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
+ { "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode },
{ "GENEX_EVAL", &genexEvalNode },
{ "BUILD_INTERFACE", &buildInterfaceNode },
{ "INSTALL_INTERFACE", &installInterfaceNode },
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 5e352b2957..90cddb56b2 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -3,6 +3,7 @@
#include "cmGeneratorTarget.h"
#include <algorithm>
+#include <array>
#include <cassert>
#include <cerrno>
#include <cstddef>
@@ -26,6 +27,7 @@
#include "cmAlgorithms.h"
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
+#include "cmEvaluatedTargetProperty.h"
#include "cmFileSet.h"
#include "cmFileTimes.h"
#include "cmGeneratedFileStream.h"
@@ -88,31 +90,38 @@ cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>(
return tgt->GetLocation(config);
}
-class cmGeneratorTarget::TargetPropertyEntry
-{
-protected:
- static cmLinkImplItem NoLinkImplItem;
+cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
+class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
+{
public:
- TargetPropertyEntry(cmLinkImplItem const& item)
- : LinkImplItem(item)
+ TargetPropertyEntryString(BT<std::string> propertyValue,
+ cmLinkImplItem const& item = NoLinkImplItem)
+ : cmGeneratorTarget::TargetPropertyEntry(item)
+ , PropertyValue(std::move(propertyValue))
{
}
- virtual ~TargetPropertyEntry() = default;
- virtual const std::string& Evaluate(
- cmLocalGenerator* lg, const std::string& config,
- cmGeneratorTarget const* headTarget,
- cmGeneratorExpressionDAGChecker* dagChecker,
- std::string const& language) const = 0;
+ const std::string& Evaluate(cmLocalGenerator*, const std::string&,
+ cmGeneratorTarget const*,
+ cmGeneratorExpressionDAGChecker*,
+ std::string const&) const override
+ {
+ return this->PropertyValue.Value;
+ }
- virtual cmListFileBacktrace GetBacktrace() const = 0;
- virtual std::string const& GetInput() const = 0;
- virtual bool GetHadContextSensitiveCondition() const { return false; }
+ cmListFileBacktrace GetBacktrace() const override
+ {
+ return this->PropertyValue.Backtrace;
+ }
+ std::string const& GetInput() const override
+ {
+ return this->PropertyValue.Value;
+ }
- cmLinkImplItem const& LinkImplItem;
+private:
+ BT<std::string> PropertyValue;
};
-cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry
{
@@ -149,37 +158,6 @@ private:
const std::unique_ptr<cmCompiledGeneratorExpression> ge;
};
-class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
-{
-public:
- TargetPropertyEntryString(BT<std::string> propertyValue,
- cmLinkImplItem const& item = NoLinkImplItem)
- : cmGeneratorTarget::TargetPropertyEntry(item)
- , PropertyValue(std::move(propertyValue))
- {
- }
-
- const std::string& Evaluate(cmLocalGenerator*, const std::string&,
- cmGeneratorTarget const*,
- cmGeneratorExpressionDAGChecker*,
- std::string const&) const override
- {
- return this->PropertyValue.Value;
- }
-
- cmListFileBacktrace GetBacktrace() const override
- {
- return this->PropertyValue.Backtrace;
- }
- std::string const& GetInput() const override
- {
- return this->PropertyValue.Value;
- }
-
-private:
- BT<std::string> PropertyValue;
-};
-
class TargetPropertyEntryFileSet
: public cmGeneratorTarget::TargetPropertyEntry
{
@@ -262,6 +240,18 @@ std::unique_ptr<
cm::make_unique<TargetPropertyEntryString>(propertyValue));
}
+cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry(
+ cmLinkImplItem const& item)
+ : LinkImplItem(item)
+{
+}
+
+bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition()
+ const
+{
+ return false;
+}
+
static void CreatePropertyGeneratorExpressions(
cmake& cmakeInstance, cmBTStringRange entries,
std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items,
@@ -273,69 +263,6 @@ static void CreatePropertyGeneratorExpressions(
}
}
-namespace {
-// Represent a target property entry after evaluating generator expressions
-// and splitting up lists.
-struct EvaluatedTargetPropertyEntry
-{
- EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
- cmListFileBacktrace bt)
- : LinkImplItem(item)
- , Backtrace(std::move(bt))
- {
- }
-
- // Move-only.
- EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
- EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
- EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
- delete;
- EvaluatedTargetPropertyEntry& operator=(
- EvaluatedTargetPropertyEntry const&) = delete;
-
- cmLinkImplItem const& LinkImplItem;
- cmListFileBacktrace Backtrace;
- std::vector<std::string> Values;
- bool ContextDependent = false;
-};
-
-EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
- cmGeneratorTarget const* thisTarget, std::string const& config,
- std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
- cmGeneratorTarget::TargetPropertyEntry& entry)
-{
- EvaluatedTargetPropertyEntry ee(entry.LinkImplItem, entry.GetBacktrace());
- cmExpandList(entry.Evaluate(thisTarget->GetLocalGenerator(), config,
- thisTarget, dagChecker, lang),
- ee.Values);
- if (entry.GetHadContextSensitiveCondition()) {
- ee.ContextDependent = true;
- }
- return ee;
-}
-
-struct EvaluatedTargetPropertyEntries
-{
- std::vector<EvaluatedTargetPropertyEntry> Entries;
- bool HadContextSensitiveCondition = false;
-};
-
-EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
- cmGeneratorTarget const* thisTarget, std::string const& config,
- std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
- std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
- in)
-{
- EvaluatedTargetPropertyEntries out;
- out.Entries.reserve(in.size());
- for (auto const& entry : in) {
- out.Entries.emplace_back(EvaluateTargetPropertyEntry(
- thisTarget, config, lang, dagChecker, *entry));
- }
- return out;
-}
-}
-
cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
: Target(t)
{
@@ -458,6 +385,11 @@ std::string const& cmGeneratorTarget::GetSafeProperty(
const char* cmGeneratorTarget::GetOutputTargetType(
cmStateEnums::ArtifactType artifact) const
{
+ if (this->IsFrameworkOnApple() || this->GetGlobalGenerator()->IsXcode()) {
+ // import file (i.e. .tbd file) is always in same location as library
+ artifact = cmStateEnums::RuntimeBinaryArtifact;
+ }
+
switch (this->GetType()) {
case cmStateEnums::SHARED_LIBRARY:
if (this->IsDLLPlatform()) {
@@ -470,9 +402,15 @@ const char* cmGeneratorTarget::GetOutputTargetType(
return "ARCHIVE";
}
} else {
- // For non-DLL platforms shared libraries are treated as
- // library targets.
- return "LIBRARY";
+ switch (artifact) {
+ case cmStateEnums::RuntimeBinaryArtifact:
+ // For non-DLL platforms shared libraries are treated as
+ // library targets.
+ return "LIBRARY";
+ case cmStateEnums::ImportLibraryArtifact:
+ // Library import libraries are treated as archive targets.
+ return "ARCHIVE";
+ }
}
break;
case cmStateEnums::STATIC_LIBRARY:
@@ -1000,12 +938,27 @@ const std::string& cmGeneratorTarget::GetObjectName(cmSourceFile const* file)
const char* cmGeneratorTarget::GetCustomObjectExtension() const
{
- static std::string extension;
- const bool has_ptx_extension =
- this->GetPropertyAsBool("CUDA_PTX_COMPILATION");
- if (has_ptx_extension) {
- extension = ".ptx";
- return extension.c_str();
+ struct compiler_mode
+ {
+ std::string variable;
+ std::string extension;
+ };
+ static std::array<compiler_mode, 4> const modes{
+ { { "CUDA_PTX_COMPILATION", ".ptx" },
+ { "CUDA_CUBIN_COMPILATION", ".cubin" },
+ { "CUDA_FATBIN_COMPILATION", ".fatbin" },
+ { "CUDA_OPTIX_COMPILATION", ".optixir" } }
+ };
+
+ std::string const& compiler =
+ this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
+ if (!compiler.empty()) {
+ for (const auto& m : modes) {
+ const bool has_extension = this->GetPropertyAsBool(m.variable);
+ if (has_extension) {
+ return m.extension.c_str();
+ }
+ }
}
return nullptr;
}
@@ -1222,10 +1175,12 @@ bool cmGeneratorTarget::IsInBuildSystem() const
case cmStateEnums::GLOBAL_TARGET:
return true;
case cmStateEnums::INTERFACE_LIBRARY:
- // An INTERFACE library is in the build system if it has SOURCES or
- // HEADER_SETS.
+ // An INTERFACE library is in the build system if it has SOURCES,
+ // HEADER_SETS, or C++ module filesets.
if (!this->SourceEntries.empty() ||
- !this->Target->GetHeaderSetsEntries().empty()) {
+ !this->Target->GetHeaderSetsEntries().empty() ||
+ !this->Target->GetCxxModuleSetsEntries().empty() ||
+ !this->Target->GetCxxModuleHeaderSetsEntries().empty()) {
return true;
}
break;
@@ -1235,6 +1190,16 @@ bool cmGeneratorTarget::IsInBuildSystem() const
return false;
}
+bool cmGeneratorTarget::IsNormal() const
+{
+ return this->Target->IsNormal();
+}
+
+bool cmGeneratorTarget::IsSynthetic() const
+{
+ return this->Target->IsSynthetic();
+}
+
bool cmGeneratorTarget::IsImported() const
{
return this->Target->IsImported();
@@ -1568,84 +1533,6 @@ void AddLangSpecificImplicitIncludeDirectories(
}
}
-void addInterfaceEntry(cmGeneratorTarget const* headTarget,
- std::string const& config, std::string const& prop,
- std::string const& lang,
- cmGeneratorExpressionDAGChecker* dagChecker,
- EvaluatedTargetPropertyEntries& entries,
- LinkInterfaceFor interfaceFor,
- std::vector<cmLinkImplItem> const& libraries)
-{
- for (cmLinkImplItem const& lib : libraries) {
- if (lib.Target) {
- EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
- // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
- // caller's property and hand-evaluate it as if it were compiled.
- // Create a context as cmCompiledGeneratorExpression::Evaluate does.
- cmGeneratorExpressionContext context(
- headTarget->GetLocalGenerator(), config, false, headTarget, headTarget,
- true, lib.Backtrace, lang);
- cmExpandList(lib.Target->EvaluateInterfaceProperty(
- prop, &context, dagChecker, interfaceFor),
- ee.Values);
- ee.ContextDependent = context.HadContextSensitiveCondition;
- entries.Entries.emplace_back(std::move(ee));
- }
- }
-}
-
-// IncludeRuntimeInterface is used to break the cycle in computing
-// the necessary transitive dependencies of targets that can occur
-// now that we have implicit language runtime targets.
-//
-// To determine the set of languages that a target has we need to iterate
-// all the sources which includes transitive INTERFACE sources.
-// Therefore we can't determine what language runtimes are needed
-// for a target until after all sources are computed.
-//
-// Therefore while computing the applicable INTERFACE_SOURCES we
-// must ignore anything in LanguageRuntimeLibraries or we would
-// create a cycle ( INTERFACE_SOURCES requires LanguageRuntimeLibraries,
-// LanguageRuntimeLibraries requires INTERFACE_SOURCES).
-//
-enum class IncludeRuntimeInterface
-{
- Yes,
- No
-};
-void AddInterfaceEntries(
- cmGeneratorTarget const* headTarget, std::string const& config,
- std::string const& prop, std::string const& lang,
- cmGeneratorExpressionDAGChecker* dagChecker,
- EvaluatedTargetPropertyEntries& entries,
- IncludeRuntimeInterface searchRuntime,
- LinkInterfaceFor interfaceFor = LinkInterfaceFor::Usage)
-{
- if (searchRuntime == IncludeRuntimeInterface::Yes) {
- if (cmLinkImplementation const* impl =
- headTarget->GetLinkImplementation(config, interfaceFor)) {
- entries.HadContextSensitiveCondition =
- impl->HadContextSensitiveCondition;
-
- auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang);
- if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
- addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
- interfaceFor, runtimeLibIt->second);
- }
- addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
- interfaceFor, impl->Libraries);
- }
- } else {
- if (cmLinkImplementationLibraries const* impl =
- headTarget->GetLinkImplementationLibraries(config, interfaceFor)) {
- entries.HadContextSensitiveCondition =
- impl->HadContextSensitiveCondition;
- addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
- interfaceFor, impl->Libraries);
- }
- }
-}
-
void AddObjectEntries(cmGeneratorTarget const* headTarget,
std::string const& config,
cmGeneratorExpressionDAGChecker* dagChecker,
@@ -1702,7 +1589,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget,
std::move(entryCge), fileSet);
entries.Entries.emplace_back(
EvaluateTargetPropertyEntry(headTarget, config, "", dagChecker, tpe));
- for (auto const& file : entries.Entries.back().Values) {
+ EvaluatedTargetPropertyEntry const& entry = entries.Entries.back();
+ for (auto const& file : entry.Values) {
auto* sf = headTarget->Makefile->GetOrCreateSource(file);
if (fileSet->GetType() == "HEADERS"_s) {
sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
@@ -1713,13 +1601,11 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget,
std::string w;
auto path = sf->ResolveFullPath(&e, &w);
if (!w.empty()) {
- cm->IssueMessage(MessageType::AUTHOR_WARNING, w,
- headTarget->GetBacktrace());
+ cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
}
if (path.empty()) {
if (!e.empty()) {
- cm->IssueMessage(MessageType::FATAL_ERROR, e,
- headTarget->GetBacktrace());
+ cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
}
return;
}
@@ -1794,11 +1680,11 @@ bool processSources(cmGeneratorTarget const* tgt,
std::string fullPath = sf->ResolveFullPath(&e, &w);
cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
if (!w.empty()) {
- cm->IssueMessage(MessageType::AUTHOR_WARNING, w, tgt->GetBacktrace());
+ cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
}
if (fullPath.empty()) {
if (!e.empty()) {
- cm->IssueMessage(MessageType::FATAL_ERROR, e, tgt->GetBacktrace());
+ cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
}
return contextDependent;
}
@@ -2506,7 +2392,8 @@ bool cmGeneratorTarget::CanGenerateInstallNameDir(
return !skip;
}
-std::string cmGeneratorTarget::GetSOName(const std::string& config) const
+std::string cmGeneratorTarget::GetSOName(
+ const std::string& config, cmStateEnums::ArtifactType artifact) const
{
if (this->IsImported()) {
// Lookup the imported soname.
@@ -2534,7 +2421,9 @@ std::string cmGeneratorTarget::GetSOName(const std::string& config) const
return "";
}
// Compute the soname that will be built.
- return this->GetLibraryNames(config).SharedObject;
+ return artifact == cmStateEnums::RuntimeBinaryArtifact
+ ? this->GetLibraryNames(config).SharedObject
+ : this->GetLibraryNames(config).ImportLibrary;
}
namespace {
@@ -3062,6 +2951,16 @@ void cmGeneratorTarget::ComputeModuleDefinitionInfo(
}
}
+bool cmGeneratorTarget::IsAIX() const
+{
+ return this->Target->IsAIX();
+}
+
+bool cmGeneratorTarget::IsApple() const
+{
+ return this->Target->IsApple();
+}
+
bool cmGeneratorTarget::IsDLLPlatform() const
{
return this->Target->IsDLLPlatform();
@@ -3397,11 +3296,12 @@ std::string cmGeneratorTarget::GetCompilePDBDirectory(
return "";
}
-void cmGeneratorTarget::GetAppleArchs(const std::string& config,
- std::vector<std::string>& archVec) const
+std::vector<std::string> cmGeneratorTarget::GetAppleArchs(
+ std::string const& config, cm::optional<std::string> lang) const
{
- if (!this->Makefile->IsOn("APPLE")) {
- return;
+ std::vector<std::string> archVec;
+ if (!this->IsApple()) {
+ return archVec;
}
cmValue archs = nullptr;
if (!config.empty()) {
@@ -3415,9 +3315,15 @@ void cmGeneratorTarget::GetAppleArchs(const std::string& config,
if (archs) {
cmExpandList(*archs, archVec);
}
- if (archVec.empty()) {
+ if (archVec.empty() &&
+ // Fall back to a default architecture if no compiler target is set.
+ (!lang ||
+ this->Makefile
+ ->GetDefinition(cmStrCat("CMAKE_", *lang, "_COMPILER_TARGET"))
+ .IsEmpty())) {
this->Makefile->GetDefExpandList("_CMAKE_APPLE_ARCHS_DEFAULT", archVec);
}
+ return archVec;
}
void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
@@ -3568,7 +3474,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
for (CudaArchitecture& architecture : architectures) {
flags +=
- " --generate-code=arch=compute_" + architecture.name + ",code=[";
+ " \"--generate-code=arch=compute_" + architecture.name + ",code=[";
if (architecture.virtual_) {
flags += "compute_" + architecture.name;
@@ -3588,7 +3494,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
flags += "sm_" + architecture.name;
}
- flags += "]";
+ flags += "]\"";
}
} else if (compiler == "Clang") {
for (CudaArchitecture& architecture : architectures) {
@@ -3898,7 +3804,7 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetIncludeDirectories(
AddInterfaceEntries(this, config, "INTERFACE_INCLUDE_DIRECTORIES", lang,
&dagChecker, entries, IncludeRuntimeInterface::Yes);
- if (this->Makefile->IsOn("APPLE")) {
+ if (this->IsApple()) {
if (cmLinkImplementationLibraries const* impl =
this->GetLinkImplementationLibraries(config,
LinkInterfaceFor::Usage)) {
@@ -5066,10 +4972,18 @@ void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
f = cmStrCat(dir, '/', targetNames.PDB);
gg->AddToManifest(f);
}
+
+ dir = this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
+ if (!targetNames.ImportOutput.empty()) {
+ f = cmStrCat(dir, '/', targetNames.ImportOutput);
+ gg->AddToManifest(f);
+ }
if (!targetNames.ImportLibrary.empty()) {
- f =
- cmStrCat(this->GetDirectory(config, cmStateEnums::ImportLibraryArtifact),
- '/', targetNames.ImportLibrary);
+ f = cmStrCat(dir, '/', targetNames.ImportLibrary);
+ gg->AddToManifest(f);
+ }
+ if (!targetNames.ImportReal.empty()) {
+ f = cmStrCat(dir, '/', targetNames.ImportReal);
gg->AddToManifest(f);
}
}
@@ -5189,14 +5103,20 @@ std::string cmGeneratorTarget::NormalGetFullPath(
}
break;
case cmStateEnums::ImportLibraryArtifact:
- fpath += this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
+ if (realname) {
+ fpath +=
+ this->NormalGetRealName(config, cmStateEnums::ImportLibraryArtifact);
+ } else {
+ fpath +=
+ this->GetFullName(config, cmStateEnums::ImportLibraryArtifact);
+ }
break;
}
return fpath;
}
std::string cmGeneratorTarget::NormalGetRealName(
- const std::string& config) const
+ const std::string& config, cmStateEnums::ArtifactType artifact) const
{
// This should not be called for imported targets.
// TODO: Split cmTarget into a class hierarchy to get compile-time
@@ -5207,12 +5127,13 @@ std::string cmGeneratorTarget::NormalGetRealName(
this->LocalGenerator->IssueMessage(MessageType::INTERNAL_ERROR, msg);
}
- if (this->GetType() == cmStateEnums::EXECUTABLE) {
- // Compute the real name that will be built.
- return this->GetExecutableNames(config).Real;
- }
+ Names names = this->GetType() == cmStateEnums::EXECUTABLE
+ ? this->GetExecutableNames(config)
+ : this->GetLibraryNames(config);
+
// Compute the real name that will be built.
- return this->GetLibraryNames(config).Real;
+ return artifact == cmStateEnums::RuntimeBinaryArtifact ? names.Real
+ : names.ImportReal;
}
cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
@@ -5257,17 +5178,16 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
// The library name.
targetNames.Base = components.base;
targetNames.Output =
- components.prefix + targetNames.Base + components.suffix;
+ cmStrCat(components.prefix, targetNames.Base, components.suffix);
if (this->IsFrameworkOnApple()) {
targetNames.Real = components.prefix;
if (!this->Makefile->PlatformIsAppleEmbedded()) {
- targetNames.Real += "Versions/";
- targetNames.Real += this->GetFrameworkVersion();
- targetNames.Real += "/";
+ targetNames.Real +=
+ cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
}
- targetNames.Real += targetNames.Base + components.suffix;
- targetNames.SharedObject = targetNames.Real + components.suffix;
+ targetNames.Real += cmStrCat(targetNames.Base, components.suffix);
+ targetNames.SharedObject = targetNames.Real;
} else {
// The library's soname.
this->ComputeVersionedName(targetNames.SharedObject, components.prefix,
@@ -5280,11 +5200,36 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetLibraryNames(
targetNames.Output, version);
}
- // The import library name.
+ // The import library names.
if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->GetType() == cmStateEnums::MODULE_LIBRARY) {
- targetNames.ImportLibrary =
- this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
+ NameComponents const& importComponents =
+ this->GetFullNameInternalComponents(config,
+ cmStateEnums::ImportLibraryArtifact);
+ targetNames.ImportOutput = cmStrCat(
+ importComponents.prefix, importComponents.base, importComponents.suffix);
+
+ if (this->IsFrameworkOnApple() && this->IsSharedLibraryWithExports()) {
+ targetNames.ImportReal = components.prefix;
+ if (!this->Makefile->PlatformIsAppleEmbedded()) {
+ targetNames.ImportReal +=
+ cmStrCat("Versions/", this->GetFrameworkVersion(), '/');
+ }
+ targetNames.ImportReal +=
+ cmStrCat(importComponents.base, importComponents.suffix);
+ targetNames.ImportLibrary = targetNames.ImportOutput;
+ } else {
+ // The import library's soname.
+ this->ComputeVersionedName(
+ targetNames.ImportLibrary, importComponents.prefix,
+ importComponents.base, importComponents.suffix,
+ targetNames.ImportOutput, soversion);
+
+ // The import library's real name on disk.
+ this->ComputeVersionedName(
+ targetNames.ImportReal, importComponents.prefix, importComponents.base,
+ importComponents.suffix, targetNames.ImportOutput, version);
+ }
}
// The program database file name.
@@ -5346,6 +5291,8 @@ cmGeneratorTarget::Names cmGeneratorTarget::GetExecutableNames(
// The import library name.
targetNames.ImportLibrary =
this->GetFullNameInternal(config, cmStateEnums::ImportLibraryArtifact);
+ targetNames.ImportReal = targetNames.ImportLibrary;
+ targetNames.ImportOutput = targetNames.ImportLibrary;
// The program database file name.
targetNames.PDB = this->GetPDBName(config);
@@ -5427,15 +5374,18 @@ cmGeneratorTarget::GetFullNameInternalComponents(
}
// Compute the full name for main target types.
- const std::string configPostfix = this->GetFilePostfix(config);
+ std::string configPostfix = this->GetFilePostfix(config);
- // frameworks have directory prefix but no suffix
+ // frameworks have directory prefix
std::string fw_prefix;
if (this->IsFrameworkOnApple()) {
fw_prefix =
cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
targetPrefix = cmValue(fw_prefix);
- targetSuffix = nullptr;
+ if (!isImportedLibraryArtifact) {
+ // no suffix
+ targetSuffix = nullptr;
+ }
}
if (this->IsCFBundleOnApple()) {
@@ -5454,8 +5404,8 @@ cmGeneratorTarget::GetFullNameInternalComponents(
// When using Xcode, the postfix should be part of the suffix rather than
// the base, because the suffix ends up being used in Xcode's
// EXECUTABLE_SUFFIX attribute.
- if (this->IsFrameworkOnApple() &&
- this->GetGlobalGenerator()->GetName() == "Xcode") {
+ if (this->IsFrameworkOnApple() && this->GetGlobalGenerator()->IsXcode()) {
+ configPostfix += *targetSuffix;
targetSuffix = cmValue(configPostfix);
} else {
outBase += configPostfix;
@@ -5463,9 +5413,15 @@ cmGeneratorTarget::GetFullNameInternalComponents(
// Name shared libraries with their version number on some platforms.
if (cmValue soversion = this->GetProperty("SOVERSION")) {
+ cmValue dllProp;
+ if (this->IsDLLPlatform()) {
+ dllProp = this->GetProperty("DLL_NAME_WITH_SOVERSION");
+ }
if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
!isImportedLibraryArtifact &&
- this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")) {
+ (dllProp.IsOn() ||
+ (!dllProp.IsSet() &&
+ this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")))) {
outBase += "-";
outBase += *soversion;
}
@@ -6745,12 +6701,12 @@ void cmGeneratorTarget::ComputeVersionedName(
std::string& vName, std::string const& prefix, std::string const& base,
std::string const& suffix, std::string const& name, cmValue version) const
{
- vName = this->Makefile->IsOn("APPLE") ? (prefix + base) : name;
+ vName = this->IsApple() ? (prefix + base) : name;
if (version) {
vName += ".";
vName += *version;
}
- vName += this->Makefile->IsOn("APPLE") ? suffix : std::string();
+ vName += this->IsApple() ? suffix : std::string();
}
std::vector<std::string> cmGeneratorTarget::GetPropertyKeys() const
@@ -7103,6 +7059,11 @@ cmGeneratorTarget::OutputInfo const* cmGeneratorTarget::GetOutputInfo(
return nullptr;
}
+ // Synthetic targets don't have output.
+ if (this->IsSynthetic()) {
+ return nullptr;
+ }
+
// Only libraries and executables have well-defined output files.
if (!this->HaveWellDefinedOutputFiles()) {
std::string msg = cmStrCat("cmGeneratorTarget::GetOutputInfo called for ",
@@ -8514,19 +8475,38 @@ bool cmGeneratorTarget::HasContextDependentSources() const
bool cmGeneratorTarget::IsExecutableWithExports() const
{
- return (this->GetType() == cmStateEnums::EXECUTABLE &&
- this->GetPropertyAsBool("ENABLE_EXPORTS"));
+ return this->Target->IsExecutableWithExports();
+}
+
+bool cmGeneratorTarget::IsSharedLibraryWithExports() const
+{
+ return this->Target->IsSharedLibraryWithExports();
}
bool cmGeneratorTarget::HasImportLibrary(std::string const& config) const
{
+ bool generate_Stubs = true;
+ if (this->GetGlobalGenerator()->IsXcode()) {
+ // take care of CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS variable
+ // as well as XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS property
+ if (cmValue propGenStubs =
+ this->GetProperty("XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
+ generate_Stubs = propGenStubs == "YES";
+ } else if (cmValue varGenStubs = this->Makefile->GetDefinition(
+ "CMAKE_XCODE_ATTRIBUTE_GENERATE_TEXT_BASED_STUBS")) {
+ generate_Stubs = varGenStubs == "YES";
+ }
+ }
+
return (this->IsDLLPlatform() &&
(this->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->IsExecutableWithExports()) &&
// Assemblies which have only managed code do not have
// import libraries.
this->GetManagedType(config) != ManagedType::Managed) ||
- (this->Target->IsAIX() && this->IsExecutableWithExports());
+ (this->IsAIX() && this->IsExecutableWithExports()) ||
+ (this->Makefile->PlatformSupportsAppleTextStubs() &&
+ this->IsSharedLibraryWithExports() && generate_Stubs);
}
bool cmGeneratorTarget::NeedImportLibraryName(std::string const& config) const
@@ -8564,17 +8544,12 @@ bool cmGeneratorTarget::IsLinkable() const
bool cmGeneratorTarget::IsFrameworkOnApple() const
{
- return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
- this->GetType() == cmStateEnums::STATIC_LIBRARY) &&
- this->Makefile->IsOn("APPLE") &&
- this->GetPropertyAsBool("FRAMEWORK"));
+ return this->Target->IsFrameworkOnApple();
}
bool cmGeneratorTarget::IsAppBundleOnApple() const
{
- return (this->GetType() == cmStateEnums::EXECUTABLE &&
- this->Makefile->IsOn("APPLE") &&
- this->GetPropertyAsBool("MACOSX_BUNDLE"));
+ return this->Target->IsAppBundleOnApple();
}
bool cmGeneratorTarget::IsXCTestOnApple() const
@@ -8584,8 +8559,8 @@ bool cmGeneratorTarget::IsXCTestOnApple() const
bool cmGeneratorTarget::IsCFBundleOnApple() const
{
- return (this->GetType() == cmStateEnums::MODULE_LIBRARY &&
- this->Makefile->IsOn("APPLE") && this->GetPropertyAsBool("BUNDLE"));
+ return (this->GetType() == cmStateEnums::MODULE_LIBRARY && this->IsApple() &&
+ this->GetPropertyAsBool("BUNDLE"));
}
cmGeneratorTarget::ManagedType cmGeneratorTarget::CheckManagedType(
@@ -8850,10 +8825,10 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
cmGeneratedFileStream fout(filename);
fout.SetCopyIfDifferent(true);
- // IWYU pragma: associated allows include what you use to
+ // The IWYU "associated" pragma tells include-what-you-use to
// consider the headerFile as part of the entire language
// unit within include-what-you-use and as a result allows
- // one to get IWYU advice for headers :)
+ // one to get IWYU advice for headers.
fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n";
fout.close();
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index afd9da4330..87227fd29c 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -50,6 +50,8 @@ public:
cmGlobalGenerator* GetGlobalGenerator() const;
bool IsInBuildSystem() const;
+ bool IsNormal() const;
+ bool IsSynthetic() const;
bool IsImported() const;
bool IsImportedGloballyVisible() const;
bool CanCompileSources() const;
@@ -271,7 +273,9 @@ public:
std::string NormalGetFullPath(const std::string& config,
cmStateEnums::ArtifactType artifact,
bool realname) const;
- std::string NormalGetRealName(const std::string& config) const;
+ std::string NormalGetRealName(const std::string& config,
+ cmStateEnums::ArtifactType artifact =
+ cmStateEnums::RuntimeBinaryArtifact) const;
/** Get the names of an object library's object files underneath
its object file directory. */
@@ -346,7 +350,9 @@ public:
const std::string* GetExportMacro() const;
/** Get the soname of the target. Allowed only for a shared library. */
- std::string GetSOName(const std::string& config) const;
+ std::string GetSOName(const std::string& config,
+ cmStateEnums::ArtifactType artifact =
+ cmStateEnums::RuntimeBinaryArtifact) const;
struct NameComponents
{
@@ -387,6 +393,11 @@ public:
ModuleDefinitionInfo const* GetModuleDefinitionInfo(
std::string const& config) const;
+ /** Return whether or not we are targeting AIX. */
+ bool IsAIX() const;
+ /** Return whether or not we are targeting Apple. */
+ bool IsApple() const;
+
/** Return whether or not the target is for a DLL platform. */
bool IsDLLPlatform() const;
@@ -473,8 +484,8 @@ public:
holding object files for the given configuration. */
std::string GetObjectDirectory(std::string const& config) const;
- void GetAppleArchs(const std::string& config,
- std::vector<std::string>& archVec) const;
+ std::vector<std::string> GetAppleArchs(std::string const& config,
+ cm::optional<std::string> lang) const;
void AddExplicitLanguageFlags(std::string& flags,
cmSourceFile const& sf) const;
@@ -733,6 +744,8 @@ public:
std::string Base;
std::string Output;
std::string Real;
+ std::string ImportOutput;
+ std::string ImportReal;
std::string ImportLibrary;
std::string PDB;
std::string SharedObject;
@@ -779,6 +792,10 @@ public:
bool IsExecutableWithExports() const;
+ /* Return whether this target is a shared library with capability to generate
+ * a file describing symbols exported (for example, .tbd file on Apple). */
+ bool IsSharedLibraryWithExports() const;
+
/** Return whether or not the target has a DLL import library. */
bool HasImportLibrary(std::string const& config) const;
@@ -1274,3 +1291,25 @@ private:
};
mutable std::map<std::string, InfoByConfig> Configs;
};
+
+class cmGeneratorTarget::TargetPropertyEntry
+{
+protected:
+ static cmLinkImplItem NoLinkImplItem;
+
+public:
+ TargetPropertyEntry(cmLinkImplItem const& item);
+ virtual ~TargetPropertyEntry() = default;
+
+ virtual const std::string& Evaluate(
+ cmLocalGenerator* lg, const std::string& config,
+ cmGeneratorTarget const* headTarget,
+ cmGeneratorExpressionDAGChecker* dagChecker,
+ std::string const& language) const = 0;
+
+ virtual cmListFileBacktrace GetBacktrace() const = 0;
+ virtual std::string const& GetInput() const = 0;
+ virtual bool GetHadContextSensitiveCondition() const;
+
+ cmLinkImplItem const& LinkImplItem;
+};
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index b55fcb8504..3563a1a17d 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -110,8 +110,6 @@ cmGlobalGenerator::cmGlobalGenerator(cmake* cm)
this->ConfigureDoneCMP0026AndCMP0024 = false;
this->FirstTimeProgress = 0.0f;
- this->RecursionDepth = 0;
-
cm->GetState()->SetIsGeneratorMultiConfig(false);
cm->GetState()->SetMinGWMake(false);
cm->GetState()->SetMSYSShell(false);
@@ -1327,6 +1325,16 @@ void cmGlobalGenerator::Configure()
this->BinaryDirectories.insert(
this->CMakeInstance->GetHomeOutputDirectory());
+ if (this->ExtraGenerator && !this->CMakeInstance->GetIsInTryCompile()) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::DEPRECATION_WARNING,
+ cmStrCat("Support for \"Extra Generators\" like\n ",
+ this->ExtraGenerator->GetName(),
+ "\nis deprecated and will be removed from a future version "
+ "of CMake. IDEs may use the cmake-file-api(7) to view "
+ "CMake-generated project build trees."));
+ }
+
// now do it
this->ConfigureDoneCMP0026AndCMP0024 = false;
dirMf->Configure();
@@ -1535,10 +1543,13 @@ bool cmGlobalGenerator::Compute()
return false;
}
- // Iterate through all targets and set up AUTOMOC, AUTOUIC and AUTORCC
- if (!this->QtAutoGen()) {
+#ifndef CMAKE_BOOTSTRAP
+ this->QtAutoGen =
+ cm::make_unique<cmQtAutoGenGlobalInitializer>(this->LocalGenerators);
+ if (!this->QtAutoGen->InitializeCustomTargets()) {
return false;
}
+#endif
// Add generator specific helper commands
for (const auto& localGen : this->LocalGenerators) {
@@ -1619,6 +1630,17 @@ void cmGlobalGenerator::Generate()
this->CMakeInstance->UpdateProgress("Generating", 0.1f);
+#ifndef CMAKE_BOOTSTRAP
+ if (!this->QtAutoGen->SetupCustomTargets()) {
+ if (!cmSystemTools::GetErrorOccurredFlag()) {
+ this->GetCMakeInstance()->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "Problem setting up custom targets for QtAutoGen");
+ }
+ return;
+ }
+#endif
+
// Generate project files
for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
this->SetCurrentMakefile(this->LocalGenerators[i]->GetMakefile());
@@ -1755,16 +1777,6 @@ void cmGlobalGenerator::ComputeTargetOrder(cmGeneratorTarget const* gt,
entry->second = index++;
}
-bool cmGlobalGenerator::QtAutoGen()
-{
-#ifndef CMAKE_BOOTSTRAP
- cmQtAutoGenGlobalInitializer initializer(this->LocalGenerators);
- return initializer.generate();
-#else
- return true;
-#endif
-}
-
bool cmGlobalGenerator::AddHeaderSetVerification()
{
for (auto const& gen : this->LocalGenerators) {
@@ -2910,7 +2922,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install(
singleLine.push_back(cfgArg);
cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
} else {
- cfgArg += *mf->GetDefinition("CMAKE_CFG_INTDIR");
+ cfgArg += this->GetCMakeCFGIntDir();
}
singleLine.push_back(cfgArg);
}
@@ -3534,3 +3546,32 @@ cmInstallRuntimeDependencySet* cmGlobalGenerator::GetNamedRuntimeDependencySet(
}
return it->second;
}
+
+cmGlobalGenerator::StripCommandStyle cmGlobalGenerator::GetStripCommandStyle(
+ std::string const& strip)
+{
+#ifdef __APPLE__
+ auto i = this->StripCommandStyleMap.find(strip);
+ if (i == this->StripCommandStyleMap.end()) {
+ StripCommandStyle style = StripCommandStyle::Default;
+
+ // Try running strip tool with Apple-specific options.
+ std::vector<std::string> cmd{ strip, "-u", "-r" };
+ std::string out;
+ std::string err;
+ int ret;
+ if (cmSystemTools::RunSingleCommand(cmd, &out, &err, &ret, nullptr,
+ cmSystemTools::OUTPUT_NONE) &&
+ // Check for Apple-specific output.
+ ret != 0 && cmHasLiteralPrefix(err, "fatal error: /") &&
+ err.find("/usr/bin/strip: no files specified") != std::string::npos) {
+ style = StripCommandStyle::Apple;
+ }
+ i = this->StripCommandStyleMap.emplace(strip, style).first;
+ }
+ return i->second;
+#else
+ static_cast<void>(strip);
+ return StripCommandStyle::Default;
+#endif
+}
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index f4862a0ef9..dde364886e 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -50,6 +50,7 @@ class cmLinkLineComputer;
class cmLocalGenerator;
class cmMakefile;
class cmOutputConverter;
+class cmQtAutoGenGlobalInitializer;
class cmSourceFile;
class cmState;
class cmStateDirectory;
@@ -591,7 +592,7 @@ public:
std::string MakeSilentFlag;
- int RecursionDepth;
+ size_t RecursionDepth = 0;
virtual void GetQtAutoGenConfigs(std::vector<std::string>& configs) const
{
@@ -607,6 +608,13 @@ public:
cmInstallRuntimeDependencySet* GetNamedRuntimeDependencySet(
const std::string& name);
+ enum class StripCommandStyle
+ {
+ Default,
+ Apple,
+ };
+ StripCommandStyle GetStripCommandStyle(std::string const& strip);
+
protected:
// for a project collect all its targets by following depend
// information, and also collect all the targets
@@ -630,10 +638,6 @@ protected:
void CxxModuleSupportCheck() const;
- /// @brief Qt AUTOMOC/UIC/RCC target generation
- /// @return true on success
- bool QtAutoGen();
-
bool AddHeaderSetVerification();
bool AddAutomaticSources();
@@ -680,6 +684,11 @@ protected:
cmake* CMakeInstance;
std::vector<std::unique_ptr<cmMakefile>> Makefiles;
LocalGeneratorVector LocalGenerators;
+
+#ifndef CMAKE_BOOTSTRAP
+ std::unique_ptr<cmQtAutoGenGlobalInitializer> QtAutoGen;
+#endif
+
cmMakefile* CurrentConfigureMakefile;
// map from project name to vector of local generators in that project
std::map<std::string, std::vector<cmLocalGenerator*>> ProjectMap;
@@ -737,6 +746,10 @@ private:
std::map<std::string, int> LanguageToLinkerPreference;
std::map<std::string, std::string> LanguageToOriginalSharedLibFlags;
+#ifdef __APPLE__
+ std::map<std::string, StripCommandStyle> StripCommandStyleMap;
+#endif
+
mutable bool DiagnosedCxxModuleSupport = false;
// Deferral id generation.
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 3da15f643a..b1f2b4a393 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -22,7 +22,6 @@
#include "cmLocalGhsMultiGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
-#include "cmPolicies.h"
#include "cmSourceFile.h"
#include "cmState.h"
#include "cmStateTypes.h"
@@ -717,7 +716,6 @@ bool cmGlobalGhsMultiGenerator::AddCheckTarget()
cc->SetDepends(listFiles);
cc->SetCommandLines(commandLines);
cc->SetComment("Checking Build System");
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetStdPipesUTF8(true);
@@ -747,7 +745,6 @@ void cmGlobalGhsMultiGenerator::AddAllTarget()
// Use no actual command lines so that the target itself is not
// considered always out of date.
auto cc = cm::make_unique<cmCustomCommand>();
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetComment("Build all projects");
cmTarget* allBuild = gen[0]->AddUtilityCommand(this->GetAllTargetName(),
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index f8e97db16d..0c28776a82 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -330,12 +330,6 @@ void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
}
}
- if (build.Variables.count("dyndep") > 0) {
- // The ninja 'cleandead' operation does not account for outputs
- // discovered by 'dyndep' bindings. Avoid removing them.
- this->DisableCleandead = true;
- }
-
os << buildStr << arguments << assignments << "\n";
}
@@ -589,6 +583,7 @@ void cmGlobalNinjaGenerator::Generate()
msg.str());
return;
}
+ this->InitOutputPathPrefix();
if (!this->OpenBuildFileStreams()) {
return;
}
@@ -600,10 +595,8 @@ void cmGlobalNinjaGenerator::Generate()
it.second.TargetDependsClosures.clear();
}
- this->InitOutputPathPrefix();
this->TargetAll = this->NinjaOutputPath("all");
this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
- this->DisableCleandead = false;
this->DiagnosedCxxModuleNinjaSupport = false;
this->ClangTidyExportFixesDirs.clear();
this->ClangTidyExportFixesFiles.clear();
@@ -1286,6 +1279,10 @@ void cmGlobalNinjaGenerator::AppendTargetOutputs(
}
CM_FALLTHROUGH;
case cmStateEnums::EXECUTABLE: {
+ if (target->IsApple() && target->HasImportLibrary(config)) {
+ outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
+ config, cmStateEnums::ImportLibraryArtifact, realname)));
+ }
outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
config, cmStateEnums::RuntimeBinaryArtifact, realname)));
break;
@@ -1372,17 +1369,7 @@ void cmGlobalNinjaGenerator::AppendTargetDepends(
}
void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
- cmGeneratorTarget const* target, cmNinjaDeps& outputs,
- const std::string& config, const std::string& fileConfig, bool genexOutput)
-{
- cmNinjaOuts outs;
- this->AppendTargetDependsClosure(target, outs, config, fileConfig,
- genexOutput, true);
- cm::append(outputs, outs);
-}
-
-void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
- cmGeneratorTarget const* target, cmNinjaOuts& outputs,
+ cmGeneratorTarget const* target, std::unordered_set<std::string>& outputs,
const std::string& config, const std::string& fileConfig, bool genexOutput,
bool omit_self)
{
@@ -1403,7 +1390,8 @@ void cmGlobalNinjaGenerator::AppendTargetDependsClosure(
// relevant for filling the cache entries properly isolated and a global
// result set that is relevant for the result of the top level call to
// AppendTargetDependsClosure.
- cmNinjaOuts this_outs; // this will be the new cache entry
+ std::unordered_set<std::string>
+ this_outs; // this will be the new cache entry
for (auto const& dep_target : this->GetTargetDirectDepends(target)) {
if (!dep_target->IsInBuildSystem()) {
@@ -2076,9 +2064,10 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
build.Outputs.front() = this->BuildAlias(
this->NinjaOutputPath(this->GetCleanTargetName()), config);
if (this->IsMultiConfig()) {
- build.Variables["TARGETS"] =
- cmStrCat(this->BuildAlias(GetByproductsForCleanTargetName(), config),
- " ", GetByproductsForCleanTargetName());
+ build.Variables["TARGETS"] = cmStrCat(
+ this->BuildAlias(
+ this->NinjaOutputPath(GetByproductsForCleanTargetName()), config),
+ " ", this->NinjaOutputPath(GetByproductsForCleanTargetName()));
}
build.ExplicitDeps.clear();
if (additionalFiles) {
@@ -2093,7 +2082,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
if (this->IsMultiConfig()) {
build.Variables["FILE_ARG"] = cmStrCat(
"-f ",
- cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig));
+ this->NinjaOutputPath(
+ cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig)));
}
this->WriteBuild(*this->GetImplFileStream(fileConfig), build);
}
@@ -2115,8 +2105,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
std::vector<std::string> byproducts;
byproducts.reserve(this->CrossConfigs.size());
for (auto const& config : this->CrossConfigs) {
- byproducts.push_back(
- this->BuildAlias(GetByproductsForCleanTargetName(), config));
+ byproducts.push_back(this->BuildAlias(
+ this->NinjaOutputPath(GetByproductsForCleanTargetName()), config));
}
byproducts.emplace_back(GetByproductsForCleanTargetName());
build.Variables["TARGETS"] = cmJoin(byproducts, " ");
@@ -2124,7 +2114,8 @@ void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
for (auto const& fileConfig : configs) {
build.Variables["FILE_ARG"] = cmStrCat(
"-f ",
- cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig));
+ this->NinjaOutputPath(
+ cmGlobalNinjaMultiGenerator::GetNinjaImplFilename(fileConfig)));
this->WriteBuild(*this->GetImplFileStream(fileConfig), build);
}
}
@@ -2924,7 +2915,8 @@ bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams()
*this->DefaultFileStream << "# Build using rules for '"
<< this->DefaultFileConfig << "'.\n\n"
<< "include "
- << GetNinjaImplFilename(this->DefaultFileConfig)
+ << this->NinjaOutputPath(
+ GetNinjaImplFilename(this->DefaultFileConfig))
<< "\n\n";
// Write a comment about this file.
@@ -2957,7 +2949,8 @@ bool cmGlobalNinjaMultiGenerator::OpenBuildFileStreams()
*this->ConfigFileStreams[config]
<< "# This file contains aliases specific to the \"" << config
<< "\"\n# configuration.\n\n"
- << "include " << GetNinjaImplFilename(config) << "\n\n";
+ << "include " << this->NinjaOutputPath(GetNinjaImplFilename(config))
+ << "\n\n";
return true;
});
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 7f01b09b85..bd541685c9 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -351,15 +351,10 @@ public:
const std::string& fileConfig,
cmNinjaTargetDepends depends);
void AppendTargetDependsClosure(cmGeneratorTarget const* target,
- cmNinjaDeps& outputs,
+ std::unordered_set<std::string>& outputs,
const std::string& config,
const std::string& fileConfig,
- bool genexOutput);
- void AppendTargetDependsClosure(cmGeneratorTarget const* target,
- cmNinjaOuts& outputs,
- const std::string& config,
- const std::string& fileConfig,
- bool genexOutput, bool omit_self);
+ bool genexOutput, bool omit_self = true);
void AppendDirectoryForConfig(const std::string& prefix,
const std::string& config,
@@ -602,7 +597,6 @@ private:
std::string OutputPathPrefix;
std::string TargetAll;
std::string CMakeCacheFile;
- bool DisableCleandead = false;
struct ByConfig
{
@@ -618,7 +612,8 @@ private:
bool GenexOutput;
};
- std::map<TargetDependsClosureKey, cmNinjaOuts> TargetDependsClosures;
+ std::map<TargetDependsClosureKey, std::unordered_set<std::string>>
+ TargetDependsClosures;
TargetAliasMap TargetAliases;
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 1e01dd6a2b..321f3779d0 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -525,6 +525,30 @@ bool cmGlobalVisualStudio10Generator::InitializeAndroid(cmMakefile* mf)
return false;
}
+bool cmGlobalVisualStudio10Generator::InitializePlatform(cmMakefile* mf)
+{
+ if (this->SystemName == "Windows" || this->SystemName == "WindowsStore") {
+ if (!this->InitializePlatformWindows(mf)) {
+ return false;
+ }
+ } else if (!this->SystemName.empty() &&
+ !this->VerifyNoGeneratorPlatformVersion(mf)) {
+ return false;
+ }
+ return this->cmGlobalVisualStudio8Generator::InitializePlatform(mf);
+}
+
+bool cmGlobalVisualStudio10Generator::InitializePlatformWindows(cmMakefile*)
+{
+ return true;
+}
+
+bool cmGlobalVisualStudio10Generator::VerifyNoGeneratorPlatformVersion(
+ cmMakefile*, cm::optional<std::string>) const
+{
+ return true;
+}
+
bool cmGlobalVisualStudio10Generator::SelectWindowsPhoneToolset(
std::string& toolset) const
{
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index deed206c13..6917ffcc06 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -183,6 +183,11 @@ protected:
virtual bool InitializeTegraAndroid(cmMakefile* mf);
virtual bool InitializeAndroid(cmMakefile* mf);
+ bool InitializePlatform(cmMakefile* mf) override;
+ virtual bool InitializePlatformWindows(cmMakefile* mf);
+ virtual bool VerifyNoGeneratorPlatformVersion(
+ cmMakefile* mf, cm::optional<std::string> reason = cm::nullopt) const;
+
virtual bool ProcessGeneratorToolsetField(std::string const& key,
std::string const& value);
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index 7424ca3980..4300d5c9f3 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -12,6 +12,7 @@
#include "cmGlobalVisualStudioGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmPolicies.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
@@ -137,12 +138,36 @@ bool cmGlobalVisualStudio14Generator::MatchesGeneratorName(
return false;
}
-bool cmGlobalVisualStudio14Generator::InitializeWindows(cmMakefile* mf)
+bool cmGlobalVisualStudio14Generator::InitializePlatformWindows(cmMakefile* mf)
{
if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
- return this->SelectWindows10SDK(mf, false);
+ return this->SelectWindows10SDK(mf);
}
- return true;
+ return this->VerifyNoGeneratorPlatformVersion(mf);
+}
+
+bool cmGlobalVisualStudio14Generator::VerifyNoGeneratorPlatformVersion(
+ cmMakefile* mf, cm::optional<std::string> reason) const
+{
+ if (!this->GeneratorPlatformVersion) {
+ return true;
+ }
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "given platform specification containing a\n"
+ " version=" << *this->GeneratorPlatformVersion << "\n"
+ "field. The version field is not supported when targeting\n"
+ " " << this->SystemName << " " << this->SystemVersion << "\n"
+ ;
+ /* clang-format on */
+ if (reason) {
+ e << *reason << ".";
+ }
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
}
bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf)
@@ -162,9 +187,6 @@ bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf)
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
- if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
- return this->SelectWindows10SDK(mf, true);
- }
return true;
}
@@ -173,19 +195,51 @@ bool cmGlobalVisualStudio14Generator::InitializeAndroid(cmMakefile*)
return true;
}
-bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf,
- bool required)
+bool cmGlobalVisualStudio14Generator::ProcessGeneratorPlatformField(
+ std::string const& key, std::string const& value)
+{
+ if (key == "version") {
+ this->GeneratorPlatformVersion = value;
+ return true;
+ }
+ return false;
+}
+
+bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf)
{
+ if (this->GeneratorPlatformVersion &&
+ this->GeneratorPlatformVersion->empty()) {
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Generator\n ", this->GetName(),
+ "\ngiven platform specification with empty\n version=\n"
+ "field."));
+ return false;
+ }
+
// Find the default version of the Windows 10 SDK.
std::string const version = this->GetWindows10SDKVersion(mf);
- if (required && version.empty()) {
- std::ostringstream e;
- e << "Could not find an appropriate version of the Windows 10 SDK"
- << " installed on this machine";
- mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
- return false;
+ if (version.empty()) {
+ if (this->GeneratorPlatformVersion) {
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ cmStrCat("Generator\n ", this->GetName(),
+ "\ngiven platform specification with\n version=",
+ *this->GeneratorPlatformVersion,
+ "\nfield, but no Windows SDK with that version was found."));
+ return false;
+ }
+
+ if (this->SystemName == "WindowsStore") {
+ mf->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "Could not find an appropriate version of the Windows 10 SDK"
+ " installed on this machine");
+ return false;
+ }
}
+
this->SetWindowsTargetPlatformVersion(version, mf);
return true;
}
@@ -302,6 +356,16 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion(
cmMakefile* mf)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
+ // Accept specific version requests as-is.
+ if (this->GeneratorPlatformVersion) {
+ std::string const& ver = *this->GeneratorPlatformVersion;
+
+ // VS 2019 and above support specifying plain "10.0".
+ if (this->Version >= VSVersion::VS16 && ver == "10.0") {
+ return ver;
+ }
+ }
+
std::vector<std::string> win10Roots;
{
@@ -360,10 +424,35 @@ std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion(
// Sort the results to make sure we select the most recent one.
std::sort(sdks.begin(), sdks.end(), cmSystemTools::VersionCompareGreater);
- // Look for a SDK exactly matching the requested target version.
- for (std::string const& i : sdks) {
- if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) {
- return i;
+ // Look for a SDK exactly matching the requested version, if any.
+ if (this->GeneratorPlatformVersion) {
+ for (std::string const& i : sdks) {
+ if (cmSystemTools::VersionCompareEqual(
+ i, *this->GeneratorPlatformVersion)) {
+ return i;
+ }
+ }
+ // An exact version was requested but not found.
+ // Our caller will issue the error message.
+ return std::string();
+ }
+
+ if (mf->GetPolicyStatus(cmPolicies::CMP0149) == cmPolicies::NEW) {
+ if (cm::optional<std::string> const envVer =
+ cmSystemTools::GetEnvVar("WindowsSDKVersion")) {
+ // Look for a SDK exactly matching the environment variable.
+ for (std::string const& i : sdks) {
+ if (cmSystemTools::VersionCompareEqual(i, *envVer)) {
+ return i;
+ }
+ }
+ }
+ } else {
+ // Look for a SDK exactly matching the target Windows version.
+ for (std::string const& i : sdks) {
+ if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) {
+ return i;
+ }
}
}
diff --git a/Source/cmGlobalVisualStudio14Generator.h b/Source/cmGlobalVisualStudio14Generator.h
index 7fb9b4bbb8..f59a323308 100644
--- a/Source/cmGlobalVisualStudio14Generator.h
+++ b/Source/cmGlobalVisualStudio14Generator.h
@@ -7,6 +7,8 @@
#include <memory>
#include <string>
+#include <cm/optional>
+
#include "cmGlobalVisualStudio12Generator.h"
class cmGlobalGeneratorFactory;
@@ -30,7 +32,6 @@ protected:
cmGlobalVisualStudio14Generator(cmake* cm, const std::string& name,
std::string const& platformInGeneratorName);
- bool InitializeWindows(cmMakefile* mf) override;
bool InitializeWindowsStore(cmMakefile* mf) override;
bool InitializeAndroid(cmMakefile* mf) override;
bool SelectWindowsStoreToolset(std::string& toolset) const override;
@@ -39,6 +40,14 @@ protected:
// of the toolset is installed
bool IsWindowsStoreToolsetInstalled() const;
+ bool InitializePlatformWindows(cmMakefile* mf) override;
+ bool VerifyNoGeneratorPlatformVersion(
+ cmMakefile* mf,
+ cm::optional<std::string> reason = cm::nullopt) const override;
+
+ bool ProcessGeneratorPlatformField(std::string const& key,
+ std::string const& value) override;
+
// Used to adjust the max-SDK-version calculation to accommodate user
// configuration.
std::string GetWindows10SDKMaxVersion(cmMakefile* mf) const;
@@ -47,7 +56,7 @@ protected:
// version of the toolset.
virtual std::string GetWindows10SDKMaxVersionDefault(cmMakefile* mf) const;
- virtual bool SelectWindows10SDK(cmMakefile* mf, bool required);
+ virtual bool SelectWindows10SDK(cmMakefile* mf);
void SetWindowsTargetPlatformVersion(std::string const& version,
cmMakefile* mf);
@@ -61,4 +70,6 @@ protected:
private:
class Factory;
friend class Factory;
+
+ cm::optional<std::string> GeneratorPlatformVersion;
};
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index d483135342..5de3a55939 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -309,6 +309,26 @@ void cmGlobalVisualStudio7Generator::Generate()
GetSLNFile(this->LocalGenerators[0].get()));
}
+ if (this->Version == VSVersion::VS9 &&
+ !this->CMakeInstance->GetIsInTryCompile()) {
+ std::string cmakeWarnVS9;
+ if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
+ "CMAKE_WARN_VS9")) {
+ this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS9");
+ cmakeWarnVS9 = *cached;
+ } else {
+ cmSystemTools::GetEnv("CMAKE_WARN_VS9", cmakeWarnVS9);
+ }
+ if (cmakeWarnVS9.empty() || !cmIsOff(cmakeWarnVS9)) {
+ this->CMakeInstance->IssueMessage(
+ MessageType::WARNING,
+ "The \"Visual Studio 9 2008\" generator is deprecated "
+ "and will be removed in a future version of CMake."
+ "\n"
+ "Add CMAKE_WARN_VS9=OFF to the cache to disable this warning.");
+ }
+ }
+
if (this->Version == VSVersion::VS11 &&
!this->CMakeInstance->GetIsInTryCompile()) {
std::string cmakeWarnVS11;
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 647fc2d3b2..2aba46fe2c 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -94,7 +94,9 @@ bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p,
return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform(p, mf);
}
- this->GeneratorPlatform = p;
+ if (!this->ParseGeneratorPlatform(p, mf)) {
+ return false;
+ }
// FIXME: Add CMAKE_GENERATOR_PLATFORM field to set the framework.
// For now, just report the generator's default, if any.
@@ -114,12 +116,100 @@ bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p,
*targetFrameworkTargetsVersion);
}
+ if (!this->InitializePlatform(mf)) {
+ return false;
+ }
+
// The generator name does not contain the platform name, and so supports
// explicit platform specification. We handled that above, so pass an
// empty platform name to our base class implementation so it does not error.
return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform("", mf);
}
+bool cmGlobalVisualStudio8Generator::ParseGeneratorPlatform(
+ std::string const& p, cmMakefile* mf)
+{
+ this->GeneratorPlatform.clear();
+
+ std::vector<std::string> const fields = cmTokenize(p, ",");
+ auto fi = fields.begin();
+ if (fi == fields.end()) {
+ return true;
+ }
+
+ // The first field may be the VS platform.
+ if (fi->find('=') == fi->npos) {
+ this->GeneratorPlatform = *fi;
+ ++fi;
+ }
+
+ std::set<std::string> handled;
+
+ // The rest of the fields must be key=value pairs.
+ for (; fi != fields.end(); ++fi) {
+ std::string::size_type pos = fi->find('=');
+ if (pos == fi->npos) {
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "given platform specification\n"
+ " " << p << "\n"
+ "that contains a field after the first ',' with no '='."
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+ }
+ std::string const key = fi->substr(0, pos);
+ std::string const value = fi->substr(pos + 1);
+ if (!handled.insert(key).second) {
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "given platform specification\n"
+ " " << p << "\n"
+ "that contains duplicate field key '" << key << "'."
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+ }
+ if (!this->ProcessGeneratorPlatformField(key, value)) {
+ std::ostringstream e;
+ /* clang-format off */
+ e <<
+ "Generator\n"
+ " " << this->GetName() << "\n"
+ "given platform specification\n"
+ " " << p << "\n"
+ "that contains invalid field '" << *fi << "'."
+ ;
+ /* clang-format on */
+ mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool cmGlobalVisualStudio8Generator::ProcessGeneratorPlatformField(
+ std::string const& key, std::string const& value)
+{
+ static_cast<void>(key);
+ static_cast<void>(value);
+ return false;
+}
+
+bool cmGlobalVisualStudio8Generator::InitializePlatform(cmMakefile*)
+{
+ return true;
+}
+
cm::optional<std::string> const&
cmGlobalVisualStudio8Generator::GetTargetFrameworkVersion() const
{
@@ -169,7 +259,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
cm::static_reference_cast<cmLocalVisualStudio7Generator>(generators[0]);
auto cc = cm::make_unique<cmCustomCommand>();
- cc->SetCMP0116Status(cmPolicies::NEW);
cmTarget* tgt = lg.AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false,
std::move(cc));
@@ -225,7 +314,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
cc->SetByproducts(byproducts);
cc->SetCommandLines(verifyCommandLines);
cc->SetComment("Checking File Globs");
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetStdPipesUTF8(stdPipesUTF8);
lg.AddCustomCommandToTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET,
cmCustomCommandType::PRE_BUILD,
@@ -260,7 +348,6 @@ bool cmGlobalVisualStudio8Generator::AddCheckTarget()
cc->SetDepends(listFiles);
cc->SetCommandLines(commandLines);
cc->SetComment("Checking Build System");
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetStdPipesUTF8(stdPipesUTF8);
if (cmSourceFile* file =
diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h
index fe57c545fd..5555e9b2de 100644
--- a/Source/cmGlobalVisualStudio8Generator.h
+++ b/Source/cmGlobalVisualStudio8Generator.h
@@ -60,6 +60,11 @@ protected:
cmGlobalVisualStudio8Generator(cmake* cm, const std::string& name,
std::string const& platformInGeneratorName);
+ virtual bool InitializePlatform(cmMakefile* mf);
+
+ virtual bool ProcessGeneratorPlatformField(std::string const& key,
+ std::string const& value);
+
void AddExtraIDETargets() override;
std::string FindDevEnvCommand() override;
@@ -96,4 +101,7 @@ protected:
cm::optional<std::string> DefaultTargetFrameworkVersion;
cm::optional<std::string> DefaultTargetFrameworkIdentifier;
cm::optional<std::string> DefaultTargetFrameworkTargetsVersion;
+
+private:
+ bool ParseGeneratorPlatform(std::string const& is, cmMakefile* mf);
};
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
index 9f6550bd72..e396405146 100644
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ b/Source/cmGlobalVisualStudio9Generator.cxx
@@ -64,7 +64,7 @@ public:
cmDocumentationEntry GetDocumentation() const override
{
return { std::string(vs9generatorName) + " [arch]",
- "Generates Visual Studio 2008 project files. "
+ "Deprecated. Generates Visual Studio 2008 project files. "
"Optional [arch] can be \"Win64\" or \"IA64\"." };
}
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 31f6f77fc3..702199d3e9 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -201,7 +201,6 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
// Use no actual command lines so that the target itself is not
// considered always out of date.
auto cc = cm::make_unique<cmCustomCommand>();
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetComment("Build all projects");
cmTarget* allBuild =
@@ -545,12 +544,12 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
cmSystemTools::ConvertToUnixSlashes(s1);
std::string keyname;
- HKEY hkey = NULL;
+ HKEY hkey = nullptr;
LONG result = ERROR_SUCCESS;
DWORD index = 0;
keyname = regKeyBase + "\\OtherProjects7";
- hkey = NULL;
+ hkey = nullptr;
result =
RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
0, KEY_READ, &hkey);
@@ -568,7 +567,7 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
RegEnumKeyExW(hkey, index, subkeyname, &cch_subkeyname, 0, keyclass,
&cch_keyclass, &lastWriteTime)) {
// Open the subkey and query the values of interest:
- HKEY hsubkey = NULL;
+ HKEY hsubkey = nullptr;
result = RegOpenKeyExW(hkey, subkeyname, 0, KEY_READ, &hsubkey);
if (ERROR_SUCCESS == result) {
DWORD valueType = REG_SZ;
@@ -642,7 +641,7 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
nextAvailableSubKeyName = std::to_string(index);
keyname = regKeyBase + "\\RecordingProject7";
- hkey = NULL;
+ hkey = nullptr;
result =
RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
0, KEY_READ, &hkey);
@@ -688,13 +687,13 @@ void WriteVSMacrosFileRegistryEntry(const std::string& nextAvailableSubKeyName,
const std::string& regKeyBase)
{
std::string keyname = regKeyBase + "\\OtherProjects7";
- HKEY hkey = NULL;
+ HKEY hkey = nullptr;
LONG result =
RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
0, KEY_READ | KEY_WRITE, &hkey);
if (ERROR_SUCCESS == result) {
// Create the subkey and set the values of interest:
- HKEY hsubkey = NULL;
+ HKEY hsubkey = nullptr;
wchar_t lpClass[] = L"";
result = RegCreateKeyExW(
hkey, cmsys::Encoding::ToWide(nextAvailableSubKeyName).c_str(), 0,
@@ -961,13 +960,13 @@ void cmGlobalVisualStudioGenerator::AddSymbolExportCommand(
static bool OpenSolution(std::string const& sln)
{
HRESULT comInitialized =
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED(comInitialized)) {
return false;
}
- HINSTANCE hi =
- ShellExecuteA(NULL, "open", sln.c_str(), NULL, NULL, SW_SHOWNORMAL);
+ HINSTANCE hi = ShellExecuteA(nullptr, "open", sln.c_str(), nullptr, nullptr,
+ SW_SHOWNORMAL);
CoUninitialize();
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 415eb7cf55..f28419a80c 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -885,7 +885,8 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
return AuxToolset::PropsMissing;
}
-bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
+bool cmGlobalVisualStudioVersionedGenerator::InitializePlatformWindows(
+ cmMakefile* mf)
{
// If the Win 8.1 SDK is installed then we can select a SDK matching
// the target Windows version.
@@ -894,13 +895,14 @@ bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
!cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
this->SetWindowsTargetPlatformVersion("8.1", mf);
- return true;
+ return this->VerifyNoGeneratorPlatformVersion(
+ mf, "with the Windows 8.1 SDK installed");
}
- return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
+ return cmGlobalVisualStudio14Generator::InitializePlatformWindows(mf);
}
// Otherwise we must choose a Win 10 SDK even if we are not targeting
// Windows 10.
- return this->SelectWindows10SDK(mf, false);
+ return this->SelectWindows10SDK(mf);
}
bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.h b/Source/cmGlobalVisualStudioVersionedGenerator.h
index 45aca74a80..fb4b1d7934 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.h
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.h
@@ -61,7 +61,6 @@ protected:
VSVersion version, cmake* cm, const std::string& name,
std::string const& platformInGeneratorName);
- bool InitializeWindows(cmMakefile* mf) override;
bool SelectWindowsStoreToolset(std::string& toolset) const override;
// Used to verify that the Desktop toolset for the current generator is
@@ -72,6 +71,8 @@ protected:
// of the toolset is installed
bool IsWindowsStoreToolsetInstalled() const;
+ bool InitializePlatformWindows(cmMakefile* mf) override;
+
// Check for a Win 8 SDK known to the registry or VS installer tool.
bool IsWin81SDKInstalled() const;
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 4746507bae..139635797f 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -615,7 +615,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetCommandLines(
cmMakeSingleCommandLine({ "echo", "Build all projects" }));
- cc->SetCMP0116Status(cmPolicies::NEW);
cmTarget* allbuild =
root->AddUtilityCommand("ALL_BUILD", true, std::move(cc));
@@ -655,7 +654,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
cmSystemTools::ReplaceString(file, "\\ ", " ");
cc = cm::make_unique<cmCustomCommand>();
cc->SetCommandLines(cmMakeSingleCommandLine({ "make", "-f", file }));
- cc->SetCMP0116Status(cmPolicies::NEW);
cmTarget* check = root->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET,
true, std::move(cc));
@@ -687,7 +685,6 @@ void cmGlobalXCodeGenerator::AddExtraTargets(
cc->SetCommandLines(legacyDependHelperCommandLines);
cc->SetComment("Depend check for xcode");
cc->SetWorkingDirectory(legacyDependHelperDir.c_str());
- cc->SetCMP0116Status(cmPolicies::NEW);
gen->AddCustomCommandToTarget(
target->GetName(), cmCustomCommandType::POST_BUILD, std::move(cc),
cmObjectLibraryCommands::Accept);
@@ -1742,7 +1739,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
std::string str_so_file =
cmStrCat("$<TARGET_SONAME_FILE:", gtgt->GetName(), '>');
std::string str_link_file =
- cmStrCat("$<TARGET_LINKER_FILE:", gtgt->GetName(), '>');
+ cmStrCat("$<TARGET_LINKER_LIBRARY_FILE:", gtgt->GetName(), '>');
cmCustomCommandLines cmd = cmMakeSingleCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library",
str_file, str_so_file, str_link_file });
@@ -1757,6 +1754,27 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
postbuild.push_back(std::move(command));
}
+ if (gtgt->HasImportLibrary("") && !gtgt->IsFrameworkOnApple()) {
+ // create symbolic links for .tbd file
+ std::string file = cmStrCat("$<TARGET_IMPORT_FILE:", gtgt->GetName(), '>');
+ std::string soFile =
+ cmStrCat("$<TARGET_SONAME_IMPORT_FILE:", gtgt->GetName(), '>');
+ std::string linkFile =
+ cmStrCat("$<TARGET_LINKER_IMPORT_FILE:", gtgt->GetName(), '>');
+ cmCustomCommandLines symlink_command = cmMakeSingleCommandLine(
+ { cmSystemTools::GetCMakeCommand(), "-E", "cmake_symlink_library", file,
+ soFile, linkFile });
+
+ cmCustomCommand command;
+ command.SetCommandLines(symlink_command);
+ command.SetComment("Creating import symlinks");
+ command.SetWorkingDirectory("");
+ command.SetBacktrace(this->CurrentMakefile->GetBacktrace());
+ command.SetStdPipesUTF8(true);
+
+ postbuild.push_back(std::move(command));
+ }
+
cmXCodeObject* legacyCustomCommandsBuildPhase = nullptr;
cmXCodeObject* preBuildPhase = nullptr;
cmXCodeObject* preLinkPhase = nullptr;
@@ -2495,8 +2513,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
}
// Set target-specific architectures.
- std::vector<std::string> archs;
- gtgt->GetAppleArchs(configName, archs);
+ std::vector<std::string> archs =
+ gtgt->GetAppleArchs(configName, cm::nullopt);
if (!archs.empty()) {
// Enable ARCHS attribute.
@@ -2685,6 +2703,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
buildSettings->AddAttribute("LIBRARY_STYLE",
this->CreateString("DYNAMIC"));
+
+ if (gtgt->HasImportLibrary(configName)) {
+ // Request .tbd file generation
+ buildSettings->AddAttribute("GENERATE_TEXT_BASED_STUBS",
+ this->CreateString("YES"));
+ }
break;
}
case cmStateEnums::EXECUTABLE: {
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx
index 92423448ca..3695a9b26d 100644
--- a/Source/cmIncludeCommand.cxx
+++ b/Source/cmIncludeCommand.cxx
@@ -20,7 +20,12 @@ bool cmIncludeCommand(std::vector<std::string> const& args,
{
static std::map<std::string, cmPolicies::PolicyID> DeprecatedModules;
if (DeprecatedModules.empty()) {
+ DeprecatedModules["Dart"] = cmPolicies::CMP0145;
DeprecatedModules["Documentation"] = cmPolicies::CMP0106;
+ DeprecatedModules["FindCUDA"] = cmPolicies::CMP0146;
+ DeprecatedModules["FindDart"] = cmPolicies::CMP0145;
+ DeprecatedModules["FindPythonInterp"] = cmPolicies::CMP0148;
+ DeprecatedModules["FindPythonLibs"] = cmPolicies::CMP0148;
DeprecatedModules["WriteCompilerDetectionHeader"] = cmPolicies::CMP0120;
}
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 82adca83ca..40230d9fb6 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -359,13 +359,15 @@ bool HandleScriptMode(std::vector<std::string> const& args,
} else if (doing_script) {
doing_script = false;
std::string script = arg;
- if (!cmSystemTools::FileIsFullPath(script)) {
- script =
- cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg);
- }
- if (cmSystemTools::FileIsDirectory(script)) {
- status.SetError("given a directory as value of SCRIPT argument.");
- return false;
+ if (!cmHasLiteralPrefix(script, "$<INSTALL_PREFIX>")) {
+ if (!cmSystemTools::FileIsFullPath(script)) {
+ script =
+ cmStrCat(helper.Makefile->GetCurrentSourceDirectory(), '/', arg);
+ }
+ if (cmSystemTools::FileIsDirectory(script)) {
+ status.SetError("given a directory as value of SCRIPT argument.");
+ return false;
+ }
}
helper.Makefile->AddInstallGenerator(
cm::make_unique<cmInstallScriptGenerator>(
@@ -551,34 +553,35 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
// Enforce argument rules too complex to specify for the
// general-purpose parser.
- if (archiveArgs.GetNamelinkOnly() || runtimeArgs.GetNamelinkOnly() ||
- objectArgs.GetNamelinkOnly() || frameworkArgs.GetNamelinkOnly() ||
- bundleArgs.GetNamelinkOnly() || privateHeaderArgs.GetNamelinkOnly() ||
+ if (runtimeArgs.GetNamelinkOnly() || objectArgs.GetNamelinkOnly() ||
+ frameworkArgs.GetNamelinkOnly() || bundleArgs.GetNamelinkOnly() ||
+ privateHeaderArgs.GetNamelinkOnly() ||
publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() ||
std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
[](const cmInstallCommandFileSetArguments& fileSetArg)
-> bool { return fileSetArg.GetNamelinkOnly(); }) ||
cxxModuleBmiArgs.GetNamelinkOnly()) {
status.SetError(
- "TARGETS given NAMELINK_ONLY option not in LIBRARY group. "
- "The NAMELINK_ONLY option may be specified only following LIBRARY.");
+ "TARGETS given NAMELINK_ONLY option not in LIBRARY or ARCHIVE group. "
+ "The NAMELINK_ONLY option may be specified only following LIBRARY or "
+ "ARCHIVE.");
return false;
}
- if (archiveArgs.GetNamelinkSkip() || runtimeArgs.GetNamelinkSkip() ||
- objectArgs.GetNamelinkSkip() || frameworkArgs.GetNamelinkSkip() ||
- bundleArgs.GetNamelinkSkip() || privateHeaderArgs.GetNamelinkSkip() ||
+ if (runtimeArgs.GetNamelinkSkip() || objectArgs.GetNamelinkSkip() ||
+ frameworkArgs.GetNamelinkSkip() || bundleArgs.GetNamelinkSkip() ||
+ privateHeaderArgs.GetNamelinkSkip() ||
publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() ||
std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
[](const cmInstallCommandFileSetArguments& fileSetArg)
-> bool { return fileSetArg.GetNamelinkSkip(); }) ||
cxxModuleBmiArgs.GetNamelinkSkip()) {
status.SetError(
- "TARGETS given NAMELINK_SKIP option not in LIBRARY group. "
- "The NAMELINK_SKIP option may be specified only following LIBRARY.");
+ "TARGETS given NAMELINK_SKIP option not in LIBRARY or ARCHIVE group. "
+ "The NAMELINK_SKIP option may be specified only following LIBRARY or "
+ "ARCHIVE.");
return false;
}
- if (archiveArgs.HasNamelinkComponent() ||
- runtimeArgs.HasNamelinkComponent() ||
+ if (runtimeArgs.HasNamelinkComponent() ||
objectArgs.HasNamelinkComponent() ||
frameworkArgs.HasNamelinkComponent() ||
bundleArgs.HasNamelinkComponent() ||
@@ -590,9 +593,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
-> bool { return fileSetArg.HasNamelinkComponent(); }) ||
cxxModuleBmiArgs.HasNamelinkComponent()) {
status.SetError(
- "TARGETS given NAMELINK_COMPONENT option not in LIBRARY group. "
- "The NAMELINK_COMPONENT option may be specified only following "
- "LIBRARY.");
+ "TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE "
+ "group. The NAMELINK_COMPONENT option may be specified only following "
+ "LIBRARY or ARCHIVE.");
return false;
}
if (libraryArgs.GetNamelinkOnly() && libraryArgs.GetNamelinkSkip()) {
@@ -672,6 +675,14 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
} else if (libraryArgs.GetNamelinkSkip()) {
namelinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
}
+ // Select the mode for installing symlinks to versioned imported libraries.
+ cmInstallTargetGenerator::NamelinkModeType importlinkMode =
+ cmInstallTargetGenerator::NamelinkModeNone;
+ if (archiveArgs.GetNamelinkOnly()) {
+ importlinkMode = cmInstallTargetGenerator::NamelinkModeOnly;
+ } else if (archiveArgs.GetNamelinkSkip()) {
+ importlinkMode = cmInstallTargetGenerator::NamelinkModeSkip;
+ }
// Check if there is something to do.
if (targetList.empty()) {
@@ -723,6 +734,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
bool installsArchive = false;
bool installsLibrary = false;
bool installsNamelink = false;
+ bool installsImportlink = false;
bool installsRuntime = false;
bool installsObject = false;
bool installsFramework = false;
@@ -740,6 +752,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
std::unique_ptr<cmInstallTargetGenerator> archiveGenerator;
std::unique_ptr<cmInstallTargetGenerator> libraryGenerator;
std::unique_ptr<cmInstallTargetGenerator> namelinkGenerator;
+ std::unique_ptr<cmInstallTargetGenerator> importlinkGenerator;
std::unique_ptr<cmInstallTargetGenerator> runtimeGenerator;
std::unique_ptr<cmInstallTargetGenerator> objectGenerator;
std::unique_ptr<cmInstallTargetGenerator> frameworkGenerator;
@@ -883,6 +896,32 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
}
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
+
+ if (target.GetMakefile()->PlatformSupportsAppleTextStubs() &&
+ target.IsSharedLibraryWithExports()) {
+ // Apple .tbd files use the ARCHIVE properties
+ if (!archiveArgs.GetDestination().empty()) {
+ artifactsSpecified = true;
+ }
+ if (importlinkMode !=
+ cmInstallTargetGenerator::NamelinkModeOnly) {
+ archiveGenerator = CreateInstallTargetGenerator(
+ target, archiveArgs, true, helper.Makefile->GetBacktrace(),
+ helper.GetLibraryDestination(&archiveArgs));
+ archiveGenerator->SetImportlinkMode(
+ cmInstallTargetGenerator::NamelinkModeSkip);
+ }
+ if (importlinkMode !=
+ cmInstallTargetGenerator::NamelinkModeSkip) {
+ importlinkGenerator = CreateInstallTargetGenerator(
+ target, archiveArgs, true, helper.Makefile->GetBacktrace(),
+ helper.GetLibraryDestination(&archiveArgs), false, true);
+ importlinkGenerator->SetImportlinkMode(
+ cmInstallTargetGenerator::NamelinkModeOnly);
+ }
+ namelinkOnly =
+ (importlinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
+ }
}
if (runtimeDependencySet && libraryGenerator) {
runtimeDependencySet->AddLibrary(libraryGenerator.get());
@@ -1155,6 +1194,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
installsArchive = installsArchive || archiveGenerator;
installsLibrary = installsLibrary || libraryGenerator;
installsNamelink = installsNamelink || namelinkGenerator;
+ installsImportlink = installsImportlink || importlinkGenerator;
installsRuntime = installsRuntime || runtimeGenerator;
installsObject = installsObject || objectGenerator;
installsFramework = installsFramework || frameworkGenerator;
@@ -1167,6 +1207,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
helper.Makefile->AddInstallGenerator(std::move(archiveGenerator));
helper.Makefile->AddInstallGenerator(std::move(libraryGenerator));
helper.Makefile->AddInstallGenerator(std::move(namelinkGenerator));
+ helper.Makefile->AddInstallGenerator(std::move(importlinkGenerator));
helper.Makefile->AddInstallGenerator(std::move(runtimeGenerator));
helper.Makefile->AddInstallGenerator(std::move(objectGenerator));
helper.Makefile->AddInstallGenerator(std::move(frameworkGenerator));
@@ -1201,6 +1242,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
libraryArgs.GetNamelinkComponent());
}
+ if (installsImportlink) {
+ helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
+ archiveArgs.GetNamelinkComponent());
+ }
if (installsRuntime) {
helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
runtimeArgs.GetComponent());
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.cxx b/Source/cmInstallRuntimeDependencySetGenerator.cxx
index 44f03e1af9..1e2e66377d 100644
--- a/Source/cmInstallRuntimeDependencySetGenerator.cxx
+++ b/Source/cmInstallRuntimeDependencySetGenerator.cxx
@@ -256,8 +256,7 @@ void cmInstallRuntimeDependencySetGenerator::GenerateStripFixup(
if (!strip.empty()) {
os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n"
<< indent.Next() << "execute_process(COMMAND \"" << strip << "\" ";
- if (this->LocalGenerator->GetMakefile()->GetSafeDefinition(
- "CMAKE_HOST_SYSTEM_NAME") == "Darwin") {
+ if (this->LocalGenerator->GetMakefile()->IsOn("APPLE")) {
os << "-x ";
}
os << "\""
diff --git a/Source/cmInstallScriptGenerator.cxx b/Source/cmInstallScriptGenerator.cxx
index a5625fe92f..af531f2463 100644
--- a/Source/cmInstallScriptGenerator.cxx
+++ b/Source/cmInstallScriptGenerator.cxx
@@ -56,12 +56,12 @@ bool cmInstallScriptGenerator::Compute(cmLocalGenerator* lg)
std::string cmInstallScriptGenerator::GetScript(
std::string const& config) const
{
- std::string script;
+ std::string script = this->Script;
if (this->AllowGenex && this->ActionsPerConfig) {
- script = cmGeneratorExpression::Evaluate(this->Script,
- this->LocalGenerator, config);
- } else {
- script = this->Script;
+ cmGeneratorExpression::ReplaceInstallPrefix(script,
+ "${CMAKE_INSTALL_PREFIX}");
+ script =
+ cmGeneratorExpression::Evaluate(script, this->LocalGenerator, config);
}
return script;
}
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 16c5002406..3ac100d612 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -4,12 +4,15 @@
#include <algorithm>
#include <cassert>
+#include <functional>
#include <map>
#include <set>
#include <sstream>
#include <utility>
#include <vector>
+#include <cm/optional>
+
#include "cmComputeLinkInformation.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
@@ -40,6 +43,84 @@ std::string computeInstallObjectDir(cmGeneratorTarget* gt,
objectDir += gt->GetName();
return objectDir;
}
+
+void computeFilesToInstall(
+ cmInstallTargetGenerator::Files& files,
+ cmInstallTargetGenerator::NamelinkModeType namelinkMode,
+ std::string const& fromDirConfig, std::string const& output,
+ std::string const& library, std::string const& real,
+ cm::optional<std::function<void(std::string const&)>> GNUToMS = cm::nullopt)
+{
+ bool haveNamelink = false;
+ auto convert = [&GNUToMS](std::string const& file) {
+ if (GNUToMS) {
+ (*GNUToMS)(file);
+ }
+ };
+
+ // Library link name.
+ std::string fromName = cmStrCat(fromDirConfig, output);
+ std::string toName = output;
+
+ // Library interface name.
+ std::string fromSOName;
+ std::string toSOName;
+ if (library != output) {
+ haveNamelink = true;
+ fromSOName = cmStrCat(fromDirConfig, library);
+ toSOName = library;
+ }
+
+ // Library implementation name.
+ std::string fromRealName;
+ std::string toRealName;
+ if (real != output && real != library) {
+ haveNamelink = true;
+ fromRealName = cmStrCat(fromDirConfig, real);
+ toRealName = real;
+ }
+
+ // Add the names based on the current namelink mode.
+ if (haveNamelink) {
+ files.NamelinkMode = namelinkMode;
+ // With a namelink we need to check the mode.
+ if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
+ // Install the namelink only.
+ files.From.emplace_back(fromName);
+ files.To.emplace_back(toName);
+ convert(output);
+ } else {
+ // Install the real file if it has its own name.
+ if (!fromRealName.empty()) {
+ files.From.emplace_back(fromRealName);
+ files.To.emplace_back(toRealName);
+ convert(real);
+ }
+
+ // Install the soname link if it has its own name.
+ if (!fromSOName.empty()) {
+ files.From.emplace_back(fromSOName);
+ files.To.emplace_back(toSOName);
+ convert(library);
+ }
+
+ // Install the namelink if it is not to be skipped.
+ if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
+ files.From.emplace_back(fromName);
+ files.To.emplace_back(toName);
+ convert(output);
+ }
+ }
+ } else {
+ // Without a namelink there will be only one file. Install it
+ // if this is not a namelink-only rule.
+ if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
+ files.From.emplace_back(fromName);
+ files.To.emplace_back(toName);
+ convert(output);
+ }
+ }
+}
}
cmInstallTargetGenerator::cmInstallTargetGenerator(
@@ -56,6 +137,7 @@ cmInstallTargetGenerator::cmInstallTargetGenerator(
{
this->ActionsPerConfig = true;
this->NamelinkMode = NamelinkModeNone;
+ this->ImportlinkMode = NamelinkModeNone;
}
cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
@@ -247,18 +329,21 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
this->Target->GetLibraryNames(config);
if (this->ImportLibrary) {
// There is a bug in cmInstallCommand if this fails.
- assert(this->NamelinkMode == NamelinkModeNone);
+ assert(this->Target->Makefile->PlatformSupportsAppleTextStubs() ||
+ this->ImportlinkMode == NamelinkModeNone);
+
+ auto GNUToMS = [this, &config, &files,
+ &fromDirConfig](const std::string& lib) {
+ std::string importLib;
+ if (this->Target->GetImplibGNUtoMS(config, lib, importLib)) {
+ files.From.emplace_back(fromDirConfig + importLib);
+ files.To.emplace_back(importLib);
+ }
+ };
- std::string from1 = fromDirConfig + targetNames.ImportLibrary;
- std::string to1 = targetNames.ImportLibrary;
- files.From.emplace_back(std::move(from1));
- files.To.emplace_back(std::move(to1));
- std::string targetNameImportLib;
- if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
- targetNameImportLib)) {
- files.From.emplace_back(fromDirConfig + targetNameImportLib);
- files.To.emplace_back(targetNameImportLib);
- }
+ computeFilesToInstall(
+ files, this->ImportlinkMode, fromDirConfig, targetNames.ImportOutput,
+ targetNames.ImportLibrary, targetNames.ImportReal, GNUToMS);
// An import library looks like a static library.
files.Type = cmInstallType_STATIC_LIBRARY;
@@ -318,66 +403,9 @@ cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
files.From.emplace_back(std::move(from1));
files.To.emplace_back(std::move(to1));
} else {
- bool haveNamelink = false;
-
- // Library link name.
- std::string fromName = fromDirConfig + targetNames.Output;
- std::string toName = targetNames.Output;
-
- // Library interface name.
- std::string fromSOName;
- std::string toSOName;
- if (targetNames.SharedObject != targetNames.Output) {
- haveNamelink = true;
- fromSOName = fromDirConfig + targetNames.SharedObject;
- toSOName = targetNames.SharedObject;
- }
-
- // Library implementation name.
- std::string fromRealName;
- std::string toRealName;
- if (targetNames.Real != targetNames.Output &&
- targetNames.Real != targetNames.SharedObject) {
- haveNamelink = true;
- fromRealName = fromDirConfig + targetNames.Real;
- toRealName = targetNames.Real;
- }
-
- // Add the names based on the current namelink mode.
- if (haveNamelink) {
- files.NamelinkMode = this->NamelinkMode;
- // With a namelink we need to check the mode.
- if (this->NamelinkMode == NamelinkModeOnly) {
- // Install the namelink only.
- files.From.emplace_back(fromName);
- files.To.emplace_back(toName);
- } else {
- // Install the real file if it has its own name.
- if (!fromRealName.empty()) {
- files.From.emplace_back(fromRealName);
- files.To.emplace_back(toRealName);
- }
-
- // Install the soname link if it has its own name.
- if (!fromSOName.empty()) {
- files.From.emplace_back(fromSOName);
- files.To.emplace_back(toSOName);
- }
-
- // Install the namelink if it is not to be skipped.
- if (this->NamelinkMode != NamelinkModeSkip) {
- files.From.emplace_back(fromName);
- files.To.emplace_back(toName);
- }
- }
- } else {
- // Without a namelink there will be only one file. Install it
- // if this is not a namelink-only rule.
- if (this->NamelinkMode != NamelinkModeOnly) {
- files.From.emplace_back(fromName);
- files.To.emplace_back(toName);
- }
- }
+ computeFilesToInstall(files, this->NamelinkMode, fromDirConfig,
+ targetNames.Output, targetNames.SharedObject,
+ targetNames.Real);
}
}
@@ -425,6 +453,12 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
"${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
fname = targetNames.ImportLibrary;
}
+ } else if (nameType == NameImplibReal) {
+ // Use the import library name.
+ if (!target->GetImplibGNUtoMS(config, targetNames.ImportReal, fname,
+ "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
+ fname = targetNames.ImportReal;
+ }
} else if (nameType == NameReal) {
// Use the canonical name.
fname = targetNames.Real;
@@ -434,11 +468,14 @@ std::string cmInstallTargetGenerator::GetInstallFilename(
}
} else {
cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
- if (nameType == NameImplib) {
+ if (nameType == NameImplib || nameType == NameImplibReal) {
+ const auto& importName = nameType == NameImplib
+ ? targetNames.ImportLibrary
+ : targetNames.ImportReal;
// Use the import library name.
- if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
+ if (!target->GetImplibGNUtoMS(config, importName, fname,
"${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
- fname = targetNames.ImportLibrary;
+ fname = importName;
}
} else if (nameType == NameSO) {
// Use the soname.
@@ -492,7 +529,7 @@ void cmInstallTargetGenerator::AddInstallNamePatchRule(
std::ostream& os, Indent indent, const std::string& config,
std::string const& toDestDirPath)
{
- if (this->ImportLibrary ||
+ if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
!(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
@@ -589,7 +626,8 @@ void cmInstallTargetGenerator::AddRPathCheckRule(
std::string const& toDestDirPath)
{
// Skip the chrpath if the target does not need it.
- if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
+ if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
+ !this->Target->IsChrpathUsed(config)) {
return;
}
// Skip if on Apple
@@ -640,7 +678,8 @@ void cmInstallTargetGenerator::AddChrpathPatchRule(
std::string const& toDestDirPath)
{
// Skip the chrpath if the target does not need it.
- if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
+ if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
+ !this->Target->IsChrpathUsed(config)) {
return;
}
@@ -779,36 +818,39 @@ void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
// don't strip static and import libraries, because it removes the only
// symbol table they have so you can't link to them anymore
if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
- this->ImportLibrary) {
+ this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly) {
return;
}
// Don't handle OSX Bundles.
- if (this->Target->Target->GetMakefile()->IsOn("APPLE") &&
+ if (this->Target->IsApple() &&
this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
return;
}
- if (!this->Target->Target->GetMakefile()->IsSet("CMAKE_STRIP")) {
+ std::string const& strip =
+ this->Target->Target->GetMakefile()->GetSafeDefinition("CMAKE_STRIP");
+ if (strip.empty()) {
return;
}
std::string stripArgs;
-
- // macOS 'strip' is picky, executables need '-u -r' and dylibs need '-x'.
- if (this->Target->Target->GetMakefile()->IsOn("APPLE")) {
+ if (this->Target->IsApple()) {
if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
+ // Strip tools need '-x' to strip Apple dylibs correctly.
stripArgs = "-x ";
- } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE) {
+ } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE &&
+ this->Target->GetGlobalGenerator()->GetStripCommandStyle(
+ strip) == cmGlobalGenerator::StripCommandStyle::Apple) {
+ // Apple's strip tool needs '-u -r' to strip executables correctly.
stripArgs = "-u -r ";
}
}
os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n";
- os << indent << " execute_process(COMMAND \""
- << this->Target->Target->GetMakefile()->GetSafeDefinition("CMAKE_STRIP")
- << "\" " << stripArgs << "\"" << toDestDirPath << "\")\n";
+ os << indent << " execute_process(COMMAND \"" << strip << "\" " << stripArgs
+ << "\"" << toDestDirPath << "\")\n";
os << indent << "endif()\n";
}
@@ -822,7 +864,7 @@ void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent,
// Perform post-installation processing on the file depending
// on its type.
- if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) {
+ if (!this->Target->IsApple()) {
return;
}
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index 3fc4b598ca..2f41163bc7 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -39,6 +39,10 @@ public:
NamelinkModeSkip
};
void SetNamelinkMode(NamelinkModeType mode) { this->NamelinkMode = mode; }
+ void SetImportlinkMode(NamelinkModeType mode)
+ {
+ this->ImportlinkMode = mode;
+ }
std::string GetInstallFilename(const std::string& config) const;
@@ -50,7 +54,8 @@ public:
NameNormal,
NameImplib,
NameSO,
- NameReal
+ NameReal,
+ NameImplibReal
};
static std::string GetInstallFilename(const cmGeneratorTarget* target,
@@ -121,6 +126,7 @@ protected:
cmGeneratorTarget* Target = nullptr;
std::string const FilePermissions;
NamelinkModeType NamelinkMode;
+ NamelinkModeType ImportlinkMode;
bool const ImportLibrary;
bool const Optional;
};
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
index f7151b52f8..94641deab0 100644
--- a/Source/cmJSONHelpers.h
+++ b/Source/cmJSONHelpers.h
@@ -2,9 +2,12 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
+#include "cmConfigure.h" // IWYU pragma: keep
+
#include <algorithm>
#include <cstddef>
#include <functional>
+#include <iostream>
#include <map>
#include <string>
#include <vector>
@@ -14,20 +17,129 @@
#include <cm3p/json/value.h>
-template <typename T, typename E, typename... CallState>
+#include "cmJSONState.h"
+
+template <typename T>
using cmJSONHelper =
- std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
+ std::function<bool(T& out, const Json::Value* value, cmJSONState* state)>;
+
+using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
+
+namespace JsonErrors {
+enum ObjectError
+{
+ RequiredMissing,
+ InvalidObject,
+ ExtraField,
+ MissingRequired
+};
+using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
+using ObjectErrorGenerator =
+ std::function<ErrorGenerator(ObjectError, const Json::Value::Members&)>;
+const auto EXPECTED_TYPE = [](const std::string& type) {
+ return [type](const Json::Value* value, cmJSONState* state) -> void {
+#if !defined(CMAKE_BOOTSTRAP)
+ if (state->key().empty()) {
+ state->AddErrorAtValue(cmStrCat("Expected ", type), value);
+ return;
+ }
+ std::string errMsg = cmStrCat("\"", state->key(), "\" expected ", type);
+ if (value && value->isConvertibleTo(Json::ValueType::stringValue)) {
+ errMsg = cmStrCat(errMsg, ", got: ", value->asString());
+ }
+ state->AddErrorAtValue(errMsg, value);
+#endif
+ };
+};
+const auto INVALID_STRING = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("a string")(value, state);
+};
+const auto INVALID_BOOL = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("a bool")(value, state);
+};
+const auto INVALID_INT = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("an integer")(value, state);
+};
+const auto INVALID_UINT = [](const Json::Value* value,
+ cmJSONState* state) -> void {
+ JsonErrors::EXPECTED_TYPE("an unsigned integer")(value, state);
+};
+const auto INVALID_NAMED_OBJECT =
+ [](const std::function<std::string(const Json::Value*, cmJSONState*)>&
+ nameGenerator) -> ObjectErrorGenerator {
+ return [nameGenerator](
+ ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return [nameGenerator, errorType, extraFields](
+ const Json::Value* value, cmJSONState* state) -> void {
+#if !defined(CMAKE_BOOTSTRAP)
+ std::string name = nameGenerator(value, state);
+ switch (errorType) {
+ case ObjectError::RequiredMissing:
+ state->AddErrorAtValue(cmStrCat("Invalid Required ", name), value);
+ break;
+ case ObjectError::InvalidObject:
+ state->AddErrorAtValue(cmStrCat("Invalid ", name), value);
+ break;
+ case ObjectError::ExtraField: {
+ for (auto const& member : extraFields) {
+ if (value) {
+ state->AddErrorAtValue(
+ cmStrCat("Invalid extra field \"", member, "\" in ", name),
+ &(*value)[member]);
+ } else {
+ state->AddError(
+ cmStrCat("Invalid extra field \"", member, "\" in ", name));
+ }
+ }
+ } break;
+ case ObjectError::MissingRequired:
+ state->AddErrorAtValue(cmStrCat("Missing required field \"",
+ state->key(), "\" in ", name),
+ value);
+ break;
+ }
+#endif
+ };
+ };
+};
+const auto INVALID_OBJECT =
+ [](ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState*) -> std::string { return "Object"; })(
+ errorType, extraFields);
+};
+const auto INVALID_NAMED_OBJECT_KEY =
+ [](ObjectError errorType,
+ const Json::Value::Members& extraFields) -> ErrorGenerator {
+ return INVALID_NAMED_OBJECT(
+ [](const Json::Value*, cmJSONState* state) -> std::string {
+ for (auto it = state->parseStack.rbegin();
+ it != state->parseStack.rend(); ++it) {
+ if (it->first.rfind("$vector_item_", 0) == 0) {
+ continue;
+ }
+ return cmStrCat("\"", it->first, "\"");
+ }
+ return "root";
+ })(errorType, extraFields);
+};
+}
-template <typename E, typename... CallState>
struct cmJSONHelperBuilder
{
+
template <typename T>
class Object
{
public:
- Object(E&& success, E&& fail, bool allowExtra = true)
- : Success(std::move(success))
- , Fail(std::move(fail))
+ Object(JsonErrors::ObjectErrorGenerator error = JsonErrors::INVALID_OBJECT,
+ bool allowExtra = true)
+ : Error(std::move(error))
, AllowExtra(allowExtra)
{
}
@@ -38,8 +150,8 @@ struct cmJSONHelperBuilder
{
return this->BindPrivate(
name,
- [func, member](T& out, const Json::Value* value, CallState&&... state)
- -> E { return func(out.*member, value, std::forward(state)...); },
+ [func, member](T& out, const Json::Value* value, cmJSONState* state)
+ -> bool { return func(out.*member, value, state); },
required);
}
template <typename M, typename F>
@@ -49,9 +161,9 @@ struct cmJSONHelperBuilder
return this->BindPrivate(
name,
[func](T& /*out*/, const Json::Value* value,
- CallState&&... state) -> E {
+ cmJSONState* state) -> bool {
M dummy;
- return func(dummy, value, std::forward(state)...);
+ return func(dummy, value, state);
},
required);
}
@@ -61,46 +173,56 @@ struct cmJSONHelperBuilder
return this->BindPrivate(name, MemberFunction(func), required);
}
- E operator()(T& out, const Json::Value* value, CallState&&... state) const
+ bool operator()(T& out, const Json::Value* value, cmJSONState* state) const
{
+ Json::Value::Members extraFields;
+ bool success = true;
if (!value && this->AnyRequired) {
- return this->Fail;
+ Error(JsonErrors::ObjectError::RequiredMissing, extraFields)(value,
+ state);
+ return false;
}
if (value && !value->isObject()) {
- return this->Fail;
+ Error(JsonErrors::ObjectError::InvalidObject, extraFields)(value,
+ state);
+ return false;
}
- Json::Value::Members extraFields;
if (value) {
extraFields = value->getMemberNames();
}
for (auto const& m : this->Members) {
std::string name(m.Name.data(), m.Name.size());
+ state->push_stack(name, value);
if (value && value->isMember(name)) {
- E result = m.Function(out, &(*value)[name], std::forward(state)...);
- if (result != this->Success) {
- return result;
+ if (!m.Function(out, &(*value)[name], state)) {
+ success = false;
}
extraFields.erase(
std::find(extraFields.begin(), extraFields.end(), name));
} else if (!m.Required) {
- E result = m.Function(out, nullptr, std::forward(state)...);
- if (result != this->Success) {
- return result;
+ if (!m.Function(out, nullptr, state)) {
+ success = false;
}
} else {
- return this->Fail;
+ Error(JsonErrors::ObjectError::MissingRequired, extraFields)(value,
+ state);
+ success = false;
}
+ state->pop_stack();
}
- return this->AllowExtra || extraFields.empty() ? this->Success
- : this->Fail;
+ if (!this->AllowExtra && !extraFields.empty()) {
+ Error(JsonErrors::ObjectError::ExtraField, extraFields)(value, state);
+ success = false;
+ }
+ return success;
}
private:
// Not a true cmJSONHelper, it just happens to match the signature
- using MemberFunction =
- std::function<E(T& out, const Json::Value* value, CallState&&... state)>;
+ using MemberFunction = std::function<bool(T& out, const Json::Value* value,
+ cmJSONState* state)>;
struct Member
{
cm::string_view Name;
@@ -109,8 +231,7 @@ struct cmJSONHelperBuilder
};
std::vector<Member> Members;
bool AnyRequired = false;
- E Success;
- E Fail;
+ JsonErrors::ObjectErrorGenerator Error;
bool AllowExtra;
Object& BindPrivate(const cm::string_view& name, MemberFunction&& func,
@@ -127,175 +248,218 @@ struct cmJSONHelperBuilder
return *this;
}
};
- static cmJSONHelper<std::string, E, CallState...> String(
- E success, E fail, const std::string& defval = "")
+
+ static cmJSONHelper<std::string> String(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_STRING,
+ const std::string& defval = "")
{
- return [success, fail, defval](std::string& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](std::string& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isString()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asString();
- return success;
+ return true;
};
- }
+ };
+
+ static cmJSONHelper<std::string> String(const std::string& defval)
+ {
+ return String(JsonErrors::INVALID_STRING, defval);
+ };
- static cmJSONHelper<int, E, CallState...> Int(E success, E fail,
- int defval = 0)
+ static cmJSONHelper<int> Int(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_INT,
+ int defval = 0)
{
- return [success, fail, defval](int& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](int& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isInt()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asInt();
- return success;
+ return true;
};
}
- static cmJSONHelper<unsigned int, E, CallState...> UInt(
- E success, E fail, unsigned int defval = 0)
+ static cmJSONHelper<int> Int(int defval)
+ {
+ return Int(JsonErrors::INVALID_INT, defval);
+ };
+
+ static cmJSONHelper<unsigned int> UInt(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_UINT,
+ unsigned int defval = 0)
{
- return [success, fail, defval](unsigned int& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](unsigned int& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isUInt()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asUInt();
- return success;
+ return true;
};
}
- static cmJSONHelper<bool, E, CallState...> Bool(E success, E fail,
- bool defval = false)
+ static cmJSONHelper<unsigned int> UInt(unsigned int defval)
+ {
+ return UInt(JsonErrors::INVALID_UINT, defval);
+ }
+
+ static cmJSONHelper<bool> Bool(
+ const JsonErrors::ErrorGenerator& error = JsonErrors::INVALID_BOOL,
+ bool defval = false)
{
- return [success, fail, defval](bool& out, const Json::Value* value,
- CallState&&... /*state*/) -> E {
+ return [error, defval](bool& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out = defval;
- return success;
+ return true;
}
if (!value->isBool()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out = value->asBool();
- return success;
+ return true;
};
}
+ static cmJSONHelper<bool> Bool(bool defval)
+ {
+ return Bool(JsonErrors::INVALID_BOOL, defval);
+ }
+
template <typename T, typename F, typename Filter>
- static cmJSONHelper<std::vector<T>, E, CallState...> VectorFilter(
- E success, E fail, F func, Filter filter)
+ static cmJSONHelper<std::vector<T>> VectorFilter(
+ const JsonErrors::ErrorGenerator& error, F func, Filter filter)
{
- return [success, fail, func, filter](std::vector<T>& out,
- const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func, filter](std::vector<T>& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
+ bool success = true;
if (!value) {
out.clear();
- return success;
+ return true;
}
if (!value->isArray()) {
- return fail;
+ error(value, state);
+ return false;
}
out.clear();
+ int index = 0;
for (auto const& item : *value) {
+ state->push_stack(cmStrCat("$vector_item_", index++), &item);
T t;
- E result = func(t, &item, std::forward(state)...);
- if (result != success) {
- return result;
+ if (!func(t, &item, state)) {
+ success = false;
}
if (!filter(t)) {
+ state->pop_stack();
continue;
}
out.push_back(std::move(t));
+ state->pop_stack();
}
return success;
};
}
template <typename T, typename F>
- static cmJSONHelper<std::vector<T>, E, CallState...> Vector(E success,
- E fail, F func)
+ static cmJSONHelper<std::vector<T>> Vector(JsonErrors::ErrorGenerator error,
+ F func)
{
- return VectorFilter<T, F>(success, fail, func,
+ return VectorFilter<T, F>(std::move(error), func,
[](const T&) { return true; });
}
template <typename T, typename F, typename Filter>
- static cmJSONHelper<std::map<std::string, T>, E, CallState...> MapFilter(
- E success, E fail, F func, Filter filter)
+ static cmJSONHelper<std::map<std::string, T>> MapFilter(
+ const JsonErrors::ErrorGenerator& error, F func, Filter filter)
{
- return [success, fail, func, filter](std::map<std::string, T>& out,
- const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func, filter](std::map<std::string, T>& out,
+ const Json::Value* value,
+ cmJSONState* state) -> bool {
+ bool success = true;
if (!value) {
out.clear();
- return success;
+ return true;
}
if (!value->isObject()) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
out.clear();
for (auto const& key : value->getMemberNames()) {
+ state->push_stack(cmStrCat(key, ""), &(*value)[key]);
if (!filter(key)) {
+ state->pop_stack();
continue;
}
T t;
- E result = func(t, &(*value)[key], std::forward(state)...);
- if (result != success) {
- return result;
+ if (!func(t, &(*value)[key], state)) {
+ success = false;
}
out[key] = std::move(t);
+ state->pop_stack();
}
return success;
};
}
template <typename T, typename F>
- static cmJSONHelper<std::map<std::string, T>, E, CallState...> Map(E success,
- E fail,
- F func)
+ static cmJSONHelper<std::map<std::string, T>> Map(
+ const JsonErrors::ErrorGenerator& error, F func)
{
- return MapFilter<T, F>(success, fail, func,
+ return MapFilter<T, F>(error, func,
[](const std::string&) { return true; });
}
template <typename T, typename F>
- static cmJSONHelper<cm::optional<T>, E, CallState...> Optional(E success,
- F func)
+ static cmJSONHelper<cm::optional<T>> Optional(F func)
{
- return [success, func](cm::optional<T>& out, const Json::Value* value,
- CallState&&... state) -> E {
+ return [func](cm::optional<T>& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
out.reset();
- return success;
+ return true;
}
out.emplace();
- return func(*out, value, std::forward(state)...);
+ return func(*out, value, state);
};
}
template <typename T, typename F>
- static cmJSONHelper<T, E, CallState...> Required(E fail, F func)
+ static cmJSONHelper<T> Required(const JsonErrors::ErrorGenerator& error,
+ F func)
{
- return [fail, func](T& out, const Json::Value* value,
- CallState&&... state) -> E {
+ return [error, func](T& out, const Json::Value* value,
+ cmJSONState* state) -> bool {
if (!value) {
- return fail;
+ error(value, state);
+ ;
+ return false;
}
- return func(out, value, std::forward(state)...);
+ return func(out, value, state);
};
}
};
diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx
new file mode 100644
index 0000000000..92bde778a7
--- /dev/null
+++ b/Source/cmJSONState.cxx
@@ -0,0 +1,163 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmJSONState.h"
+
+#include <sstream>
+
+#include <cm/memory>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmStringAlgorithms.h"
+
+cmJSONState::cmJSONState(const std::string& filename, Json::Value* root)
+{
+ cmsys::ifstream fin(filename.c_str(), std::ios::in | std::ios::binary);
+ if (!fin) {
+ this->AddError(cmStrCat("File not found: ", filename));
+ return;
+ }
+ // If there's a BOM, toss it.
+ cmsys::FStream::ReadBOM(fin);
+
+ // Save the entire document.
+ std::streampos finBegin = fin.tellg();
+ this->doc = std::string(std::istreambuf_iterator<char>(fin),
+ std::istreambuf_iterator<char>());
+ if (this->doc.empty()) {
+ this->AddError("A JSON document cannot be empty");
+ return;
+ }
+ fin.seekg(finBegin);
+
+ // Parse the document.
+ Json::CharReaderBuilder builder;
+ Json::CharReaderBuilder::strictMode(&builder.settings_);
+ std::string errMsg;
+ if (!Json::parseFromStream(builder, fin, root, &errMsg)) {
+ errMsg = cmStrCat("JSON Parse Error: ", filename, ":\n", errMsg);
+ this->AddError(errMsg);
+ }
+}
+
+void cmJSONState::AddError(std::string const& errMsg)
+{
+ this->errors.push_back(Error(errMsg));
+}
+
+void cmJSONState::AddErrorAtValue(std::string const& errMsg,
+ const Json::Value* value)
+{
+ if (value && !value->isNull()) {
+ this->AddErrorAtOffset(errMsg, value->getOffsetStart());
+ } else {
+ this->AddError(errMsg);
+ }
+}
+
+void cmJSONState::AddErrorAtOffset(std::string const& errMsg,
+ std::ptrdiff_t offset)
+{
+ if (doc.empty()) {
+ this->AddError(errMsg);
+ } else {
+ Location loc = LocateInDocument(offset);
+ this->errors.push_back(Error(loc, errMsg));
+ }
+}
+
+std::string cmJSONState::GetErrorMessage(bool showContext)
+{
+ std::string message;
+ for (auto const& error : this->errors) {
+ message = cmStrCat(message, error.GetErrorMessage(), "\n");
+ if (showContext) {
+ Location loc = error.GetLocation();
+ if (loc.column > 0) {
+ message = cmStrCat(message, GetJsonContext(loc), "\n");
+ }
+ }
+ }
+ message = cmStrCat("\n", message);
+ message.pop_back();
+ return message;
+}
+
+std::string cmJSONState::key()
+{
+ if (!this->parseStack.empty()) {
+ return this->parseStack.back().first;
+ }
+ return "";
+}
+
+std::string cmJSONState::key_after(std::string const& k)
+{
+ for (auto it = this->parseStack.begin(); it != this->parseStack.end();
+ ++it) {
+ if (it->first == k && (++it) != this->parseStack.end()) {
+ return it->first;
+ }
+ }
+ return "";
+}
+
+const Json::Value* cmJSONState::value_after(std::string const& k)
+{
+ for (auto it = this->parseStack.begin(); it != this->parseStack.end();
+ ++it) {
+ if (it->first == k && (++it) != this->parseStack.end()) {
+ return it->second;
+ }
+ }
+ return nullptr;
+}
+
+void cmJSONState::push_stack(std::string const& k, const Json::Value* value)
+{
+ this->parseStack.push_back(JsonPair(k, value));
+}
+
+void cmJSONState::pop_stack()
+{
+ this->parseStack.pop_back();
+}
+
+std::string cmJSONState::GetJsonContext(Location loc)
+{
+ std::string line;
+ std::stringstream sstream(doc);
+ for (int i = 0; i < loc.line; ++i) {
+ std::getline(sstream, line, '\n');
+ }
+ return cmStrCat(line, '\n', std::string(loc.column - 1, ' '), '^');
+}
+
+cmJSONState::Location cmJSONState::LocateInDocument(ptrdiff_t offset)
+{
+ int line = 1;
+ int col = 1;
+ const char* beginDoc = doc.data();
+ const char* last = beginDoc + offset;
+ for (; beginDoc != last; ++beginDoc) {
+ switch (*beginDoc) {
+ case '\r':
+ if (beginDoc + 1 != last && beginDoc[1] == '\n') {
+ continue; // consume CRLF as a single token.
+ }
+ CM_FALLTHROUGH; // CR without a following LF is same as LF
+ case '\n':
+ col = 1;
+ ++line;
+ break;
+ default:
+ ++col;
+ break;
+ }
+ }
+ return { line, col };
+}
diff --git a/Source/cmJSONState.h b/Source/cmJSONState.h
new file mode 100644
index 0000000000..4984c81b34
--- /dev/null
+++ b/Source/cmJSONState.h
@@ -0,0 +1,73 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <cstddef>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cmJSONState.h"
+#include "cmStringAlgorithms.h"
+
+namespace Json {
+class Value;
+}
+
+class cmJSONState
+{
+ using Location = struct
+ {
+ int line;
+ int column;
+ };
+
+public:
+ using JsonPair = std::pair<const std::string, const Json::Value*>;
+ cmJSONState() = default;
+ cmJSONState(const std::string& filename, Json::Value* root);
+ void AddError(std::string const& errMsg);
+ void AddErrorAtValue(std::string const& errMsg, const Json::Value* value);
+ void AddErrorAtOffset(std::string const& errMsg, std::ptrdiff_t offset);
+ std::string GetErrorMessage(bool showContext = true);
+ std::string key();
+ std::string key_after(std::string const& key);
+ const Json::Value* value_after(std::string const& key);
+ void push_stack(std::string const& key, const Json::Value* value);
+ void pop_stack();
+
+ class Error
+ {
+ public:
+ Error(Location loc, std::string errMsg)
+ : location(loc)
+ , message(std::move(errMsg)){};
+ Error(std::string errMsg)
+ : location({ -1, -1 })
+ , message(std::move(errMsg)){};
+ std::string GetErrorMessage() const
+ {
+ std::string output = message;
+ if (location.line > 0) {
+ output = cmStrCat("Error: @", location.line, ",", location.column,
+ ": ", output);
+ }
+ return output;
+ }
+ Location GetLocation() const { return location; }
+
+ private:
+ Location location;
+ std::string message;
+ };
+
+ std::vector<JsonPair> parseStack;
+ std::vector<Error> errors;
+ std::string doc;
+
+private:
+ std::string GetJsonContext(Location loc);
+ Location LocateInDocument(ptrdiff_t offset);
+};
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
new file mode 100644
index 0000000000..bf5a65459a
--- /dev/null
+++ b/Source/cmList.cxx
@@ -0,0 +1,998 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmList.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <set>
+#include <stdexcept>
+#include <utility>
+
+#include <cm/memory>
+
+#include "cmsys/RegularExpression.hxx"
+
+#include "cmAlgorithms.h"
+#include "cmGeneratorExpression.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmStringReplaceHelper.h"
+#include "cmSystemTools.h"
+
+cm::string_view cmList::element_separator{ ";" };
+
+cmList cmList::sublist(size_type pos, size_type length) const
+{
+ if (pos >= this->Values.size()) {
+ throw std::out_of_range(cmStrCat(
+ "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1));
+ }
+
+ size_type count = (length == npos || pos + length > this->size())
+ ? this->size()
+ : pos + length;
+ return this->sublist(this->begin() + pos, this->begin() + count);
+}
+
+cmList::size_type cmList::find(cm::string_view value) const
+{
+ auto res = std::find(this->Values.begin(), this->Values.end(), value);
+ if (res == this->Values.end()) {
+ return npos;
+ }
+
+ return std::distance(this->Values.begin(), res);
+}
+
+cmList& cmList::remove_duplicates()
+{
+ auto newEnd = cmRemoveDuplicates(this->Values);
+ this->Values.erase(newEnd, this->Values.end());
+
+ return *this;
+}
+
+namespace {
+class MatchesRegex
+{
+public:
+ MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode)
+ : Regex(regex)
+ , IncludeMatches(mode == cmList::FilterMode::INCLUDE)
+ {
+ }
+
+ bool operator()(const std::string& target)
+ {
+ return this->Regex.find(target) ^ this->IncludeMatches;
+ }
+
+private:
+ cmsys::RegularExpression& Regex;
+ const bool IncludeMatches;
+};
+}
+
+cmList& cmList::filter(cm::string_view pattern, FilterMode mode)
+{
+ cmsys::RegularExpression regex(std::string{ pattern });
+ if (!regex.is_valid()) {
+ throw std::invalid_argument(
+ cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
+ pattern, "\"."));
+ }
+
+ auto it = std::remove_if(this->Values.begin(), this->Values.end(),
+ MatchesRegex{ regex, mode });
+ this->Values.erase(it, this->Values.end());
+
+ return *this;
+}
+
+namespace {
+class StringSorter
+{
+protected:
+ using StringFilter = std::function<std::string(const std::string&)>;
+
+ using OrderMode = cmList::SortConfiguration::OrderMode;
+ using CompareMethod = cmList::SortConfiguration::CompareMethod;
+ using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity;
+
+ StringFilter GetCompareFilter(CompareMethod compare)
+ {
+ return (compare == CompareMethod::FILE_BASENAME)
+ ? cmSystemTools::GetFilenameName
+ : nullptr;
+ }
+
+ StringFilter GetCaseFilter(CaseSensitivity sensitivity)
+ {
+ return (sensitivity == CaseSensitivity::INSENSITIVE)
+ ? cmSystemTools::LowerCase
+ : nullptr;
+ }
+
+ using ComparisonFunction =
+ std::function<bool(const std::string&, const std::string&)>;
+ ComparisonFunction GetComparisonFunction(CompareMethod compare)
+ {
+ if (compare == CompareMethod::NATURAL) {
+ return std::function<bool(const std::string&, const std::string&)>(
+ [](const std::string& x, const std::string& y) {
+ return cmSystemTools::strverscmp(x, y) < 0;
+ });
+ }
+ return std::function<bool(const std::string&, const std::string&)>(
+ [](const std::string& x, const std::string& y) { return x < y; });
+ }
+
+public:
+ StringSorter(cmList::SortConfiguration const& config)
+ : Filters{ this->GetCompareFilter(config.Compare),
+ this->GetCaseFilter(config.Case) }
+ , SortMethod(this->GetComparisonFunction(config.Compare))
+ , Descending(config.Order == OrderMode::DESCENDING)
+ {
+ }
+
+ std::string ApplyFilter(const std::string& argument)
+ {
+ std::string result = argument;
+ for (auto const& filter : this->Filters) {
+ if (filter != nullptr) {
+ result = filter(result);
+ }
+ }
+ return result;
+ }
+
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ std::string af = this->ApplyFilter(a);
+ std::string bf = this->ApplyFilter(b);
+ bool result;
+ if (this->Descending) {
+ result = this->SortMethod(bf, af);
+ } else {
+ result = this->SortMethod(af, bf);
+ }
+ return result;
+ }
+
+private:
+ StringFilter Filters[2] = { nullptr, nullptr };
+ ComparisonFunction SortMethod;
+ bool Descending;
+};
+}
+
+cmList::SortConfiguration::SortConfiguration() = default;
+
+cmList& cmList::sort(const SortConfiguration& cfg)
+{
+ SortConfiguration config{ cfg };
+
+ if (config.Order == SortConfiguration::OrderMode::DEFAULT) {
+ config.Order = SortConfiguration::OrderMode::ASCENDING;
+ }
+ if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) {
+ config.Compare = SortConfiguration::CompareMethod::STRING;
+ }
+ if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) {
+ config.Case = SortConfiguration::CaseSensitivity::SENSITIVE;
+ }
+
+ if ((config.Compare == SortConfiguration::CompareMethod::STRING) &&
+ (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) &&
+ (config.Order == SortConfiguration::OrderMode::ASCENDING)) {
+ std::sort(this->Values.begin(), this->Values.end());
+ } else {
+ StringSorter sorter(config);
+ std::sort(this->Values.begin(), this->Values.end(), sorter);
+ }
+
+ return *this;
+}
+
+namespace {
+using transform_type = std::function<std::string(const std::string&)>;
+using transform_error = cmList::transform_error;
+
+class TransformSelector : public cmList::TransformSelector
+{
+public:
+ ~TransformSelector() override = default;
+
+ std::string Tag;
+
+ const std::string& GetTag() override { return this->Tag; }
+
+ virtual bool Validate(std::size_t count = 0) = 0;
+
+ virtual bool InSelection(const std::string&) = 0;
+
+ virtual void Transform(cmList::container_type& list,
+ const transform_type& transform)
+ {
+ std::transform(list.begin(), list.end(), list.begin(), transform);
+ }
+
+protected:
+ TransformSelector(std::string&& tag)
+ : Tag(std::move(tag))
+ {
+ }
+};
+
+class TransformNoSelector : public TransformSelector
+{
+public:
+ TransformNoSelector()
+ : TransformSelector("NO SELECTOR")
+ {
+ }
+
+ bool Validate(std::size_t) override { return true; }
+
+ bool InSelection(const std::string&) override { return true; }
+};
+class TransformSelectorRegex : public TransformSelector
+{
+public:
+ TransformSelectorRegex(const std::string& regex)
+ : TransformSelector("REGEX")
+ , Regex(regex)
+ {
+ }
+ TransformSelectorRegex(std::string&& regex)
+ : TransformSelector("REGEX")
+ , Regex(regex)
+ {
+ }
+
+ bool Validate(std::size_t) override { return this->Regex.is_valid(); }
+
+ bool InSelection(const std::string& value) override
+ {
+ return this->Regex.find(value);
+ }
+
+ cmsys::RegularExpression Regex;
+};
+class TransformSelectorIndexes : public TransformSelector
+{
+public:
+ std::vector<index_type> Indexes;
+
+ bool InSelection(const std::string&) override { return true; }
+
+ void Transform(std::vector<std::string>& list,
+ const transform_type& transform) override
+ {
+ this->Validate(list.size());
+
+ for (auto index : this->Indexes) {
+ list[index] = transform(list[index]);
+ }
+ }
+
+protected:
+ TransformSelectorIndexes(std::string&& tag)
+ : TransformSelector(std::move(tag))
+ {
+ }
+ TransformSelectorIndexes(std::string&& tag, std::vector<int> const& indexes)
+ : TransformSelector(std::move(tag))
+ , Indexes(indexes)
+ {
+ }
+ TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
+ : TransformSelector(std::move(tag))
+ , Indexes(indexes)
+ {
+ }
+
+ int NormalizeIndex(index_type index, std::size_t count)
+ {
+ if (index < 0) {
+ index = static_cast<index_type>(count) + index;
+ }
+ if (index < 0 || count <= static_cast<std::size_t>(index)) {
+ throw transform_error(cmStrCat(
+ "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
+ " out of range (-", count, ", ", count - 1, ")."));
+ }
+ return index;
+ }
+};
+class TransformSelectorAt : public TransformSelectorIndexes
+{
+public:
+ TransformSelectorAt(std::vector<index_type> const& indexes)
+ : TransformSelectorIndexes("AT", indexes)
+ {
+ }
+ TransformSelectorAt(std::vector<index_type>&& indexes)
+ : TransformSelectorIndexes("AT", std::move(indexes))
+ {
+ }
+
+ bool Validate(std::size_t count) override
+ {
+ decltype(this->Indexes) indexes;
+
+ for (auto index : this->Indexes) {
+ indexes.push_back(this->NormalizeIndex(index, count));
+ }
+ this->Indexes = std::move(indexes);
+
+ return true;
+ }
+};
+class TransformSelectorFor : public TransformSelectorIndexes
+{
+public:
+ TransformSelectorFor(int start, int stop, int step)
+ : TransformSelectorIndexes("FOR")
+ , Start(start)
+ , Stop(stop)
+ , Step(step)
+ {
+ }
+
+ bool Validate(std::size_t count) override
+ {
+ this->Start = this->NormalizeIndex(this->Start, count);
+ this->Stop = this->NormalizeIndex(this->Stop, count);
+
+ // Does stepping move us further from the end?
+ if (this->Start > this->Stop) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, selector FOR "
+ "expects <start> to be no greater than <stop> (",
+ this->Start, " > ", this->Stop, ")"));
+ }
+
+ // compute indexes
+ auto size = (this->Stop - this->Start + 1) / this->Step;
+ if ((this->Stop - this->Start + 1) % this->Step != 0) {
+ size += 1;
+ }
+
+ this->Indexes.resize(size);
+ auto start = this->Start;
+ auto step = this->Step;
+ std::generate(this->Indexes.begin(), this->Indexes.end(),
+ [&start, step]() -> int {
+ auto r = start;
+ start += step;
+ return r;
+ });
+
+ return true;
+ }
+
+private:
+ index_type Start, Stop, Step;
+};
+
+class TransformAction
+{
+public:
+ virtual ~TransformAction() = default;
+
+ void Initialize(TransformSelector* selector) { this->Selector = selector; }
+ virtual void Initialize(TransformSelector*, const std::string&) {}
+ virtual void Initialize(TransformSelector*, const std::string&,
+ const std::string&)
+ {
+ }
+ virtual void Initialize(TransformSelector* selector,
+ const std::vector<std::string>&)
+ {
+ this->Initialize(selector);
+ }
+
+ virtual std::string operator()(const std::string& s) = 0;
+
+protected:
+ TransformSelector* Selector;
+};
+class TransformActionAppend : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector,
+ const std::string& append) override
+ {
+ TransformAction::Initialize(selector);
+ this->Append = append;
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& append) override
+ {
+ this->Initialize(selector, append.front());
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmStrCat(s, this->Append);
+ }
+
+ return s;
+ }
+
+private:
+ std::string Append;
+};
+class TransformActionPrepend : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector,
+ const std::string& prepend) override
+ {
+ TransformAction::Initialize(selector);
+ this->Prepend = prepend;
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& prepend) override
+ {
+ this->Initialize(selector, prepend.front());
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmStrCat(this->Prepend, s);
+ }
+
+ return s;
+ }
+
+private:
+ std::string Prepend;
+};
+class TransformActionToUpper : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmSystemTools::UpperCase(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionToLower : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmSystemTools::LowerCase(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionStrip : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmTrimWhitespace(s);
+ }
+
+ return s;
+ }
+};
+class TransformActionGenexStrip : public TransformAction
+{
+public:
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ return cmGeneratorExpression::Preprocess(
+ s, cmGeneratorExpression::StripAllGeneratorExpressions);
+ }
+
+ return s;
+ }
+};
+class TransformActionReplace : public TransformAction
+{
+public:
+ using TransformAction::Initialize;
+
+ void Initialize(TransformSelector* selector, const std::string& regex,
+ const std::string& replace) override
+ {
+ TransformAction::Initialize(selector);
+ this->ReplaceHelper =
+ cm::make_unique<cmStringReplaceHelper>(regex, replace);
+
+ if (!this->ReplaceHelper->IsRegularExpressionValid()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ if (!this->ReplaceHelper->IsReplaceExpressionValid()) {
+ throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+ this->ReplaceHelper->GetError(), "."));
+ }
+ }
+ void Initialize(TransformSelector* selector,
+ const std::vector<std::string>& args) override
+ {
+ this->Initialize(selector, args[0], args[1]);
+ }
+
+ std::string operator()(const std::string& s) override
+ {
+ if (this->Selector->InSelection(s)) {
+ // Scan through the input for all matches.
+ std::string output;
+
+ if (!this->ReplaceHelper->Replace(s, output)) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, action REPLACE: ",
+ this->ReplaceHelper->GetError(), "."));
+ }
+
+ return output;
+ }
+
+ return s;
+ }
+
+private:
+ std::unique_ptr<cmStringReplaceHelper> ReplaceHelper;
+};
+
+// Descriptor of action
+// Arity: number of arguments required for the action
+// Transform: Object implementing the action
+struct ActionDescriptor
+{
+ ActionDescriptor(cmList::TransformAction action)
+ : Action(action)
+ {
+ }
+ ActionDescriptor(cmList::TransformAction action, std::string name,
+ std::size_t arity,
+ std::unique_ptr<TransformAction> transform)
+ : Action(action)
+ , Name(std::move(name))
+ , Arity(arity)
+ , Transform(std::move(transform))
+ {
+ }
+
+ operator cmList::TransformAction() const { return this->Action; }
+
+ cmList::TransformAction Action;
+ std::string Name;
+ std::size_t Arity = 0;
+ std::unique_ptr<TransformAction> Transform;
+};
+
+// Build a set of supported actions.
+using ActionDescriptorSet = std::set<
+ ActionDescriptor,
+ std::function<bool(cmList::TransformAction, cmList::TransformAction)>>;
+
+ActionDescriptorSet Descriptors([](cmList::TransformAction x,
+ cmList::TransformAction y) {
+ return x < y;
+});
+
+ActionDescriptorSet::iterator TransformConfigure(
+ cmList::TransformAction action,
+ std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity)
+{
+ if (Descriptors.empty()) {
+ Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1,
+ cm::make_unique<TransformActionAppend>());
+ Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1,
+ cm::make_unique<TransformActionPrepend>());
+ Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0,
+ cm::make_unique<TransformActionToUpper>());
+ Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0,
+ cm::make_unique<TransformActionToLower>());
+ Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0,
+ cm::make_unique<TransformActionStrip>());
+ Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0,
+ cm::make_unique<TransformActionGenexStrip>());
+ Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2,
+ cm::make_unique<TransformActionReplace>());
+ }
+
+ auto descriptor = Descriptors.find(action);
+ if (descriptor == Descriptors.end()) {
+ throw transform_error(cmStrCat(" sub-command TRANSFORM, ",
+ std::to_string(static_cast<int>(action)),
+ " invalid action."));
+ }
+
+ if (descriptor->Arity != arity) {
+ throw transform_error(cmStrCat("sub-command TRANSFORM, action ",
+ descriptor->Name, " expects ",
+ descriptor->Arity, " argument(s)."));
+ }
+ if (!selector) {
+ selector = cm::make_unique<TransformNoSelector>();
+ }
+
+ return descriptor;
+}
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::initializer_list<index_type> indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ ;
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::vector<index_type> const& indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(indexes);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT(
+ std::vector<index_type>&& indexes)
+{
+ return cm::make_unique<TransformSelectorAt>(std::move(indexes));
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::initializer_list<index_type> indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ *indexes.begin(), *(indexes.begin() + 1),
+ indexes.size() == 3 ? *(indexes.begin() + 2) : 1);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::vector<index_type> const& indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && indexes[2] < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR(
+ std::vector<index_type>&& indexes)
+{
+ if (indexes.size() < 2 || indexes.size() > 3) {
+ throw transform_error("sub-command TRANSFORM, selector FOR "
+ "expects 2 or 3 arguments");
+ }
+ if (indexes.size() == 3 && indexes[2] < 0) {
+ throw transform_error("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ }
+
+ return cm::make_unique<TransformSelectorFor>(
+ indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1);
+}
+
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
+ std::string const& regex)
+{
+ std::unique_ptr<::TransformSelector> selector =
+ cm::make_unique<TransformSelectorRegex>(regex);
+ if (!selector->Validate()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ // weird construct to please all compilers
+ return std::unique_ptr<cmList::TransformSelector>(selector.release());
+}
+std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX(
+ std::string&& regex)
+{
+ std::unique_ptr<::TransformSelector> selector =
+ cm::make_unique<TransformSelectorRegex>(std::move(regex));
+ if (!selector->Validate()) {
+ throw transform_error(
+ cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
+ "regex \"",
+ regex, "\"."));
+ }
+ // weird construct to please all compilers
+ return std::unique_ptr<cmList::TransformSelector>(selector.release());
+}
+
+cmList& cmList::transform(TransformAction action,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 0);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()));
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action, std::string const& arg,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 1);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), arg);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action, std::string const& arg1,
+ std::string const& arg2,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, 2);
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), arg1, arg2);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+cmList& cmList::transform(TransformAction action,
+ std::vector<std::string> const& args,
+ std::unique_ptr<TransformSelector> selector)
+{
+ auto descriptor = TransformConfigure(action, selector, args.size());
+
+ descriptor->Transform->Initialize(
+ static_cast<::TransformSelector*>(selector.get()), args);
+
+ static_cast<::TransformSelector&>(*selector).Transform(
+ this->Values, [&descriptor](const std::string& s) -> std::string {
+ return (*descriptor->Transform)(s);
+ });
+
+ return *this;
+}
+
+std::string cmList::join(cm::string_view glue) const
+{
+ return cmJoin(this->Values, glue);
+}
+
+std::string& cmList::append(cm::string_view value, std::string& list)
+{
+ if (list.empty()) {
+ list = std::string(value);
+ } else {
+ list += cmStrCat(cmList::element_separator, value);
+ }
+
+ return list;
+}
+
+std::string& cmList::prepend(cm::string_view value, std::string& list)
+{
+ if (list.empty()) {
+ list = std::string(value);
+ } else {
+ list.insert(0, cmStrCat(value, cmList::element_separator));
+ }
+
+ return list;
+}
+
+cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const
+{
+ if (boundCheck) {
+ if (this->Values.empty()) {
+ throw std::out_of_range(
+ cmStrCat("index: ", pos, " out of range (0, 0)"));
+ }
+
+ if (!this->Values.empty()) {
+ auto length = this->Values.size();
+ if (pos < 0) {
+ pos = static_cast<index_type>(length) + pos;
+ }
+ if (pos < 0 || length <= static_cast<size_type>(pos)) {
+ throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
+ this->Values.size(), ", ",
+ this->Values.size() - 1, ")"));
+ }
+ }
+ return pos;
+ }
+
+ return pos < 0 ? this->Values.size() + pos : pos;
+}
+cmList::size_type cmList::ComputeInsertIndex(index_type pos,
+ bool boundCheck) const
+{
+ if (boundCheck) {
+ if (this->Values.empty() && pos != 0) {
+ throw std::out_of_range(
+ cmStrCat("index: ", pos, " out of range (0, 0)"));
+ }
+
+ if (!this->Values.empty()) {
+ auto length = this->Values.size();
+ if (pos < 0) {
+ pos = static_cast<index_type>(length) + pos;
+ }
+ if (pos < 0 || length < static_cast<size_type>(pos)) {
+ throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-",
+ this->Values.size(), ", ",
+ this->Values.size(), ")"));
+ }
+ }
+ return pos;
+ }
+
+ return pos < 0 ? this->Values.size() + pos : pos;
+}
+
+cmList cmList::GetItems(std::vector<index_type>&& indexes) const
+{
+ cmList listItems;
+
+ for (auto index : indexes) {
+ listItems.emplace_back(this->at(index));
+ }
+
+ return listItems;
+}
+
+cmList& cmList::RemoveItems(std::vector<index_type>&& indexes)
+{
+ if (indexes.empty()) {
+ return *this;
+ }
+
+ // compute all indexes
+ std::vector<size_type> idx(indexes.size());
+ std::transform(
+ indexes.cbegin(), indexes.cend(), idx.begin(),
+ [this](const index_type& index) { return this->ComputeIndex(index); });
+
+ std::sort(idx.begin(), idx.end(),
+ [](size_type l, size_type r) { return l > r; });
+ auto newEnd = std::unique(idx.begin(), idx.end());
+ idx.erase(newEnd, idx.end());
+
+ for (auto index : idx) {
+ this->erase(this->begin() + index);
+ }
+
+ return *this;
+}
+
+cmList& cmList::RemoveItems(std::vector<std::string>&& items)
+{
+ std::sort(items.begin(), items.end());
+ auto last = std::unique(items.begin(), items.end());
+ auto first = items.begin();
+
+ auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last));
+ this->Values.erase(newEnd, this->Values.end());
+
+ return *this;
+}
+
+cmList::container_type::iterator cmList::Insert(
+ container_type::const_iterator pos, std::string&& value,
+ container_type& container, ExpandElements expandElements,
+ EmptyElements emptyElements)
+{
+ auto delta = std::distance(container.cbegin(), pos);
+ auto insertPos = container.begin() + delta;
+
+ if (expandElements == ExpandElements::Yes) {
+ // If argument is empty, it is an empty list.
+ if (emptyElements == EmptyElements::No && value.empty()) {
+ return insertPos;
+ }
+
+ // if there are no ; in the name then just copy the current string
+ if (value.find(';') == std::string::npos) {
+ return container.insert(insertPos, std::move(value));
+ }
+
+ std::string newValue;
+ // Break the string at non-escaped semicolons not nested in [].
+ int squareNesting = 0;
+ auto last = value.begin();
+ auto const cend = value.end();
+ for (auto c = last; c != cend; ++c) {
+ switch (*c) {
+ case '\\': {
+ // We only want to allow escaping of semicolons. Other
+ // escapes should not be processed here.
+ auto cnext = c + 1;
+ if ((cnext != cend) && *cnext == ';') {
+ newValue.append(last, c);
+ // Skip over the escape character
+ last = cnext;
+ c = cnext;
+ }
+ } break;
+ case '[': {
+ ++squareNesting;
+ } break;
+ case ']': {
+ --squareNesting;
+ } break;
+ case ';': {
+ // brackets.
+ if (squareNesting == 0) {
+ newValue.append(last, c);
+ // Skip over the semicolon
+ last = c + 1;
+ if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
+ // Add the last argument.
+ insertPos = container.insert(insertPos, newValue);
+ insertPos++;
+ newValue.clear();
+ }
+ }
+ } break;
+ default: {
+ // Just append this character.
+ } break;
+ }
+ }
+ newValue.append(last, cend);
+ if (!newValue.empty() || emptyElements == EmptyElements::Yes) {
+ // Add the last argument.
+ container.insert(insertPos, std::move(newValue));
+ }
+ } else if (!value.empty() || emptyElements == EmptyElements::Yes) {
+ return container.insert(insertPos, std::move(value));
+ }
+ return container.begin() + delta;
+}
diff --git a/Source/cmList.h b/Source/cmList.h
new file mode 100644
index 0000000000..d994ad3890
--- /dev/null
+++ b/Source/cmList.h
@@ -0,0 +1,1198 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <algorithm>
+#include <initializer_list>
+#include <iterator>
+#include <memory>
+#include <numeric>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmValue.h"
+
+/**
+ * CMake lists management
+ *
+ * For all operations, input arguments (single value like cm::string_view or
+ * multiple values specified through pair of iterators) are, by default,
+ * expanded. The expansion can be controlled by the cmList::ExpandElements
+ * option.
+ *
+ * There is an exception to this rule. The following methods do not expand
+ * their argument: cmList::push_back, cmList::emplace and cmList::emplace_back.
+ */
+
+class cmList
+{
+public:
+ using container_type = std::vector<std::string>;
+
+ using value_type = container_type::value_type;
+ using allocator_type = container_type::allocator_type;
+ using index_type = int;
+ using size_type = container_type::size_type;
+ using difference_type = container_type::difference_type;
+ using reference = container_type::reference;
+ using const_reference = container_type::const_reference;
+ using iterator = container_type::iterator;
+ using const_iterator = container_type::const_iterator;
+ using reverse_iterator = container_type::reverse_iterator;
+ using const_reverse_iterator = container_type::const_reverse_iterator;
+
+ static const size_type npos = static_cast<size_type>(-1);
+
+ static cm::string_view element_separator;
+
+ enum class EmptyElements
+ {
+ No,
+ Yes,
+ };
+ enum class ExpandElements
+ {
+ No,
+ Yes,
+ };
+
+ cmList() = default;
+ cmList(const cmList&) = default;
+ cmList(cmList&&) = default;
+
+ cmList(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(value, expandElements, emptyElements);
+ }
+ cmList(cm::string_view value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ cmList(cmValue list, ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (list) {
+ this->assign(*list, expandElements, emptyElements);
+ }
+ }
+ cmList(cmValue list, EmptyElements emptyElements)
+ : cmList(list, ExpandElements::Yes, emptyElements)
+ {
+ }
+ template <typename InputIterator>
+ cmList(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(first, last, expandElements, emptyElements);
+ }
+ template <typename InputIterator>
+ cmList(InputIterator first, InputIterator last, EmptyElements emptyElements)
+ : cmList(first, last, ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(const container_type& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ : cmList(init.begin(), init.end(), expandElements, emptyElements)
+ {
+ }
+ cmList(const container_type& init, EmptyElements emptyElements)
+ : cmList(init, ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(container_type&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ : cmList(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements)
+ {
+ init.clear();
+ }
+ cmList(container_type&& init, EmptyElements emptyElements)
+ : cmList(std::move(init), ExpandElements::Yes, emptyElements)
+ {
+ }
+ cmList(std::initializer_list<std::string> init) { this->assign(init); }
+
+ ~cmList() = default;
+
+ cmList& operator=(const cmList&) = default;
+ cmList& operator=(cmList&&) = default;
+
+ cmList& operator=(cm::string_view value)
+ {
+ this->assign(value);
+ return *this;
+ }
+ cmList& operator=(cmValue value)
+ {
+ if (value) {
+ this->operator=(*value);
+ } else {
+ this->clear();
+ }
+
+ return *this;
+ }
+
+ cmList& operator=(const container_type& init)
+ {
+ this->assign(init);
+ return *this;
+ }
+ cmList& operator=(container_type&& init)
+ {
+ this->assign(std::move(init));
+
+ return *this;
+ }
+
+ cmList& operator=(std::initializer_list<std::string> init)
+ {
+ this->assign(init);
+ return *this;
+ }
+
+ void assign(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->clear();
+ this->append(value, expandElements, emptyElements);
+ }
+ void assign(cm::string_view value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ void assign(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ this->assign(*value, expandElements, emptyElements);
+ } else {
+ this->clear();
+ }
+ }
+ void assign(cmValue value, EmptyElements emptyElements)
+ {
+ this->assign(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ void assign(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->clear();
+ this->append(first, last, expandElements, emptyElements);
+ }
+ template <typename InputIterator>
+ void assign(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ this->assign(first, last, ExpandElements::Yes, emptyElements);
+ }
+ void assign(const cmList& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(init.begin(), init.end(), expandElements, emptyElements);
+ }
+ void assign(const cmList& init, EmptyElements emptyElements)
+ {
+ this->assign(init, ExpandElements::Yes, emptyElements);
+ }
+ void assign(cmList&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements);
+ init.clear();
+ }
+ void assign(cmList&& init, EmptyElements emptyElements)
+ {
+ this->assign(std::move(init), ExpandElements::Yes, emptyElements);
+ }
+ void assign(const container_type& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(init.begin(), init.end(), expandElements, emptyElements);
+ }
+ void assign(const container_type& init, EmptyElements emptyElements)
+ {
+ this->assign(init, ExpandElements::Yes, emptyElements);
+ }
+ void assign(container_type&& init,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ this->assign(std::make_move_iterator(init.begin()),
+ std::make_move_iterator(init.end()), expandElements,
+ emptyElements);
+ init.clear();
+ }
+ void assign(container_type&& init, EmptyElements emptyElements)
+ {
+ this->assign(std::move(init), ExpandElements::Yes, emptyElements);
+ }
+ void assign(std::initializer_list<std::string> init)
+ {
+ this->assign(init.begin(), init.end());
+ }
+
+ // Conversions
+ std::string to_string() const
+ {
+ return this->join(cmList::element_separator);
+ }
+
+ operator container_type&() & noexcept { return this->Values; }
+ operator const container_type&() const& noexcept { return this->Values; }
+ operator container_type&&() && noexcept { return std::move(this->Values); }
+
+ // Element access
+ reference at(index_type pos)
+ {
+ return this->Values.at(this->ComputeIndex(pos));
+ }
+ const_reference at(index_type pos) const
+ {
+ return this->Values.at(this->ComputeIndex(pos));
+ }
+
+ reference operator[](index_type pos)
+ {
+ return this->Values[this->ComputeIndex(pos, false)];
+ }
+ const_reference operator[](index_type pos) const
+ {
+ return this->Values[this->ComputeIndex(pos, false)];
+ }
+
+ reference front() { return this->Values.front(); }
+ const_reference front() const { return this->Values.front(); }
+
+ reference back() { return this->Values.back(); }
+ const_reference back() const { return this->Values.back(); }
+
+ // extract sublist in range [first, last)
+ cmList sublist(const_iterator first, const_iterator last) const
+ {
+ return cmList{ first, last };
+ }
+ // Extract sublist in range [first, last)
+ // Throw std::out_of_range if pos is invalid
+ cmList sublist(size_type pos = 0, size_type length = npos) const;
+
+ // Returns the list of elements
+ // Throw std::out_of_range if any index is invalid
+ cmList get_items(std::initializer_list<index_type> indexes) const
+ {
+ return this->GetItems(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ }
+ template <typename InputIterator>
+ cmList get_items(InputIterator first, InputIterator last) const
+ {
+ return this->GetItems(std::vector<index_type>{ first, last });
+ }
+
+ size_type find(cm::string_view value) const;
+ size_type find(cmValue value) const
+ {
+ if (value) {
+ return this->find(*value);
+ }
+
+ return npos;
+ }
+
+ container_type& data() noexcept { return this->Values; }
+ const container_type& data() const noexcept { return this->Values; }
+
+ // Iterators
+ iterator begin() noexcept { return this->Values.begin(); }
+ const_iterator begin() const noexcept { return this->Values.begin(); }
+ const_iterator cbegin() const noexcept { return this->Values.cbegin(); }
+
+ iterator end() noexcept { return this->Values.end(); }
+ const_iterator end() const noexcept { return this->Values.end(); }
+ const_iterator cend() const noexcept { return this->Values.cend(); }
+
+ reverse_iterator rbegin() noexcept { return this->Values.rbegin(); }
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return this->Values.rbegin();
+ }
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return this->Values.crbegin();
+ }
+
+ reverse_iterator rend() noexcept { return this->Values.rend(); }
+ const_reverse_iterator rend() const noexcept { return this->Values.rend(); }
+ const_reverse_iterator crend() const noexcept
+ {
+ return this->Values.crend();
+ }
+
+ // Capacity
+ bool empty() const noexcept { return this->Values.empty(); }
+ size_type size() const noexcept { return this->Values.size(); }
+
+ // Modifiers
+ void clear() noexcept { this->Values.clear(); }
+
+ iterator insert(const_iterator pos, cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, std::string(value), this->Values,
+ expandElements, emptyElements);
+ }
+ iterator insert(const_iterator pos, cm::string_view value,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, value, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->insert(pos, *value, expandElements, emptyElements);
+ }
+
+ auto delta = std::distance(this->cbegin(), pos);
+ return this->begin() + delta;
+ }
+ iterator insert(const_iterator pos, cmValue value,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator insert(const_iterator pos, InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, first, last, this->Values, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator insert(const_iterator pos, InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(pos, values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const cmList& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, values, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->insert(pos, std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator insert(const_iterator pos, cmList&& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(pos, values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, const container_type& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, values, ExpandElements::Yes, emptyElements);
+ }
+ iterator insert(const_iterator pos, container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->insert(pos, std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator insert(const_iterator pos, container_type&& values,
+ EmptyElements emptyElements)
+ {
+ return this->insert(pos, std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator insert(const_iterator pos, std::initializer_list<std::string> ilist)
+ {
+ return this->insert(pos, ilist.begin(), ilist.end());
+ }
+
+ iterator append(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cend(), value, expandElements, emptyElements);
+ }
+ iterator append(cm::string_view value, EmptyElements emptyElements)
+ {
+ return this->append(value, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->append(*value, expandElements, emptyElements);
+ }
+
+ return this->end();
+ }
+ iterator append(cmValue value, EmptyElements emptyElements)
+ {
+ return this->append(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator append(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cend(), first, last, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator append(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->append(first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->append(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator append(const cmList& values, EmptyElements emptyElements)
+ {
+ return this->append(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->append(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator append(cmList&& values, EmptyElements emptyElements)
+ {
+ return this->append(std::move(values), ExpandElements::Yes, emptyElements);
+ }
+ iterator append(const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->append(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator append(const container_type& values, EmptyElements emptyElements)
+ {
+ return this->append(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator append(container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->append(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator append(container_type&& values, EmptyElements emptyElements)
+ {
+ return this->append(std::move(values), ExpandElements::Yes, emptyElements);
+ }
+ iterator append(std::initializer_list<std::string> ilist)
+ {
+ return this->insert(this->cend(), ilist);
+ }
+
+ iterator prepend(cm::string_view value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cbegin(), value, expandElements, emptyElements);
+ }
+ iterator prepend(cm::string_view value, EmptyElements emptyElements)
+ {
+ return this->prepend(value, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(cmValue value,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return this->prepend(*value, expandElements, emptyElements);
+ }
+
+ return this->begin();
+ }
+ iterator prepend(cmValue value, EmptyElements emptyElements)
+ {
+ return this->prepend(value, ExpandElements::Yes, emptyElements);
+ }
+ template <typename InputIterator>
+ iterator prepend(InputIterator first, InputIterator last,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->insert(this->cbegin(), first, last, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ iterator prepend(InputIterator first, InputIterator last,
+ EmptyElements emptyElements)
+ {
+ return this->prepend(first, last, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(const cmList& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->prepend(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator prepend(const cmList& values, EmptyElements emptyElements)
+ {
+ return this->prepend(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(cmList&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->prepend(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator prepend(cmList&& values, EmptyElements emptyElements)
+ {
+ return this->prepend(std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator prepend(const container_type& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return this->prepend(values.begin(), values.end(), expandElements,
+ emptyElements);
+ }
+ iterator prepend(const container_type& values, EmptyElements emptyElements)
+ {
+ return this->prepend(values, ExpandElements::Yes, emptyElements);
+ }
+ iterator prepend(container_type&& values,
+ ExpandElements expandElements = ExpandElements::Yes,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ auto result = this->prepend(std::make_move_iterator(values.begin()),
+ std::make_move_iterator(values.end()),
+ expandElements, emptyElements);
+ values.clear();
+
+ return result;
+ }
+ iterator prepend(container_type&& values, EmptyElements emptyElements)
+ {
+ return this->prepend(std::move(values), ExpandElements::Yes,
+ emptyElements);
+ }
+ iterator prepend(std::initializer_list<std::string> ilist)
+ {
+ return this->insert(this->cbegin(), ilist);
+ }
+
+ void push_back(cm::string_view value)
+ {
+ this->Values.push_back(std::string{ value });
+ }
+ void push_back(std::string&& value)
+ {
+ this->Values.push_back(std::move(value));
+ }
+
+ template <typename... Args>
+ iterator emplace(const_iterator pos, Args&&... args)
+ {
+ return this->Values.emplace(pos, std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void emplace_back(Args&&... args)
+ {
+ this->Values.emplace_back(std::forward<Args>(args)...);
+ }
+
+ // Inserts elements in the list
+ // Throw std::out_of_range if index is invalid
+ template <typename InputIterator>
+ cmList& insert_items(index_type index, InputIterator first,
+ InputIterator last)
+ {
+ this->insert(this->begin() + this->ComputeInsertIndex(index), first, last);
+ return *this;
+ }
+ cmList& insert_items(index_type index,
+ std::initializer_list<std::string> values)
+ {
+ return this->insert_items(index, values.begin(), values.end());
+ }
+
+ iterator erase(const_iterator pos)
+ {
+ // convert const_iterator in iterator to please non standard c++11
+ // compilers (gcc 4.8 for example)
+ auto pos2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), pos);
+ return this->Values.erase(pos2);
+ }
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ // convert const_iterator in iterator to please non standard c++11
+ // compilers (gcc 4.8 for example)
+ auto first2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), first);
+ auto last2 =
+ this->Values.begin() + std::distance(this->Values.cbegin(), last);
+ return this->Values.erase(first2, last2);
+ }
+
+ void pop_back() { this->Values.pop_back(); }
+ void pop_front() { this->Values.erase(this->begin()); }
+
+ // Removes elements from the list
+ // Throw std::out_of_range if any index is invalid
+ cmList& remove_items(std::initializer_list<index_type> indexes)
+ {
+ return this->RemoveItems(
+ std::vector<index_type>{ indexes.begin(), indexes.end() });
+ }
+ cmList& remove_items(std::initializer_list<std::string> values)
+ {
+ return this->RemoveItems(
+ std::vector<std::string>{ values.begin(), values.end() });
+ }
+ template <typename InputIterator>
+ cmList& remove_items(InputIterator first, InputIterator last)
+ {
+ return this->RemoveItems(
+ std::vector<typename InputIterator::value_type>{ first, last });
+ }
+
+ cmList& remove_duplicates();
+
+ enum class FilterMode
+ {
+ INCLUDE,
+ EXCLUDE
+ };
+ // Includes or removes items from the list
+ // Throw std::invalid_argument if regular expression is invalid
+ cmList& filter(cm::string_view regex, FilterMode mode);
+
+ cmList& reverse()
+ {
+ std::reverse(this->Values.begin(), this->Values.end());
+ return *this;
+ }
+
+ struct SortConfiguration
+ {
+ enum class OrderMode
+ {
+ DEFAULT,
+ ASCENDING,
+ DESCENDING,
+ } Order = OrderMode::DEFAULT;
+
+ enum class CompareMethod
+ {
+ DEFAULT,
+ STRING,
+ FILE_BASENAME,
+ NATURAL,
+ } Compare = CompareMethod::DEFAULT;
+
+ enum class CaseSensitivity
+ {
+ DEFAULT,
+ SENSITIVE,
+ INSENSITIVE,
+ } Case = CaseSensitivity::DEFAULT;
+
+ // declare the default constructor to work-around clang bug
+ SortConfiguration();
+
+ SortConfiguration(OrderMode order, CompareMethod compare,
+ CaseSensitivity caseSensitivity)
+ : Order(order)
+ , Compare(compare)
+ , Case(caseSensitivity)
+ {
+ }
+ };
+ cmList& sort(const SortConfiguration& config = SortConfiguration{});
+
+ // exception raised on error during transform operations
+ class transform_error : public std::runtime_error
+ {
+ public:
+ transform_error(const std::string& error)
+ : std::runtime_error(error)
+ {
+ }
+ };
+
+ class TransformSelector
+ {
+ public:
+ using index_type = cmList::index_type;
+
+ // define some structs used as template selector
+ struct AT;
+ struct FOR;
+ struct REGEX;
+
+ virtual ~TransformSelector() = default;
+
+ virtual const std::string& GetTag() = 0;
+
+ // method NEW is used to allocate a selector of the needed type.
+ // For example:
+ // cmList::TransformSelector::New<AT>({1, 2, 5, 6});
+ // or
+ // cmList::TransformSelector::New<REGEX>("^XX.*");
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(
+ std::initializer_list<index_type>);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(
+ std::vector<index_type> const&);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::vector<index_type>&&);
+
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::string const&);
+ template <typename Type>
+ static std::unique_ptr<TransformSelector> New(std::string&&);
+
+ private:
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::initializer_list<index_type> init);
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::vector<index_type> const& init);
+ static std::unique_ptr<TransformSelector> NewAT(
+ std::vector<index_type>&& init);
+
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::initializer_list<index_type> init);
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::vector<index_type> const& init);
+ static std::unique_ptr<TransformSelector> NewFOR(
+ std::vector<index_type>&& init);
+
+ static std::unique_ptr<TransformSelector> NewREGEX(
+ std::string const& init);
+ static std::unique_ptr<TransformSelector> NewREGEX(std::string&& init);
+ };
+
+ enum class TransformAction
+ {
+ APPEND,
+ PREPEND,
+ TOLOWER,
+ TOUPPER,
+ STRIP,
+ GENEX_STRIP,
+ REPLACE
+ };
+
+ // Transforms the list by applying an action
+ // Throw std::transform_error is any of arguments specified are invalid
+ cmList& transform(TransformAction action,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action, std::string const& arg,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action, std::string const& arg1,
+ std::string const& arg2,
+ std::unique_ptr<TransformSelector> = {});
+ cmList& transform(TransformAction action,
+ std::vector<std::string> const& args,
+ std::unique_ptr<TransformSelector> = {});
+
+ std::string join(cm::string_view glue) const;
+
+ void swap(cmList& other) noexcept { this->Values.swap(other.Values); }
+
+ // static members
+ // ==============
+ // these methods can be used to store CMake list expansion directly in a
+ // std::vector.
+ static void assign(cm::string_view value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ container.clear();
+ cmList::append(value, container, emptyElements);
+ }
+ static void assign(cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ cmList::assign(*value, container, emptyElements);
+ } else {
+ container.clear();
+ }
+ }
+ template <typename InputIterator>
+ static void assign(InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ container.clear();
+ cmList::append(first, last, container, emptyElements);
+ }
+
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, cm::string_view value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, std::string(value), container,
+ ExpandElements::Yes, emptyElements);
+ }
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, cmValue value,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::insert(pos, *value, container, emptyElements);
+ }
+
+ auto delta = std::distance(container.cbegin(), pos);
+ return container.begin() + delta;
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator insert(
+ std::vector<std::string>::const_iterator pos, InputIterator first,
+ InputIterator last, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::Insert(pos, first, last, container, ExpandElements::Yes,
+ emptyElements);
+ }
+
+ static std::vector<std::string>::iterator append(
+ cm::string_view value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cend(), value, container, emptyElements);
+ }
+ static std::vector<std::string>::iterator append(
+ cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::append(*value, container, emptyElements);
+ }
+
+ return container.end();
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator append(
+ InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cend(), first, last, container,
+ emptyElements);
+ }
+
+ static std::vector<std::string>::iterator prepend(
+ cm::string_view value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cbegin(), value, container, emptyElements);
+ }
+ static std::vector<std::string>::iterator prepend(
+ cmValue value, std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ if (value) {
+ return cmList::prepend(*value, container, emptyElements);
+ }
+
+ return container.begin();
+ }
+ template <typename InputIterator>
+ static std::vector<std::string>::iterator prepend(
+ InputIterator first, InputIterator last,
+ std::vector<std::string>& container,
+ EmptyElements emptyElements = EmptyElements::No)
+ {
+ return cmList::insert(container.cbegin(), first, last, container,
+ emptyElements);
+ }
+
+ // The following methods offer the possibility to extend a CMake list
+ // but without any intermediate expansion. So the operation is simply a
+ // string concatenation with special handling for the CMake list item
+ // separator
+ static std::string& append(cm::string_view value, std::string& list);
+ template <typename InputIterator>
+ static std::string& append(InputIterator first, InputIterator last,
+ std::string& list)
+ {
+ if (first == last) {
+ return list;
+ }
+
+ return cmList::append(cm::string_view{ std::accumulate(
+ std::next(first), last, *first,
+ [](std::string a, const std::string& b) {
+ return std::move(a) +
+ std::string(cmList::element_separator) + b;
+ }) },
+ list);
+ }
+
+ static std::string& prepend(cm::string_view value, std::string& list);
+ template <typename InputIterator>
+ static std::string& prepend(InputIterator first, InputIterator last,
+ std::string& list)
+ {
+ if (first == last) {
+ return list;
+ }
+
+ return cmList::prepend(cm::string_view{ std::accumulate(
+ std::next(first), last, *first,
+ [](std::string a, const std::string& b) {
+ return std::move(a) +
+ std::string(cmList::element_separator) + b;
+ }) },
+ list);
+ }
+
+ // Non-members
+ // ===========
+ friend inline bool operator==(const cmList& lhs, const cmList& rhs) noexcept
+ {
+ return lhs.Values == rhs.Values;
+ }
+ friend inline bool operator!=(const cmList& lhs, const cmList& rhs) noexcept
+ {
+ return lhs.Values != rhs.Values;
+ }
+
+private:
+ size_type ComputeIndex(index_type pos, bool boundCheck = true) const;
+ size_type ComputeInsertIndex(index_type pos, bool boundCheck = true) const;
+
+ cmList GetItems(std::vector<index_type>&& indexes) const;
+
+ cmList& RemoveItems(std::vector<index_type>&& indexes);
+ cmList& RemoveItems(std::vector<std::string>&& items);
+
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ std::string&& value,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements);
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ const std::string& value,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements)
+ {
+ auto tmp = value;
+ return cmList::Insert(pos, std::move(tmp), container, expandElements,
+ emptyElements);
+ }
+ template <typename InputIterator>
+ static container_type::iterator Insert(container_type::const_iterator pos,
+ InputIterator first,
+ InputIterator last,
+ container_type& container,
+ ExpandElements expandElements,
+ EmptyElements emptyElements)
+ {
+ auto delta = std::distance(container.cbegin(), pos);
+
+ if (first == last) {
+ return container.begin() + delta;
+ }
+
+ auto insertPos = container.begin() + delta;
+ if (expandElements == ExpandElements::Yes) {
+ for (; first != last; ++first) {
+ auto size = container.size();
+ insertPos = cmList::Insert(insertPos, *first, container,
+ expandElements, emptyElements);
+ insertPos += container.size() - size;
+ }
+ } else {
+ for (; first != last; ++first) {
+ if (!first->empty() || emptyElements == EmptyElements::Yes) {
+ insertPos = container.insert(insertPos, *first);
+ insertPos++;
+ }
+ }
+ }
+
+ return container.begin() + delta;
+ }
+
+ container_type Values;
+};
+
+// specializations for cmList::TransformSelector allocators
+// ========================================================
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::initializer_list<index_type> init)
+{
+ return cmList::TransformSelector::NewAT(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::vector<index_type> const& init)
+{
+ return cmList::TransformSelector::NewAT(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::vector<index_type>&& init)
+{
+ return cmList::TransformSelector::NewAT(std::move(init));
+}
+
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::initializer_list<index_type> init)
+{
+ return cmList::TransformSelector::NewFOR(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::vector<index_type> const& init)
+{
+ return cmList::TransformSelector::NewFOR(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ std::vector<index_type>&& init)
+{
+ return cmList::TransformSelector::NewFOR(std::move(init));
+}
+
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
+ std::string const& init)
+{
+ return cmList::TransformSelector::NewREGEX(init);
+}
+template <>
+inline std::unique_ptr<cmList::TransformSelector>
+cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
+ std::string&& init)
+{
+ return cmList::TransformSelector::NewREGEX(std::move(init));
+}
+
+// Non-member functions
+// ====================
+inline std::vector<std::string>& operator+=(std::vector<std::string>& l,
+ const cmList& r)
+{
+ l.insert(l.end(), r.begin(), r.end());
+ return l;
+}
+inline std::vector<std::string>& operator+=(std::vector<std::string>& l,
+ cmList&& r)
+{
+ std::move(r.begin(), r.end(), std::back_inserter(l));
+ r.clear();
+
+ return l;
+}
+
+namespace cm {
+inline void erase(cmList& list, const std::string& value)
+{
+ list.erase(std::remove(list.begin(), list.end(), value), list.end());
+}
+
+template <typename Predicate>
+inline void erase_if(cmList& list, Predicate pred)
+{
+ list.erase(std::remove_if(list.begin(), list.end(), pred), list.end());
+}
+}
+
+namespace srd {
+inline void swap(cmList& lhs, cmList& rhs) noexcept
+{
+ lhs.swap(rhs);
+}
+}
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index d412534c0e..40be0ceeb5 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -2,11 +2,9 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmListCommand.h"
-#include <algorithm>
#include <cassert>
#include <cstdio>
#include <functional>
-#include <iterator>
#include <set>
#include <sstream>
#include <stdexcept>
@@ -14,22 +12,18 @@
#include <vector>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
-#include "cmsys/RegularExpression.hxx"
-
-#include "cmAlgorithms.h"
#include "cmExecutionStatus.h"
-#include "cmGeneratorExpression.h"
+#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
-#include "cmStringReplaceHelper.h"
#include "cmSubcommandTable.h"
-#include "cmSystemTools.h"
#include "cmValue.h"
namespace {
@@ -70,11 +64,6 @@ bool GetIndexArg(const std::string& arg, int* idx, cmMakefile& mf)
return true;
}
-bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
- std::string const& listName,
- std::vector<std::string>& varArgsExpanded,
- cmExecutionStatus& status);
-
bool GetListString(std::string& listString, const std::string& var,
const cmMakefile& makefile)
{
@@ -87,22 +76,25 @@ bool GetListString(std::string& listString, const std::string& var,
return true;
}
-bool GetList(std::vector<std::string>& list, const std::string& var,
- const cmMakefile& makefile)
+cm::optional<cmList> GetList(const std::string& var,
+ const cmMakefile& makefile)
{
+ cm::optional<cmList> list;
+
std::string listString;
if (!GetListString(listString, var, makefile)) {
- return false;
+ return list;
}
// if the size of the list
if (listString.empty()) {
- return true;
+ list.emplace();
+ return list;
}
// expand the variable into a list
- cmExpandList(listString, list, true);
+ list.emplace(listString, cmList::EmptyElements::Yes);
// if no empty elements then just return
- if (!cm::contains(list, std::string())) {
- return true;
+ if (!cm::contains(*list, std::string())) {
+ return list;
}
// if we have empty elements we need to check policy CMP0007
switch (makefile.GetPolicyStatus(cmPolicies::CMP0007)) {
@@ -111,31 +103,29 @@ bool GetList(std::vector<std::string>& list, const std::string& var,
// OLD behavior is to allow compatibility, so recall
// ExpandListArgument without the true which will remove
// empty values
- list.clear();
- cmExpandList(listString, list);
+ list->assign(listString);
std::string warn =
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0007),
" List has value = [", listString, "].");
makefile.IssueMessage(MessageType::AUTHOR_WARNING, warn);
- return true;
+ return list;
}
case cmPolicies::OLD:
// OLD behavior is to allow compatibility, so recall
// ExpandListArgument without the true which will remove
// empty values
- list.clear();
- cmExpandList(listString, list);
- return true;
+ list->assign(listString);
+ return list;
case cmPolicies::NEW:
- return true;
+ return list;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
makefile.IssueMessage(
MessageType::FATAL_ERROR,
cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0007));
- return false;
+ return {};
}
- return true;
+ return list;
}
bool HandleLengthCommand(std::vector<std::string> const& args,
@@ -148,16 +138,11 @@ bool HandleLengthCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
const std::string& variableName = args.back();
- std::vector<std::string> varArgsExpanded;
- // do not check the return value here
- // if the list var is not found varArgsExpanded will have size 0
- // and we will return 0
- GetList(varArgsExpanded, listName, status.GetMakefile());
- size_t length = varArgsExpanded.size();
- char buffer[1024];
- snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length));
-
- status.GetMakefile().AddDefinition(variableName, buffer);
+
+ auto list = GetList(listName, status.GetMakefile());
+ status.GetMakefile().AddDefinition(variableName,
+ std::to_string(list ? list->size() : 0));
+
return true;
}
@@ -172,42 +157,35 @@ bool HandleGetCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
const std::string& variableName = args.back();
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+ if (!list) {
status.GetMakefile().AddDefinition(variableName, "NOTFOUND");
return true;
}
// FIXME: Add policy to make non-existing lists an error like empty lists.
- if (varArgsExpanded.empty()) {
+ if (list->empty()) {
status.SetError("GET given empty list");
return false;
}
- std::string value;
- size_t cc;
- const char* sep = "";
- size_t nitem = varArgsExpanded.size();
- for (cc = 2; cc < args.size() - 1; cc++) {
- int item;
- if (!GetIndexArg(args[cc], &item, status.GetMakefile())) {
+ std::vector<int> indexes;
+ for (std::size_t cc = 2; cc < args.size() - 1; cc++) {
+ int index;
+ if (!GetIndexArg(args[cc], &index, status.GetMakefile())) {
status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
return false;
}
- value += sep;
- sep = ";";
- if (item < 0) {
- item = static_cast<int>(nitem) + item;
- }
- if (item < 0 || nitem <= static_cast<size_t>(item)) {
- status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
- ", ", nitem - 1, ")"));
- return false;
- }
- value += varArgsExpanded[item];
+ indexes.push_back(index);
}
- status.GetMakefile().AddDefinition(variableName, value);
- return true;
+ try {
+ auto values = list->get_items(indexes.begin(), indexes.end());
+ status.GetMakefile().AddDefinition(variableName, values.to_string());
+ return true;
+ } catch (std::out_of_range& e) {
+ status.SetError(e.what());
+ return false;
+ }
}
bool HandleAppendCommand(std::vector<std::string> const& args,
@@ -226,13 +204,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
std::string listString;
GetListString(listString, listName, makefile);
- // If `listString` or `args` is empty, no need to append `;`,
- // then index is going to be `1` and points to the end-of-string ";"
- auto const offset =
- static_cast<std::string::size_type>(listString.empty() || args.empty());
- listString += &";"[offset] + cmJoin(cmMakeRange(args).advance(2), ";");
-
- makefile.AddDefinition(listName, listString);
+ makefile.AddDefinition(
+ listName, cmList::append(args.begin() + 2, args.end(), listString));
return true;
}
@@ -252,14 +225,8 @@ bool HandlePrependCommand(std::vector<std::string> const& args,
std::string listString;
GetListString(listString, listName, makefile);
- // If `listString` or `args` is empty, no need to append `;`,
- // then `offset` is going to be `1` and points to the end-of-string ";"
- auto const offset =
- static_cast<std::string::size_type>(listString.empty() || args.empty());
- listString.insert(0,
- cmJoin(cmMakeRange(args).advance(2), ";") + &";"[offset]);
-
- makefile.AddDefinition(listName, listString);
+ makefile.AddDefinition(
+ listName, cmList::prepend(args.begin() + 2, args.end(), listString));
return true;
}
@@ -272,8 +239,9 @@ bool HandlePopBackCommand(std::vector<std::string> const& args,
auto ai = args.cbegin();
++ai; // Skip subcommand name
std::string const& listName = *ai++;
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, makefile)) {
+ auto list = GetList(listName, makefile);
+
+ if (!list) {
// Can't get the list definition... undefine any vars given after.
for (; ai != args.cend(); ++ai) {
makefile.RemoveDefinition(*ai);
@@ -281,16 +249,16 @@ bool HandlePopBackCommand(std::vector<std::string> const& args,
return true;
}
- if (!varArgsExpanded.empty()) {
+ if (!list->empty()) {
if (ai == args.cend()) {
// No variables are given... Just remove one element.
- varArgsExpanded.pop_back();
+ list->pop_back();
} else {
// Ok, assign elements to be removed to the given variables
- for (; !varArgsExpanded.empty() && ai != args.cend(); ++ai) {
+ for (; !list->empty() && ai != args.cend(); ++ai) {
assert(!ai->empty());
- makefile.AddDefinition(*ai, varArgsExpanded.back());
- varArgsExpanded.pop_back();
+ makefile.AddDefinition(*ai, list->back());
+ list->pop_back();
}
// Undefine the rest variables if the list gets empty earlier...
for (; ai != args.cend(); ++ai) {
@@ -298,7 +266,7 @@ bool HandlePopBackCommand(std::vector<std::string> const& args,
}
}
- makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
+ makefile.AddDefinition(listName, list->to_string());
} else if (ai !=
args.cend()) { // The list is empty, but some args were given
@@ -320,8 +288,9 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args,
auto ai = args.cbegin();
++ai; // Skip subcommand name
std::string const& listName = *ai++;
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, makefile)) {
+ auto list = GetList(listName, makefile);
+
+ if (!list) {
// Can't get the list definition... undefine any vars given after.
for (; ai != args.cend(); ++ai) {
makefile.RemoveDefinition(*ai);
@@ -329,25 +298,25 @@ bool HandlePopFrontCommand(std::vector<std::string> const& args,
return true;
}
- if (!varArgsExpanded.empty()) {
+ if (!list->empty()) {
if (ai == args.cend()) {
// No variables are given... Just remove one element.
- varArgsExpanded.erase(varArgsExpanded.begin());
+ list->pop_front();
} else {
// Ok, assign elements to be removed to the given variables
- auto vi = varArgsExpanded.begin();
- for (; vi != varArgsExpanded.end() && ai != args.cend(); ++ai, ++vi) {
+ auto vi = list->begin();
+ for (; vi != list->end() && ai != args.cend(); ++ai, ++vi) {
assert(!ai->empty());
makefile.AddDefinition(*ai, *vi);
}
- varArgsExpanded.erase(varArgsExpanded.begin(), vi);
+ list->erase(list->begin(), vi);
// Undefine the rest variables if the list gets empty earlier...
for (; ai != args.cend(); ++ai) {
makefile.RemoveDefinition(*ai);
}
}
- makefile.AddDefinition(listName, cmJoin(varArgsExpanded, ";"));
+ makefile.AddDefinition(listName, list->to_string());
} else if (ai !=
args.cend()) { // The list is empty, but some args were given
@@ -371,21 +340,16 @@ bool HandleFindCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
const std::string& variableName = args.back();
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
- status.GetMakefile().AddDefinition(variableName, "-1");
- return true;
- }
+ auto list = GetList(listName, status.GetMakefile());
- auto it = std::find(varArgsExpanded.begin(), varArgsExpanded.end(), args[2]);
- if (it != varArgsExpanded.end()) {
- status.GetMakefile().AddDefinition(
- variableName,
- std::to_string(std::distance(varArgsExpanded.begin(), it)));
+ if (!list) {
+ status.GetMakefile().AddDefinition(variableName, "-1");
return true;
}
- status.GetMakefile().AddDefinition(variableName, "-1");
+ auto index = list->find(args[2]);
+ status.GetMakefile().AddDefinition(
+ variableName, index == cmList::npos ? "-1" : std::to_string(index));
return true;
}
@@ -400,38 +364,24 @@ bool HandleInsertCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- int item;
- if (!GetIndexArg(args[2], &item, status.GetMakefile())) {
+ int index;
+ if (!GetIndexArg(args[2], &index, status.GetMakefile())) {
status.SetError(cmStrCat("index: ", args[2], " is not a valid index"));
return false;
}
- std::vector<std::string> varArgsExpanded;
- if ((!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
- varArgsExpanded.empty()) &&
- item != 0) {
- status.SetError(cmStrCat("index: ", item, " out of range (0, 0)"));
- return false;
+ auto list = GetList(listName, status.GetMakefile());
+ if (!list) {
+ list = cmList{};
}
- if (!varArgsExpanded.empty()) {
- size_t nitem = varArgsExpanded.size();
- if (item < 0) {
- item = static_cast<int>(nitem) + item;
- }
- if (item < 0 || nitem < static_cast<size_t>(item)) {
- status.SetError(cmStrCat("index: ", item, " out of range (-",
- varArgsExpanded.size(), ", ",
- varArgsExpanded.size(), ")"));
- return false;
- }
+ try {
+ list->insert_items(index, args.begin() + 3, args.end());
+ status.GetMakefile().AddDefinition(listName, list->to_string());
+ return true;
+ } catch (std::out_of_range& e) {
+ status.SetError(e.what());
+ return false;
}
-
- varArgsExpanded.insert(varArgsExpanded.begin() + item, args.begin() + 3,
- args.end());
-
- std::string value = cmJoin(varArgsExpanded, ";");
- status.GetMakefile().AddDefinition(listName, value);
- return true;
}
bool HandleJoinCommand(std::vector<std::string> const& args,
@@ -448,16 +398,14 @@ bool HandleJoinCommand(std::vector<std::string> const& args,
const std::string& variableName = args[3];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list) {
status.GetMakefile().AddDefinition(variableName, "");
return true;
}
- std::string value =
- cmJoin(cmMakeRange(varArgsExpanded.begin(), varArgsExpanded.end()), glue);
-
- status.GetMakefile().AddDefinition(variableName, value);
+ status.GetMakefile().AddDefinition(variableName, list->join(glue));
return true;
}
@@ -472,21 +420,14 @@ bool HandleRemoveItemCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list) {
return true;
}
- std::vector<std::string> remove(args.begin() + 2, args.end());
- std::sort(remove.begin(), remove.end());
- auto remEnd = std::unique(remove.begin(), remove.end());
- auto remBegin = remove.begin();
-
- auto argsEnd =
- cmRemoveMatching(varArgsExpanded, cmMakeRange(remBegin, remEnd));
- auto argsBegin = varArgsExpanded.cbegin();
- std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
- status.GetMakefile().AddDefinition(listName, value);
+ status.GetMakefile().AddDefinition(
+ listName, list->remove_items(args.begin() + 2, args.end()).to_string());
return true;
}
@@ -501,14 +442,13 @@ bool HandleReverseCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list) {
return true;
}
- std::string value = cmJoin(cmReverseRange(varArgsExpanded), ";");
-
- status.GetMakefile().AddDefinition(listName, value);
+ status.GetMakefile().AddDefinition(listName, list->reverse().to_string());
return true;
}
@@ -523,237 +463,17 @@ bool HandleRemoveDuplicatesCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list) {
return true;
}
- auto argsEnd = cmRemoveDuplicates(varArgsExpanded);
- auto argsBegin = varArgsExpanded.cbegin();
- std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
-
- status.GetMakefile().AddDefinition(listName, value);
+ status.GetMakefile().AddDefinition(listName,
+ list->remove_duplicates().to_string());
return true;
}
-// Helpers for list(TRANSFORM <list> ...)
-using transform_type = std::function<std::string(const std::string&)>;
-
-class transform_error : public std::runtime_error
-{
-public:
- transform_error(const std::string& error)
- : std::runtime_error(error)
- {
- }
-};
-
-class TransformSelector
-{
-public:
- virtual ~TransformSelector() = default;
-
- std::string Tag;
-
- virtual bool Validate(std::size_t count = 0) = 0;
-
- virtual bool InSelection(const std::string&) = 0;
-
- virtual void Transform(std::vector<std::string>& list,
- const transform_type& transform)
- {
- std::transform(list.begin(), list.end(), list.begin(), transform);
- }
-
-protected:
- TransformSelector(std::string&& tag)
- : Tag(std::move(tag))
- {
- }
-};
-class TransformNoSelector : public TransformSelector
-{
-public:
- TransformNoSelector()
- : TransformSelector("NO SELECTOR")
- {
- }
-
- bool Validate(std::size_t) override { return true; }
-
- bool InSelection(const std::string&) override { return true; }
-};
-class TransformSelectorRegex : public TransformSelector
-{
-public:
- TransformSelectorRegex(const std::string& regex)
- : TransformSelector("REGEX")
- , Regex(regex)
- {
- }
-
- bool Validate(std::size_t) override { return this->Regex.is_valid(); }
-
- bool InSelection(const std::string& value) override
- {
- return this->Regex.find(value);
- }
-
- cmsys::RegularExpression Regex;
-};
-class TransformSelectorIndexes : public TransformSelector
-{
-public:
- std::vector<int> Indexes;
-
- bool InSelection(const std::string&) override { return true; }
-
- void Transform(std::vector<std::string>& list,
- const transform_type& transform) override
- {
- this->Validate(list.size());
-
- for (auto index : this->Indexes) {
- list[index] = transform(list[index]);
- }
- }
-
-protected:
- TransformSelectorIndexes(std::string&& tag)
- : TransformSelector(std::move(tag))
- {
- }
- TransformSelectorIndexes(std::string&& tag, std::vector<int>&& indexes)
- : TransformSelector(std::move(tag))
- , Indexes(indexes)
- {
- }
-
- int NormalizeIndex(int index, std::size_t count)
- {
- if (index < 0) {
- index = static_cast<int>(count) + index;
- }
- if (index < 0 || count <= static_cast<std::size_t>(index)) {
- throw transform_error(cmStrCat(
- "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index,
- " out of range (-", count, ", ", count - 1, ")."));
- }
- return index;
- }
-};
-class TransformSelectorAt : public TransformSelectorIndexes
-{
-public:
- TransformSelectorAt(std::vector<int>&& indexes)
- : TransformSelectorIndexes("AT", std::move(indexes))
- {
- }
-
- bool Validate(std::size_t count) override
- {
- decltype(this->Indexes) indexes;
-
- for (auto index : this->Indexes) {
- indexes.push_back(this->NormalizeIndex(index, count));
- }
- this->Indexes = std::move(indexes);
-
- return true;
- }
-};
-class TransformSelectorFor : public TransformSelectorIndexes
-{
-public:
- TransformSelectorFor(int start, int stop, int step)
- : TransformSelectorIndexes("FOR")
- , Start(start)
- , Stop(stop)
- , Step(step)
- {
- }
-
- bool Validate(std::size_t count) override
- {
- this->Start = this->NormalizeIndex(this->Start, count);
- this->Stop = this->NormalizeIndex(this->Stop, count);
-
- // Does stepping move us further from the end?
- if (this->Start > this->Stop) {
- throw transform_error(
- cmStrCat("sub-command TRANSFORM, selector FOR "
- "expects <start> to be no greater than <stop> (",
- this->Start, " > ", this->Stop, ")"));
- }
-
- // compute indexes
- auto size = (this->Stop - this->Start + 1) / this->Step;
- if ((this->Stop - this->Start + 1) % this->Step != 0) {
- size += 1;
- }
-
- this->Indexes.resize(size);
- auto start = this->Start;
- auto step = this->Step;
- std::generate(this->Indexes.begin(), this->Indexes.end(),
- [&start, step]() -> int {
- auto r = start;
- start += step;
- return r;
- });
-
- return true;
- }
-
-private:
- int Start, Stop, Step;
-};
-
-class TransformAction
-{
-public:
- virtual ~TransformAction() = default;
-
- virtual std::string Transform(const std::string& input) = 0;
-};
-class TransformReplace : public TransformAction
-{
-public:
- TransformReplace(const std::vector<std::string>& arguments,
- cmMakefile* makefile)
- : ReplaceHelper(arguments[0], arguments[1], makefile)
- {
- makefile->ClearMatches();
-
- if (!this->ReplaceHelper.IsRegularExpressionValid()) {
- throw transform_error(
- cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile "
- "regex \"",
- arguments[0], "\"."));
- }
- if (!this->ReplaceHelper.IsReplaceExpressionValid()) {
- throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
- this->ReplaceHelper.GetError(), "."));
- }
- }
-
- std::string Transform(const std::string& input) override
- {
- // Scan through the input for all matches.
- std::string output;
-
- if (!this->ReplaceHelper.Replace(input, output)) {
- throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ",
- this->ReplaceHelper.GetError(), "."));
- }
-
- return output;
- }
-
-private:
- cmStringReplaceHelper ReplaceHelper;
-};
-
bool HandleTransformCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
@@ -763,119 +483,50 @@ bool HandleTransformCommand(std::vector<std::string> const& args,
return false;
}
- // Structure collecting all elements of the command
- struct Command
- {
- Command(const std::string& listName)
- : ListName(listName)
- , OutputName(listName)
- {
- }
-
- std::string Name;
- std::string ListName;
- std::vector<std::string> Arguments;
- std::unique_ptr<TransformAction> Action;
- std::unique_ptr<TransformSelector> Selector;
- std::string OutputName;
- } command(args[1]);
-
// Descriptor of action
+ // Action: enum value identifying action
// Arity: number of arguments required for the action
- // Transform: lambda function implementing the action
struct ActionDescriptor
{
ActionDescriptor(std::string name)
: Name(std::move(name))
{
}
- ActionDescriptor(std::string name, int arity, transform_type transform)
+ ActionDescriptor(std::string name, cmList::TransformAction action,
+ int arity)
: Name(std::move(name))
+ , Action(action)
, Arity(arity)
-#if defined(__GNUC__) && __GNUC__ == 6 && defined(__aarch64__)
- // std::function move constructor miscompiles on this architecture
- , Transform(transform)
-#else
- , Transform(std::move(transform))
-#endif
{
}
operator const std::string&() const { return this->Name; }
std::string Name;
+ cmList::TransformAction Action;
int Arity = 0;
- transform_type Transform;
};
// Build a set of supported actions.
std::set<ActionDescriptor,
std::function<bool(const std::string&, const std::string&)>>
- descriptors(
- [](const std::string& x, const std::string& y) { return x < y; });
- descriptors = { { "APPEND", 1,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return s + command.Arguments[0];
- }
-
- return s;
- } },
- { "PREPEND", 1,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return command.Arguments[0] + s;
- }
-
- return s;
- } },
- { "TOUPPER", 0,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return cmSystemTools::UpperCase(s);
- }
-
- return s;
- } },
- { "TOLOWER", 0,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return cmSystemTools::LowerCase(s);
- }
-
- return s;
- } },
- { "STRIP", 0,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return cmTrimWhitespace(s);
- }
-
- return s;
- } },
- { "GENEX_STRIP", 0,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return cmGeneratorExpression::Preprocess(
- s,
- cmGeneratorExpression::StripAllGeneratorExpressions);
- }
-
- return s;
- } },
- { "REPLACE", 2,
- [&command](const std::string& s) -> std::string {
- if (command.Selector->InSelection(s)) {
- return command.Action->Transform(s);
- }
-
- return s;
- } } };
+ descriptors{ { { "APPEND", cmList::TransformAction::APPEND, 1 },
+ { "PREPEND", cmList::TransformAction::PREPEND, 1 },
+ { "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
+ { "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
+ { "STRIP", cmList::TransformAction::STRIP, 0 },
+ { "GENEX_STRIP", cmList::TransformAction::GENEX_STRIP, 0 },
+ { "REPLACE", cmList::TransformAction::REPLACE, 2 } },
+ [](const std::string& x, const std::string& y) {
+ return x < y;
+ } };
+ const std::string& listName = args[1];
+
+ // Parse all possible function parameters
using size_type = std::vector<std::string>::size_type;
size_type index = 2;
- // Parse all possible function parameters
auto descriptor = descriptors.find(args[index]);
if (descriptor == descriptors.end()) {
@@ -893,295 +544,184 @@ bool HandleTransformCommand(std::vector<std::string> const& args,
return false;
}
- command.Name = descriptor->Name;
+ std::vector<std::string> arguments;
index += descriptor->Arity;
if (descriptor->Arity > 0) {
- command.Arguments =
+ arguments =
std::vector<std::string>(args.begin() + 3, args.begin() + index);
}
- if (command.Name == "REPLACE") {
- try {
- command.Action = cm::make_unique<TransformReplace>(
- command.Arguments, &status.GetMakefile());
- } catch (const transform_error& e) {
- status.SetError(e.what());
- return false;
- }
- }
-
const std::string REGEX{ "REGEX" };
const std::string AT{ "AT" };
const std::string FOR{ "FOR" };
const std::string OUTPUT_VARIABLE{ "OUTPUT_VARIABLE" };
+ std::unique_ptr<cmList::TransformSelector> selector;
+ std::string outputName = listName;
- // handle optional arguments
- while (args.size() > index) {
- if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
- command.Selector) {
- status.SetError(
- cmStrCat("sub-command TRANSFORM, selector already specified (",
- command.Selector->Tag, ")."));
-
- return false;
- }
+ try {
+ // handle optional arguments
+ while (args.size() > index) {
+ if ((args[index] == REGEX || args[index] == AT || args[index] == FOR) &&
+ selector) {
+ status.SetError(
+ cmStrCat("sub-command TRANSFORM, selector already specified (",
+ selector->GetTag(), ")."));
- // REGEX selector
- if (args[index] == REGEX) {
- if (args.size() == ++index) {
- status.SetError("sub-command TRANSFORM, selector REGEX expects "
- "'regular expression' argument.");
return false;
}
- command.Selector = cm::make_unique<TransformSelectorRegex>(args[index]);
- if (!command.Selector->Validate()) {
- status.SetError(
- cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile "
- "regex \"",
- args[index], "\"."));
- return false;
- }
+ // REGEX selector
+ if (args[index] == REGEX) {
+ if (args.size() == ++index) {
+ status.SetError("sub-command TRANSFORM, selector REGEX expects "
+ "'regular expression' argument.");
+ return false;
+ }
- index += 1;
- continue;
- }
+ selector =
+ cmList::TransformSelector::New<cmList::TransformSelector::REGEX>(
+ args[index]);
- // AT selector
- if (args[index] == AT) {
- // get all specified indexes
- std::vector<int> indexes;
- while (args.size() > ++index) {
- std::size_t pos;
- int value;
+ index += 1;
+ continue;
+ }
- try {
- value = std::stoi(args[index], &pos);
- if (pos != args[index].length()) {
+ // AT selector
+ if (args[index] == AT) {
+ // get all specified indexes
+ std::vector<cmList::index_type> indexes;
+ while (args.size() > ++index) {
+ std::size_t pos;
+ int value;
+
+ try {
+ value = std::stoi(args[index], &pos);
+ if (pos != args[index].length()) {
+ // this is not a number, stop processing
+ break;
+ }
+ indexes.push_back(value);
+ } catch (const std::invalid_argument&) {
// this is not a number, stop processing
break;
}
- indexes.push_back(value);
- } catch (const std::invalid_argument&) {
- // this is not a number, stop processing
- break;
}
- }
- if (indexes.empty()) {
- status.SetError(
- "sub-command TRANSFORM, selector AT expects at least one "
- "numeric value.");
- return false;
- }
+ if (indexes.empty()) {
+ status.SetError(
+ "sub-command TRANSFORM, selector AT expects at least one "
+ "numeric value.");
+ return false;
+ }
- command.Selector =
- cm::make_unique<TransformSelectorAt>(std::move(indexes));
+ selector =
+ cmList::TransformSelector::New<cmList::TransformSelector::AT>(
+ std::move(indexes));
- continue;
- }
-
- // FOR selector
- if (args[index] == FOR) {
- if (args.size() <= ++index + 1) {
- status.SetError(
- "sub-command TRANSFORM, selector FOR expects, at least,"
- " two arguments.");
- return false;
+ continue;
}
- int start = 0;
- int stop = 0;
- int step = 1;
- bool valid = true;
- try {
- std::size_t pos;
-
- start = std::stoi(args[index], &pos);
- if (pos != args[index].length()) {
- // this is not a number
- valid = false;
- } else {
- stop = std::stoi(args[++index], &pos);
- if (pos != args[index].length()) {
- // this is not a number
- valid = false;
- }
+ // FOR selector
+ if (args[index] == FOR) {
+ if (args.size() <= ++index + 1) {
+ status.SetError(
+ "sub-command TRANSFORM, selector FOR expects, at least,"
+ " two arguments.");
+ return false;
}
- } catch (const std::invalid_argument&) {
- // this is not numbers
- valid = false;
- }
- if (!valid) {
- status.SetError("sub-command TRANSFORM, selector FOR expects, "
- "at least, two numeric values.");
- return false;
- }
- // try to read a third numeric value for step
- if (args.size() > ++index) {
+
+ cmList::index_type start = 0;
+ cmList::index_type stop = 0;
+ cmList::index_type step = 1;
+ bool valid = true;
try {
std::size_t pos;
- step = std::stoi(args[index], &pos);
+ start = std::stoi(args[index], &pos);
if (pos != args[index].length()) {
// this is not a number
- step = 1;
+ valid = false;
} else {
- index += 1;
+ stop = std::stoi(args[++index], &pos);
+ if (pos != args[index].length()) {
+ // this is not a number
+ valid = false;
+ }
}
} catch (const std::invalid_argument&) {
- // this is not number, ignore exception
+ // this is not numbers
+ valid = false;
+ }
+ if (!valid) {
+ status.SetError("sub-command TRANSFORM, selector FOR expects, "
+ "at least, two numeric values.");
+ return false;
+ }
+ // try to read a third numeric value for step
+ if (args.size() > ++index) {
+ try {
+ std::size_t pos;
+
+ step = std::stoi(args[index], &pos);
+ if (pos != args[index].length()) {
+ // this is not a number
+ step = 1;
+ } else {
+ index += 1;
+ }
+ } catch (const std::invalid_argument&) {
+ // this is not number, ignore exception
+ }
}
- }
- if (step <= 0) {
- status.SetError("sub-command TRANSFORM, selector FOR expects "
- "positive numeric value for <step>.");
- return false;
- }
+ if (step <= 0) {
+ status.SetError("sub-command TRANSFORM, selector FOR expects "
+ "positive numeric value for <step>.");
+ return false;
+ }
- command.Selector =
- cm::make_unique<TransformSelectorFor>(start, stop, step);
+ selector =
+ cmList::TransformSelector::New<cmList::TransformSelector::FOR>(
+ { start, stop, step });
- continue;
- }
+ continue;
+ }
- // output variable
- if (args[index] == OUTPUT_VARIABLE) {
- if (args.size() == ++index) {
- status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
- "expects variable name argument.");
- return false;
+ // output variable
+ if (args[index] == OUTPUT_VARIABLE) {
+ if (args.size() == ++index) {
+ status.SetError("sub-command TRANSFORM, OUTPUT_VARIABLE "
+ "expects variable name argument.");
+ return false;
+ }
+
+ outputName = args[index++];
+ continue;
}
- command.OutputName = args[index++];
- continue;
+ status.SetError(cmStrCat("sub-command TRANSFORM, '",
+ cmJoin(cmMakeRange(args).advance(index), " "),
+ "': unexpected argument(s)."));
+ return false;
}
- status.SetError(cmStrCat("sub-command TRANSFORM, '",
- cmJoin(cmMakeRange(args).advance(index), " "),
- "': unexpected argument(s)."));
- return false;
- }
-
- // expand the list variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, command.ListName, status.GetMakefile())) {
- status.GetMakefile().AddDefinition(command.OutputName, "");
- return true;
- }
+ // expand the list variable
+ auto list = GetList(listName, status.GetMakefile());
- if (!command.Selector) {
- // no selector specified, apply transformation to all elements
- command.Selector = cm::make_unique<TransformNoSelector>();
- }
+ if (!list) {
+ status.GetMakefile().AddDefinition(outputName, "");
+ return true;
+ }
- try {
- command.Selector->Transform(varArgsExpanded, descriptor->Transform);
- } catch (const transform_error& e) {
+ list->transform(descriptor->Action, arguments, std::move(selector));
+ status.GetMakefile().AddDefinition(outputName, list->to_string());
+ return true;
+ } catch (cmList::transform_error& e) {
status.SetError(e.what());
return false;
}
-
- status.GetMakefile().AddDefinition(command.OutputName,
- cmJoin(varArgsExpanded, ";"));
-
- return true;
}
-class cmStringSorter
-{
-public:
- enum class Order
- {
- UNINITIALIZED,
- ASCENDING,
- DESCENDING,
- };
-
- enum class Compare
- {
- UNINITIALIZED,
- STRING,
- FILE_BASENAME,
- NATURAL,
- };
- enum class CaseSensitivity
- {
- UNINITIALIZED,
- SENSITIVE,
- INSENSITIVE,
- };
-
-protected:
- using StringFilter = std::string (*)(const std::string&);
- StringFilter GetCompareFilter(Compare compare)
- {
- return (compare == Compare::FILE_BASENAME) ? cmSystemTools::GetFilenameName
- : nullptr;
- }
-
- StringFilter GetCaseFilter(CaseSensitivity sensitivity)
- {
- return (sensitivity == CaseSensitivity::INSENSITIVE)
- ? cmSystemTools::LowerCase
- : nullptr;
- }
-
- using ComparisonFunction =
- std::function<bool(const std::string&, const std::string&)>;
- ComparisonFunction GetComparisonFunction(Compare compare)
- {
- if (compare == Compare::NATURAL) {
- return std::function<bool(const std::string&, const std::string&)>(
- [](const std::string& x, const std::string& y) {
- return cmSystemTools::strverscmp(x, y) < 0;
- });
- }
- return std::function<bool(const std::string&, const std::string&)>(
- [](const std::string& x, const std::string& y) { return x < y; });
- }
-
-public:
- cmStringSorter(Compare compare, CaseSensitivity caseSensitivity,
- Order desc = Order::ASCENDING)
- : filters{ this->GetCompareFilter(compare),
- this->GetCaseFilter(caseSensitivity) }
- , sortMethod(this->GetComparisonFunction(compare))
- , descending(desc == Order::DESCENDING)
- {
- }
-
- std::string ApplyFilter(const std::string& argument)
- {
- std::string result = argument;
- for (auto filter : this->filters) {
- if (filter != nullptr) {
- result = filter(result);
- }
- }
- return result;
- }
-
- bool operator()(const std::string& a, const std::string& b)
- {
- std::string af = this->ApplyFilter(a);
- std::string bf = this->ApplyFilter(b);
- bool result;
- if (this->descending) {
- result = this->sortMethod(bf, af);
- } else {
- result = this->sortMethod(af, bf);
- }
- return result;
- }
-
-protected:
- StringFilter filters[2] = { nullptr, nullptr };
- ComparisonFunction sortMethod;
- bool descending;
-};
-
bool HandleSortCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
@@ -1191,9 +731,8 @@ bool HandleSortCommand(std::vector<std::string> const& args,
return false;
}
- auto sortCompare = cmStringSorter::Compare::UNINITIALIZED;
- auto sortCaseSensitivity = cmStringSorter::CaseSensitivity::UNINITIALIZED;
- auto sortOrder = cmStringSorter::Order::UNINITIALIZED;
+ using SortConfig = cmList::SortConfiguration;
+ SortConfig sortConfig;
size_t argumentIndex = 2;
const std::string messageHint = "sub-command SORT ";
@@ -1201,7 +740,7 @@ bool HandleSortCommand(std::vector<std::string> const& args,
while (argumentIndex < args.size()) {
std::string const& option = args[argumentIndex++];
if (option == "COMPARE") {
- if (sortCompare != cmStringSorter::Compare::UNINITIALIZED) {
+ if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) {
std::string error = cmStrCat(messageHint, "option \"", option,
"\" has been specified multiple times.");
status.SetError(error);
@@ -1210,11 +749,11 @@ bool HandleSortCommand(std::vector<std::string> const& args,
if (argumentIndex < args.size()) {
std::string const& argument = args[argumentIndex++];
if (argument == "STRING") {
- sortCompare = cmStringSorter::Compare::STRING;
+ sortConfig.Compare = SortConfig::CompareMethod::STRING;
} else if (argument == "FILE_BASENAME") {
- sortCompare = cmStringSorter::Compare::FILE_BASENAME;
+ sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME;
} else if (argument == "NATURAL") {
- sortCompare = cmStringSorter::Compare::NATURAL;
+ sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
} else {
std::string error =
cmStrCat(messageHint, "value \"", argument, "\" for option \"",
@@ -1228,8 +767,7 @@ bool HandleSortCommand(std::vector<std::string> const& args,
return false;
}
} else if (option == "CASE") {
- if (sortCaseSensitivity !=
- cmStringSorter::CaseSensitivity::UNINITIALIZED) {
+ if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) {
status.SetError(cmStrCat(messageHint, "option \"", option,
"\" has been specified multiple times."));
return false;
@@ -1237,9 +775,9 @@ bool HandleSortCommand(std::vector<std::string> const& args,
if (argumentIndex < args.size()) {
std::string const& argument = args[argumentIndex++];
if (argument == "SENSITIVE") {
- sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
+ sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
} else if (argument == "INSENSITIVE") {
- sortCaseSensitivity = cmStringSorter::CaseSensitivity::INSENSITIVE;
+ sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
} else {
status.SetError(cmStrCat(messageHint, "value \"", argument,
"\" for option \"", option,
@@ -1253,7 +791,7 @@ bool HandleSortCommand(std::vector<std::string> const& args,
}
} else if (option == "ORDER") {
- if (sortOrder != cmStringSorter::Order::UNINITIALIZED) {
+ if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
status.SetError(cmStrCat(messageHint, "option \"", option,
"\" has been specified multiple times."));
return false;
@@ -1261,9 +799,9 @@ bool HandleSortCommand(std::vector<std::string> const& args,
if (argumentIndex < args.size()) {
std::string const& argument = args[argumentIndex++];
if (argument == "ASCENDING") {
- sortOrder = cmStringSorter::Order::ASCENDING;
+ sortConfig.Order = SortConfig::OrderMode::ASCENDING;
} else if (argument == "DESCENDING") {
- sortOrder = cmStringSorter::Order::DESCENDING;
+ sortConfig.Order = SortConfig::OrderMode::DESCENDING;
} else {
status.SetError(cmStrCat(messageHint, "value \"", argument,
"\" for option \"", option,
@@ -1281,35 +819,17 @@ bool HandleSortCommand(std::vector<std::string> const& args,
return false;
}
}
- // set Default Values if Option is not given
- if (sortCompare == cmStringSorter::Compare::UNINITIALIZED) {
- sortCompare = cmStringSorter::Compare::STRING;
- }
- if (sortCaseSensitivity == cmStringSorter::CaseSensitivity::UNINITIALIZED) {
- sortCaseSensitivity = cmStringSorter::CaseSensitivity::SENSITIVE;
- }
- if (sortOrder == cmStringSorter::Order::UNINITIALIZED) {
- sortOrder = cmStringSorter::Order::ASCENDING;
- }
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
- return true;
- }
+ auto list = GetList(listName, status.GetMakefile());
- if ((sortCompare == cmStringSorter::Compare::STRING) &&
- (sortCaseSensitivity == cmStringSorter::CaseSensitivity::SENSITIVE) &&
- (sortOrder == cmStringSorter::Order::ASCENDING)) {
- std::sort(varArgsExpanded.begin(), varArgsExpanded.end());
- } else {
- cmStringSorter sorter(sortCompare, sortCaseSensitivity, sortOrder);
- std::sort(varArgsExpanded.begin(), varArgsExpanded.end(), sorter);
+ if (!list) {
+ return true;
}
- std::string value = cmJoin(varArgsExpanded, ";");
- status.GetMakefile().AddDefinition(listName, value);
+ status.GetMakefile().AddDefinition(listName,
+ list->sort(sortConfig).to_string());
return true;
}
@@ -1326,9 +846,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args,
const std::string& variableName = args.back();
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
- varArgsExpanded.empty()) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list || list->empty()) {
status.GetMakefile().AddDefinition(variableName, "");
return true;
}
@@ -1344,11 +864,9 @@ bool HandleSublistCommand(std::vector<std::string> const& args,
return false;
}
- using size_type = decltype(varArgsExpanded)::size_type;
-
- if (start < 0 || static_cast<size_type>(start) >= varArgsExpanded.size()) {
+ if (start < 0) {
status.SetError(cmStrCat("begin index: ", start, " is out of range 0 - ",
- varArgsExpanded.size() - 1));
+ list->size() - 1));
return false;
}
if (length < -1) {
@@ -1356,15 +874,17 @@ bool HandleSublistCommand(std::vector<std::string> const& args,
return false;
}
- const size_type end =
- (length == -1 ||
- static_cast<size_type>(start + length) > varArgsExpanded.size())
- ? varArgsExpanded.size()
- : static_cast<size_type>(start + length);
- std::vector<std::string> sublist(varArgsExpanded.begin() + start,
- varArgsExpanded.begin() + end);
- status.GetMakefile().AddDefinition(variableName, cmJoin(sublist, ";"));
- return true;
+ using size_type = cmList::size_type;
+
+ try {
+ auto sublist = list->sublist(static_cast<size_type>(start),
+ static_cast<size_type>(length));
+ status.GetMakefile().AddDefinition(variableName, sublist.to_string());
+ return true;
+ } catch (std::out_of_range& e) {
+ status.SetError(e.what());
+ return false;
+ }
}
bool HandleRemoveAtCommand(std::vector<std::string> const& args,
@@ -1378,9 +898,9 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile()) ||
- varArgsExpanded.empty()) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list || list->empty()) {
std::ostringstream str;
str << "index: ";
for (size_t i = 1; i < args.size(); ++i) {
@@ -1395,36 +915,25 @@ bool HandleRemoveAtCommand(std::vector<std::string> const& args,
}
size_t cc;
- std::vector<size_t> removed;
- size_t nitem = varArgsExpanded.size();
+ std::vector<cmList::index_type> removed;
for (cc = 2; cc < args.size(); ++cc) {
- int item;
- if (!GetIndexArg(args[cc], &item, status.GetMakefile())) {
+ int index;
+ if (!GetIndexArg(args[cc], &index, status.GetMakefile())) {
status.SetError(cmStrCat("index: ", args[cc], " is not a valid index"));
return false;
}
- if (item < 0) {
- item = static_cast<int>(nitem) + item;
- }
- if (item < 0 || nitem <= static_cast<size_t>(item)) {
- status.SetError(cmStrCat("index: ", item, " out of range (-", nitem,
- ", ", nitem - 1, ")"));
- return false;
- }
- removed.push_back(static_cast<size_t>(item));
+ removed.push_back(index);
}
- std::sort(removed.begin(), removed.end());
- auto remEnd = std::unique(removed.begin(), removed.end());
- auto remBegin = removed.begin();
-
- auto argsEnd =
- cmRemoveIndices(varArgsExpanded, cmMakeRange(remBegin, remEnd));
- auto argsBegin = varArgsExpanded.cbegin();
- std::string value = cmJoin(cmMakeRange(argsBegin, argsEnd), ";");
-
- status.GetMakefile().AddDefinition(listName, value);
- return true;
+ try {
+ status.GetMakefile().AddDefinition(
+ listName,
+ list->remove_items(removed.begin(), removed.end()).to_string());
+ return true;
+ } catch (std::out_of_range& e) {
+ status.SetError(e.what());
+ return false;
+ }
}
bool HandleFilterCommand(std::vector<std::string> const& args,
@@ -1447,11 +956,11 @@ bool HandleFilterCommand(std::vector<std::string> const& args,
}
const std::string& op = args[2];
- bool includeMatches;
+ cmList::FilterMode filterMode;
if (op == "INCLUDE") {
- includeMatches = true;
+ filterMode = cmList::FilterMode::INCLUDE;
} else if (op == "EXCLUDE") {
- includeMatches = false;
+ filterMode = cmList::FilterMode::EXCLUDE;
} else {
status.SetError("sub-command FILTER does not recognize operator " + op);
return false;
@@ -1459,70 +968,33 @@ bool HandleFilterCommand(std::vector<std::string> const& args,
const std::string& listName = args[1];
// expand the variable
- std::vector<std::string> varArgsExpanded;
- if (!GetList(varArgsExpanded, listName, status.GetMakefile())) {
+ auto list = GetList(listName, status.GetMakefile());
+
+ if (!list) {
return true;
}
const std::string& mode = args[3];
- if (mode == "REGEX") {
- if (args.size() != 5) {
- status.SetError("sub-command FILTER, mode REGEX "
- "requires five arguments.");
- return false;
- }
- return FilterRegex(args, includeMatches, listName, varArgsExpanded,
- status);
- }
-
- status.SetError("sub-command FILTER does not recognize mode " + mode);
- return false;
-}
-
-class MatchesRegex
-{
-public:
- MatchesRegex(cmsys::RegularExpression& in_regex, bool in_includeMatches)
- : regex(in_regex)
- , includeMatches(in_includeMatches)
- {
+ if (mode != "REGEX") {
+ status.SetError("sub-command FILTER does not recognize mode " + mode);
+ return false;
}
-
- bool operator()(const std::string& target)
- {
- return this->regex.find(target) ^ this->includeMatches;
+ if (args.size() != 5) {
+ status.SetError("sub-command FILTER, mode REGEX "
+ "requires five arguments.");
+ return false;
}
-
-private:
- cmsys::RegularExpression& regex;
- const bool includeMatches;
-};
-
-bool FilterRegex(std::vector<std::string> const& args, bool includeMatches,
- std::string const& listName,
- std::vector<std::string>& varArgsExpanded,
- cmExecutionStatus& status)
-{
const std::string& pattern = args[4];
- cmsys::RegularExpression regex(pattern);
- if (!regex.is_valid()) {
- std::string error =
- cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"",
- pattern, "\".");
- status.SetError(error);
+
+ try {
+ status.GetMakefile().AddDefinition(
+ listName, list->filter(pattern, filterMode).to_string());
+ return true;
+ } catch (std::invalid_argument& e) {
+ status.SetError(e.what());
return false;
}
-
- auto argsBegin = varArgsExpanded.begin();
- auto argsEnd = varArgsExpanded.end();
- auto newArgsEnd =
- std::remove_if(argsBegin, argsEnd, MatchesRegex(regex, includeMatches));
-
- std::string value = cmJoin(cmMakeRange(argsBegin, newArgsEnd), ";");
- status.GetMakefile().AddDefinition(listName, value);
- return true;
}
-
} // namespace
bool cmListCommand(std::vector<std::string> const& args,
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 75ec69426d..01e4241360 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -85,6 +85,7 @@ static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
"CMAKE_RANLIB",
"CMAKE_LINKER",
"CMAKE_MT",
+ "CMAKE_TAPI",
"CMAKE_CUDA_HOST_COMPILER",
"CMAKE_CUDA_HOST_LINK_LAUNCHER",
"CMAKE_CL_SHOWINCLUDES_PREFIX" };
@@ -134,6 +135,13 @@ cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
}
+ // OSX SYSROOT can be required by some tools, like tapi
+ {
+ cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
+ this->VariableMappings["CMAKE_OSX_SYSROOT"] =
+ osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true);
+ }
+
if (cmValue appleArchSysroots =
this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
std::string const& appleArchs =
@@ -827,13 +835,18 @@ cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const
return this->Makefile->GetStateSnapshot();
}
-cmValue cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
- const std::string& prop)
+std::string cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
+ const std::string& prop,
+ const std::string& config)
{
+ cmValue value = this->Makefile->GetProperty(prop);
if (target) {
- return target->GetProperty(prop);
+ value = target->GetProperty(prop);
}
- return this->Makefile->GetProperty(prop);
+ if (value) {
+ return cmGeneratorExpression::Evaluate(*value, this, config, target);
+ }
+ return "";
}
std::string cmLocalGenerator::ConvertToIncludeReference(
@@ -1639,7 +1652,7 @@ static std::string GetFrameworkFlags(const std::string& lang,
cmLocalGenerator* lg = target->GetLocalGenerator();
cmMakefile* mf = lg->GetMakefile();
- if (!mf->IsOn("APPLE")) {
+ if (!target->IsApple()) {
return std::string();
}
@@ -1813,7 +1826,7 @@ std::string cmLocalGenerator::GetLinkLibsCMP0065(
// OLD behavior is to always add the flags, except on AIX where
// we compute symbol exports if ENABLE_EXPORTS is on.
add_shlib_flags =
- !(tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"));
+ !(tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"));
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
@@ -1825,7 +1838,7 @@ std::string cmLocalGenerator::GetLinkLibsCMP0065(
// NEW behavior is to only add the flags if ENABLE_EXPORTS is on,
// except on AIX where we compute symbol exports.
add_shlib_flags =
- !tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS");
+ !tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS");
break;
}
@@ -1859,9 +1872,8 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags,
const std::string& filterArch)
{
// Only add Apple specific flags on Apple platforms
- if (this->Makefile->IsOn("APPLE") && this->EmitUniversalBinaryFlags) {
- std::vector<std::string> archs;
- target->GetAppleArchs(config, archs);
+ if (target->IsApple() && this->EmitUniversalBinaryFlags) {
+ std::vector<std::string> archs = target->GetAppleArchs(config, lang);
if (!archs.empty() &&
(lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX" ||
lang == "ASM")) {
@@ -2581,7 +2593,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
std::vector<std::string> architectures;
if (!this->GetGlobalGenerator()->IsXcode()) {
- target->GetAppleArchs(config, architectures);
+ architectures = target->GetAppleArchs(config, lang);
}
if (architectures.empty()) {
architectures.emplace_back();
@@ -2841,7 +2853,6 @@ void cmLocalGenerator::CopyPchCompilePdb(
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetCommandLines(commandLines);
cc->SetComment(no_message);
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetStdPipesUTF8(true);
if (this->GetGlobalGenerator()->IsVisualStudio()) {
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 20f23de810..bda82bce00 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -532,7 +532,9 @@ public:
void CreateEvaluationFileOutputs(const std::string& config);
void ProcessEvaluationFiles(std::vector<std::string>& generatedFiles);
- cmValue GetRuleLauncher(cmGeneratorTarget* target, const std::string& prop);
+ std::string GetRuleLauncher(cmGeneratorTarget* target,
+ const std::string& prop,
+ const std::string& config);
protected:
// The default implementation converts to a Windows shortpath to
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 3443cd31b3..d0bd3758f4 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -5,11 +5,11 @@
#include <algorithm>
#include <cassert>
#include <cstdio>
-#include <iterator>
#include <memory>
#include <sstream>
#include <utility>
+#include <cm/unordered_set>
#include <cmext/string_view>
#include "cmsys/FStream.hxx"
@@ -26,6 +26,7 @@
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmNinjaTargetGenerator.h"
+#include "cmNinjaTypes.h"
#include "cmPolicies.h"
#include "cmRulePlaceholderExpander.h"
#include "cmSourceFile.h"
@@ -601,34 +602,32 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
continue;
}
- cmNinjaDeps orderOnlyDeps;
-
- // A custom command may appear on multiple targets. However, some build
- // systems exist where the target dependencies on some of the targets are
- // overspecified, leading to a dependency cycle. If we assume all target
- // dependencies are a superset of the true target dependencies for this
- // custom command, we can take the set intersection of all target
- // dependencies to obtain a correct dependency list.
- //
- // FIXME: This won't work in certain obscure scenarios involving indirect
- // dependencies.
- auto j = targets.begin();
- assert(j != targets.end());
- this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
- *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
- std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
- ++j;
-
- for (; j != targets.end(); ++j) {
- std::vector<std::string> jDeps;
- std::vector<std::string> depsIntersection;
+ std::unordered_set<std::string> orderOnlyDeps;
+
+ if (!cc->GetDependsExplicitOnly()) {
+ // A custom command may appear on multiple targets. However, some build
+ // systems exist where the target dependencies on some of the targets are
+ // overspecified, leading to a dependency cycle. If we assume all target
+ // dependencies are a superset of the true target dependencies for this
+ // custom command, we can take the set intersection of all target
+ // dependencies to obtain a correct dependency list.
+ //
+ // FIXME: This won't work in certain obscure scenarios involving indirect
+ // dependencies.
+ auto j = targets.begin();
+ assert(j != targets.end());
this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
- *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
- std::sort(jDeps.begin(), jDeps.end());
- std::set_intersection(orderOnlyDeps.begin(), orderOnlyDeps.end(),
- jDeps.begin(), jDeps.end(),
- std::back_inserter(depsIntersection));
- orderOnlyDeps = depsIntersection;
+ *j, orderOnlyDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
+ ++j;
+
+ for (; j != targets.end(); ++j) {
+ std::unordered_set<std::string> jDeps;
+ this->GetGlobalNinjaGenerator()->AppendTargetDependsClosure(
+ *j, jDeps, ccg.GetOutputConfig(), fileConfig, ccgs.size() > 1);
+ cm::erase_if(orderOnlyDeps, [&jDeps](std::string const& dep) {
+ return jDeps.find(dep) == jDeps.end();
+ });
+ }
}
const std::vector<std::string>& outputs = ccg.GetOutputs();
@@ -656,13 +655,17 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
std::vector<std::string> cmdLines;
this->AppendCustomCommandLines(ccg, cmdLines);
+ cmNinjaDeps sortedOrderOnlyDeps(orderOnlyDeps.begin(),
+ orderOnlyDeps.end());
+ std::sort(sortedOrderOnlyDeps.begin(), sortedOrderOnlyDeps.end());
+
if (cmdLines.empty()) {
cmNinjaBuild build("phony");
build.Comment = cmStrCat("Phony custom command for ", mainOutput);
build.Outputs = std::move(ccOutputs.ExplicitOuts);
build.WorkDirOuts = std::move(ccOutputs.WorkDirOuts);
build.ExplicitDeps = std::move(ninjaDeps);
- build.OrderOnlyDeps = orderOnlyDeps;
+ build.OrderOnlyDeps = std::move(sortedOrderOnlyDeps);
gg->WriteBuild(this->GetImplFileStream(fileConfig), build);
} else {
std::string customStep = cmSystemTools::GetFilenameName(mainOutput);
@@ -708,7 +711,8 @@ void cmLocalNinjaGenerator::WriteCustomCommandBuildStatement(
this->ConstructComment(ccg), comment, depfile, cc->GetJobPool(),
cc->GetUsesTerminal(),
/*restat*/ !symbolic || !byproducts.empty(), fileConfig,
- std::move(ccOutputs), std::move(ninjaDeps), std::move(orderOnlyDeps));
+ std::move(ccOutputs), std::move(ninjaDeps),
+ std::move(sortedOrderOnlyDeps));
}
}
}
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 7172d34a75..56a41b1a98 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -1003,7 +1003,9 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand(
std::string launcher;
// Short-circuit if there is no launcher.
- cmValue val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM");
+ std::string val = this->GetRuleLauncher(
+ target, "RULE_LAUNCH_CUSTOM",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
// Expand rule variables referenced in the given launcher command.
cmRulePlaceholderExpander::RuleVariables vars;
@@ -1022,7 +1024,7 @@ void cmLocalUnixMakefileGenerator3::AppendCustomCommand(
}
vars.Output = output.c_str();
- launcher = *val;
+ launcher = val;
rulePlaceholderExpander->ExpandRuleVariables(this, launcher, vars);
if (!launcher.empty()) {
launcher += " ";
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index ded1647029..239748db67 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -141,7 +141,6 @@ void cmLocalVisualStudio7Generator::FixGlobalTargets()
cc->SetOutputs(force);
cc->SetCommandLines(force_commands);
cc->SetComment(" ");
- cc->SetCMP0116Status(cmPolicies::NEW);
if (cmSourceFile* file =
this->AddCustomCommandToOutput(std::move(cc), true)) {
l->AddSource(file->ResolveFullPath());
@@ -269,9 +268,9 @@ cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule()
cc->SetDepends(listFiles);
cc->SetCommandLines(commandLines);
cc->SetComment(comment.c_str());
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetStdPipesUTF8(true);
+ cc->SetUsesTerminal(true);
this->AddCustomCommandToOutput(std::move(cc), true);
if (cmSourceFile* file = this->Makefile->GetSource(makefileIn)) {
// Finalize the source file path now since we're adding this after
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index d26f383dca..3a69b2eeed 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -67,6 +67,24 @@
# include "cmVariableWatch.h"
#endif
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+// Select a recursion limit that fits within the stack size.
+// See stack size flags in '../CompileFlags.cmake'.
+#ifndef CMake_DEFAULT_RECURSION_LIMIT
+# if __has_feature(address_sanitizer)
+# define CMake_DEFAULT_RECURSION_LIMIT 400
+# elif defined(_MSC_VER) && defined(_DEBUG)
+# define CMake_DEFAULT_RECURSION_LIMIT 600
+# elif defined(__ibmxl__) && defined(__linux)
+# define CMake_DEFAULT_RECURSION_LIMIT 600
+# else
+# define CMake_DEFAULT_RECURSION_LIMIT 1000
+# endif
+#endif
+
class cmMessenger;
cmDirectoryId::cmDirectoryId(std::string s)
@@ -99,7 +117,6 @@ cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator,
this->StateSnapshot =
this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot(
this->StateSnapshot);
- this->RecursionDepth = 0;
// Enter a policy level for this directory.
this->PushPolicy();
@@ -208,32 +225,47 @@ bool cmMakefile::CheckCMP0037(std::string const& targetName,
return true;
}
-void cmMakefile::MaybeWarnCMP0074(std::string const& pkg)
+void cmMakefile::MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef,
+ cm::optional<std::string> const& rootEnv)
{
- // Warn if a <pkg>_ROOT variable we may use is set.
- std::string const varName = pkg + "_ROOT";
- cmValue var = this->GetDefinition(varName);
- std::string env;
- cmSystemTools::GetEnv(varName, env);
-
- bool const haveVar = cmNonempty(var);
- bool const haveEnv = !env.empty();
- if ((haveVar || haveEnv) && this->WarnedCMP0074.insert(varName).second) {
+ // Warn if a <PackageName>_ROOT variable we may use is set.
+ if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) {
std::ostringstream w;
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0074) << "\n";
- if (haveVar) {
- w << "CMake variable " << varName << " is set to:\n"
- << " " << *var << "\n";
+ if (rootDef) {
+ w << "CMake variable " << rootVar << " is set to:\n"
+ << " " << *rootDef << "\n";
}
- if (haveEnv) {
- w << "Environment variable " << varName << " is set to:\n"
- << " " << env << "\n";
+ if (rootEnv) {
+ w << "Environment variable " << rootVar << " is set to:\n"
+ << " " << *rootEnv << "\n";
}
w << "For compatibility, CMake is ignoring the variable.";
this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
}
}
+void cmMakefile::MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF,
+ cm::optional<std::string> const& rootENV)
+{
+ // Warn if a <PACKAGENAME>_ROOT variable we may use is set.
+ if ((rootDEF || rootENV) && this->WarnedCMP0144.insert(rootVAR).second) {
+ std::ostringstream w;
+ w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0144) << "\n";
+ if (rootDEF) {
+ w << "CMake variable " << rootVAR << " is set to:\n"
+ << " " << *rootDEF << "\n";
+ }
+ if (rootENV) {
+ w << "Environment variable " << rootVAR << " is set to:\n"
+ << " " << *rootENV << "\n";
+ }
+ w << "For compatibility, find_package is ignoring the variable, but "
+ "code in a .cmake module might still use it.";
+ this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
+ }
+}
+
cmBTStringRange cmMakefile::GetIncludeDirectoriesEntries() const
{
return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries();
@@ -308,7 +340,7 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff,
cm::optional<std::string> const& deferId = bt.Top().DeferId;
switch (this->GetCMakeInstance()->GetTraceFormat()) {
- case cmake::TraceFormat::TRACE_JSON_V1: {
+ case cmake::TraceFormat::JSONv1: {
#ifndef CMAKE_BOOTSTRAP
Json::Value val;
Json::StreamWriterBuilder builder;
@@ -335,7 +367,7 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff,
#endif
break;
}
- case cmake::TraceFormat::TRACE_HUMAN:
+ case cmake::TraceFormat::Human:
msg << full_path << "(" << lff.Line() << "):";
if (deferId) {
msg << "DEFERRED:" << *deferId << ":";
@@ -347,8 +379,8 @@ void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff,
}
msg << ")";
break;
- case cmake::TraceFormat::TRACE_UNDEFINED:
- msg << "INTERNAL ERROR: Trace format is TRACE_UNDEFINED";
+ case cmake::TraceFormat::Undefined:
+ msg << "INTERNAL ERROR: Trace format is Undefined";
break;
}
@@ -439,18 +471,10 @@ bool cmMakefile::ExecuteCommand(const cmListFileFunction& lff,
static_cast<void>(stack_manager);
// Check for maximum recursion depth.
- int depth = CMake_DEFAULT_RECURSION_LIMIT;
- cmValue depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
- if (depthStr) {
- std::istringstream s(*depthStr);
- int d;
- if (s >> d) {
- depth = d;
- }
- }
- if (this->RecursionDepth > depth) {
+ size_t depthLimit = this->GetRecursionDepthLimit();
+ if (this->RecursionDepth > depthLimit) {
std::ostringstream e;
- e << "Maximum recursion depth of " << depth << " exceeded";
+ e << "Maximum recursion depth of " << depthLimit << " exceeded";
this->IssueMessage(MessageType::FATAL_ERROR, e.str());
cmSystemTools::SetFatalErrorOccurred();
return false;
@@ -1117,7 +1141,7 @@ cmTarget* cmMakefile::AddCustomCommandToTarget(
// Always create the byproduct sources and mark them generated.
this->CreateGeneratedOutputs(byproducts);
- cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116));
+ cc->RecordPolicyValues(this->GetStateSnapshot());
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
@@ -1156,7 +1180,7 @@ void cmMakefile::AddCustomCommandToOutput(
this->CreateGeneratedOutputs(outputs);
this->CreateGeneratedOutputs(byproducts);
- cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116));
+ cc->RecordPolicyValues(this->GetStateSnapshot());
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
@@ -1214,7 +1238,7 @@ void cmMakefile::AddCustomCommandOldStyle(
// Each output must get its own copy of this rule.
cmsys::RegularExpression sourceFiles(
- "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|ixx|cppm|cu|m|mm|"
+ "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|mpp|ixx|cppm|ccm|cxxm|c\\+\\+m|cu|m|mm|"
"rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|"
"hm|hpp|hxx|in|txx|inl)$");
@@ -1274,7 +1298,7 @@ cmTarget* cmMakefile::AddUtilityCommand(const std::string& utilityName,
// Always create the byproduct sources and mark them generated.
this->CreateGeneratedOutputs(byproducts);
- cc->SetCMP0116Status(this->GetPolicyStatus(cmPolicies::CMP0116));
+ cc->RecordPolicyValues(this->GetStateSnapshot());
// Dispatch command creation to allow generator expressions in outputs.
this->AddGeneratorAction(
@@ -2115,12 +2139,21 @@ cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type,
return &this->CreateNewTarget(name, type).first;
}
+cmTarget* cmMakefile::AddSynthesizedTarget(cmStateEnums::TargetType type,
+ const std::string& name)
+{
+ return &this
+ ->CreateNewTarget(name, type, cmTarget::PerConfig::Yes,
+ cmTarget::Visibility::Generated)
+ .first;
+}
+
std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget(
const std::string& name, cmStateEnums::TargetType type,
- cmTarget::PerConfig perConfig)
+ cmTarget::PerConfig perConfig, cmTarget::Visibility vis)
{
- auto ib = this->Targets.emplace(
- name, cmTarget(name, type, cmTarget::VisibilityNormal, this, perConfig));
+ auto ib =
+ this->Targets.emplace(name, cmTarget(name, type, vis, this, perConfig));
auto it = ib.first;
if (!ib.second) {
return std::make_pair(std::ref(it->second), false);
@@ -2469,6 +2502,11 @@ bool cmMakefile::PlatformIsAppleEmbedded() const
return this->GetAppleSDKType() != AppleSDK::MacOS;
}
+bool cmMakefile::PlatformSupportsAppleTextStubs() const
+{
+ return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI");
+}
+
const char* cmMakefile::GetSONameFlag(const std::string& language) const
{
std::string name = "CMAKE_SHARED_LIBRARY_SONAME";
@@ -2836,12 +2874,31 @@ bool cmMakefile::IsProjectFile(const char* filename) const
!cmSystemTools::IsSubDirectory(filename, "/CMakeFiles"));
}
-int cmMakefile::GetRecursionDepth() const
+size_t cmMakefile::GetRecursionDepthLimit() const
+{
+ size_t depth = CMake_DEFAULT_RECURSION_LIMIT;
+ if (cmValue depthStr =
+ this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH")) {
+ unsigned long depthUL;
+ if (cmStrToULong(depthStr.GetCStr(), &depthUL)) {
+ depth = depthUL;
+ }
+ } else if (cm::optional<std::string> depthEnv =
+ cmSystemTools::GetEnvVar("CMAKE_MAXIMUM_RECURSION_DEPTH")) {
+ unsigned long depthUL;
+ if (cmStrToULong(*depthEnv, &depthUL)) {
+ depth = depthUL;
+ }
+ }
+ return depth;
+}
+
+size_t cmMakefile::GetRecursionDepth() const
{
return this->RecursionDepth;
}
-void cmMakefile::SetRecursionDepth(int recursionDepth)
+void cmMakefile::SetRecursionDepth(size_t recursionDepth)
{
this->RecursionDepth = recursionDepth;
}
@@ -4203,8 +4260,8 @@ cmTarget* cmMakefile::AddImportedTarget(const std::string& name,
// Create the target.
std::unique_ptr<cmTarget> target(
new cmTarget(name, type,
- global ? cmTarget::VisibilityImportedGlobally
- : cmTarget::VisibilityImported,
+ global ? cmTarget::Visibility::ImportedGlobally
+ : cmTarget::Visibility::Imported,
this, cmTarget::PerConfig::Yes));
// Add to the set of available imported targets.
@@ -4486,7 +4543,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id,
}
// Deprecate old policies.
- if (status == cmPolicies::OLD && id <= cmPolicies::CMP0108 &&
+ if (status == cmPolicies::OLD && id <= cmPolicies::CMP0114 &&
!(this->GetCMakeInstance()->GetIsInTryCompile() &&
(
// Policies set by cmCoreTryCompile::TryCompileCode.
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 3866aca7f2..a43ff41f9c 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -241,10 +241,13 @@ public:
std::pair<cmTarget&, bool> CreateNewTarget(
const std::string& name, cmStateEnums::TargetType type,
- cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes);
+ cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes,
+ cmTarget::Visibility vis = cmTarget::Visibility::Normal);
cmTarget* AddNewTarget(cmStateEnums::TargetType type,
const std::string& name);
+ cmTarget* AddSynthesizedTarget(cmStateEnums::TargetType type,
+ const std::string& name);
/** Create a target instance for the utility. */
cmTarget* AddNewUtilityTarget(const std::string& utilityName,
@@ -559,6 +562,10 @@ public:
/** Return whether the target platform is Apple iOS. */
bool PlatformIsAppleEmbedded() const;
+ /** Return whether the target platform supports generation of text base stubs
+ (.tbd file) describing exports (Apple specific). */
+ bool PlatformSupportsAppleTextStubs() const;
+
/** Retrieve soname flag for the specified language if supported */
const char* GetSONameFlag(const std::string& language) const;
@@ -1008,13 +1015,18 @@ public:
bool GetDebugFindPkgMode() const;
- void MaybeWarnCMP0074(std::string const& pkg);
+ void MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef,
+ cm::optional<std::string> const& rootEnv);
+ void MaybeWarnCMP0144(std::string const& rootVAR, cmValue rootDEF,
+ cm::optional<std::string> const& rootENV);
void MaybeWarnUninitialized(std::string const& variable,
const char* sourceFilename) const;
bool IsProjectFile(const char* filename) const;
- int GetRecursionDepth() const;
- void SetRecursionDepth(int recursionDepth);
+ size_t GetRecursionDepthLimit() const;
+
+ size_t GetRecursionDepth() const;
+ void SetRecursionDepth(size_t recursionDepth);
std::string NewDeferId() const;
bool DeferCall(std::string id, std::string fileName, cmListFileFunction lff);
@@ -1080,7 +1092,7 @@ protected:
private:
cmStateSnapshot StateSnapshot;
cmListFileBacktrace Backtrace;
- int RecursionDepth;
+ size_t RecursionDepth = 0;
struct DeferCommand
{
@@ -1186,6 +1198,7 @@ private:
bool CheckSystemVars;
bool CheckCMP0000;
std::set<std::string> WarnedCMP0074;
+ std::set<std::string> WarnedCMP0144;
bool IsSourceFileTryCompile;
mutable bool SuppressSideEffects;
ImportedTargetScope CurrentImportedTargetScope = ImportedTargetScope::Local;
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index e53d28cec3..196007328e 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -185,14 +185,15 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule(
std::string linkLibs;
this->CreateLinkLibs(
linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends,
- cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
+ linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
// Construct object file lists that may be needed to expand the
// rule.
std::string buildObjs;
this->CreateObjectLists(
useLinkScript, false, useResponseFileForObjects, buildObjs, depends,
- false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
+ false, linkLanguage,
+ cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
cmRulePlaceholderExpander::RuleVariables vars;
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
@@ -222,10 +223,11 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule(
std::string launcher;
- cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
- "RULE_LAUNCH_LINK");
+ std::string val = this->LocalGenerator->GetRuleLauncher(
+ this->GeneratorTarget, "RULE_LAUNCH_LINK",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -502,13 +504,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
// Collect up flags to link in needed libraries.
std::string linkLibs;
this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
- useResponseFileForLibs, depends);
+ useResponseFileForLibs, depends, linkLanguage);
// Construct object file lists that may be needed to expand the
// rule.
std::string buildObjs;
this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects,
- buildObjs, depends, useWatcomQuote);
+ buildObjs, depends, useWatcomQuote, linkLanguage);
if (!this->DeviceLinkObject.empty()) {
buildObjs += " " +
this->LocalGenerator->ConvertToOutputFormat(
@@ -587,10 +589,11 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
std::string launcher;
- cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
- "RULE_LAUNCH_LINK");
+ std::string val = this->LocalGenerator->GetRuleLauncher(
+ this->GeneratorTarget, "RULE_LAUNCH_LINK",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index 96692934bb..3e4a08ee27 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -327,14 +327,14 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules(
this->CreateLinkLibs(
linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends,
- cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
+ linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
// Construct object file lists that may be needed to expand the
// rule.
std::string buildObjs;
this->CreateObjectLists(
useLinkScript, false, // useArchiveRules
- useResponseFileForObjects, buildObjs, depends, false,
+ useResponseFileForObjects, buildObjs, depends, false, linkLanguage,
cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
@@ -362,10 +362,11 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules(
vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
std::string launcher;
- cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
- "RULE_LAUNCH_LINK");
+ std::string val = this->LocalGenerator->GetRuleLauncher(
+ this->GeneratorTarget, "RULE_LAUNCH_LINK",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -464,9 +465,20 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
std::string outpathImp;
if (this->GeneratorTarget->IsFrameworkOnApple()) {
outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
+ cmOSXBundleGenerator::SkipParts bundleSkipParts;
+ if (this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) {
+ bundleSkipParts.TextStubs = false;
+ }
this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
- outpath, this->GetConfigName());
+ outpath, this->GetConfigName(),
+ bundleSkipParts);
outpath += '/';
+ if (!this->TargetNames.ImportLibrary.empty()) {
+ outpathImp = this->GeneratorTarget->GetDirectory(
+ this->GetConfigName(), cmStateEnums::ImportLibraryArtifact);
+ cmSystemTools::MakeDirectory(outpathImp);
+ outpathImp += '/';
+ }
} else if (this->GeneratorTarget->IsCFBundleOnApple()) {
outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath,
@@ -678,11 +690,12 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
}
// Expand the rule variables.
+ std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
+ this->LocalGenerator->CreateRulePlaceholderExpander());
+ bool useWatcomQuote =
+ this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
std::vector<std::string> real_link_commands;
{
- bool useWatcomQuote =
- this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
-
// Set path conversion for link script shells.
this->LocalGenerator->SetLinkScriptShell(useLinkScript);
@@ -699,7 +712,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
linkLineComputer->SetRelink(relink);
this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
- useResponseFileForLibs, depends);
+ useResponseFileForLibs, depends, linkLanguage);
}
// Construct object file lists that may be needed to expand the
@@ -707,7 +720,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
std::string buildObjs;
this->CreateObjectLists(useLinkScript, useArchiveRules,
useResponseFileForObjects, buildObjs, depends,
- useWatcomQuote);
+ useWatcomQuote, linkLanguage);
if (!this->DeviceLinkObject.empty()) {
buildObjs += " " +
this->LocalGenerator->ConvertToOutputFormat(
@@ -808,14 +821,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
}
std::string launcher;
- cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
- "RULE_LAUNCH_LINK");
+ std::string val = this->LocalGenerator->GetRuleLauncher(
+ this->GeneratorTarget, "RULE_LAUNCH_LINK",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
- std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
- this->LocalGenerator->CreateRulePlaceholderExpander());
// Construct the main link rule and expand placeholders.
rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
if (useArchiveRules) {
@@ -948,6 +960,86 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
commands, false);
+ // Add rule to generate text-based stubs, if required
+ if (this->GeneratorTarget->IsApple() &&
+ this->GeneratorTarget->HasImportLibrary(this->GetConfigName())) {
+ auto genStubsRule =
+ this->Makefile->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
+ auto genStubs_commands = cmExpandedList(genStubsRule);
+
+ std::string TBDFullPath =
+ cmStrCat(outpathImp, this->TargetNames.ImportOutput);
+ std::string TBDFullPathReal =
+ cmStrCat(outpathImp, this->TargetNames.ImportReal);
+ std::string TBDFullPathSO =
+ cmStrCat(outpathImp, this->TargetNames.ImportLibrary);
+
+ // Expand placeholders.
+ cmRulePlaceholderExpander::RuleVariables vars;
+ std::string target = this->LocalGenerator->ConvertToOutputFormat(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
+ cmOutputConverter::SHELL, useWatcomQuote);
+ vars.Target = target.c_str();
+ std::string TBDOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal),
+ cmOutputConverter::SHELL, useWatcomQuote);
+ rulePlaceholderExpander->SetTargetImpLib(TBDOutPathReal);
+ for (std::string& command : genStubs_commands) {
+ rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
+ command, vars);
+ }
+ outputs.clear();
+ outputs.push_back(TBDFullPathReal);
+ if (this->TargetNames.ImportLibrary != this->TargetNames.ImportReal) {
+ outputs.push_back(TBDFullPathSO);
+ }
+ if (this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary &&
+ this->TargetNames.ImportOutput != this->TargetNames.ImportReal) {
+ outputs.push_back(TBDFullPath);
+ }
+ this->ExtraFiles.insert(TBDFullPath);
+
+ depends.clear();
+ depends.push_back(targetFullPathReal);
+
+ // Add a rule to create necessary symlinks for the library.
+ // Frameworks are handled by cmOSXBundleGenerator.
+ if (TBDFullPath != TBDFullPathReal &&
+ !this->GeneratorTarget->IsFrameworkOnApple()) {
+ auto TBDOutPathSO = this->LocalGenerator->ConvertToOutputFormat(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathSO),
+ cmOutputConverter::SHELL, useWatcomQuote);
+ auto TBDOutPath = this->LocalGenerator->ConvertToOutputFormat(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath),
+ cmOutputConverter::SHELL, useWatcomQuote);
+
+ std::string symlink =
+ cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_library ", TBDOutPathReal,
+ ' ', TBDOutPathSO, ' ', TBDOutPath);
+ commands1.push_back(std::move(symlink));
+ this->LocalGenerator->CreateCDCommand(
+ commands1, this->Makefile->GetCurrentBinaryDirectory(),
+ this->LocalGenerator->GetBinaryDirectory());
+ cm::append(genStubs_commands, commands1);
+ commands1.clear();
+ }
+
+ this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
+ genStubs_commands, false);
+
+ // clean actions for apple specific outputs
+ // clean actions for ImportLibrary are already specified
+ if (this->TargetNames.ImportReal != this->TargetNames.ImportLibrary) {
+ libCleanFiles.insert(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPathReal));
+ }
+ if (this->TargetNames.ImportOutput != this->TargetNames.ImportReal &&
+ this->TargetNames.ImportOutput != this->TargetNames.ImportLibrary) {
+ libCleanFiles.insert(
+ this->LocalGenerator->MaybeRelativeToCurBinDir(TBDFullPath));
+ }
+ }
+
// Write the main driver rule to build everything in this target.
this->WriteTargetDriverRule(targetFullPath, relink);
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index c40d6859ae..3112acd578 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -3,6 +3,7 @@
#include "cmMakefileTargetGenerator.h"
#include <algorithm>
+#include <array>
#include <cassert>
#include <cstdio>
#include <iterator>
@@ -540,8 +541,8 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
*this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n";
*this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n";
- std::vector<std::string> architectures;
- this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), architectures);
+ std::vector<std::string> architectures =
+ this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), language);
architectures.emplace_back();
for (const std::string& arch : architectures) {
@@ -670,8 +671,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
std::string configUpper = cmSystemTools::UpperCase(config);
// Add precompile headers dependencies
- std::vector<std::string> architectures;
- this->GeneratorTarget->GetAppleArchs(config, architectures);
+ std::vector<std::string> architectures =
+ this->GeneratorTarget->GetAppleArchs(config, lang);
if (architectures.empty()) {
architectures.emplace_back();
}
@@ -977,11 +978,23 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, " ");
}
- if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
- const std::string& ptxFlag =
- this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_PTX_FLAG");
- cudaCompileMode = cmStrCat(cudaCompileMode, ptxFlag);
- } else {
+
+ static std::array<cm::string_view, 4> const compileModes{
+ { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
+ };
+ bool useNormalCompileMode = true;
+ for (cm::string_view mode : compileModes) {
+ auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
+ auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
+ if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
+ const std::string& flag =
+ this->Makefile->GetRequiredDefinition(defName);
+ cudaCompileMode = cmStrCat(cudaCompileMode, flag);
+ useNormalCompileMode = false;
+ break;
+ }
+ }
+ if (useNormalCompileMode) {
const std::string& wholeFlag =
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
@@ -1044,7 +1057,8 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
- *clauncher, this->LocalGenerator, config);
+ *clauncher, this->LocalGenerator, config, this->GeneratorTarget,
+ nullptr, this->GeneratorTarget, lang);
if (!evaluatedClauncher.empty()) {
compilerLauncher = evaluatedClauncher;
}
@@ -1053,18 +1067,51 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
// Maybe insert an include-what-you-use runner.
if (!compileCommands.empty() &&
(lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
- std::string const tidy_prop = lang + "_CLANG_TIDY";
- cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+ cmValue tidy = nullptr;
cmValue iwyu = nullptr;
cmValue cpplint = nullptr;
cmValue cppcheck = nullptr;
+ std::string evaluatedTIDY;
+ std::string evaluatedIWYU;
+ std::string evaluatedCPPlint;
+ std::string evaluatedCPPcheck;
+
+ std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
+ tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+ evaluatedTIDY = cmGeneratorExpression::Evaluate(
+ *tidy, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
+ if (!evaluatedTIDY.empty()) {
+ tidy = cmValue(&evaluatedTIDY);
+ }
+
if (lang == "C" || lang == "CXX") {
- std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
+ std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
- std::string const cpplint_prop = lang + "_CPPLINT";
+ evaluatedIWYU = cmGeneratorExpression::Evaluate(
+ *iwyu, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
+ if (!evaluatedIWYU.empty()) {
+ iwyu = cmValue(&evaluatedIWYU);
+ }
+
+ std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
- std::string const cppcheck_prop = lang + "_CPPCHECK";
+ evaluatedCPPlint = cmGeneratorExpression::Evaluate(
+ *cpplint, this->LocalGenerator, config, this->GeneratorTarget,
+ nullptr, this->GeneratorTarget, lang);
+ if (!evaluatedCPPlint.empty()) {
+ cpplint = cmValue(&evaluatedCPPlint);
+ }
+
+ std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
+ evaluatedCPPcheck = cmGeneratorExpression::Evaluate(
+ *cppcheck, this->LocalGenerator, config, this->GeneratorTarget,
+ nullptr, this->GeneratorTarget, lang);
+ if (!evaluatedCPPcheck.empty()) {
+ cppcheck = cmValue(&evaluatedCPPcheck);
+ }
}
if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
cmNonempty(cppcheck)) {
@@ -1166,10 +1213,11 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
std::string launcher;
{
- cmValue val = this->LocalGenerator->GetRuleLauncher(
- this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
+ std::string val = this->LocalGenerator->GetRuleLauncher(
+ this->GeneratorTarget, "RULE_LAUNCH_COMPILE",
+ this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
}
@@ -2174,16 +2222,16 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries(
std::string cmMakefileTargetGenerator::CreateResponseFile(
const std::string& name, std::string const& options,
- std::vector<std::string>& makefile_depends)
+ std::vector<std::string>& makefile_depends, std::string const& language)
{
// FIXME: Find a better way to determine the response file encoding,
// perhaps using tool-specific platform information variables.
// For now, use the makefile encoding as a heuristic.
codecvt::Encoding responseEncoding =
this->GlobalGenerator->GetMakefileEncoding();
- // Non-MSVC tooling may not understand a BOM.
+ // Non-MSVC tooling doesn't understand BOM encoded files.
if (responseEncoding == codecvt::UTF8_WITH_BOM &&
- !this->Makefile->IsOn("MSVC")) {
+ (language == "CUDA" || !this->Makefile->IsOn("MSVC"))) {
responseEncoding = codecvt::UTF8;
}
@@ -2220,7 +2268,7 @@ cmMakefileTargetGenerator::CreateLinkLineComputer(
void cmMakefileTargetGenerator::CreateLinkLibs(
cmLinkLineComputer* linkLineComputer, std::string& linkLibs,
bool useResponseFile, std::vector<std::string>& makefile_depends,
- ResponseFlagFor responseMode)
+ std::string const& linkLanguage, ResponseFlagFor responseMode)
{
std::string frameworkPath;
std::string linkPath;
@@ -2238,8 +2286,9 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
// Create this response file.
std::string responseFileName =
(responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp";
- std::string link_rsp =
- this->CreateResponseFile(responseFileName, linkLibs, makefile_depends);
+ std::string responseLang = (responseMode == Link) ? linkLanguage : "CUDA";
+ std::string link_rsp = this->CreateResponseFile(
+ responseFileName, linkLibs, makefile_depends, responseLang);
// Reference the response file.
linkLibs = cmStrCat(responseFlag,
@@ -2251,7 +2300,8 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
void cmMakefileTargetGenerator::CreateObjectLists(
bool useLinkScript, bool useArchiveRules, bool useResponseFile,
std::string& buildObjs, std::vector<std::string>& makefile_depends,
- bool useWatcomQuote, ResponseFlagFor responseMode)
+ bool useWatcomQuote, std::string const& linkLanguage,
+ ResponseFlagFor responseMode)
{
std::string variableName;
std::string variableNameExternal;
@@ -2280,7 +2330,7 @@ void cmMakefileTargetGenerator::CreateObjectLists(
// Create this response file.
std::string objects_rsp = this->CreateResponseFile(
- responseFileName, object_strings[i], makefile_depends);
+ responseFileName, object_strings[i], makefile_depends, linkLanguage);
// Separate from previous response file references.
buildObjs += sep;
@@ -2332,8 +2382,8 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
}
std::string name = cmStrCat("includes_", lang, ".rsp");
std::string arg = std::move(responseFlag) +
- this->CreateResponseFile(name, includeFlags,
- this->FlagFileDepends[lang]);
+ this->CreateResponseFile(name, includeFlags, this->FlagFileDepends[lang],
+ lang);
this->LocalGenerator->AppendFlags(flags, arg);
} else {
this->LocalGenerator->AppendFlags(flags, includeFlags);
diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h
index 5d614febf4..98c3a0e8db 100644
--- a/Source/cmMakefileTargetGenerator.h
+++ b/Source/cmMakefileTargetGenerator.h
@@ -160,7 +160,8 @@ protected:
response file name. */
std::string CreateResponseFile(const std::string& name,
std::string const& options,
- std::vector<std::string>& makefile_depends);
+ std::vector<std::string>& makefile_depends,
+ std::string const& language);
bool CheckUseResponseFileForObjects(std::string const& l) const;
bool CheckUseResponseFileForLibraries(std::string const& l) const;
@@ -175,13 +176,14 @@ protected:
void CreateLinkLibs(cmLinkLineComputer* linkLineComputer,
std::string& linkLibs, bool useResponseFile,
std::vector<std::string>& makefile_depends,
+ std::string const& linkLanguage,
ResponseFlagFor responseMode = ResponseFlagFor::Link);
/** Create lists of object files for linking and cleaning. */
void CreateObjectLists(bool useLinkScript, bool useArchiveRules,
bool useResponseFile, std::string& buildObjs,
std::vector<std::string>& makefile_depends,
- bool useWatcomQuote,
+ bool useWatcomQuote, std::string const& linkLanguage,
ResponseFlagFor responseMode = ResponseFlagFor::Link);
/** Add commands for generate def files */
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index a1633ca5ee..4d68460521 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -204,6 +204,15 @@ std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
'_', config);
}
+std::string cmNinjaNormalTargetGenerator::TextStubsGeneratorRule(
+ const std::string& config) const
+{
+ return cmStrCat(
+ "TEXT_STUBS_GENERATOR__",
+ cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
+ '_', config);
+}
+
struct cmNinjaRemoveNoOpCommands
{
bool operator()(std::string const& cmd)
@@ -263,10 +272,10 @@ void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
std::string launcher;
- cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
- this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
+ std::string val = this->GetLocalGenerator()->GetRuleLauncher(
+ this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config);
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -458,10 +467,10 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
}
std::string launcher;
- cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
- this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
+ std::string val = this->GetLocalGenerator()->GetRuleLauncher(
+ this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config);
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
@@ -527,6 +536,45 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
this->GetGlobalGenerator()->AddRule(rule);
}
}
+
+ if (this->GetGeneratorTarget()->IsApple() &&
+ this->GetGeneratorTarget()->HasImportLibrary(config)) {
+ cmNinjaRule rule(this->TextStubsGeneratorRule(config));
+ rule.Comment = cmStrCat("Rule for generating text-based stubs for ",
+ this->GetVisibleTypeName(), '.');
+ rule.Description = "Creating text-based stubs $out";
+
+ std::string cmd =
+ this->GetMakefile()->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
+ std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
+ this->GetLocalGenerator()->CreateRulePlaceholderExpander());
+ cmRulePlaceholderExpander::RuleVariables vars;
+ vars.Target = "$in";
+ rulePlaceholderExpander->SetTargetImpLib("$out");
+ rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
+ cmd, vars);
+
+ rule.Command =
+ this->GetLocalGenerator()->BuildCommandLine({ cmd }, config, config);
+ this->GetGlobalGenerator()->AddRule(rule);
+
+ if (tgtNames.ImportOutput != tgtNames.ImportReal &&
+ !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
+ cmNinjaRule slRule("CMAKE_SYMLINK_IMPORT_LIBRARY");
+ {
+ std::string cmakeCommand =
+ this->GetLocalGenerator()->ConvertToOutputFormat(
+ cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
+ std::string slCmd =
+ cmStrCat(cmakeCommand, " -E cmake_symlink_library $in $SONAME $out");
+ slRule.Command = this->GetLocalGenerator()->BuildCommandLine(
+ { slCmd }, config, config);
+ }
+ slRule.Description = "Creating import library symlink $out";
+ slRule.Comment = "Rule for creating import library symlink.";
+ this->GetGlobalGenerator()->AddRule(slRule);
+ }
+ }
}
std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
@@ -1030,9 +1078,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
// the current configuration has a postfix. The non-postfix configuration
// Info.plist can be used by all the other configurations.
if (!postFix.empty()) {
- bundleSkipParts.infoPlist = true;
+ bundleSkipParts.InfoPlist = true;
}
}
+ if (gt->HasImportLibrary(config)) {
+ bundleSkipParts.TextStubs = false;
+ }
this->OSXBundleGenerator->CreateFramework(
tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
@@ -1214,7 +1265,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
- if (!tgtNames.ImportLibrary.empty()) {
+ if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) {
const std::string impLibPath = localGen.ConvertToOutputFormat(
targetOutputImplib, cmOutputConverter::SHELL);
vars["TARGET_IMPLIB"] = impLibPath;
@@ -1471,6 +1522,55 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
// Add aliases for the file name and the target name.
globalGen->AddTargetAlias(tgtNames.Output, gt, config);
globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
+
+ if (this->GetGeneratorTarget()->IsApple() &&
+ this->GetGeneratorTarget()->HasImportLibrary(config)) {
+ auto dirTBD =
+ gt->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
+ auto targetTBD =
+ this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportReal));
+ this->EnsureParentDirectoryExists(targetTBD);
+ cmNinjaBuild build(this->TextStubsGeneratorRule(config));
+ build.Comment = cmStrCat("Generate the text-based stubs file ", targetTBD);
+ build.Outputs.push_back(targetTBD);
+ build.ExplicitDeps.push_back(targetOutputReal);
+ globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
+
+ if (tgtNames.ImportOutput != tgtNames.ImportReal &&
+ !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
+ auto outputTBD =
+ this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportOutput));
+ std::string const soNameTBD = this->ConvertToNinjaPath(
+ cmStrCat(dirTBD, '/', tgtNames.ImportLibrary));
+
+ cmNinjaBuild slBuild("CMAKE_SYMLINK_IMPORT_LIBRARY");
+ slBuild.Comment = cmStrCat("Create import library symlink ", outputTBD);
+ cmNinjaVars slVars;
+
+ // If one link has to be created.
+ if (targetTBD == soNameTBD || outputTBD == soNameTBD) {
+ slVars["SONAME"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+ soNameTBD, cmOutputConverter::SHELL);
+ } else {
+ slVars["SONAME"].clear();
+ slBuild.Outputs.push_back(soNameTBD);
+ if (firstForConfig) {
+ globalGen->GetByproductsForCleanTarget(config).push_back(soNameTBD);
+ }
+ }
+ slBuild.Outputs.push_back(outputTBD);
+ if (firstForConfig) {
+ globalGen->GetByproductsForCleanTarget(config).push_back(outputTBD);
+ }
+ slBuild.ExplicitDeps.push_back(targetTBD);
+ slBuild.Variables = std::move(slVars);
+
+ globalGen->WriteBuild(this->GetImplFileStream(fileConfig), slBuild);
+ }
+
+ // Add alias for the import file name
+ globalGen->AddTargetAlias(tgtNames.ImportOutput, gt, config);
+ }
}
void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h
index 30127fe8b5..85f42a4487 100644
--- a/Source/cmNinjaNormalTargetGenerator.h
+++ b/Source/cmNinjaNormalTargetGenerator.h
@@ -25,6 +25,7 @@ private:
std::string LanguageLinkerCudaDeviceCompileRule(
const std::string& config) const;
std::string LanguageLinkerCudaFatbinaryRule(const std::string& config) const;
+ std::string TextStubsGeneratorRule(const std::string& config) const;
const char* GetVisibleTypeName() const;
void WriteLanguagesRules(const std::string& config);
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 5abf6093b6..5dbc283fac 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -3,6 +3,7 @@
#include "cmNinjaTargetGenerator.h"
#include <algorithm>
+#include <array>
#include <cassert>
#include <functional>
#include <iterator>
@@ -20,6 +21,8 @@
#include <cm3p/json/value.h>
#include <cm3p/json/writer.h>
+#include "cmsys/RegularExpression.hxx"
+
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
#include "cmDyndepCollation.h"
@@ -168,9 +171,9 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
cmSourceFile const* source, const std::string& language,
const std::string& config)
{
- std::vector<std::string> architectures;
std::unordered_map<std::string, std::string> pchSources;
- this->GeneratorTarget->GetAppleArchs(config, architectures);
+ std::vector<std::string> architectures =
+ this->GeneratorTarget->GetAppleArchs(config, language);
if (architectures.empty()) {
architectures.emplace_back();
}
@@ -670,10 +673,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
cmLocalGenerator::SHELL);
std::string launcher;
- cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
- this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE");
+ std::string val = this->GetLocalGenerator()->GetRuleLauncher(
+ this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", config);
if (cmNonempty(val)) {
- launcher = cmStrCat(*val, ' ');
+ launcher = cmStrCat(val, ' ');
}
std::string const cmakeCmd =
@@ -859,11 +862,22 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, " ");
}
- if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
- const std::string& ptxFlag =
- this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_PTX_FLAG");
- cudaCompileMode = cmStrCat(cudaCompileMode, ptxFlag);
- } else {
+ static std::array<cm::string_view, 4> const compileModes{
+ { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
+ };
+ bool useNormalCompileMode = true;
+ for (cm::string_view mode : compileModes) {
+ auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
+ auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
+ if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
+ const std::string& flag =
+ this->Makefile->GetRequiredDefinition(defName);
+ cudaCompileMode = cmStrCat(cudaCompileMode, flag);
+ useNormalCompileMode = false;
+ break;
+ }
+ }
+ if (useNormalCompileMode) {
const std::string& wholeFlag =
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
@@ -886,7 +900,8 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
- *clauncher, this->LocalGenerator, config);
+ *clauncher, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
if (!evaluatedClauncher.empty()) {
compilerLauncher = evaluatedClauncher;
}
@@ -895,18 +910,51 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
// Maybe insert an include-what-you-use runner.
if (!compileCmds.empty() &&
(lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
- std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
- cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+ cmValue tidy = nullptr;
cmValue iwyu = nullptr;
cmValue cpplint = nullptr;
cmValue cppcheck = nullptr;
+ std::string evaluatedTIDY;
+ std::string evaluatedIWYU;
+ std::string evaluatedCPPlint;
+ std::string evaluatedCPPcheck;
+
+ std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
+ tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+ evaluatedTIDY = cmGeneratorExpression::Evaluate(
+ *tidy, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
+ if (!evaluatedTIDY.empty()) {
+ tidy = cmValue(&evaluatedTIDY);
+ }
+
if (lang == "C" || lang == "CXX") {
std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
+ evaluatedIWYU = cmGeneratorExpression::Evaluate(
+ *iwyu, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
+ if (!evaluatedIWYU.empty()) {
+ iwyu = cmValue(&evaluatedIWYU);
+ }
+
std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT");
cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
+ evaluatedCPPlint = cmGeneratorExpression::Evaluate(
+ *cpplint, this->LocalGenerator, config, this->GeneratorTarget, nullptr,
+ this->GeneratorTarget, lang);
+ if (!evaluatedCPPlint.empty()) {
+ cpplint = cmValue(&evaluatedCPPlint);
+ }
+
std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK");
cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
+ evaluatedCPPcheck = cmGeneratorExpression::Evaluate(
+ *cppcheck, this->LocalGenerator, config, this->GeneratorTarget,
+ nullptr, this->GeneratorTarget, lang);
+ if (!evaluatedCPPcheck.empty()) {
+ cppcheck = cmValue(&evaluatedCPPcheck);
+ }
}
if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
cmNonempty(cppcheck)) {
@@ -1139,27 +1187,32 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
}
}
- for (auto const& langDDIFiles : this->Configs[config].DDIFiles) {
- std::string const& language = langDDIFiles.first;
- cmNinjaDeps const& ddiFiles = langDDIFiles.second;
+ for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) {
+ std::string const& language = langScanningFiles.first;
+ std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second;
cmNinjaBuild build(this->LanguageDyndepRule(language, config));
build.Outputs.push_back(this->GetDyndepFilePath(language, config));
- build.ExplicitDeps = ddiFiles;
+ build.ImplicitOuts.push_back(
+ cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/',
+ this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+ this->GetGlobalGenerator()->ConfigDirectory(config), '/',
+ language, "Modules.json"));
+ for (auto const& scanFiles : scanningFiles) {
+ if (!scanFiles.ScanningOutput.empty()) {
+ build.ExplicitDeps.push_back(scanFiles.ScanningOutput);
+ }
+ if (!scanFiles.ModuleMapFile.empty()) {
+ build.ImplicitOuts.push_back(scanFiles.ModuleMapFile);
+ }
+ }
this->WriteTargetDependInfo(language, config);
- // Make sure dyndep files for all our dependencies have already
- // been generated so that the '<LANG>Modules.json' files they
- // produced as side-effects are available for us to read.
- // Ideally we should depend on the '<LANG>Modules.json' files
- // from our dependencies directly, but we don't know which of
- // our dependencies produces them. Fixing this will require
- // refactoring the Ninja generator to generate targets in
- // dependency order so that we can collect the needed information.
- this->GetLocalGenerator()->AppendTargetDepends(
- this->GeneratorTarget, build.OrderOnlyDeps, config, fileConfig,
- DependOnTargetArtifact);
+ for (std::string const& l :
+ this->GetLinkedTargetDirectories(language, config)) {
+ build.ImplicitDeps.push_back(cmStrCat(l, '/', language, "Modules.json"));
+ }
this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
build);
@@ -1208,8 +1261,8 @@ namespace {
cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
const std::string& ppFileName,
bool compilePP, bool compilePPWithDefines,
+ cmValue ppExcludeFlagsRegex,
cmNinjaBuild& objBuild, cmNinjaVars& vars,
- std::string const& modmapFormat,
const std::string& objectFileName,
cmLocalGenerator* lg)
{
@@ -1237,6 +1290,20 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
// Scanning and compilation generally use the same flags.
scanBuild.Variables["FLAGS"] = vars["FLAGS"];
+ // Exclude flags not valid during preprocessing.
+ if (compilePP && !ppExcludeFlagsRegex.IsEmpty()) {
+ std::string in = std::move(scanBuild.Variables["FLAGS"]);
+ std::string out;
+ cmsys::RegularExpression regex(*ppExcludeFlagsRegex);
+ std::string::size_type pos = 0;
+ while (regex.find(in.c_str() + pos)) {
+ out = cmStrCat(out, in.substr(pos, regex.start()), ' ');
+ pos += regex.end();
+ }
+ out = cmStrCat(out, in.substr(pos));
+ scanBuild.Variables["FLAGS"] = std::move(out);
+ }
+
if (compilePP && !compilePPWithDefines) {
// Move preprocessor definitions to the scan/preprocessor build statement.
std::swap(scanBuild.Variables["DEFINES"], vars["DEFINES"]);
@@ -1278,15 +1345,6 @@ cmNinjaBuild GetScanBuildStatement(const std::string& ruleName,
vars.erase("DEP_FILE");
}
- if (!modmapFormat.empty()) {
- // XXX(modmap): If changing this path construction, change
- // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding
- // file path.
- std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
- scanBuild.Variables["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
- scanBuild.ImplicitOuts.push_back(ddModmapFile);
- }
-
return scanBuild;
}
}
@@ -1382,8 +1440,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
// Add precompile headers dependencies
std::vector<std::string> depList;
- std::vector<std::string> architectures;
- this->GeneratorTarget->GetAppleArchs(config, architectures);
+ std::vector<std::string> architectures =
+ this->GeneratorTarget->GetAppleArchs(config, language);
if (architectures.empty()) {
architectures.emplace_back();
}
@@ -1470,18 +1528,22 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
std::string scanRuleName;
std::string ppFileName;
+ cmValue ppExcludeFlagsRegex;
if (compilePP) {
scanRuleName = this->LanguagePreprocessAndScanRule(language, config);
ppFileName = this->ConvertToNinjaPath(
this->GetPreprocessedFilePath(source, config));
+ ppExcludeFlagsRegex = this->Makefile->GetDefinition(cmStrCat(
+ "CMAKE_", language, "_PREPROCESS_SOURCE_EXCLUDE_FLAGS_REGEX"));
} else {
scanRuleName = this->LanguageScanRule(language, config);
ppFileName = cmStrCat(objectFileName, ".ddi.i");
}
cmNinjaBuild ppBuild = GetScanBuildStatement(
- scanRuleName, ppFileName, compilePP, compilePPWithDefines, objBuild,
- vars, modmapFormat, objectFileName, this->LocalGenerator);
+ scanRuleName, ppFileName, compilePP, compilePPWithDefines,
+ ppExcludeFlagsRegex, objBuild, vars, objectFileName,
+ this->LocalGenerator);
if (compilePP) {
// In case compilation requires flags that are incompatible with
@@ -1502,9 +1564,10 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
vars["INCLUDES"] = cmStrCat(sourceDirectoryFlag, ' ', vars["INCLUDES"]);
}
+ ScanningFiles scanningFiles;
+
if (firstForConfig) {
- std::string const ddiFile = cmStrCat(objectFileName, ".ddi");
- this->Configs[config].DDIFiles[language].push_back(ddiFile);
+ scanningFiles.ScanningOutput = cmStrCat(objectFileName, ".ddi");
}
this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
@@ -1518,9 +1581,17 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
vars["dyndep"] = dyndep;
if (!modmapFormat.empty()) {
- std::string const ddModmapFile = cmStrCat(objectFileName, ".modmap");
+ // XXX(modmap): If changing this path construction, change
+ // `cmGlobalNinjaGenerator::WriteDyndep` to expect the corresponding file
+ // path.
+ std::string ddModmapFile = cmStrCat(objectFileName, ".modmap");
vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
objBuild.OrderOnlyDeps.push_back(ddModmapFile);
+ scanningFiles.ModuleMapFile = std::move(ddModmapFile);
+ }
+
+ if (!scanningFiles.IsEmpty()) {
+ this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles);
}
}
@@ -1788,11 +1859,22 @@ void cmNinjaTargetGenerator::ExportObjectCompileCommand(
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, " ");
}
- if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
- const std::string& ptxFlag =
- this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_PTX_FLAG");
- cudaCompileMode = cmStrCat(cudaCompileMode, ptxFlag);
- } else {
+ static std::array<cm::string_view, 4> const compileModes{
+ { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
+ };
+ bool useNormalCompileMode = true;
+ for (cm::string_view mode : compileModes) {
+ auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
+ auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
+ if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
+ const std::string& flag =
+ this->Makefile->GetRequiredDefinition(defName);
+ cudaCompileMode = cmStrCat(cudaCompileMode, flag);
+ useNormalCompileMode = false;
+ break;
+ }
+ }
+ if (useNormalCompileMode) {
const std::string& wholeFlag =
this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 8bf79867bd..8f4a76413f 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -222,12 +222,23 @@ protected:
private:
cmLocalNinjaGenerator* LocalGenerator;
+ struct ScanningFiles
+ {
+ bool IsEmpty() const
+ {
+ return this->ScanningOutput.empty() && this->ModuleMapFile.empty();
+ }
+
+ std::string ScanningOutput;
+ std::string ModuleMapFile;
+ };
+
struct ByConfig
{
/// List of object files for this target.
cmNinjaDeps Objects;
- // Fortran Support
- std::map<std::string, cmNinjaDeps> DDIFiles;
+ // Dyndep Support
+ std::map<std::string, std::vector<ScanningFiles>> ScanningInfo;
// Swift Support
Json::Value SwiftOutputMap;
std::vector<cmCustomCommand const*> CustomCommands;
diff --git a/Source/cmNinjaTypes.h b/Source/cmNinjaTypes.h
index c8a411e1df..b77e0b5285 100644
--- a/Source/cmNinjaTypes.h
+++ b/Source/cmNinjaTypes.h
@@ -5,8 +5,8 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <map>
-#include <set>
#include <string>
+#include <unordered_set>
#include <utility>
#include <vector>
@@ -17,7 +17,6 @@ enum cmNinjaTargetDepends
};
using cmNinjaDeps = std::vector<std::string>;
-using cmNinjaOuts = std::set<std::string>;
using cmNinjaVars = std::map<std::string, std::string>;
class cmNinjaRule
diff --git a/Source/cmOSXBundleGenerator.cxx b/Source/cmOSXBundleGenerator.cxx
index 674735b9c0..4301f052f8 100644
--- a/Source/cmOSXBundleGenerator.cxx
+++ b/Source/cmOSXBundleGenerator.cxx
@@ -11,6 +11,7 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
+#include "cmValue.h"
class cmSourceFile;
@@ -77,7 +78,7 @@ void cmOSXBundleGenerator::CreateFramework(
std::string frameworkVersion = this->GT->GetFrameworkVersion();
std::string name = cmSystemTools::GetFilenameName(targetName);
- if (!skipParts.infoPlist) {
+ if (!skipParts.InfoPlist) {
// Configure the Info.plist file
std::string plist = newoutpath;
if (!this->Makefile->PlatformIsAppleEmbedded()) {
@@ -120,6 +121,17 @@ void cmOSXBundleGenerator::CreateFramework(
cmSystemTools::CreateSymlink(oldName, newName);
this->Makefile->AddCMakeOutputFile(newName);
+ if (!skipParts.TextStubs) {
+ // foo.tbd -> Versions/Current/foo.tbd
+ cmValue tbdSuffix =
+ this->Makefile->GetDefinition("CMAKE_APPLE_IMPORT_FILE_SUFFIX");
+ oldName = cmStrCat("Versions/Current/", name, tbdSuffix);
+ newName = cmStrCat(contentdir, name, tbdSuffix);
+ cmSystemTools::RemoveFile(newName);
+ cmSystemTools::CreateSymlink(oldName, newName);
+ this->Makefile->AddCMakeOutputFile(newName);
+ }
+
// Resources -> Versions/Current/Resources
if (this->MacContentFolders->find("Resources") !=
this->MacContentFolders->end()) {
diff --git a/Source/cmOSXBundleGenerator.h b/Source/cmOSXBundleGenerator.h
index c33b08720f..38453fd3d7 100644
--- a/Source/cmOSXBundleGenerator.h
+++ b/Source/cmOSXBundleGenerator.h
@@ -20,11 +20,10 @@ public:
struct SkipParts
{
- SkipParts()
- : infoPlist(false)
- {
- }
- bool infoPlist; // NOLINT(modernize-use-default-member-init)
+ SkipParts() {} // NOLINT(modernize-use-equals-default)
+
+ bool InfoPlist = false;
+ bool TextStubs = true;
};
// create an app bundle at a given root, and return
@@ -35,7 +34,7 @@ public:
// create a framework at a given root
void CreateFramework(const std::string& targetName, const std::string& root,
const std::string& config,
- const SkipParts& skipParts = SkipParts());
+ const SkipParts& skipParts = SkipParts{});
// create a cf bundle at a given root
void CreateCFBundle(const std::string& targetName, const std::string& root,
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index da5f5e5151..d5e5725bf3 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -259,8 +259,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
{
// Warn about policy versions for which support will be removed.
if (warnCompat == WarnCompat::On &&
- (majorVer < 2 || (majorVer == 2 && minorVer < 8) ||
- (majorVer == 2 && minorVer == 8 && patchVer < 12)) &&
+ (majorVer < 3 || (majorVer == 3 && minorVer < 5)) &&
// Avoid warning on calls generated by install(EXPORT)
// in CMake versions prior to 3.18.
!(majorVer == 2 && minorVer == 6 && patchVer == 0 &&
@@ -269,7 +268,7 @@ bool cmPolicies::ApplyPolicyVersion(cmMakefile* mf, unsigned int majorVer,
"cmake_policy") == 0)) {
mf->IssueMessage(
MessageType::DEPRECATION_WARNING,
- "Compatibility with CMake < 2.8.12 will be removed from "
+ "Compatibility with CMake < 3.5 will be removed from "
"a future version of CMake.\n"
"Update the VERSION argument <min> value or use a ...<max> suffix "
"to tell CMake that the project does not need compatibility with "
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index fa24f571af..fe883826aa 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -434,7 +434,23 @@ class cmMakefile;
3, 25, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0143, \
"Global property USE_FOLDERS treated as ON by default", 3, 26, 0, \
- cmPolicies::WARN)
+ cmPolicies::WARN) \
+ SELECT(POLICY, CMP0144, \
+ "find_package uses upper-case <PACKAGENAME>_ROOT variables.", 3, 27, \
+ 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0145, "The Dart and FindDart modules are removed.", 3, \
+ 27, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0146, "The FindCUDA module is removed.", 3, 27, 0, \
+ cmPolicies::WARN) \
+ SELECT(POLICY, CMP0147, \
+ "Visual Studio generators build custom commands in parallel.", 3, \
+ 27, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0148, \
+ "The FindPythonInterp and FindPythonLibs modules are removed.", 3, \
+ 27, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0149, \
+ "Visual Studio generators select latest Windows SDK by default.", 3, \
+ 27, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
@@ -474,6 +490,10 @@ class cmMakefile;
F(CMP0131) \
F(CMP0142)
+#define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F) \
+ F(CMP0116) \
+ F(CMP0147)
+
/** \class cmPolicies
* \brief Handles changes in CMake behavior and policies
*
diff --git a/Source/cmProcessOutput.cxx b/Source/cmProcessOutput.cxx
index 10c421542c..e1df66116f 100644
--- a/Source/cmProcessOutput.cxx
+++ b/Source/cmProcessOutput.cxx
@@ -85,7 +85,7 @@ bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded,
rawparts[id - 1] += *(raw.end() - 1);
raw.resize(raw.size() - 1);
}
- success = DoDecodeText(raw, decoded, NULL);
+ success = DoDecodeText(raw, decoded, nullptr);
} else {
bool restoreDecoded = false;
std::string firstDecoded = decoded;
@@ -114,7 +114,7 @@ bool cmProcessOutput::DecodeText(std::string raw, std::string& decoded,
}
}
} else {
- success = DoDecodeText(raw, decoded, NULL);
+ success = DoDecodeText(raw, decoded, nullptr);
}
}
return success;
@@ -143,7 +143,7 @@ bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded,
{
bool success = false;
const int wlength =
- MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), NULL, 0);
+ MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()), nullptr, 0);
auto wdata = cm::make_unique<wchar_t[]>(wlength);
int r = MultiByteToWideChar(codepage, 0, raw.c_str(), int(raw.size()),
wdata.get(), wlength);
@@ -156,10 +156,10 @@ bool cmProcessOutput::DoDecodeText(std::string raw, std::string& decoded,
}
}
int length = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
- NULL, 0, NULL, NULL);
+ nullptr, 0, nullptr, nullptr);
auto data = cm::make_unique<char[]>(length);
r = WideCharToMultiByte(defaultCodepage, 0, wdata.get(), wlength,
- data.get(), length, NULL, NULL);
+ data.get(), length, nullptr, nullptr);
if (r > 0) {
decoded = std::string(data.get(), length);
success = true;
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index b7ea7d6d7e..1da8847fb6 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -13,7 +13,6 @@
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
-#include "cmPolicies.h"
#include "cmProcessOutput.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenInitializer.h"
@@ -173,7 +172,6 @@ void cmQtAutoGenGlobalInitializer::GetOrCreateGlobalTarget(
// Create utility target
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(makefile->GetHomeOutputDirectory().c_str());
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetComment(comment.c_str());
cmTarget* target = localGen->AddUtilityCommand(name, true, std::move(cc));
@@ -266,11 +264,6 @@ cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
return res;
}
-bool cmQtAutoGenGlobalInitializer::generate()
-{
- return (this->InitializeCustomTargets() && this->SetupCustomTargets());
-}
-
bool cmQtAutoGenGlobalInitializer::InitializeCustomTargets()
{
// Initialize global autogen targets
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index 3de5c1a850..e8569a538f 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -51,14 +51,12 @@ public:
Keywords const& kw() const { return this->Keywords_; }
- bool generate();
+ bool InitializeCustomTargets();
+ bool SetupCustomTargets();
private:
friend class cmQtAutoGenInitializer;
- bool InitializeCustomTargets();
- bool SetupCustomTargets();
-
void GetOrCreateGlobalTarget(cmLocalGenerator* localGen,
std::string const& name,
std::string const& comment);
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 66e591eda0..782c154653 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -5,6 +5,7 @@
#include <cstddef>
#include <deque>
#include <initializer_list>
+#include <limits>
#include <map>
#include <set>
#include <sstream> // for basic_ios, istringstream
@@ -27,8 +28,10 @@
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandLines.h"
+#include "cmEvaluatedTargetProperty.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
@@ -42,6 +45,7 @@
#include "cmSourceFile.h"
#include "cmSourceFileLocationKind.h"
#include "cmSourceGroup.h"
+#include "cmStandardLevelResolver.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
@@ -457,12 +461,23 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
// Autogen target parallel processing
{
+ using ParallelType = decltype(this->AutogenTarget.Parallel);
+ unsigned long propInt = 0;
std::string const& prop =
this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
if (prop.empty() || (prop == "AUTO")) {
// Autodetect number of CPUs
this->AutogenTarget.Parallel = GetParallelCPUCount();
+ } else if (cmStrToULong(prop, &propInt) && propInt > 0 &&
+ propInt <= std::numeric_limits<ParallelType>::max()) {
+ this->AutogenTarget.Parallel = static_cast<ParallelType>(propInt);
} else {
+ // Warn the project author that AUTOGEN_PARALLEL is not valid.
+ this->Makefile->IssueMessage(
+ MessageType::AUTHOR_WARNING,
+ cmStrCat("AUTOGEN_PARALLEL=\"", prop, "\" for target \"",
+ this->GenTarget->GetName(),
+ "\" is not valid. Using AUTOGEN_PARALLEL=1"));
this->AutogenTarget.Parallel = 1;
}
}
@@ -1238,7 +1253,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetDepends(uicDependencies);
cc->SetComment("");
cc->SetWorkingDirectory(this->Dir.Work.c_str());
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetStdPipesUTF8(stdPipesUTF8);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
@@ -1332,7 +1346,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetByproducts(timestampTargetProvides);
cc->SetDepends(dependencies);
cc->SetCommandLines(timestampTargetCommandLines);
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand(
timestampTargetName, true, std::move(cc));
@@ -1371,7 +1384,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetCommandLines(commandLines);
cc->SetComment(autogenComment.c_str());
cc->SetWorkingDirectory(this->Dir.Work.c_str());
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetDepfile(this->AutogenTarget.DepFile);
cc->SetStdPipesUTF8(stdPipesUTF8);
@@ -1391,7 +1403,6 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
cc->SetByproducts(autogenByproducts);
cc->SetDepends(dependencies);
cc->SetCommandLines(commandLines);
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetEscapeOldStyle(false);
cc->SetComment(autogenComment.c_str());
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
@@ -1472,7 +1483,6 @@ bool cmQtAutoGenInitializer::InitRccTargets()
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetCommandLines(commandLines);
- cc->SetCMP0116Status(cmPolicies::NEW);
cc->SetComment(ccComment.c_str());
cc->SetStdPipesUTF8(true);
@@ -1683,6 +1693,23 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.SetArray("MOC_OPTIONS", this->Moc.Options);
info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
+
+ cmGeneratorExpressionDAGChecker dagChecker(
+ this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr);
+ EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries;
+
+ AddInterfaceEntries(this->GenTarget, this->ConfigDefault,
+ "INTERFACE_AUTOMOC_MACRO_NAMES", "CXX", &dagChecker,
+ InterfaceAutoMocMacroNamesEntries,
+ IncludeRuntimeInterface::Yes);
+
+ for (auto const& entry : InterfaceAutoMocMacroNamesEntries.Entries) {
+ this->Moc.MacroNames.insert(this->Moc.MacroNames.end(),
+ entry.Values.begin(), entry.Values.end());
+ }
+ this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
+ this->Moc.MacroNames.end());
+
info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
info.SetArrayArray(
"MOC_DEPEND_FILTERS", this->Moc.DependFilters,
@@ -1692,8 +1719,22 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
jval[1u] = pair.second;
});
info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
- info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
+
+ cmStandardLevelResolver resolver{ this->Makefile };
+ auto CompileOptionFlag =
+ resolver.GetCompileOptionDef(this->GenTarget, "CXX", "");
+
+ auto CompileOptionValue =
+ this->GenTarget->Makefile->GetSafeDefinition(CompileOptionFlag);
+
+ if (!CompileOptionValue.empty()) {
+ if (this->Moc.PredefsCmd.size() >= 3) {
+ this->Moc.PredefsCmd.insert(this->Moc.PredefsCmd.begin() + 1,
+ CompileOptionValue);
+ }
+ }
+ info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
}
// Write uic settings
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index fa9d4cc25a..f48330d5e1 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -20,8 +20,8 @@ cmRST::cmRST(std::ostream& os, std::string docroot)
: OS(os)
, DocRoot(std::move(docroot))
, CMakeDirective("^.. (cmake:)?("
- "command|envvar|genex|variable"
- ")::[ \t]+([^ \t\n]+)$")
+ "command|envvar|genex|signature|variable"
+ ")::")
, CMakeModuleDirective("^.. cmake-module::[ \t]+([^ \t\n]+)$")
, ParsedLiteralDirective("^.. parsed-literal::[ \t]*(.*)$")
, CodeBlockDirective("^.. code-block::[ \t]*(.*)$")
@@ -33,6 +33,7 @@ cmRST::cmRST(std::ostream& os, std::string docroot)
, VersionDirective("^.. version(added|changed)::[ \t]*(.*)$")
, ModuleRST(R"(^#\[(=*)\[\.rst:$)")
, CMakeRole("(:cmake)?:("
+ "cref|"
"command|cpack_gen|generator|genex|"
"variable|envvar|module|policy|"
"prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|"
@@ -126,27 +127,27 @@ void cmRST::Reset()
if (!this->MarkupLines.empty()) {
cmRST::UnindentLines(this->MarkupLines);
}
- switch (this->Directive) {
- case DirectiveNone:
+ switch (this->DirectiveType) {
+ case Directive::None:
break;
- case DirectiveParsedLiteral:
+ case Directive::ParsedLiteral:
this->ProcessDirectiveParsedLiteral();
break;
- case DirectiveLiteralBlock:
+ case Directive::LiteralBlock:
this->ProcessDirectiveLiteralBlock();
break;
- case DirectiveCodeBlock:
+ case Directive::CodeBlock:
this->ProcessDirectiveCodeBlock();
break;
- case DirectiveReplace:
+ case Directive::Replace:
this->ProcessDirectiveReplace();
break;
- case DirectiveTocTree:
+ case Directive::TocTree:
this->ProcessDirectiveTocTree();
break;
}
- this->Markup = MarkupNone;
- this->Directive = DirectiveNone;
+ this->MarkupType = Markup::None;
+ this->DirectiveType = Directive::None;
this->MarkupLines.clear();
}
@@ -160,9 +161,9 @@ void cmRST::ProcessLine(std::string const& line)
(line.size() >= 3 && line[0] == '.' && line[1] == '.' &&
isspace(line[2]))) {
this->Reset();
- this->Markup =
- (line.find_first_not_of(" \t", 2) == std::string::npos ? MarkupEmpty
- : MarkupNormal);
+ this->MarkupType =
+ (line.find_first_not_of(" \t", 2) == std::string::npos ? Markup::Empty
+ : Markup::Normal);
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
if (this->CMakeDirective.find(line)) {
@@ -171,33 +172,33 @@ void cmRST::ProcessLine(std::string const& line)
} else if (this->CMakeModuleDirective.find(line)) {
// Process cmake-module directive: scan .cmake file comments.
std::string file = this->CMakeModuleDirective.match(1);
- if (file.empty() || !this->ProcessInclude(file, IncludeModule)) {
+ if (file.empty() || !this->ProcessInclude(file, Include::Module)) {
this->NormalLine(line);
}
} else if (this->ParsedLiteralDirective.find(line)) {
// Record the literal lines to output after whole block.
- this->Directive = DirectiveParsedLiteral;
+ this->DirectiveType = Directive::ParsedLiteral;
this->MarkupLines.push_back(this->ParsedLiteralDirective.match(1));
} else if (this->CodeBlockDirective.find(line)) {
// Record the literal lines to output after whole block.
// Ignore the language spec and record the opening line as blank.
- this->Directive = DirectiveCodeBlock;
+ this->DirectiveType = Directive::CodeBlock;
this->MarkupLines.emplace_back();
} else if (this->ReplaceDirective.find(line)) {
// Record the replace directive content.
- this->Directive = DirectiveReplace;
+ this->DirectiveType = Directive::Replace;
this->ReplaceName = this->ReplaceDirective.match(1);
this->MarkupLines.push_back(this->ReplaceDirective.match(2));
} else if (this->IncludeDirective.find(line)) {
// Process the include directive or output the directive and its
// content normally if it fails.
std::string file = this->IncludeDirective.match(1);
- if (file.empty() || !this->ProcessInclude(file, IncludeNormal)) {
+ if (file.empty() || !this->ProcessInclude(file, Include::Normal)) {
this->NormalLine(line);
}
} else if (this->TocTreeDirective.find(line)) {
// Record the toctree entries to process after whole block.
- this->Directive = DirectiveTocTree;
+ this->DirectiveType = Directive::TocTree;
this->MarkupLines.push_back(this->TocTreeDirective.match(1));
} else if (this->ProductionListDirective.find(line)) {
// Output productionlist directives and their content normally.
@@ -211,14 +212,15 @@ void cmRST::ProcessLine(std::string const& line)
this->NormalLine(line);
}
}
- // An explicit markup start followed nothing but whitespace and a
+ // An explicit markup start followed by nothing but whitespace and a
// blank line does not consume any indented text following.
- else if (this->Markup == MarkupEmpty && line.empty()) {
+ else if (this->MarkupType == Markup::Empty && line.empty()) {
this->NormalLine(line);
}
// Indented lines following an explicit markup start are explicit markup.
- else if (this->Markup && (line.empty() || isspace(line[0]))) {
- this->Markup = MarkupNormal;
+ else if (this->MarkupType != Markup::None &&
+ (line.empty() || isspace(line[0]))) {
+ this->MarkupType = Markup::Normal;
// Record markup lines if the start line was recorded.
if (!this->MarkupLines.empty()) {
this->MarkupLines.push_back(line);
@@ -227,8 +229,8 @@ void cmRST::ProcessLine(std::string const& line)
// A blank line following a paragraph ending in "::" starts a literal block.
else if (lastLineEndedInColonColon && line.empty()) {
// Record the literal lines to output after whole block.
- this->Markup = MarkupNormal;
- this->Directive = DirectiveLiteralBlock;
+ this->MarkupType = Markup::Normal;
+ this->DirectiveType = Directive::LiteralBlock;
this->MarkupLines.emplace_back();
this->OutputLine("", false);
}
@@ -354,14 +356,14 @@ void cmRST::OutputMarkupLines(bool inlineMarkup)
this->OutputLinePending = true;
}
-bool cmRST::ProcessInclude(std::string file, IncludeType type)
+bool cmRST::ProcessInclude(std::string file, Include type)
{
bool found = false;
if (this->IncludeDepth < 10) {
cmRST r(this->OS, this->DocRoot);
r.IncludeDepth = this->IncludeDepth + 1;
r.OutputLinePending = this->OutputLinePending;
- if (type != IncludeTocTree) {
+ if (type != Include::TocTree) {
r.Replace = this->Replace;
}
if (file[0] == '/') {
@@ -369,8 +371,8 @@ bool cmRST::ProcessInclude(std::string file, IncludeType type)
} else {
file = this->DocDir + "/" + file;
}
- found = r.ProcessFile(file, type == IncludeModule);
- if (type != IncludeTocTree) {
+ found = r.ProcessFile(file, type == Include::Module);
+ if (type != Include::TocTree) {
this->Replace = r.Replace;
}
this->OutputLinePending = r.OutputLinePending;
@@ -408,9 +410,9 @@ void cmRST::ProcessDirectiveTocTree()
if (!line.empty() && line[0] != ':') {
if (this->TocTreeLink.find(line)) {
std::string const& link = this->TocTreeLink.match(1);
- this->ProcessInclude(link + ".rst", IncludeTocTree);
+ this->ProcessInclude(link + ".rst", Include::TocTree);
} else {
- this->ProcessInclude(line + ".rst", IncludeTocTree);
+ this->ProcessInclude(line + ".rst", Include::TocTree);
}
}
}
diff --git a/Source/cmRST.h b/Source/cmRST.h
index ea4ef221da..65a8a71a49 100644
--- a/Source/cmRST.h
+++ b/Source/cmRST.h
@@ -29,26 +29,26 @@ public:
bool ProcessFile(std::string const& fname, bool isModule = false);
private:
- enum IncludeType
+ enum class Include
{
- IncludeNormal,
- IncludeModule,
- IncludeTocTree
+ Normal,
+ Module,
+ TocTree
};
- enum MarkupType
+ enum class Markup
{
- MarkupNone,
- MarkupNormal,
- MarkupEmpty
+ None,
+ Normal,
+ Empty
};
- enum DirectiveType
+ enum class Directive
{
- DirectiveNone,
- DirectiveParsedLiteral,
- DirectiveLiteralBlock,
- DirectiveCodeBlock,
- DirectiveReplace,
- DirectiveTocTree
+ None,
+ ParsedLiteral,
+ LiteralBlock,
+ CodeBlock,
+ Replace,
+ TocTree
};
void ProcessRST(std::istream& is);
@@ -59,7 +59,7 @@ private:
void OutputLine(std::string const& line, bool inlineMarkup);
std::string ReplaceSubstitutions(std::string const& line);
void OutputMarkupLines(bool inlineMarkup);
- bool ProcessInclude(std::string file, IncludeType type);
+ bool ProcessInclude(std::string file, Include type);
void ProcessDirectiveParsedLiteral();
void ProcessDirectiveLiteralBlock();
void ProcessDirectiveCodeBlock();
@@ -72,8 +72,8 @@ private:
int IncludeDepth = 0;
bool OutputLinePending = false;
bool LastLineEndedInColonColon = false;
- MarkupType Markup = MarkupNone;
- DirectiveType Directive = DirectiveNone;
+ Markup MarkupType = Markup::None;
+ Directive DirectiveType = Directive::None;
cmsys::RegularExpression CMakeDirective;
cmsys::RegularExpression CMakeModuleDirective;
cmsys::RegularExpression ParsedLiteralDirective;
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index 3fa0051e24..6224d0ece3 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -4,6 +4,9 @@
#include <utility>
+#include <cm/string_view>
+#include <cmext/string_view>
+
#include "cmGlobalGenerator.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
@@ -221,6 +224,11 @@ bool cmSourceFile::FindFullPath(std::string* error,
case cmPolicies::NEW:
break;
}
+ if (lPath == "FILE_SET"_s) {
+ err += "\nHint: the FILE_SET keyword may only appear after a visibility "
+ "specifier or another FILE_SET within the target_sources() "
+ "command.";
+ }
if (error != nullptr) {
*error = std::move(err);
} else {
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index c1c52010c8..9308af4430 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -183,8 +183,8 @@ private:
#define CM_HEADER_REGEX "\\.(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$"
#define CM_SOURCE_REGEX \
- "\\.(C|F|M|c|c\\+\\+|cc|cpp|mpp|cxx|ixx|cppm|cu|f|f90|for|fpp|ftn|m|mm|" \
- "rc|def|r|odl|idl|hpj|bat)$"
+ "\\.(C|F|M|c|c\\+\\+|cc|cpp|mpp|cxx|ixx|cppm|ccm|cxxm|c\\+\\+m|cu" \
+ "|f|f90|for|fpp|ftn|m|mm|rc|def|r|odl|idl|hpj|bat)$"
#define CM_PCH_REGEX "cmake_pch(_[^.]+)?\\.(h|hxx)$"
diff --git a/Source/cmState.h b/Source/cmState.h
index 9a17b22404..0a42df08a7 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -253,18 +253,6 @@ public:
private:
friend class cmake;
- void AddCacheEntry(const std::string& key, const char* value,
- const char* helpString, cmStateEnums::CacheEntryType type)
- {
- this->AddCacheEntry(key,
- value ? cmValue(std::string(value)) : cmValue(nullptr),
- helpString, type);
- }
- void AddCacheEntry(const std::string& key, const std::string& value,
- const char* helpString, cmStateEnums::CacheEntryType type)
- {
- this->AddCacheEntry(key, cmValue(value), helpString, type);
- }
void AddCacheEntry(const std::string& key, cmValue value,
const char* helpString,
cmStateEnums::CacheEntryType type);
diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h
index 010d7e3e91..24b809b571 100644
--- a/Source/cmStateTypes.h
+++ b/Source/cmStateTypes.h
@@ -60,3 +60,14 @@ enum ArtifactType
ImportLibraryArtifact
};
}
+
+namespace cmTraceEnums {
+
+/** \brief Define supported trace formats **/
+enum class TraceOutputFormat
+{
+ Undefined,
+ Human,
+ JSONv1
+};
+};
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 0b29b0d67a..1fb0079103 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -956,7 +956,7 @@ std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
// uv_fs_realpath uses Windows Vista API so fallback to kwsys if not found
std::string resolved_path;
uv_fs_t req;
- int err = uv_fs_realpath(NULL, &req, path.c_str(), NULL);
+ int err = uv_fs_realpath(nullptr, &req, path.c_str(), nullptr);
if (!err) {
resolved_path = std::string((char*)req.ptr);
cmSystemTools::ConvertToUnixSlashes(resolved_path);
@@ -967,12 +967,12 @@ std::string cmSystemTools::GetRealPathResolvingWindowsSubst(
} else if (err == UV_ENOSYS) {
resolved_path = cmsys::SystemTools::GetRealPath(path, errorMessage);
} else if (errorMessage) {
- LPSTR message = NULL;
+ LPSTR message = nullptr;
DWORD size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message, 0,
- NULL);
+ nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&message,
+ 0, nullptr);
*errorMessage = std::string(message, size);
LocalFree(message);
@@ -1329,32 +1329,33 @@ std::string cmSystemTools::ComputeCertificateThumbprint(
std::string thumbprint;
CRYPT_INTEGER_BLOB cryptBlob;
- HCERTSTORE certStore = NULL;
- PCCERT_CONTEXT certContext = NULL;
+ HCERTSTORE certStore = nullptr;
+ PCCERT_CONTEXT certContext = nullptr;
HANDLE certFile = CreateFileW(
cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ,
- FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
- if (certFile != INVALID_HANDLE_VALUE && certFile != NULL) {
- DWORD fileSize = GetFileSize(certFile, NULL);
+ if (certFile != INVALID_HANDLE_VALUE && certFile != nullptr) {
+ DWORD fileSize = GetFileSize(certFile, nullptr);
if (fileSize != INVALID_FILE_SIZE) {
auto certData = cm::make_unique<BYTE[]>(fileSize);
- if (certData != NULL) {
+ if (certData != nullptr) {
DWORD dwRead = 0;
- if (ReadFile(certFile, certData.get(), fileSize, &dwRead, NULL)) {
+ if (ReadFile(certFile, certData.get(), fileSize, &dwRead, nullptr)) {
cryptBlob.cbData = fileSize;
cryptBlob.pbData = certData.get();
// Verify that this is a valid cert
if (PFXIsPFXBlob(&cryptBlob)) {
// Open the certificate as a store
- certStore = PFXImportCertStore(&cryptBlob, NULL, CRYPT_EXPORTABLE);
- if (certStore != NULL) {
+ certStore =
+ PFXImportCertStore(&cryptBlob, nullptr, CRYPT_EXPORTABLE);
+ if (certStore != nullptr) {
// There should only be 1 cert.
certContext =
CertEnumCertificatesInStore(certStore, certContext);
- if (certContext != NULL) {
+ if (certContext != nullptr) {
// The hash is 20 bytes
BYTE hashData[20];
DWORD hashLength = 20;
@@ -1647,6 +1648,32 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top,
return out;
}
+cm::optional<std::string> cmSystemTools::GetEnvVar(std::string const& var)
+{
+ cm::optional<std::string> result;
+ {
+ std::string value;
+ if (cmSystemTools::GetEnv(var, value)) {
+ result = std::move(value);
+ }
+ }
+ return result;
+}
+
+std::vector<std::string> cmSystemTools::SplitEnvPath(std::string const& value)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ static cm::string_view sep = ";"_s;
+#else
+ static cm::string_view sep = ":"_s;
+#endif
+ std::vector<std::string> paths = cmTokenize(value, sep);
+ for (std::string& p : paths) {
+ SystemTools::ConvertToUnixSlashes(p);
+ }
+ return paths;
+}
+
#ifndef CMAKE_BOOTSTRAP
bool cmSystemTools::UnsetEnv(const char* value)
{
@@ -2375,22 +2402,22 @@ static void EnsureStdPipe(DWORD fd)
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
- sa.lpSecurityDescriptor = NULL;
+ sa.lpSecurityDescriptor = nullptr;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFileW(
L"NUL",
fd == STD_INPUT_HANDLE ? FILE_GENERIC_READ
: FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, NULL);
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, nullptr);
if (h == INVALID_HANDLE_VALUE) {
- LPSTR message = NULL;
+ LPSTR message = nullptr;
DWORD size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR)&message, 0, NULL);
+ nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&message, 0, nullptr);
std::string msg = std::string(message, size);
LocalFree(message);
std::cerr << "failed to open NUL for missing stdio pipe: " << msg;
@@ -2535,10 +2562,10 @@ void cmSystemTools::FindCMakeResources(const char* argv0)
#if defined(_WIN32) && !defined(__CYGWIN__)
(void)argv0; // ignore this on windows
wchar_t modulepath[_MAX_PATH];
- ::GetModuleFileNameW(NULL, modulepath, sizeof(modulepath));
+ ::GetModuleFileNameW(nullptr, modulepath, sizeof(modulepath));
std::string path = cmsys::Encoding::ToNarrow(modulepath);
std::string realPath =
- cmSystemTools::GetRealPathResolvingWindowsSubst(path, NULL);
+ cmSystemTools::GetRealPathResolvingWindowsSubst(path, nullptr);
if (realPath.empty()) {
realPath = path;
}
@@ -3196,20 +3223,41 @@ bool VersionCompare(cmSystemTools::CompareOp op, const char* lhss,
{
const char* endl = lhss;
const char* endr = rhss;
- unsigned long lhs;
- unsigned long rhs;
while (((*endl >= '0') && (*endl <= '9')) ||
((*endr >= '0') && (*endr <= '9'))) {
- // Do component-wise comparison.
- lhs = strtoul(endl, const_cast<char**>(&endl), 10);
- rhs = strtoul(endr, const_cast<char**>(&endr), 10);
+ // Do component-wise comparison, ignoring leading zeros
+ // (components are treated as integers, not as mantissas)
+ while (*endl == '0') {
+ endl++;
+ }
+ while (*endr == '0') {
+ endr++;
+ }
+
+ const char* beginl = endl;
+ const char* beginr = endr;
+
+ // count significant digits
+ while ((*endl >= '0') && (*endl <= '9')) {
+ endl++;
+ }
+ while ((*endr >= '0') && (*endr <= '9')) {
+ endr++;
+ }
+
+ // compare number of digits first
+ ptrdiff_t r = ((endl - beginl) - (endr - beginr));
+ if (r == 0) {
+ // compare the digits if number of digits is equal
+ r = strncmp(beginl, beginr, endl - beginl);
+ }
- if (lhs < rhs) {
+ if (r < 0) {
// lhs < rhs, so true if operation is LESS
return (op & cmSystemTools::OP_LESS) != 0;
}
- if (lhs > rhs) {
+ if (r > 0) {
// lhs > rhs, so true if operation is GREATER
return (op & cmSystemTools::OP_GREATER) != 0;
}
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 09f2bf0ce0..7d55d4b15b 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -399,6 +399,9 @@ public:
static std::string RelativeIfUnder(std::string const& top,
std::string const& in);
+ static cm::optional<std::string> GetEnvVar(std::string const& var);
+ static std::vector<std::string> SplitEnvPath(std::string const& value);
+
#ifndef CMAKE_BOOTSTRAP
/** Remove an environment variable */
static bool UnsetEnv(const char* value);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index debf593c39..5e558714e2 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -4,7 +4,6 @@
#include <algorithm>
#include <cassert>
-#include <initializer_list>
#include <iterator>
#include <map>
#include <set>
@@ -273,6 +272,352 @@ struct UsageRequirementProperty
std::vector<BT<std::string>> Entries;
};
+
+struct TargetProperty
+{
+ enum class InitCondition
+ {
+ // Always initialize the property.
+ Always,
+ // Never initialize the property.
+ Never,
+ // Only initialize if the target can compile sources.
+ CanCompileSources,
+ // Only apply to Xcode generators.
+ NeedsXcode,
+ // Only apply to Xcode generators on targets that can compile sources.
+ NeedsXcodeAndCanCompileSources,
+ // Needs to be a "normal" target (any non-global, non-utility target).
+ NormalTarget,
+ // Any non-imported target.
+ NonImportedTarget,
+ // Needs to be a "normal" target (any non-global, non-utility target) that
+ // is not `IMPORTED`.
+ NormalNonImportedTarget,
+ // Needs to be a "normal" target with an artifact (no `INTERFACE`
+ // libraries).
+ TargetWithArtifact,
+ // Needs to be a "normal" target with an artifact that is not an
+ // executable.
+ NonExecutableWithArtifact,
+ // Needs to be a linkable library target (no `OBJECT` or `MODULE`
+ // libraries).
+ LinkableLibraryTarget,
+ // Needs to be an executable.
+ ExecutableTarget,
+ // Needs to be a shared library (`SHARED`).
+ SharedLibraryTarget,
+ // Needs to be a target with meaningful symbol exports (`SHARED` or
+ // `EXECUTABLE`).
+ TargetWithSymbolExports,
+ // Targets with "commands" associated with them. Basically everything
+ // except global and `INTERFACE` targets.
+ TargetWithCommands,
+ };
+
+ enum class Repetition
+ {
+ Once,
+ PerConfig,
+ PerConfigPrefix,
+ };
+
+ TargetProperty(cm::static_string_view name)
+ : Name(name)
+ {
+ }
+
+ TargetProperty(cm::static_string_view name, cm::static_string_view dflt,
+ InitCondition init)
+ : Name(name)
+ , Default(dflt)
+ , InitConditional(init)
+ {
+ }
+
+ TargetProperty(cm::static_string_view name, InitCondition init)
+ : Name(name)
+ , InitConditional(init)
+ {
+ }
+
+ TargetProperty(cm::static_string_view name, InitCondition init,
+ Repetition repeat)
+ : Name(name)
+ , InitConditional(init)
+ , Repeat(repeat)
+ {
+ }
+
+ cm::static_string_view const Name;
+ cm::optional<cm::static_string_view> const Default = {};
+ InitCondition const InitConditional = InitCondition::Always;
+ Repetition const Repeat = Repetition::Once;
+};
+
+#define IC TargetProperty::InitCondition
+#define R TargetProperty::Repetition
+
+/* clang-format off */
+#define COMMON_LANGUAGE_PROPERTIES(lang) \
+ { #lang "_COMPILER_LAUNCHER"_s, IC::CanCompileSources }, \
+ { #lang "_STANDARD"_s, IC::CanCompileSources }, \
+ { #lang "_STANDARD_REQUIRED"_s, IC::CanCompileSources }, \
+ { #lang "_EXTENSIONS"_s, IC::CanCompileSources }, \
+ { #lang "_VISIBILITY_PRESET"_s, IC::CanCompileSources }
+/* clang-format on */
+
+TargetProperty const StaticTargetProperties[] = {
+ /* clang-format off */
+ // Compilation properties
+ { "COMPILE_WARNING_AS_ERROR"_s, IC::CanCompileSources },
+ { "INTERPROCEDURAL_OPTIMIZATION"_s, IC::CanCompileSources },
+ { "INTERPROCEDURAL_OPTIMIZATION_"_s, IC::TargetWithArtifact, R::PerConfig },
+ { "NO_SYSTEM_FROM_IMPORTED"_s, IC::CanCompileSources },
+ // Set to `True` for `SHARED` and `MODULE` targets.
+ { "POSITION_INDEPENDENT_CODE"_s, IC::CanCompileSources },
+ { "VISIBILITY_INLINES_HIDDEN"_s, IC::CanCompileSources },
+ // -- Features
+ // ---- PCH
+ { "DISABLE_PRECOMPILE_HEADERS"_s, IC::CanCompileSources },
+ { "PCH_WARN_INVALID"_s, "ON"_s, IC::CanCompileSources },
+ { "PCH_INSTANTIATE_TEMPLATES"_s, "ON"_s, IC::CanCompileSources },
+ // -- Platforms
+ // ---- Android
+ { "ANDROID_API"_s, IC::CanCompileSources },
+ { "ANDROID_API_MIN"_s, IC::CanCompileSources },
+ { "ANDROID_ARCH"_s, IC::CanCompileSources },
+ { "ANDROID_ASSETS_DIRECTORIES"_s, IC::CanCompileSources },
+ { "ANDROID_JAVA_SOURCE_DIR"_s, IC::CanCompileSources },
+ { "ANDROID_STL_TYPE"_s, IC::CanCompileSources },
+ // ---- macOS
+ { "OSX_ARCHITECTURES"_s, IC::CanCompileSources },
+ // ---- Windows
+ { "MSVC_DEBUG_INFORMATION_FORMAT"_s, IC::CanCompileSources },
+ { "MSVC_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
+ { "VS_JUST_MY_CODE_DEBUGGING"_s, IC::CanCompileSources },
+ // ---- OpenWatcom
+ { "WATCOM_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
+ // -- Language
+ // ---- C
+ COMMON_LANGUAGE_PROPERTIES(C),
+ // ---- C++
+ COMMON_LANGUAGE_PROPERTIES(CXX),
+ // ---- CSharp
+ { "DOTNET_SDK"_s, IC::NonImportedTarget },
+ { "DOTNET_TARGET_FRAMEWORK"_s, IC::TargetWithCommands },
+ { "DOTNET_TARGET_FRAMEWORK_VERSION"_s, IC::TargetWithCommands },
+ // ---- CUDA
+ COMMON_LANGUAGE_PROPERTIES(CUDA),
+ { "CUDA_SEPARABLE_COMPILATION"_s, IC::CanCompileSources },
+ { "CUDA_ARCHITECTURES"_s, IC::CanCompileSources },
+ // ---- Fortran
+ { "Fortran_FORMAT"_s, IC::CanCompileSources },
+ { "Fortran_MODULE_DIRECTORY"_s, IC::CanCompileSources },
+ { "Fortran_COMPILER_LAUNCHER"_s, IC::CanCompileSources },
+ { "Fortran_PREPRPOCESS"_s, IC::CanCompileSources },
+ { "Fortran_VISIBILITY_PRESET"_s, IC::CanCompileSources },
+ // ---- HIP
+ COMMON_LANGUAGE_PROPERTIES(HIP),
+ { "HIP_ARCHITECTURES"_s, IC::CanCompileSources },
+ // ---- ISPC
+ { "ISPC_COMPILER_LAUNCHER"_s, IC::CanCompileSources },
+ { "ISPC_HEADER_DIRECTORY"_s, IC::CanCompileSources },
+ { "ISPC_HEADER_SUFFIX"_s, "_ispc.h"_s, IC::CanCompileSources },
+ { "ISPC_INSTRUCTION_SETS"_s, IC::CanCompileSources },
+ // ---- Objective C
+ COMMON_LANGUAGE_PROPERTIES(OBJC),
+ // ---- Objective C++
+ COMMON_LANGUAGE_PROPERTIES(OBJCXX),
+ // ---- Swift
+ { "Swift_LANGUAGE_VERSION"_s, IC::CanCompileSources },
+ { "Swift_MODULE_DIRECTORY"_s, IC::CanCompileSources },
+ // ---- moc
+ { "AUTOMOC"_s, IC::CanCompileSources },
+ { "AUTOMOC_COMPILER_PREDEFINES"_s, IC::CanCompileSources },
+ { "AUTOMOC_MACRO_NAMES"_s, IC::CanCompileSources },
+ { "AUTOMOC_MOC_OPTIONS"_s, IC::CanCompileSources },
+ { "AUTOMOC_PATH_PREFIX"_s, IC::CanCompileSources },
+ { "AUTOMOC_EXECUTABLE"_s, IC::CanCompileSources },
+ // ---- uic
+ { "AUTOUIC"_s, IC::CanCompileSources },
+ { "AUTOUIC_OPTIONS"_s, IC::CanCompileSources },
+ { "AUTOUIC_SEARCH_PATHS"_s, IC::CanCompileSources },
+ { "AUTOUIC_EXECUTABLE"_s, IC::CanCompileSources },
+ // ---- rcc
+ { "AUTORCC"_s, IC::CanCompileSources },
+ { "AUTORCC_OPTIONS"_s, IC::CanCompileSources },
+ { "AUTORCC_EXECUTABLE"_s, IC::CanCompileSources },
+
+ // Linking properties
+ { "ENABLE_EXPORTS"_s, IC::TargetWithSymbolExports },
+ { "LINK_LIBRARIES_ONLY_TARGETS"_s, IC::NormalNonImportedTarget },
+ { "LINK_SEARCH_START_STATIC"_s, IC::CanCompileSources },
+ { "LINK_SEARCH_END_STATIC"_s, IC::CanCompileSources },
+ // Initialize per-configuration name postfix property from the variable only
+ // for non-executable targets. This preserves compatibility with previous
+ // CMake versions in which executables did not support this variable.
+ // Projects may still specify the property directly.
+ { "_POSTFIX"_s, IC::NonExecutableWithArtifact, R::PerConfigPrefix },
+ // -- Dependent library lookup
+ { "MACOSX_RPATH"_s, IC::CanCompileSources },
+ // ---- Build
+ { "BUILD_RPATH"_s, IC::CanCompileSources },
+ { "BUILD_RPATH_USE_ORIGIN"_s, IC::CanCompileSources },
+ { "SKIP_BUILD_RPATH"_s, "OFF"_s, IC::CanCompileSources },
+ { "BUILD_WITH_INSTALL_RPATH"_s, "OFF"_s, IC::CanCompileSources },
+ { "BUILD_WITH_INSTALL_NAME_DIR"_s, IC::CanCompileSources },
+ // ---- Install
+ { "INSTALL_NAME_DIR"_s, IC::CanCompileSources },
+ { "INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, IC::CanCompileSources },
+ { "INSTALL_RPATH"_s, ""_s, IC::CanCompileSources },
+ { "INSTALL_RPATH_USE_LINK_PATH"_s, "OFF"_s, IC::CanCompileSources },
+ // -- Platforms
+ // ---- AIX
+ { "AIX_EXPORT_ALL_SYMBOLS"_s, IC::TargetWithSymbolExports },
+ // ---- Android
+ { "ANDROID_GUI"_s, IC::ExecutableTarget },
+ { "ANDROID_JAR_DIRECTORIES"_s, IC::CanCompileSources },
+ { "ANDROID_JAR_DEPENDENCIES"_s, IC::CanCompileSources },
+ { "ANDROID_NATIVE_LIB_DIRECTORIES"_s, IC::CanCompileSources },
+ { "ANDROID_NATIVE_LIB_DEPENDENCIES"_s, IC::CanCompileSources },
+ { "ANDROID_PROGUARD"_s, IC::CanCompileSources },
+ { "ANDROID_PROGUARD_CONFIG_PATH"_s, IC::CanCompileSources },
+ { "ANDROID_SECURE_PROPS_PATH"_s, IC::CanCompileSources },
+ // ---- iOS
+ { "IOS_INSTALL_COMBINED"_s, IC::CanCompileSources },
+ // ---- macOS
+ { "FRAMEWORK_MULTI_CONFIG_POSTFIX_"_s, IC::LinkableLibraryTarget, R::PerConfig },
+ // ---- Windows
+ { "DLL_NAME_WITH_SOVERSION"_s, IC::SharedLibraryTarget },
+ { "GNUtoMS"_s, IC::CanCompileSources },
+ { "WIN32_EXECUTABLE"_s, IC::CanCompileSources },
+ { "WINDOWS_EXPORT_ALL_SYMBOLS"_s, IC::TargetWithSymbolExports },
+ // -- Languages
+ // ---- C
+ { "C_LINKER_LAUNCHER"_s, IC::CanCompileSources },
+ // ---- C++
+ { "CXX_LINKER_LAUNCHER"_s, IC::CanCompileSources },
+ // ---- CUDA
+ { "CUDA_RESOLVE_DEVICE_SYMBOLS"_s, IC::CanCompileSources },
+ { "CUDA_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
+ // ---- HIP
+ { "HIP_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
+ // ---- Objective C
+ { "OBJC_LINKER_LAUNCHER"_s, IC::CanCompileSources },
+ // ---- Objective C++
+ { "OBJCXX_LINKER_LAUNCHER"_s, IC::CanCompileSources },
+
+ // Static analysis
+ // -- C
+ { "C_CLANG_TIDY"_s, IC::CanCompileSources },
+ { "C_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
+ { "C_CPPLINT"_s, IC::CanCompileSources },
+ { "C_CPPCHECK"_s, IC::CanCompileSources },
+ { "C_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources },
+ // -- C++
+ { "CXX_CLANG_TIDY"_s, IC::CanCompileSources },
+ { "CXX_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
+ { "CXX_CPPLINT"_s, IC::CanCompileSources },
+ { "CXX_CPPCHECK"_s, IC::CanCompileSources },
+ { "CXX_INCLUDE_WHAT_YOU_USE"_s, IC::CanCompileSources },
+ // -- Objective C
+ { "OBJC_CLANG_TIDY"_s, IC::CanCompileSources },
+ { "OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
+ // -- Objective C++
+ { "OBJCXX_CLANG_TIDY"_s, IC::CanCompileSources },
+ { "OBJCXX_CLANG_TIDY_EXPORT_FIXES_DIR"_s, IC::CanCompileSources },
+ // -- Linking
+ { "LINK_WHAT_YOU_USE"_s, IC::CanCompileSources },
+
+ // Build graph properties
+ { "LINK_DEPENDS_NO_SHARED"_s, IC::CanCompileSources },
+ { "UNITY_BUILD"_s, IC::CanCompileSources },
+ { "UNITY_BUILD_UNIQUE_ID"_s, IC::CanCompileSources },
+ { "UNITY_BUILD_BATCH_SIZE"_s, "8"_s, IC::CanCompileSources },
+ { "UNITY_BUILD_MODE"_s, "BATCH"_s, IC::CanCompileSources },
+ { "OPTIMIZE_DEPENDENCIES"_s, IC::CanCompileSources },
+ { "VERIFY_INTERFACE_HEADER_SETS"_s },
+ // -- Android
+ { "ANDROID_ANT_ADDITIONAL_OPTIONS"_s, IC::CanCompileSources },
+ { "ANDROID_PROCESS_MAX"_s, IC::CanCompileSources },
+ { "ANDROID_SKIP_ANT_STEP"_s, IC::CanCompileSources },
+ // -- Autogen
+ { "AUTOGEN_ORIGIN_DEPENDS"_s, IC::CanCompileSources },
+ { "AUTOGEN_PARALLEL"_s, IC::CanCompileSources },
+ // -- moc
+ { "AUTOMOC_DEPEND_FILTERS"_s, IC::CanCompileSources },
+ // -- C++
+ { "CXX_SCAN_FOR_MODULES"_s, IC::CanCompileSources },
+ // -- Ninja
+ { "JOB_POOL_COMPILE"_s, IC::CanCompileSources },
+ { "JOB_POOL_LINK"_s, IC::CanCompileSources },
+ { "JOB_POOL_PRECOMPILE_HEADER"_s, IC::CanCompileSources },
+ // -- Visual Studio
+ { "VS_NO_COMPILE_BATCHING"_s, IC::CanCompileSources },
+ { "VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION"_s, IC::CanCompileSources},
+
+ // Output location properties
+ { "ARCHIVE_OUTPUT_DIRECTORY"_s, IC::CanCompileSources },
+ { "ARCHIVE_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig },
+ { "COMPILE_PDB_OUTPUT_DIRECTORY"_s, IC::CanCompileSources },
+ { "COMPILE_PDB_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig },
+ { "LIBRARY_OUTPUT_DIRECTORY"_s, IC::CanCompileSources },
+ { "LIBRARY_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig },
+ { "PDB_OUTPUT_DIRECTORY"_s, IC::CanCompileSources },
+ { "PDB_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig },
+ { "RUNTIME_OUTPUT_DIRECTORY"_s, IC::CanCompileSources },
+ { "RUNTIME_OUTPUT_DIRECTORY_"_s, IC::TargetWithArtifact, R::PerConfig },
+
+ // macOS bundle properties
+ { "FRAMEWORK"_s, IC::CanCompileSources },
+ { "FRAMEWORK_MULTI_CONFIG_POSTFIX"_s, IC::CanCompileSources },
+ { "MACOSX_BUNDLE"_s, IC::CanCompileSources },
+
+ // Usage requirement properties
+ { "LINK_INTERFACE_LIBRARIES"_s, IC::CanCompileSources },
+ { "MAP_IMPORTED_CONFIG_"_s, IC::NormalTarget, R::PerConfig },
+
+ // Metadata
+ { "CROSSCOMPILING_EMULATOR"_s, IC::ExecutableTarget },
+ { "EXPORT_COMPILE_COMMANDS"_s, IC::CanCompileSources },
+ { "FOLDER"_s },
+
+ // Xcode properties
+ { "XCODE_GENERATE_SCHEME"_s, IC::NeedsXcode },
+
+#ifdef __APPLE__
+ { "XCODE_SCHEME_ADDRESS_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_THREAD_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_THREAD_SANITIZER_STOP"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_LAUNCH_CONFIGURATION"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_WORKING_DIRECTORY"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_MALLOC_SCRIBBLE"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_MALLOC_GUARD_EDGES"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_GUARD_MALLOC"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_LAUNCH_MODE"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ZOMBIE_OBJECTS"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_MALLOC_STACK"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_SCHEME_ENVIRONMENT"_s, IC::NeedsXcodeAndCanCompileSources },
+ { "XCODE_LINK_BUILD_PHASE_MODE"_s, "NONE"_s, IC::NeedsXcodeAndCanCompileSources },
+#endif
+ /* clang-format on */
+};
+
+#undef COMMON_LANGUAGE_PROPERTIES
+#undef IC
+#undef R
}
class cmTargetInternals
@@ -289,11 +634,11 @@ public:
bool HaveInstallRule;
bool IsDLLPlatform;
bool IsAIX;
+ bool IsApple;
bool IsAndroid;
- bool IsImportedTarget;
- bool ImportedGloballyVisible;
bool BuildInterfaceIncludesAppended;
bool PerConfig;
+ cmTarget::Visibility TargetVisibility;
std::set<BT<std::pair<std::string, bool>>> Utilities;
std::vector<cmCustomCommand> PreBuildCommands;
std::vector<cmCustomCommand> PreLinkCommands;
@@ -328,6 +673,8 @@ public:
cmTargetInternals();
+ bool IsImported() const;
+
bool CheckImportedLibName(std::string const& prop,
std::string const& value) const;
@@ -562,15 +909,6 @@ std::pair<bool, cmValue> UsageRequirementProperty::Read(
return { did_read, value };
}
-namespace {
-#define SETUP_COMMON_LANGUAGE_PROPERTIES(lang) \
- initProp(#lang "_COMPILER_LAUNCHER"); \
- initProp(#lang "_STANDARD"); \
- initProp(#lang "_STANDARD_REQUIRED"); \
- initProp(#lang "_EXTENSIONS"); \
- initProp(#lang "_VISIBILITY_PRESET")
-}
-
cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
Visibility vis, cmMakefile* mf, PerConfig perConfig)
: impl(cm::make_unique<cmTargetInternals>())
@@ -583,10 +921,9 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->impl->HaveInstallRule = false;
this->impl->IsDLLPlatform = false;
this->impl->IsAIX = false;
+ this->impl->IsApple = false;
this->impl->IsAndroid = false;
- this->impl->IsImportedTarget =
- (vis == VisibilityImported || vis == VisibilityImportedGlobally);
- this->impl->ImportedGloballyVisible = vis == VisibilityImportedGlobally;
+ this->impl->TargetVisibility = vis;
this->impl->BuildInterfaceIncludesAppended = false;
this->impl->PerConfig = (perConfig == PerConfig::Yes);
@@ -602,306 +939,17 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->impl->IsAIX = (systemName == "AIX" || systemName == "OS400");
}
+ // Check whether we are targeting Apple.
+ this->impl->IsApple = this->impl->Makefile->IsOn("APPLE");
+
// Check whether we are targeting an Android platform.
this->impl->IsAndroid = (this->impl->Makefile->GetSafeDefinition(
"CMAKE_SYSTEM_NAME") == "Android");
- std::string defKey;
- defKey.reserve(128);
- defKey += "CMAKE_";
- auto initProp = [this, mf, &defKey](const std::string& property) {
- // Replace everything after "CMAKE_"
- defKey.replace(defKey.begin() + 6, defKey.end(), property);
- if (cmValue value = mf->GetDefinition(defKey)) {
- this->SetProperty(property, value);
- }
- };
- auto initPropValue = [this, mf, &defKey](const std::string& property,
- const char* default_value) {
- // Replace everything after "CMAKE_"
- defKey.replace(defKey.begin() + 6, defKey.end(), property);
- if (cmValue value = mf->GetDefinition(defKey)) {
- this->SetProperty(property, value);
- } else if (default_value) {
- this->SetProperty(property, default_value);
- }
- };
-
- // Setup default property values.
- if (this->CanCompileSources()) {
-
- // Compilation properties
- initProp("INTERPROCEDURAL_OPTIMIZATION");
- // initProp("INTERPROCEDURAL_OPTIMIZATION_<CONFIG>"); (per-config block)
- initProp("NO_SYSTEM_FROM_IMPORTED");
- initProp("VISIBILITY_INLINES_HIDDEN");
- initProp("COMPILE_WARNING_AS_ERROR");
- // -- Features
- // ---- PCH
- initProp("DISABLE_PRECOMPILE_HEADERS");
- initPropValue("PCH_WARN_INVALID", "ON");
- initPropValue("PCH_INSTANTIATE_TEMPLATES", "ON");
- // -- Platforms
- // ---- Android
- initProp("ANDROID_API");
- initProp("ANDROID_API_MIN");
- initProp("ANDROID_ARCH");
- initProp("ANDROID_ASSETS_DIRECTORIES");
- initProp("ANDROID_JAVA_SOURCE_DIR");
- initProp("ANDROID_STL_TYPE");
- // ---- macOS
- initProp("OSX_ARCHITECTURES");
- // ---- Windows
- initProp("MSVC_DEBUG_INFORMATION_FORMAT");
- initProp("MSVC_RUNTIME_LIBRARY");
- initProp("VS_JUST_MY_CODE_DEBUGGING");
- // ---- OpenWatcom
- initProp("WATCOM_RUNTIME_LIBRARY");
- // -- Language
- // ---- C
- SETUP_COMMON_LANGUAGE_PROPERTIES(C);
- // ---- C++
- SETUP_COMMON_LANGUAGE_PROPERTIES(CXX);
- // ---- CUDA
- SETUP_COMMON_LANGUAGE_PROPERTIES(CUDA);
- initProp("CUDA_SEPARABLE_COMPILATION");
- initProp("CUDA_ARCHITECTURES");
- // ---- Fortran
- initProp("Fortran_FORMAT");
- initProp("Fortran_MODULE_DIRECTORY");
- initProp("Fortran_COMPILER_LAUNCHER");
- initProp("Fortran_PREPROCESS");
- initProp("Fortran_VISIBILITY_PRESET");
- // ---- HIP
- SETUP_COMMON_LANGUAGE_PROPERTIES(HIP);
- initProp("HIP_ARCHITECTURES");
- // ---- ISPC
- initProp("ISPC_COMPILER_LAUNCHER");
- initProp("ISPC_HEADER_DIRECTORY");
- initPropValue("ISPC_HEADER_SUFFIX", "_ispc.h");
- initProp("ISPC_INSTRUCTION_SETS");
- // ---- Objective C
- SETUP_COMMON_LANGUAGE_PROPERTIES(OBJC);
- // ---- Objective C++
- SETUP_COMMON_LANGUAGE_PROPERTIES(OBJCXX);
- // ---- Swift
- initProp("Swift_LANGUAGE_VERSION");
- initProp("Swift_MODULE_DIRECTORY");
- // ---- moc
- initProp("AUTOMOC");
- initProp("AUTOMOC_COMPILER_PREDEFINES");
- initProp("AUTOMOC_MACRO_NAMES");
- initProp("AUTOMOC_MOC_OPTIONS");
- initProp("AUTOMOC_PATH_PREFIX");
- // ---- uic
- initProp("AUTOUIC");
- initProp("AUTOUIC_OPTIONS");
- initProp("AUTOUIC_SEARCH_PATHS");
- // ---- rcc
- initProp("AUTORCC");
- initProp("AUTORCC_OPTIONS");
-
- // Linking properties
- initProp("LINK_SEARCH_START_STATIC");
- initProp("LINK_SEARCH_END_STATIC");
- // -- Dependent library lookup
- initProp("MACOSX_RPATH");
- // ---- Build
- initProp("BUILD_RPATH");
- initProp("BUILD_RPATH_USE_ORIGIN");
- initPropValue("SKIP_BUILD_RPATH", "OFF");
- initPropValue("BUILD_WITH_INSTALL_RPATH", "OFF");
- initProp("BUILD_WITH_INSTALL_NAME_DIR");
- // ---- Install
- initProp("INSTALL_NAME_DIR");
- initProp("INSTALL_REMOVE_ENVIRONMENT_RPATH");
- initPropValue("INSTALL_RPATH", "");
- initPropValue("INSTALL_RPATH_USE_LINK_PATH", "OFF");
- // -- Platforms
- // ---- Android
- initProp("ANDROID_JAR_DIRECTORIES");
- initProp("ANDROID_JAR_DEPENDENCIES");
- initProp("ANDROID_NATIVE_LIB_DIRECTORIES");
- initProp("ANDROID_NATIVE_LIB_DEPENDENCIES");
- initProp("ANDROID_PROGUARD");
- initProp("ANDROID_PROGUARD_CONFIG_PATH");
- initProp("ANDROID_SECURE_PROPS_PATH");
- // ---- iOS
- initProp("IOS_INSTALL_COMBINED");
- // ---- Windows
- initProp("GNUtoMS");
- initProp("WIN32_EXECUTABLE");
- // -- Languages
- // ---- C
- initProp("C_LINKER_LAUNCHER");
- // ---- C++
- initProp("CXX_LINKER_LAUNCHER");
- // ---- CUDA
- initProp("CUDA_RESOLVE_DEVICE_SYMBOLS");
- initProp("CUDA_RUNTIME_LIBRARY");
- // ---- HIP
- initProp("HIP_RUNTIME_LIBRARY");
- // ---- Objective C
- initProp("OBJC_LINKER_LAUNCHER");
- // ---- Objective C++
- initProp("OBJCXX_LINKER_LAUNCHER");
-
- // Static analysis
- // -- C
- initProp("C_CLANG_TIDY");
- initProp("C_CLANG_TIDY_EXPORT_FIXES_DIR");
- initProp("C_CPPLINT");
- initProp("C_CPPCHECK");
- initProp("C_INCLUDE_WHAT_YOU_USE");
- // -- C++
- initProp("CXX_CLANG_TIDY");
- initProp("CXX_CLANG_TIDY_EXPORT_FIXES_DIR");
- initProp("CXX_CPPLINT");
- initProp("CXX_CPPCHECK");
- initProp("CXX_INCLUDE_WHAT_YOU_USE");
- // -- Objective C
- initProp("OBJC_CLANG_TIDY");
- initProp("OBJC_CLANG_TIDY_EXPORT_FIXES_DIR");
- // -- Objective C++
- initProp("OBJCXX_CLANG_TIDY");
- initProp("OBJCXX_CLANG_TIDY_EXPORT_FIXES_DIR");
- // -- Linking
- initProp("LINK_WHAT_YOU_USE");
-
- // Build graph properties
- initProp("LINK_DEPENDS_NO_SHARED");
- initProp("UNITY_BUILD");
- initProp("UNITY_BUILD_UNIQUE_ID");
- initPropValue("UNITY_BUILD_BATCH_SIZE", "8");
- initPropValue("UNITY_BUILD_MODE", "BATCH");
- initProp("OPTIMIZE_DEPENDENCIES");
- // -- Android
- initProp("ANDROID_ANT_ADDITIONAL_OPTIONS");
- initProp("ANDROID_PROCESS_MAX");
- initProp("ANDROID_SKIP_ANT_STEP");
- // -- Autogen
- initProp("AUTOGEN_ORIGIN_DEPENDS");
- initProp("AUTOGEN_PARALLEL");
- // -- moc
- initProp("AUTOMOC_DEPEND_FILTERS");
- // -- C++
- initProp("CXX_SCAN_FOR_MODULES");
- // -- Ninja
- initProp("JOB_POOL_COMPILE");
- initProp("JOB_POOL_LINK");
- initProp("JOB_POOL_PRECOMPILE_HEADER");
- // -- Visual Studio
- initProp("VS_NO_COMPILE_BATCHING");
-
- // Output location properties
- initProp("ARCHIVE_OUTPUT_DIRECTORY");
- initProp("LIBRARY_OUTPUT_DIRECTORY");
- initProp("RUNTIME_OUTPUT_DIRECTORY");
- initProp("PDB_OUTPUT_DIRECTORY");
- initProp("COMPILE_PDB_OUTPUT_DIRECTORY");
-
- // -- macOS bundle properties
- initProp("FRAMEWORK");
- initProp("FRAMEWORK_MULTI_CONFIG_POSTFIX");
- initProp("MACOSX_BUNDLE");
-
- // Usage requirement properties
- initProp("LINK_INTERFACE_LIBRARIES");
-
- // Metadata
- initProp("EXPORT_COMPILE_COMMANDS");
-
-#ifdef __APPLE__
- if (this->GetGlobalGenerator()->IsXcode()) {
- initProp("XCODE_SCHEME_ADDRESS_SANITIZER");
- initProp("XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN");
- initProp("XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING");
- initProp("XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE");
- initProp("XCODE_SCHEME_THREAD_SANITIZER");
- initProp("XCODE_SCHEME_THREAD_SANITIZER_STOP");
- initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
- initProp("XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
- initProp("XCODE_SCHEME_LAUNCH_CONFIGURATION");
- initProp("XCODE_SCHEME_ENABLE_GPU_API_VALIDATION");
- initProp("XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION");
- initProp("XCODE_SCHEME_WORKING_DIRECTORY");
- initProp("XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER");
- initProp("XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP");
- initProp("XCODE_SCHEME_MALLOC_SCRIBBLE");
- initProp("XCODE_SCHEME_MALLOC_GUARD_EDGES");
- initProp("XCODE_SCHEME_GUARD_MALLOC");
- initProp("XCODE_SCHEME_LAUNCH_MODE");
- initProp("XCODE_SCHEME_ZOMBIE_OBJECTS");
- initProp("XCODE_SCHEME_MALLOC_STACK");
- initProp("XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE");
- initProp("XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS");
- initProp("XCODE_SCHEME_ENVIRONMENT");
- initPropValue("XCODE_LINK_BUILD_PHASE_MODE", "NONE");
- }
-#endif
- }
-
- initProp("FOLDER");
- initProp("VERIFY_INTERFACE_HEADER_SETS");
-
- if (this->GetGlobalGenerator()->IsXcode()) {
- initProp("XCODE_GENERATE_SCHEME");
- }
-
- // Setup per-configuration property default values.
- if (this->GetType() != cmStateEnums::UTILITY &&
- this->GetType() != cmStateEnums::GLOBAL_TARGET) {
- static const auto configProps = {
- /* clang-format needs this comment to break after the opening brace */
- "ARCHIVE_OUTPUT_DIRECTORY_"_s, "LIBRARY_OUTPUT_DIRECTORY_"_s,
- "RUNTIME_OUTPUT_DIRECTORY_"_s, "PDB_OUTPUT_DIRECTORY_"_s,
- "COMPILE_PDB_OUTPUT_DIRECTORY_"_s, "MAP_IMPORTED_CONFIG_"_s,
- "INTERPROCEDURAL_OPTIMIZATION_"_s
- };
- // Collect the set of configuration types.
- std::vector<std::string> configNames =
- mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
- for (std::string const& configName : configNames) {
- std::string configUpper = cmSystemTools::UpperCase(configName);
- for (auto const& prop : configProps) {
- // Interface libraries have no output locations, so honor only
- // the configuration map.
- if (this->impl->TargetType == cmStateEnums::INTERFACE_LIBRARY &&
- prop != "MAP_IMPORTED_CONFIG_") {
- continue;
- }
- std::string property = cmStrCat(prop, configUpper);
- initProp(property);
- }
-
- // Initialize per-configuration name postfix property from the
- // variable only for non-executable targets. This preserves
- // compatibility with previous CMake versions in which executables
- // did not support this variable. Projects may still specify the
- // property directly.
- if (this->impl->TargetType != cmStateEnums::EXECUTABLE &&
- this->impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) {
- std::string property =
- cmStrCat(cmSystemTools::UpperCase(configName), "_POSTFIX");
- initProp(property);
- }
-
- if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
- this->impl->TargetType == cmStateEnums::STATIC_LIBRARY) {
- std::string property = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
- cmSystemTools::UpperCase(configName));
- initProp(property);
- }
- }
- if (!this->IsImported()) {
- initProp("LINK_LIBRARIES_ONLY_TARGETS");
- }
- }
-
// Save the backtrace of target construction.
this->impl->Backtrace = this->impl->Makefile->GetBacktrace();
- if (!this->IsImported()) {
+ if (this->IsNormal()) {
// Initialize the INCLUDE_DIRECTORIES property based on the current value
// of the same directory property:
this->impl->IncludeDirectories.CopyFromDirectory(
@@ -921,23 +969,6 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->impl->Makefile->GetLinkDirectoriesEntries());
}
- if (this->impl->TargetType == cmStateEnums::EXECUTABLE) {
- initProp("ANDROID_GUI");
- initProp("CROSSCOMPILING_EMULATOR");
- initProp("ENABLE_EXPORTS");
- }
- if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
- this->impl->TargetType == cmStateEnums::MODULE_LIBRARY) {
- this->SetProperty("POSITION_INDEPENDENT_CODE", "True");
- } else if (this->CanCompileSources()) {
- initProp("POSITION_INDEPENDENT_CODE");
- }
- if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
- this->impl->TargetType == cmStateEnums::EXECUTABLE) {
- initProp("AIX_EXPORT_ALL_SYMBOLS");
- initProp("WINDOWS_EXPORT_ALL_SYMBOLS");
- }
-
// Record current policies for later use.
this->impl->Makefile->RecordPolicies(this->impl->PolicyMap);
@@ -949,13 +980,133 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
this->impl->PolicyMap.Set(cmPolicies::CMP0022, cmPolicies::NEW);
}
+ std::set<TargetProperty::InitCondition> metConditions;
+ metConditions.insert(TargetProperty::InitCondition::Always);
+ if (this->CanCompileSources()) {
+ metConditions.insert(TargetProperty::InitCondition::CanCompileSources);
+ }
+ if (this->GetGlobalGenerator()->IsXcode()) {
+ metConditions.insert(TargetProperty::InitCondition::NeedsXcode);
+ if (this->CanCompileSources()) {
+ metConditions.insert(
+ TargetProperty::InitCondition::NeedsXcodeAndCanCompileSources);
+ }
+ }
if (!this->IsImported()) {
- initProp("DOTNET_SDK");
+ metConditions.insert(TargetProperty::InitCondition::NonImportedTarget);
+ }
+ if (this->impl->TargetType != cmStateEnums::UTILITY &&
+ this->impl->TargetType != cmStateEnums::GLOBAL_TARGET) {
+ metConditions.insert(TargetProperty::InitCondition::NormalTarget);
+ if (this->IsNormal()) {
+ metConditions.insert(
+ TargetProperty::InitCondition::NormalNonImportedTarget);
+ }
+ if (this->impl->TargetType != cmStateEnums::INTERFACE_LIBRARY) {
+ metConditions.insert(TargetProperty::InitCondition::TargetWithArtifact);
+ if (this->impl->TargetType != cmStateEnums::EXECUTABLE) {
+ metConditions.insert(
+ TargetProperty::InitCondition::NonExecutableWithArtifact);
+ }
+ }
+ if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
+ this->impl->TargetType == cmStateEnums::STATIC_LIBRARY) {
+ metConditions.insert(
+ TargetProperty::InitCondition::LinkableLibraryTarget);
+ }
+ if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY) {
+ metConditions.insert(TargetProperty::InitCondition::SharedLibraryTarget);
+ }
+ }
+ if (this->impl->TargetType == cmStateEnums::EXECUTABLE) {
+ metConditions.insert(TargetProperty::InitCondition::ExecutableTarget);
+ }
+ if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
+ this->impl->TargetType == cmStateEnums::EXECUTABLE) {
+ metConditions.insert(
+ TargetProperty::InitCondition::TargetWithSymbolExports);
}
-
if (this->impl->TargetType <= cmStateEnums::GLOBAL_TARGET) {
- initProp("DOTNET_TARGET_FRAMEWORK");
- initProp("DOTNET_TARGET_FRAMEWORK_VERSION");
+ metConditions.insert(TargetProperty::InitCondition::TargetWithCommands);
+ }
+
+ std::vector<std::string> configNames =
+ mf->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+ for (auto& config : configNames) {
+ config = cmSystemTools::UpperCase(config);
+ }
+
+ std::string defKey;
+ defKey.reserve(128);
+ defKey += "CMAKE_";
+ auto initProperty = [this, mf, &defKey](const std::string& property,
+ const char* default_value) {
+ // special init for ENABLE_EXPORTS
+ // For SHARED_LIBRARY, only CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS variable
+ // is used
+ // For EXECUTABLE, CMAKE_EXECUTABLE_ENABLE_EXPORTS or else
+ // CMAKE_ENABLE_EXPORTS variables are used
+ if (property == "ENABLE_EXPORTS"_s) {
+ // Replace everything after "CMAKE_"
+ defKey.replace(
+ defKey.begin() + 6, defKey.end(),
+ cmStrCat(this->impl->TargetType == cmStateEnums::EXECUTABLE
+ ? "EXECUTABLE"
+ : "SHARED_LIBRARY",
+ '_', property));
+ if (cmValue value = mf->GetDefinition(defKey)) {
+ this->SetProperty(property, value);
+ return;
+ }
+ if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY) {
+ if (default_value) {
+ this->SetProperty(property, default_value);
+ }
+ return;
+ }
+ }
+
+ // Replace everything after "CMAKE_"
+ defKey.replace(defKey.begin() + 6, defKey.end(), property);
+ if (cmValue value = mf->GetDefinition(defKey)) {
+ this->SetProperty(property, value);
+ } else if (default_value) {
+ this->SetProperty(property, default_value);
+ }
+ };
+
+ std::string dflt_storage;
+ for (auto const& tp : StaticTargetProperties) {
+ // Ignore properties that we have not met the condition for.
+ if (!metConditions.count(tp.InitConditional)) {
+ continue;
+ }
+
+ const char* dflt = nullptr;
+ if (tp.Default) {
+ dflt_storage = std::string(*tp.Default);
+ dflt = dflt_storage.c_str();
+ }
+
+ if (tp.Repeat == TargetProperty::Repetition::Once) {
+ initProperty(std::string(tp.Name), dflt);
+ } else {
+ std::string propertyName;
+ for (auto const& configName : configNames) {
+ if (tp.Repeat == TargetProperty::Repetition::PerConfig) {
+ propertyName = cmStrCat(tp.Name, configName);
+ } else if (tp.Repeat == TargetProperty::Repetition::PerConfigPrefix) {
+ propertyName = cmStrCat(configName, tp.Name);
+ }
+ initProperty(propertyName, dflt);
+ }
+ }
+ }
+
+ // Clean up some property defaults.
+ if (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
+ this->impl->TargetType == cmStateEnums::MODULE_LIBRARY) {
+ this->SetProperty("POSITION_INDEPENDENT_CODE", "True");
}
// check for "CMAKE_VS_GLOBALS" variable and set up target properties
@@ -972,13 +1123,13 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
if (assignment != std::string::npos) {
const std::string propName = vsGlobal + i.substr(0, assignment);
const std::string propValue = i.substr(assignment + 1);
- initPropValue(propName, propValue.c_str());
+ initProperty(propName, propValue.c_str());
}
}
}
}
- if (this->IsImported() || mf->GetPropertyAsBool("SYSTEM")) {
+ if (!this->IsNormal() || mf->GetPropertyAsBool("SYSTEM")) {
this->SetProperty("SYSTEM", "ON");
}
@@ -1089,18 +1240,22 @@ bool cmTarget::IsExecutableWithExports() const
this->GetPropertyAsBool("ENABLE_EXPORTS"));
}
+bool cmTarget::IsSharedLibraryWithExports() const
+{
+ return (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+ this->GetPropertyAsBool("ENABLE_EXPORTS"));
+}
+
bool cmTarget::IsFrameworkOnApple() const
{
return ((this->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->GetType() == cmStateEnums::STATIC_LIBRARY) &&
- this->impl->Makefile->IsOn("APPLE") &&
- this->GetPropertyAsBool("FRAMEWORK"));
+ this->IsApple() && this->GetPropertyAsBool("FRAMEWORK"));
}
bool cmTarget::IsAppBundleOnApple() const
{
- return (this->GetType() == cmStateEnums::EXECUTABLE &&
- this->impl->Makefile->IsOn("APPLE") &&
+ return (this->GetType() == cmStateEnums::EXECUTABLE && this->IsApple() &&
this->GetPropertyAsBool("MACOSX_BUNDLE"));
}
@@ -1624,6 +1779,9 @@ MAKE_PROP(COMPILE_FEATURES);
MAKE_PROP(COMPILE_OPTIONS);
MAKE_PROP(PRECOMPILE_HEADERS);
MAKE_PROP(PRECOMPILE_HEADERS_REUSE_FROM);
+MAKE_PROP(CUDA_CUBIN_COMPILATION);
+MAKE_PROP(CUDA_FATBIN_COMPILATION);
+MAKE_PROP(CUDA_OPTIX_COMPILATION);
MAKE_PROP(CUDA_PTX_COMPILATION);
MAKE_PROP(EXPORT_NAME);
MAKE_PROP(IMPORTED);
@@ -1662,7 +1820,6 @@ std::string ConvertToString<cmValue>(cmValue value)
{
return std::string(*value);
}
-
}
template <typename ValueType>
@@ -1750,8 +1907,8 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
return;
}
/* no need to change anything if value does not change */
- if (!this->impl->ImportedGloballyVisible) {
- this->impl->ImportedGloballyVisible = true;
+ if (!this->IsImportedGloballyVisible()) {
+ this->impl->TargetVisibility = Visibility::ImportedGlobally;
this->GetGlobalGenerator()->IndexTarget(this);
}
} else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") &&
@@ -1760,14 +1917,38 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
value ? value
: std::string{})) { // NOLINT(bugprone-branch-clone)
/* error was reported by check method */
- } else if (prop == propCUDA_PTX_COMPILATION &&
- this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
- std::ostringstream e;
- e << "CUDA_PTX_COMPILATION property can only be applied to OBJECT "
- "targets (\""
- << this->impl->Name << "\")\n";
- this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
- return;
+ } else if (prop == propCUDA_CUBIN_COMPILATION ||
+ prop == propCUDA_FATBIN_COMPILATION ||
+ prop == propCUDA_OPTIX_COMPILATION ||
+ prop == propCUDA_PTX_COMPILATION) {
+ auto const& compiler =
+ this->impl->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
+ auto const& compilerVersion =
+ this->impl->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_VERSION");
+ if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
+ auto e =
+ cmStrCat(prop, " property can only be applied to OBJECT targets(",
+ this->impl->Name, ")\n");
+ this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+ return;
+ }
+ const bool flag_found =
+ (prop == propCUDA_PTX_COMPILATION &&
+ this->impl->Makefile->GetDefinition("_CMAKE_CUDA_PTX_FLAG")) ||
+ (prop == propCUDA_CUBIN_COMPILATION &&
+ this->impl->Makefile->GetDefinition("_CMAKE_CUDA_CUBIN_FLAG")) ||
+ (prop == propCUDA_FATBIN_COMPILATION &&
+ this->impl->Makefile->GetDefinition("_CMAKE_CUDA_FATBIN_FLAG")) ||
+ (prop == propCUDA_OPTIX_COMPILATION &&
+ this->impl->Makefile->GetDefinition("_CMAKE_CUDA_OPTIX_FLAG"));
+ if (flag_found) {
+ this->impl->Properties.SetProperty(prop, value);
+ } else {
+ auto e = cmStrCat(prop, " property is not supported by ", compiler,
+ " compiler version ", compilerVersion, ".");
+ this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
+ return;
+ }
} else if (prop == propPRECOMPILE_HEADERS_REUSE_FROM) {
if (this->GetProperty("PRECOMPILE_HEADERS")) {
std::ostringstream e;
@@ -2441,15 +2622,70 @@ bool cmTarget::IsAIX() const
{
return this->impl->IsAIX;
}
+bool cmTarget::IsApple() const
+{
+ return this->impl->IsApple;
+}
+
+bool cmTarget::IsNormal() const
+{
+ switch (this->impl->TargetVisibility) {
+ case Visibility::Normal:
+ return true;
+ case Visibility::Generated:
+ case Visibility::Imported:
+ case Visibility::ImportedGlobally:
+ return false;
+ }
+ assert(false && "unknown visibility (IsNormal)");
+ return false;
+}
+
+bool cmTarget::IsSynthetic() const
+{
+ switch (this->impl->TargetVisibility) {
+ case Visibility::Generated:
+ return true;
+ case Visibility::Normal:
+ case Visibility::Imported:
+ case Visibility::ImportedGlobally:
+ return false;
+ }
+ assert(false && "unknown visibility (IsSynthetic)");
+ return false;
+}
+
+bool cmTargetInternals::IsImported() const
+{
+ switch (this->TargetVisibility) {
+ case cmTarget::Visibility::Imported:
+ case cmTarget::Visibility::ImportedGlobally:
+ return true;
+ case cmTarget::Visibility::Normal:
+ case cmTarget::Visibility::Generated:
+ return false;
+ }
+ assert(false && "unknown visibility (IsImported)");
+ return false;
+}
bool cmTarget::IsImported() const
{
- return this->impl->IsImportedTarget;
+ return this->impl->IsImported();
}
bool cmTarget::IsImportedGloballyVisible() const
{
- return this->impl->ImportedGloballyVisible;
+ switch (this->impl->TargetVisibility) {
+ case Visibility::ImportedGlobally:
+ return true;
+ case Visibility::Normal:
+ case Visibility::Generated:
+ case Visibility::Imported:
+ return false;
+ }
+ assert(false && "unknown visibility (IsImportedGloballyVisible)");
+ return false;
}
bool cmTarget::IsPerConfig() const
@@ -2489,7 +2725,8 @@ const char* cmTarget::GetSuffixVariableInternal(
case cmStateEnums::RuntimeBinaryArtifact:
return "CMAKE_SHARED_LIBRARY_SUFFIX";
case cmStateEnums::ImportLibraryArtifact:
- return "CMAKE_IMPORT_LIBRARY_SUFFIX";
+ return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_SUFFIX"
+ : "CMAKE_IMPORT_LIBRARY_SUFFIX";
}
break;
case cmStateEnums::MODULE_LIBRARY:
@@ -2530,7 +2767,8 @@ const char* cmTarget::GetPrefixVariableInternal(
case cmStateEnums::RuntimeBinaryArtifact:
return "CMAKE_SHARED_LIBRARY_PREFIX";
case cmStateEnums::ImportLibraryArtifact:
- return "CMAKE_IMPORT_LIBRARY_PREFIX";
+ return this->IsApple() ? "CMAKE_APPLE_IMPORT_FILE_PREFIX"
+ : "CMAKE_IMPORT_LIBRARY_PREFIX";
}
break;
case cmStateEnums::MODULE_LIBRARY:
@@ -2745,7 +2983,7 @@ bool cmTargetInternals::CheckImportedLibName(std::string const& prop,
std::string const& value) const
{
if (this->TargetType != cmStateEnums::INTERFACE_LIBRARY ||
- !this->IsImportedTarget) {
+ !this->IsImported()) {
this->Makefile->IssueMessage(
MessageType::FATAL_ERROR,
prop +
@@ -2805,7 +3043,9 @@ bool cmTarget::GetMappedConfig(std::string const& desired_config, cmValue& loc,
bool allowImp = (this->IsDLLPlatform() &&
(this->GetType() == cmStateEnums::SHARED_LIBRARY ||
this->IsExecutableWithExports())) ||
- (this->IsAIX() && this->IsExecutableWithExports());
+ (this->IsAIX() && this->IsExecutableWithExports()) ||
+ (this->GetMakefile()->PlatformSupportsAppleTextStubs() &&
+ this->IsSharedLibraryWithExports());
// If a mapping was found, check its configurations.
for (auto mci = mappedConfigs.begin();
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 38bd0365a1..24f6fcdb2d 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -46,11 +46,12 @@ class BTs;
class cmTarget
{
public:
- enum Visibility
+ enum class Visibility
{
- VisibilityNormal,
- VisibilityImported,
- VisibilityImportedGlobally
+ Normal,
+ Generated,
+ Imported,
+ ImportedGlobally,
};
enum class PerConfig
@@ -204,7 +205,11 @@ public:
//! Return whether or not we are targeting AIX.
bool IsAIX() const;
+ //! Return whether or not we are targeting Apple.
+ bool IsApple() const;
+ bool IsNormal() const;
+ bool IsSynthetic() const;
bool IsImported() const;
bool IsImportedGloballyVisible() const;
bool IsPerConfig() const;
@@ -216,6 +221,10 @@ public:
//! Return whether this target is an executable with symbol exports enabled.
bool IsExecutableWithExports() const;
+ //! Return whether this target is a shared library with symbol exports
+ //! enabled.
+ bool IsSharedLibraryWithExports() const;
+
//! Return whether this target is a shared library Framework on Apple.
bool IsFrameworkOnApple() const;
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 52de6ff640..8926f9ed15 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -693,12 +693,12 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
switch (this->ProjectType) {
case VsProjectType::vcxproj: {
+ Elem(e0, "Import").Attribute("Project", VS10_CXX_DEFAULT_PROPS);
std::string const& props =
this->GlobalGenerator->GetPlatformToolsetVersionProps();
if (!props.empty()) {
Elem(e0, "Import").Attribute("Project", props);
}
- Elem(e0, "Import").Attribute("Project", VS10_CXX_DEFAULT_PROPS);
} break;
case VsProjectType::csproj:
Elem(e0, "Import")
@@ -955,7 +955,11 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
"executables."));
return;
}
- outputType = "Exe";
+ if (cmIsOn(win32)) {
+ outputType = "WinExe";
+ } else {
+ outputType = "Exe";
+ }
} break;
case cmStateEnums::UTILITY:
case cmStateEnums::INTERFACE_LIBRARY:
@@ -1806,10 +1810,15 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule(
this->WriteCustomRuleCSharp(e0, c, name, script, additional_inputs.str(),
outputs.str(), comment, ccg);
} else {
- // FIXME(#18405): Enable BuildInParallel::Yes via an option or policy.
+ BuildInParallel buildInParallel = BuildInParallel::No;
+ if (command.GetCMP0147Status() == cmPolicies::NEW &&
+ !command.GetUsesTerminal() &&
+ !(command.HasMainDependency() && source->GetIsGenerated())) {
+ buildInParallel = BuildInParallel::Yes;
+ }
this->WriteCustomRuleCpp(*spe2, c, script, additional_inputs.str(),
outputs.str(), comment, ccg, symbolic,
- BuildInParallel::No);
+ buildInParallel);
}
}
}
@@ -3105,6 +3114,17 @@ std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes(
return includes;
}
+std::string cmVisualStudio10TargetGenerator::GetTargetOutputName() const
+{
+ std::string config;
+ if (!this->Configurations.empty()) {
+ config = this->Configurations[0];
+ }
+ const auto& nameComponents =
+ this->GeneratorTarget->GetFullNameComponents(config);
+ return nameComponents.prefix + nameComponents.base;
+}
+
bool cmVisualStudio10TargetGenerator::ComputeClOptions()
{
return std::all_of(
@@ -3577,13 +3597,13 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions(
if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) {
cudaOptions.AddFlag("GenerateRelocatableDeviceCode", "true");
}
- bool notPtx = true;
+ bool notPtxLike = true;
if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
cudaOptions.AddFlag("NvccCompilation", "ptx");
// We drop the %(Extension) component as CMake expects all PTX files
// to not have the source file extension at all
cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).ptx");
- notPtx = false;
+ notPtxLike = false;
if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
cudaVersion, "9.0") &&
@@ -3598,9 +3618,24 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions(
"%(BaseCommandLineTemplate) [CompileOut] [FastMath] "
"[Defines] \"%(FullPath)\"");
}
- }
-
- if (notPtx &&
+ } else if (this->GeneratorTarget->GetPropertyAsBool(
+ "CUDA_CUBIN_COMPILATION")) {
+ cudaOptions.AddFlag("NvccCompilation", "cubin");
+ cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).cubin");
+ notPtxLike = false;
+ } else if (this->GeneratorTarget->GetPropertyAsBool(
+ "CUDA_FATBIN_COMPILATION")) {
+ cudaOptions.AddFlag("NvccCompilation", "fatbin");
+ cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).fatbin");
+ notPtxLike = false;
+ } else if (this->GeneratorTarget->GetPropertyAsBool(
+ "CUDA_OPTIX_COMPILATION")) {
+ cudaOptions.AddFlag("NvccCompilation", "optix-ir");
+ cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).optixir");
+ notPtxLike = false;
+ }
+
+ if (notPtxLike &&
cmSystemTools::VersionCompareGreaterEq(
"8.0", this->GlobalGenerator->GetPlatformToolsetCudaString())) {
// Explicitly state that we want this file to be treated as a
@@ -5054,8 +5089,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80(Elem& e1)
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
ConvertToWindowsSlash(artifactDir);
std::string artifactDirXML = cmVS10EscapeXML(artifactDir);
- std::string targetNameXML =
- cmVS10EscapeXML(this->GeneratorTarget->GetName());
+ const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName());
cmGeneratedFileStream fout(manifestFile);
fout.SetCopyIfDifferent(true);
@@ -5137,8 +5171,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81(Elem& e1)
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
ConvertToWindowsSlash(artifactDir);
std::string artifactDirXML = cmVS10EscapeXML(artifactDir);
- std::string targetNameXML =
- cmVS10EscapeXML(this->GeneratorTarget->GetName());
+ const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName());
cmGeneratedFileStream fout(manifestFile);
fout.SetCopyIfDifferent(true);
@@ -5200,8 +5233,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80(Elem& e1)
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
ConvertToWindowsSlash(artifactDir);
std::string artifactDirXML = cmVS10EscapeXML(artifactDir);
- std::string targetNameXML =
- cmVS10EscapeXML(this->GeneratorTarget->GetName());
+ const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName());
cmGeneratedFileStream fout(manifestFile);
fout.SetCopyIfDifferent(true);
@@ -5255,8 +5287,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81(Elem& e1)
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
ConvertToWindowsSlash(artifactDir);
std::string artifactDirXML = cmVS10EscapeXML(artifactDir);
- std::string targetNameXML =
- cmVS10EscapeXML(this->GeneratorTarget->GetName());
+ const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName());
cmGeneratedFileStream fout(manifestFile);
fout.SetCopyIfDifferent(true);
@@ -5315,8 +5346,7 @@ void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0(Elem& e1)
this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
ConvertToWindowsSlash(artifactDir);
std::string artifactDirXML = cmVS10EscapeXML(artifactDir);
- std::string targetNameXML =
- cmVS10EscapeXML(this->GeneratorTarget->GetName());
+ const std::string& targetNameXML = cmVS10EscapeXML(GetTargetOutputName());
cmGeneratedFileStream fout(manifestFile);
fout.SetCopyIfDifferent(true);
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index e00f692b76..97ae69fc37 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -117,6 +117,7 @@ private:
std::vector<std::string> GetIncludes(std::string const& config,
std::string const& lang) const;
+ std::string GetTargetOutputName() const;
bool ComputeClOptions();
bool ComputeClOptions(std::string const& configName);
diff --git a/Source/cm_codecvt.cxx b/Source/cm_codecvt.cxx
index 2d2a377eca..12877b835f 100644
--- a/Source/cm_codecvt.cxx
+++ b/Source/cm_codecvt.cxx
@@ -171,7 +171,7 @@ std::codecvt_base::result codecvt::Decode(mbstate_t& state, int size,
}
int tlen = WideCharToMultiByte(m_codepage, 0, wbuf, wlen, to_next,
- to_end - to_next, NULL, NULL);
+ to_end - to_next, nullptr, nullptr);
if (tlen <= 0) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
return std::codecvt_base::partial;
@@ -206,7 +206,7 @@ std::codecvt_base::result codecvt::DecodePartial(mbstate_t& state,
}
int tlen = WideCharToMultiByte(m_codepage, 0, wbuf, wlen, to_next,
- to_end - to_next, NULL, NULL);
+ to_end - to_next, nullptr, nullptr);
if (tlen <= 0) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
return std::codecvt_base::partial;
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 468ff73aeb..8dee5cc13c 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -52,6 +52,7 @@
#if !defined(CMAKE_BOOTSTRAP)
# include "cmMakefileProfilingData.h"
#endif
+#include "cmJSONState.h"
#include "cmMessenger.h"
#include "cmState.h"
#include "cmStateDirectory.h"
@@ -335,7 +336,7 @@ cmake::cmake(Role role, cmState::Mode mode, cmState::ProjectKind projectKind)
// The "c" extension MUST precede the "C" extension.
setupExts(this->CLikeSourceFileExtensions,
{ "c", "C", "c++", "cc", "cpp", "cxx", "cu", "mpp", "m", "M",
- "mm", "ixx", "cppm" });
+ "mm", "ixx", "cppm", "ccm", "cxxm", "c++m" });
setupExts(this->HeaderFileExtensions,
{ "h", "hh", "h++", "hm", "hpp", "hxx", "in", "txx" });
setupExts(this->CudaFileExtensions, { "cu" });
@@ -964,7 +965,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
return true;
};
- auto ToolsetLamda = [&](std::string const& value, cmake* state) -> bool {
+ auto ToolsetLambda = [&](std::string const& value, cmake* state) -> bool {
if (haveToolset) {
cmSystemTools::Error("Multiple -T options not allowed");
return false;
@@ -1016,7 +1017,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
CommandArgument::RequiresSeparator::No, PlatformLambda },
CommandArgument{ "-T", "No toolset specified for -T",
CommandArgument::Values::One,
- CommandArgument::RequiresSeparator::No, ToolsetLamda },
+ CommandArgument::RequiresSeparator::No, ToolsetLambda },
CommandArgument{ "--toolchain", "No file specified for --toolchain",
CommandArgument::Values::One, IgnoreAndTrueLambda },
CommandArgument{ "--install-prefix",
@@ -1142,49 +1143,64 @@ void cmake::SetArgs(const std::vector<std::string>& args)
std::cout << ".\n";
return true;
} },
- CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
+ CommandArgument{ "--trace", CommandArgument::Values::Zero,
[](std::string const&, cmake* state) -> bool {
- std::cout << "Running with expanded trace output on.\n";
+ std::cout << "Put cmake in trace mode.\n";
state->SetTrace(true);
- state->SetTraceExpand(true);
+ state->SetTraceExpand(false);
return true;
} },
- CommandArgument{ "--trace-format", CommandArgument::Values::One,
- [](std::string const& value, cmake* state) -> bool {
+ CommandArgument{ "--trace-expand", CommandArgument::Values::Zero,
+ [](std::string const&, cmake* state) -> bool {
+ std::cout << "Put cmake in trace mode, but with "
+ "variables expanded.\n";
state->SetTrace(true);
- const auto traceFormat = StringToTraceFormat(value);
- if (traceFormat == TraceFormat::TRACE_UNDEFINED) {
- cmSystemTools::Error(
- "Invalid format specified for --trace-format. "
- "Valid formats are human, json-v1.");
- return false;
- }
- state->SetTraceFormat(traceFormat);
+ state->SetTraceExpand(true);
return true;
} },
- CommandArgument{ "--trace-source", CommandArgument::Values::One,
- [](std::string const& value, cmake* state) -> bool {
- std::string file(value);
- cmSystemTools::ConvertToUnixSlashes(file);
- state->AddTraceSource(file);
+ CommandArgument{
+ "--trace-format", "Invalid format specified for --trace-format",
+ CommandArgument::Values::One,
+ [](std::string const& value, cmake* state) -> bool {
+ std::cout << "Put cmake in trace mode and sets the "
+ "trace output format.\n";
+ state->SetTrace(true);
+ const auto traceFormat = StringToTraceFormat(value);
+ if (traceFormat == TraceFormat::Undefined) {
+ cmSystemTools::Error("Invalid format specified for --trace-format. "
+ "Valid formats are human, json-v1.");
+ return false;
+ }
+ state->SetTraceFormat(traceFormat);
+ return true;
+ } },
+ CommandArgument{ "--trace-source", "No file specified for --trace-source",
+ CommandArgument::Values::OneOrMore,
+ [](std::string const& values, cmake* state) -> bool {
+ std::cout << "Put cmake in trace mode, but output only "
+ "lines of a specified file. Multiple "
+ "options are allowed.\n";
+ for (auto file :
+ cmSystemTools::SplitString(values, ';')) {
+ cmSystemTools::ConvertToUnixSlashes(file);
+ state->AddTraceSource(file);
+ }
state->SetTrace(true);
return true;
} },
- CommandArgument{ "--trace-redirect", CommandArgument::Values::One,
+ CommandArgument{ "--trace-redirect",
+ "No file specified for --trace-redirect",
+ CommandArgument::Values::One,
[](std::string const& value, cmake* state) -> bool {
+ std::cout
+ << "Put cmake in trace mode and redirect trace "
+ "output to a file instead of stderr.\n";
std::string file(value);
cmSystemTools::ConvertToUnixSlashes(file);
state->SetTraceFile(file);
state->SetTrace(true);
return true;
} },
- CommandArgument{ "--trace", CommandArgument::Values::Zero,
- [](std::string const&, cmake* state) -> bool {
- std::cout << "Running with trace output on.\n";
- state->SetTrace(true);
- state->SetTraceExpand(false);
- return true;
- } },
CommandArgument{ "--warn-uninitialized", CommandArgument::Values::Zero,
[](std::string const&, cmake* state) -> bool {
std::cout << "Warn about uninitialized values.\n";
@@ -1411,13 +1427,10 @@ void cmake::SetArgs(const std::vector<std::string>& args)
if (listPresets != ListPresets::None || !presetName.empty()) {
cmCMakePresetsGraph presetsGraph;
auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
- if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
+ if (result != true) {
std::string errorMsg =
- cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
- ": ", cmCMakePresetsGraph::ResultToString(result));
- if (!presetsGraph.errors.empty()) {
- errorMsg = cmStrCat(errorMsg, "\nErrors:\n", presetsGraph.errors);
- }
+ cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":",
+ presetsGraph.parseState.GetErrorMessage());
cmSystemTools::Error(errorMsg);
return;
}
@@ -1532,6 +1545,29 @@ void cmake::SetArgs(const std::vector<std::string>& args)
if (expandedPreset->DebugFind == true) {
this->SetDebugFindOutput(true);
}
+ if (expandedPreset->TraceMode &&
+ expandedPreset->TraceMode !=
+ cmCMakePresetsGraph::TraceEnableMode::Disable) {
+ this->SetTrace(true);
+ if (expandedPreset->TraceMode ==
+ cmCMakePresetsGraph::TraceEnableMode::Expand) {
+ this->SetTraceExpand(true);
+ }
+ }
+ if (expandedPreset->TraceFormat) {
+ this->SetTrace(true);
+ this->SetTraceFormat(*expandedPreset->TraceFormat);
+ }
+ if (!expandedPreset->TraceSource.empty()) {
+ this->SetTrace(true);
+ for (std::string const& filePaths : expandedPreset->TraceSource) {
+ this->AddTraceSource(filePaths);
+ }
+ }
+ if (!expandedPreset->TraceRedirect.empty()) {
+ this->SetTrace(true);
+ this->SetTraceFile(expandedPreset->TraceRedirect);
+ }
}
#endif
}
@@ -1588,8 +1624,8 @@ cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
{
using TracePair = std::pair<std::string, TraceFormat>;
static const std::vector<TracePair> levels = {
- { "human", TraceFormat::TRACE_HUMAN },
- { "json-v1", TraceFormat::TRACE_JSON_V1 },
+ { "human", TraceFormat::Human },
+ { "json-v1", TraceFormat::JSONv1 },
};
const auto traceStrLowCase = cmSystemTools::LowerCase(traceStr);
@@ -1598,7 +1634,7 @@ cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
[&traceStrLowCase](const TracePair& p) {
return p.first == traceStrLowCase;
});
- return (it != levels.cend()) ? it->second : TraceFormat::TRACE_UNDEFINED;
+ return (it != levels.cend()) ? it->second : TraceFormat::Undefined;
}
void cmake::SetTraceFile(const std::string& file)
@@ -1624,7 +1660,7 @@ void cmake::PrintTraceFormatVersion()
std::string msg;
switch (this->GetTraceFormat()) {
- case TraceFormat::TRACE_JSON_V1: {
+ case TraceFormat::JSONv1: {
#ifndef CMAKE_BOOTSTRAP
Json::Value val;
Json::Value version;
@@ -1637,11 +1673,11 @@ void cmake::PrintTraceFormatVersion()
#endif
break;
}
- case TraceFormat::TRACE_HUMAN:
+ case TraceFormat::Human:
msg = "";
break;
- case TraceFormat::TRACE_UNDEFINED:
- msg = "INTERNAL ERROR: Trace format is TRACE_UNDEFINED";
+ case TraceFormat::Undefined:
+ msg = "INTERNAL ERROR: Trace format is Undefined";
break;
}
@@ -3426,10 +3462,10 @@ int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
cmCMakePresetsGraph settingsFile;
auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
- if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
+ if (result != true) {
cmSystemTools::Error(
- cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
- ": ", cmCMakePresetsGraph::ResultToString(result)));
+ cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ":",
+ settingsFile.parseState.GetErrorMessage()));
return 1;
}
@@ -3782,10 +3818,10 @@ int cmake::Workflow(const std::string& presetName,
cmCMakePresetsGraph settingsFile;
auto result = settingsFile.ReadProjectPresets(this->GetHomeDirectory());
- if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
- cmSystemTools::Error(
- cmStrCat("Could not read presets from ", this->GetHomeDirectory(), ": ",
- cmCMakePresetsGraph::ResultToString(result)));
+ if (result != true) {
+ cmSystemTools::Error(cmStrCat("Could not read presets from ",
+ this->GetHomeDirectory(), ":",
+ settingsFile.parseState.GetErrorMessage()));
return 1;
}
diff --git a/Source/cmake.h b/Source/cmake.h
index d1f388a33b..0f8f64283c 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -118,13 +118,7 @@ public:
FIND_PACKAGE_MODE
};
- /** \brief Define supported trace formats **/
- enum TraceFormat
- {
- TRACE_UNDEFINED,
- TRACE_HUMAN,
- TRACE_JSON_V1,
- };
+ using TraceFormat = cmTraceEnums::TraceOutputFormat;
struct GeneratorInfo
{
@@ -719,7 +713,7 @@ private:
bool DebugFindOutput = false;
bool Trace = false;
bool TraceExpand = false;
- TraceFormat TraceFormatVar = TRACE_HUMAN;
+ TraceFormat TraceFormatVar = TraceFormat::Human;
cmGeneratedFileStream TraceFile;
cmake* TraceRedirect = nullptr;
#ifndef CMAKE_BOOTSTRAP
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 21d0cc935c..a2a9e09cfc 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -50,10 +50,10 @@
#endif
#include <array>
+#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
-#include <ctime>
#include <iostream>
#include <memory>
#include <sstream>
@@ -1104,27 +1104,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
if (args[1] == "time" && args.size() > 2) {
std::vector<std::string> command(args.begin() + 2, args.end());
- clock_t clock_start;
- clock_t clock_finish;
- time_t time_start;
- time_t time_finish;
-
- time(&time_start);
- clock_start = clock();
int ret = 0;
+ auto time_start = std::chrono::steady_clock::now();
cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
+ auto time_finish = std::chrono::steady_clock::now();
- clock_finish = clock();
- time(&time_finish);
-
- double clocks_per_sec = static_cast<double>(CLOCKS_PER_SEC);
- std::cout << "Elapsed time: "
- << static_cast<long>(time_finish - time_start) << " s. (time)"
- << ", "
- << static_cast<double>(clock_finish - clock_start) /
- clocks_per_sec
- << " s. (clock)"
- << "\n";
+ std::chrono::duration<double> time_elapsed = time_finish - time_start;
+ std::cout << "Elapsed time (seconds): " << time_elapsed.count() << "\n";
return ret;
}
@@ -2343,6 +2329,9 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 ||
cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) {
this->Incremental = true;
+ } else if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:NO") == 0 ||
+ cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:NO") == 0) {
+ this->Incremental = false;
} else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 ||
cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) {
this->LinkGeneratesManifest = false;
@@ -2367,17 +2356,11 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
// pass it to the link command.
this->ManifestFileRC = intDir + "/manifest.rc";
this->ManifestFileRes = intDir + "/manifest.res";
- } else if (this->UserManifests.empty()) {
- // Prior to support for user-specified manifests CMake placed the
- // linker-generated manifest next to the binary (as if it were not to be
- // embedded) when not linking incrementally. Preserve this behavior.
- this->ManifestFile = this->TargetFile + ".manifest";
- this->LinkerManifestFile = this->ManifestFile;
- }
- if (this->LinkGeneratesManifest) {
- this->LinkCommand.emplace_back("/MANIFEST");
- this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile);
+ if (this->LinkGeneratesManifest) {
+ this->LinkCommand.emplace_back("/MANIFEST");
+ this->LinkCommand.push_back("/MANIFESTFILE:" + this->LinkerManifestFile);
+ }
}
return true;
@@ -2511,20 +2494,23 @@ int cmVSLink::LinkIncremental()
int cmVSLink::LinkNonIncremental()
{
- // Run the link command (possibly generates intermediate manifest).
- if (!RunCommand("LINK", this->LinkCommand, this->Verbose, FORMAT_DECIMAL)) {
- return -1;
- }
+ // Sort out any manifests.
+ if (this->LinkGeneratesManifest || !this->UserManifests.empty()) {
+ std::string opt =
+ std::string("/MANIFEST:EMBED,ID=") + (this->Type == 1 ? '1' : '2');
+ this->LinkCommand.emplace_back(opt);
- // If we have no manifest files we are done.
- if (!this->LinkGeneratesManifest && this->UserManifests.empty()) {
- return 0;
+ for (auto const& m : this->UserManifests) {
+ opt = "/MANIFESTINPUT:" + m;
+ this->LinkCommand.emplace_back(opt);
+ }
}
- // Run the manifest tool to embed the final manifest in the binary.
- std::string mtOut =
- "/outputresource:" + this->TargetFile + (this->Type == 1 ? ";#1" : ";#2");
- return this->RunMT(mtOut, false);
+ // Run the link command.
+ if (!RunCommand("LINK", this->LinkCommand, this->Verbose, FORMAT_DECIMAL)) {
+ return -1;
+ }
+ return 0;
}
int cmVSLink::RunMT(std::string const& out, bool notify)
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index af02f7fb80..2b8eedd4e9 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -201,11 +201,7 @@ endif()
# Enable testing if building standalone.
if(KWSYS_STANDALONE)
- include(Dart)
- mark_as_advanced(BUILD_TESTING DART_ROOT TCL_TCLSH)
- if(BUILD_TESTING)
- enable_testing()
- endif()
+ include(CTest)
endif()
# Choose default shared/static build if not specified.
@@ -630,8 +626,8 @@ endif()
# Build a list of classes and headers we need to implement the
# selected components. Initialize with required components.
set(KWSYS_CLASSES)
-set(KWSYS_H_FILES Configure SharedForward)
-set(KWSYS_HXX_FILES Configure String)
+set(KWSYS_H_FILES Configure)
+set(KWSYS_HXX_FILES Configure)
# Add selected C++ classes.
set(cppclasses
@@ -1038,6 +1034,10 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY CXX_INCLUDE_WHAT_YOU_USE "")
set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx PROPERTY LABELS ${KWSYS_LABELS_EXE})
target_link_libraries(${KWSYS_NAMESPACE}TestsCxx ${KWSYS_TARGET_LINK})
+ get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if(_isMultiConfig)
+ set_property(TARGET ${KWSYS_NAMESPACE}TestsCxx APPEND PROPERTY COMPILE_DEFINITIONS BUILD_CONFIG="$<CONFIG>")
+ endif()
set(TEST_SYSTEMTOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(TEST_SYSTEMTOOLS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
@@ -1118,16 +1118,6 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
endif()
set_property(SOURCE testProcess.c PROPERTY COMPILE_FLAGS "${testProcess_COMPILE_FLAGS}")
- # Test SharedForward
- configure_file(${PROJECT_SOURCE_DIR}/testSharedForward.c.in
- ${PROJECT_BINARY_DIR}/testSharedForward.c @ONLY IMMEDIATE)
- add_executable(${KWSYS_NAMESPACE}TestSharedForward
- ${PROJECT_BINARY_DIR}/testSharedForward.c)
- set_property(TARGET ${KWSYS_NAMESPACE}TestSharedForward PROPERTY LABELS ${KWSYS_LABELS_EXE})
- add_dependencies(${KWSYS_NAMESPACE}TestSharedForward ${KWSYS_TARGET_C_LINK})
- add_test(kwsys.testSharedForward ${EXEC_DIR}/${KWSYS_NAMESPACE}TestSharedForward 1)
- set_property(TEST kwsys.testSharedForward PROPERTY LABELS ${KWSYS_LABELS_TEST})
-
# Configure some test properties.
if(KWSYS_STANDALONE)
# We expect test to fail
diff --git a/Source/kwsys/CommandLineArguments.cxx b/Source/kwsys/CommandLineArguments.cxx
index ccd5f6dccf..50171dd3c6 100644
--- a/Source/kwsys/CommandLineArguments.cxx
+++ b/Source/kwsys/CommandLineArguments.cxx
@@ -4,20 +4,19 @@
#include KWSYS_HEADER(CommandLineArguments.hxx)
#include KWSYS_HEADER(Configure.hxx)
-#include KWSYS_HEADER(String.hxx)
// Work-around CMake dependency scanning limitation. This must
// duplicate the above list of headers.
#if 0
# include "CommandLineArguments.hxx.in"
# include "Configure.hxx.in"
-# include "String.hxx.in"
#endif
#include <iostream>
#include <map>
#include <set>
#include <sstream>
+#include <string>
#include <vector>
#include <cstdio>
@@ -52,14 +51,14 @@ struct CommandLineArgumentsCallbackStructure
const char* Help;
};
-class CommandLineArgumentsVectorOfStrings : public std::vector<kwsys::String>
+class CommandLineArgumentsVectorOfStrings : public std::vector<std::string>
{
};
-class CommandLineArgumentsSetOfStrings : public std::set<kwsys::String>
+class CommandLineArgumentsSetOfStrings : public std::set<std::string>
{
};
class CommandLineArgumentsMapOfStrucs
- : public std::map<kwsys::String, CommandLineArgumentsCallbackStructure>
+ : public std::map<std::string, CommandLineArgumentsCallbackStructure>
{
};
@@ -70,7 +69,7 @@ public:
using VectorOfStrings = CommandLineArgumentsVectorOfStrings;
using CallbacksMap = CommandLineArgumentsMapOfStrucs;
- using String = kwsys::String;
+ using String = std::string;
using SetOfStrings = CommandLineArgumentsSetOfStrings;
VectorOfStrings Argv;
@@ -306,7 +305,7 @@ void CommandLineArguments::GetUnusedArguments(int* argc, char*** argv)
// Copy everything after the LastArgument, since that was not parsed.
for (cc = 0; cc < this->Internals->UnusedArguments.size(); cc++) {
- kwsys::String& str = this->Internals->UnusedArguments[cc];
+ std::string& str = this->Internals->UnusedArguments[cc];
args[cnt] = new char[str.size() + 1];
strcpy(args[cnt], str.c_str());
cnt++;
diff --git a/Source/kwsys/SharedForward.h.in b/Source/kwsys/SharedForward.h.in
deleted file mode 100644
index d6ae75c47b..0000000000
--- a/Source/kwsys/SharedForward.h.in
+++ /dev/null
@@ -1,873 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
-#ifndef @KWSYS_NAMESPACE@_SharedForward_h
-# define @KWSYS_NAMESPACE@_SharedForward_h
-
-/*
- This header is used to create a forwarding executable sets up the
- shared library search path and replaces itself with a real
- executable. This is useful when creating installations on UNIX with
- shared libraries that will run from any install directory. Typical
- usage:
-
- #if defined(CMAKE_INTDIR)
- # define CONFIG_DIR_PRE CMAKE_INTDIR "/"
- # define CONFIG_DIR_POST "/" CMAKE_INTDIR
- #else
- # define CONFIG_DIR_PRE ""
- # define CONFIG_DIR_POST ""
- #endif
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "/path/to/foo-build/bin"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL "../lib/foo-1.2"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD CONFIG_DIR_PRE "foo-real"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL
- "../lib/foo-1.2/foo-real"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print"
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd"
- #if defined(CMAKE_INTDIR)
- # define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR
- #endif
- #include <@KWSYS_NAMESPACE@/SharedForward.h>
- int main(int argc, char** argv)
- {
- return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv);
- }
-
- Specify search and executable paths relative to the forwarding
- executable location or as full paths. Include no trailing slash.
- In the case of a multi-configuration build, when CMAKE_INTDIR is
- defined, the DIR_BUILD setting should point at the directory above
- the executable (the one containing the per-configuration
- subdirectory specified by CMAKE_INTDIR). Then PATH_BUILD entries
- and EXE_BUILD should be specified relative to this location and use
- CMAKE_INTDIR as necessary. In the above example imagine appending
- the PATH_BUILD or EXE_BUILD setting to the DIR_BUILD setting. The
- result should form a valid path with per-configuration subdirectory.
-
- Additional paths may be specified in the PATH_BUILD and PATH_INSTALL
- variables by using comma-separated strings. For example:
-
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD \
- "." CONFIG_DIR_POST, "/path/to/bar-build" CONFIG_DIR_POST
- #define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL \
- "../lib/foo-1.2", "../lib/bar-4.5"
-
- See the comments below for specific explanations of each macro.
-*/
-
-/* Disable -Wcast-qual warnings since they are too hard to fix in a
- cross-platform way. */
-# if defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wcast-qual")
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wcast-qual"
-# endif
-# endif
-
-/* Full path to the directory in which this executable is built. Do
- not include a trailing slash. */
-# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD)
-# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD"
-# endif
-# if !defined(KWSYS_SHARED_FORWARD_DIR_BUILD)
-# define KWSYS_SHARED_FORWARD_DIR_BUILD \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD
-# endif
-
-/* Library search path for build tree. */
-# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD)
-# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD"
-# endif
-# if !defined(KWSYS_SHARED_FORWARD_PATH_BUILD)
-# define KWSYS_SHARED_FORWARD_PATH_BUILD \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD
-# endif
-
-/* Library search path for install tree. */
-# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL)
-# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL"
-# endif
-# if !defined(KWSYS_SHARED_FORWARD_PATH_INSTALL)
-# define KWSYS_SHARED_FORWARD_PATH_INSTALL \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL
-# endif
-
-/* The real executable to which to forward in the build tree. */
-# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD)
-# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD"
-# endif
-# if !defined(KWSYS_SHARED_FORWARD_EXE_BUILD)
-# define KWSYS_SHARED_FORWARD_EXE_BUILD \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD
-# endif
-
-/* The real executable to which to forward in the install tree. */
-# if !defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL)
-# error "Must define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL"
-# endif
-# if !defined(KWSYS_SHARED_FORWARD_EXE_INSTALL)
-# define KWSYS_SHARED_FORWARD_EXE_INSTALL \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL
-# endif
-
-/* The configuration name with which this executable was built (Debug/Release).
- */
-# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME)
-# define KWSYS_SHARED_FORWARD_CONFIG_NAME \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME
-# else
-# undef KWSYS_SHARED_FORWARD_CONFIG_NAME
-# endif
-
-/* Create command line option to replace executable. */
-# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND)
-# if !defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND)
-# define KWSYS_SHARED_FORWARD_OPTION_COMMAND \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND
-# endif
-# else
-# undef KWSYS_SHARED_FORWARD_OPTION_COMMAND
-# endif
-
-/* Create command line option to print environment setting and exit. */
-# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT)
-# if !defined(KWSYS_SHARED_FORWARD_OPTION_PRINT)
-# define KWSYS_SHARED_FORWARD_OPTION_PRINT \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT
-# endif
-# else
-# undef KWSYS_SHARED_FORWARD_OPTION_PRINT
-# endif
-
-/* Create command line option to run ldd or equivalent. */
-# if defined(@KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD)
-# if !defined(KWSYS_SHARED_FORWARD_OPTION_LDD)
-# define KWSYS_SHARED_FORWARD_OPTION_LDD \
- @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD
-# endif
-# else
-# undef KWSYS_SHARED_FORWARD_OPTION_LDD
-# endif
-
-/* Include needed system headers. */
-
-# include <errno.h>
-# include <limits.h>
-# include <stddef.h> /* size_t */
-# include <stdio.h>
-# include <stdlib.h>
-# include <string.h>
-
-# if defined(_WIN32) && !defined(__CYGWIN__)
-# include <windows.h>
-
-# include <io.h>
-# include <process.h>
-# define KWSYS_SHARED_FORWARD_ESCAPE_ARGV /* re-escape argv for execvp */
-# else
-# include <sys/stat.h>
-# include <unistd.h>
-# endif
-
-/* Configuration for this platform. */
-
-/* The path separator for this platform. */
-# if defined(_WIN32) && !defined(__CYGWIN__)
-# define KWSYS_SHARED_FORWARD_PATH_SEP ';'
-# define KWSYS_SHARED_FORWARD_PATH_SLASH '\\'
-# else
-# define KWSYS_SHARED_FORWARD_PATH_SEP ':'
-# define KWSYS_SHARED_FORWARD_PATH_SLASH '/'
-# endif
-static const char kwsys_shared_forward_path_sep[2] = {
- KWSYS_SHARED_FORWARD_PATH_SEP, 0
-};
-static const char kwsys_shared_forward_path_slash[2] = {
- KWSYS_SHARED_FORWARD_PATH_SLASH, 0
-};
-
-/* The maximum length of a file name. */
-# if defined(PATH_MAX)
-# define KWSYS_SHARED_FORWARD_MAXPATH PATH_MAX
-# elif defined(MAXPATHLEN)
-# define KWSYS_SHARED_FORWARD_MAXPATH MAXPATHLEN
-# else
-# define KWSYS_SHARED_FORWARD_MAXPATH 16384
-# endif
-
-/* Select the environment variable holding the shared library runtime
- search path for this platform and build configuration. Also select
- ldd command equivalent. */
-
-/* Linux */
-# if defined(__linux)
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-
-/* FreeBSD */
-# elif defined(__FreeBSD__)
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-
-/* OpenBSD */
-# elif defined(__OpenBSD__)
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-
-/* OS X */
-# elif defined(__APPLE__)
-# define KWSYS_SHARED_FORWARD_LDD "otool", "-L"
-# define KWSYS_SHARED_FORWARD_LDD_N 2
-# define KWSYS_SHARED_FORWARD_LDPATH "DYLD_LIBRARY_PATH"
-
-/* AIX */
-# elif defined(_AIX)
-# define KWSYS_SHARED_FORWARD_LDD "dump", "-H"
-# define KWSYS_SHARED_FORWARD_LDD_N 2
-# define KWSYS_SHARED_FORWARD_LDPATH "LIBPATH"
-
-/* SUN */
-# elif defined(__sun)
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# include <sys/isa_defs.h>
-# if defined(_ILP32)
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-# elif defined(_LP64)
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH_64"
-# endif
-
-/* HP-UX */
-# elif defined(__hpux)
-# define KWSYS_SHARED_FORWARD_LDD "chatr"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# if defined(__LP64__)
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-# else
-# define KWSYS_SHARED_FORWARD_LDPATH "SHLIB_PATH"
-# endif
-
-/* SGI MIPS */
-# elif defined(__sgi) && defined(_MIPS_SIM)
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# if _MIPS_SIM == _ABIO32
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-# elif _MIPS_SIM == _ABIN32
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARYN32_PATH"
-# elif _MIPS_SIM == _ABI64
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY64_PATH"
-# endif
-
-/* Cygwin */
-# elif defined(__CYGWIN__)
-# define KWSYS_SHARED_FORWARD_LDD \
- "cygcheck" /* TODO: cygwin 1.7 has ldd \
- */
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# define KWSYS_SHARED_FORWARD_LDPATH "PATH"
-
-/* Windows */
-# elif defined(_WIN32)
-# define KWSYS_SHARED_FORWARD_LDPATH "PATH"
-
-/* Guess on this unknown system. */
-# else
-# define KWSYS_SHARED_FORWARD_LDD "ldd"
-# define KWSYS_SHARED_FORWARD_LDD_N 1
-# define KWSYS_SHARED_FORWARD_LDPATH "LD_LIBRARY_PATH"
-# endif
-
-# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV
-typedef struct kwsys_sf_arg_info_s
-{
- const char* arg;
- int size;
- int quote;
-} kwsys_sf_arg_info;
-
-static kwsys_sf_arg_info kwsys_sf_get_arg_info(const char* in)
-{
- /* Initialize information. */
- kwsys_sf_arg_info info;
-
- /* String iterator. */
- const char* c;
-
- /* Keep track of how many backslashes have been encountered in a row. */
- int windows_backslashes = 0;
-
- /* Start with the length of the original argument, plus one for
- either a terminating null or a separating space. */
- info.arg = in;
- info.size = (int)strlen(in) + 1;
- info.quote = 0;
-
- /* Scan the string for characters that require escaping or quoting. */
- for (c = in; *c; ++c) {
- /* Check whether this character needs quotes. */
- if (strchr(" \t?'#&<>|^", *c)) {
- info.quote = 1;
- }
-
- /* On Windows only backslashes and double-quotes need escaping. */
- if (*c == '\\') {
- /* Found a backslash. It may need to be escaped later. */
- ++windows_backslashes;
- } else if (*c == '"') {
- /* Found a double-quote. We need to escape it and all
- immediately preceding backslashes. */
- info.size += windows_backslashes + 1;
- windows_backslashes = 0;
- } else {
- /* Found another character. This eliminates the possibility
- that any immediately preceding backslashes will be
- escaped. */
- windows_backslashes = 0;
- }
- }
-
- /* Check whether the argument needs surrounding quotes. */
- if (info.quote) {
- /* Surrounding quotes are needed. Allocate space for them. */
- info.size += 2;
-
- /* We must escape all ending backslashes when quoting on windows. */
- info.size += windows_backslashes;
- }
-
- return info;
-}
-
-static char* kwsys_sf_get_arg(kwsys_sf_arg_info info, char* out)
-{
- /* String iterator. */
- const char* c;
-
- /* Keep track of how many backslashes have been encountered in a row. */
- int windows_backslashes = 0;
-
- /* Whether the argument must be quoted. */
- if (info.quote) {
- /* Add the opening quote for this argument. */
- *out++ = '"';
- }
-
- /* Scan the string for characters that require escaping or quoting. */
- for (c = info.arg; *c; ++c) {
- /* On Windows only backslashes and double-quotes need escaping. */
- if (*c == '\\') {
- /* Found a backslash. It may need to be escaped later. */
- ++windows_backslashes;
- } else if (*c == '"') {
- /* Found a double-quote. Escape all immediately preceding
- backslashes. */
- while (windows_backslashes > 0) {
- --windows_backslashes;
- *out++ = '\\';
- }
-
- /* Add the backslash to escape the double-quote. */
- *out++ = '\\';
- } else {
- /* We encountered a normal character. This eliminates any
- escaping needed for preceding backslashes. */
- windows_backslashes = 0;
- }
-
- /* Store this character. */
- *out++ = *c;
- }
-
- if (info.quote) {
- /* Add enough backslashes to escape any trailing ones. */
- while (windows_backslashes > 0) {
- --windows_backslashes;
- *out++ = '\\';
- }
-
- /* Add the closing quote for this argument. */
- *out++ = '"';
- }
-
- /* Store a terminating null without incrementing. */
- *out = 0;
-
- return out;
-}
-# endif
-
-/* Function to convert a logical or relative path to a physical full path. */
-static int kwsys_shared_forward_realpath(const char* in_path, char* out_path)
-{
-# if defined(_WIN32) && !defined(__CYGWIN__)
- /* Implementation for Windows. */
- DWORD n =
- GetFullPathNameA(in_path, KWSYS_SHARED_FORWARD_MAXPATH, out_path, 0);
- return n > 0 && n <= KWSYS_SHARED_FORWARD_MAXPATH;
-# else
- /* Implementation for UNIX. */
- return realpath(in_path, out_path) != 0;
-# endif
-}
-
-static int kwsys_shared_forward_samepath(const char* file1, const char* file2)
-{
-# if defined(_WIN32)
- int result = 0;
- HANDLE h1 = CreateFileA(file1, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- HANDLE h2 = CreateFileA(file2, GENERIC_READ, FILE_SHARE_READ, NULL,
- OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- if (h1 != INVALID_HANDLE_VALUE && h2 != INVALID_HANDLE_VALUE) {
- BY_HANDLE_FILE_INFORMATION fi1;
- BY_HANDLE_FILE_INFORMATION fi2;
- GetFileInformationByHandle(h1, &fi1);
- GetFileInformationByHandle(h2, &fi2);
- result = (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
- fi1.nFileIndexHigh == fi2.nFileIndexHigh &&
- fi1.nFileIndexLow == fi2.nFileIndexLow);
- }
- CloseHandle(h1);
- CloseHandle(h2);
- return result;
-# else
- struct stat fs1, fs2;
- return (stat(file1, &fs1) == 0 && stat(file2, &fs2) == 0 &&
- memcmp(&fs2.st_dev, &fs1.st_dev, sizeof(fs1.st_dev)) == 0 &&
- memcmp(&fs2.st_ino, &fs1.st_ino, sizeof(fs1.st_ino)) == 0 &&
- fs2.st_size == fs1.st_size);
-# endif
-}
-
-/* Function to report a system error message. */
-static void kwsys_shared_forward_strerror(char* message)
-{
-# if defined(_WIN32) && !defined(__CYGWIN__)
- /* Implementation for Windows. */
- DWORD original = GetLastError();
- DWORD length =
- FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- 0, original, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- message, KWSYS_SHARED_FORWARD_MAXPATH, 0);
- if (length < 1 || length > KWSYS_SHARED_FORWARD_MAXPATH) {
- /* FormatMessage failed. Use a default message. */
- snprintf(message, KWSYS_SHARED_FORWARD_MAXPATH,
- "Error 0x%lX (FormatMessage failed with error 0x%lX)", original,
- GetLastError());
- }
-# else
- /* Implementation for UNIX. */
- strcpy(message, strerror(errno));
-# endif
-}
-
-/* Functions to execute a child process. */
-static void kwsys_shared_forward_execvp(const char* cmd,
- char const* const* argv)
-{
-# ifdef KWSYS_SHARED_FORWARD_ESCAPE_ARGV
- /* Count the number of arguments. */
- int argc = 0;
- {
- char const* const* argvc;
- for (argvc = argv; *argvc; ++argvc, ++argc) {
- }
- }
-
- /* Create the escaped arguments. */
- {
- char** nargv = (char**)malloc((argc + 1) * sizeof(char*));
- int i;
- for (i = 0; i < argc; ++i) {
- kwsys_sf_arg_info info = kwsys_sf_get_arg_info(argv[i]);
- nargv[i] = (char*)malloc(info.size);
- kwsys_sf_get_arg(info, nargv[i]);
- }
- nargv[argc] = 0;
-
- /* Replace the command line to be used. */
- argv = (char const* const*)nargv;
- }
-# endif
-
-/* Invoke the child process. */
-# if defined(_MSC_VER)
- _execvp(cmd, argv);
-# elif defined(__MINGW32__) && !defined(__MINGW64__)
- execvp(cmd, argv);
-# else
- execvp(cmd, (char* const*)argv);
-# endif
-}
-
-/* Function to get the directory containing the given file or directory. */
-static void kwsys_shared_forward_dirname(const char* begin, char* result)
-{
- /* Find the location of the last slash. */
- int last_slash_index = -1;
- const char* end = begin + strlen(begin);
- for (; begin <= end && last_slash_index < 0; --end) {
- if (*end == '/' || *end == '\\') {
- last_slash_index = (int)(end - begin);
- }
- }
-
- /* Handle each case of the index of the last slash. */
- if (last_slash_index < 0) {
- /* No slashes. */
- strcpy(result, ".");
- } else if (last_slash_index == 0) {
- /* Only one leading slash. */
- strcpy(result, kwsys_shared_forward_path_slash);
- }
-# if defined(_WIN32)
- else if (last_slash_index == 2 && begin[1] == ':') {
- /* Only one leading drive letter and slash. */
- strncpy(result, begin, (size_t)last_slash_index);
- result[last_slash_index] = KWSYS_SHARED_FORWARD_PATH_SLASH;
- result[last_slash_index + 1] = 0;
- }
-# endif
- else {
- /* A non-leading slash. */
- strncpy(result, begin, (size_t)last_slash_index);
- result[last_slash_index] = 0;
- }
-}
-
-/* Function to check if a file exists and is executable. */
-static int kwsys_shared_forward_is_executable(const char* f)
-{
-# if defined(_MSC_VER)
-# define KWSYS_SHARED_FORWARD_ACCESS _access
-# else
-# define KWSYS_SHARED_FORWARD_ACCESS access
-# endif
-# if defined(X_OK)
-# define KWSYS_SHARED_FORWARD_ACCESS_OK X_OK
-# else
-# define KWSYS_SHARED_FORWARD_ACCESS_OK 04
-# endif
- if (KWSYS_SHARED_FORWARD_ACCESS(f, KWSYS_SHARED_FORWARD_ACCESS_OK) == 0) {
- return 1;
- } else {
- return 0;
- }
-}
-
-/* Function to locate the executable currently running. */
-static int kwsys_shared_forward_self_path(const char* argv0, char* result)
-{
- /* Check whether argv0 has a slash. */
- int has_slash = 0;
- const char* p = argv0;
- for (; *p && !has_slash; ++p) {
- if (*p == '/' || *p == '\\') {
- has_slash = 1;
- }
- }
-
- if (has_slash) {
- /* There is a slash. Use the dirname of the given location. */
- kwsys_shared_forward_dirname(argv0, result);
- return 1;
- } else {
- /* There is no slash. Search the PATH for the executable. */
- const char* path = getenv("PATH");
- const char* begin = path;
- const char* end = begin + (begin ? strlen(begin) : 0);
- const char* first = begin;
- while (first != end) {
- /* Store the end of this path entry. */
- const char* last;
-
- /* Skip all path separators. */
- for (; *first && *first == KWSYS_SHARED_FORWARD_PATH_SEP; ++first)
- ;
-
- /* Find the next separator. */
- for (last = first; *last && *last != KWSYS_SHARED_FORWARD_PATH_SEP;
- ++last)
- ;
-
- /* If we got a non-empty directory, look for the executable there. */
- if (first < last) {
- /* Determine the length without trailing slash. */
- size_t length = (size_t)(last - first);
- if (*(last - 1) == '/' || *(last - 1) == '\\') {
- --length;
- }
-
- /* Construct the name of the executable in this location. */
- strncpy(result, first, length);
- result[length] = KWSYS_SHARED_FORWARD_PATH_SLASH;
- strcpy(result + (length) + 1, argv0);
-
- /* Check if it exists and is executable. */
- if (kwsys_shared_forward_is_executable(result)) {
- /* Found it. */
- result[length] = 0;
- return 1;
- }
- }
-
- /* Move to the next directory in the path. */
- first = last;
- }
- }
-
- /* We could not find the executable. */
- return 0;
-}
-
-/* Function to convert a specified path to a full path. If it is not
- already full, it is taken relative to the self path. */
-static int kwsys_shared_forward_fullpath(const char* self_path,
- const char* in_path, char* result,
- const char* desc)
-{
- /* Check the specified path type. */
- if (in_path[0] == '/') {
- /* Already a full path. */
- strcpy(result, in_path);
- }
-# if defined(_WIN32)
- else if (in_path[0] && in_path[1] == ':') {
- /* Already a full path. */
- strcpy(result, in_path);
- }
-# endif
- else {
- /* Relative to self path. */
- char temp_path[KWSYS_SHARED_FORWARD_MAXPATH];
- strcpy(temp_path, self_path);
- strcat(temp_path, kwsys_shared_forward_path_slash);
- strcat(temp_path, in_path);
- if (!kwsys_shared_forward_realpath(temp_path, result)) {
- if (desc) {
- char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH];
- kwsys_shared_forward_strerror(msgbuf);
- fprintf(stderr, "Error converting %s \"%s\" to real path: %s\n", desc,
- temp_path, msgbuf);
- }
- return 0;
- }
- }
- return 1;
-}
-
-/* Function to compute the library search path and executable name
- based on the self path. */
-static int kwsys_shared_forward_get_settings(const char* self_path,
- char* ldpath, char* exe)
-{
- /* Possible search paths. */
- static const char* search_path_build[] = { KWSYS_SHARED_FORWARD_PATH_BUILD,
- 0 };
- static const char* search_path_install[] = {
- KWSYS_SHARED_FORWARD_PATH_INSTALL, 0
- };
-
- /* Chosen paths. */
- const char** search_path;
- const char* exe_path;
-
-/* Get the real name of the build and self paths. */
-# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME)
- char build_path[] =
- KWSYS_SHARED_FORWARD_DIR_BUILD "/" KWSYS_SHARED_FORWARD_CONFIG_NAME;
- char self_path_logical[KWSYS_SHARED_FORWARD_MAXPATH];
-# else
- char build_path[] = KWSYS_SHARED_FORWARD_DIR_BUILD;
- const char* self_path_logical = self_path;
-# endif
- char build_path_real[KWSYS_SHARED_FORWARD_MAXPATH];
- char self_path_real[KWSYS_SHARED_FORWARD_MAXPATH];
- if (!kwsys_shared_forward_realpath(self_path, self_path_real)) {
- char msgbuf[KWSYS_SHARED_FORWARD_MAXPATH];
- kwsys_shared_forward_strerror(msgbuf);
- fprintf(stderr, "Error converting self path \"%s\" to real path: %s\n",
- self_path, msgbuf);
- return 0;
- }
-
- /* Check whether we are running in the build tree or an install tree. */
- if (kwsys_shared_forward_realpath(build_path, build_path_real) &&
- kwsys_shared_forward_samepath(self_path_real, build_path_real)) {
- /* Running in build tree. Use the build path and exe. */
- search_path = search_path_build;
-# if defined(_WIN32)
- exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD ".exe";
-# else
- exe_path = KWSYS_SHARED_FORWARD_EXE_BUILD;
-# endif
-
-# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME)
- /* Remove the configuration directory from self_path. */
- kwsys_shared_forward_dirname(self_path, self_path_logical);
-# endif
- } else {
- /* Running in install tree. Use the install path and exe. */
- search_path = search_path_install;
-# if defined(_WIN32)
- exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL ".exe";
-# else
- exe_path = KWSYS_SHARED_FORWARD_EXE_INSTALL;
-# endif
-
-# if defined(KWSYS_SHARED_FORWARD_CONFIG_NAME)
- /* Use the original self path directory. */
- strcpy(self_path_logical, self_path);
-# endif
- }
-
- /* Construct the runtime search path. */
- {
- const char** dir;
- for (dir = search_path; *dir; ++dir) {
- /* Add separator between path components. */
- if (dir != search_path) {
- strcat(ldpath, kwsys_shared_forward_path_sep);
- }
-
- /* Add this path component. */
- if (!kwsys_shared_forward_fullpath(self_path_logical, *dir,
- ldpath + strlen(ldpath),
- "runtime path entry")) {
- return 0;
- }
- }
- }
-
- /* Construct the executable location. */
- if (!kwsys_shared_forward_fullpath(self_path_logical, exe_path, exe,
- "executable file")) {
- return 0;
- }
- return 1;
-}
-
-/* Function to print why execution of a command line failed. */
-static void kwsys_shared_forward_print_failure(char const* const* argv)
-{
- char msg[KWSYS_SHARED_FORWARD_MAXPATH];
- char const* const* arg = argv;
- kwsys_shared_forward_strerror(msg);
- fprintf(stderr, "Error running");
- for (; *arg; ++arg) {
- fprintf(stderr, " \"%s\"", *arg);
- }
- fprintf(stderr, ": %s\n", msg);
-}
-
-/* Static storage space to store the updated environment variable. */
-static char kwsys_shared_forward_ldpath[65535] =
- KWSYS_SHARED_FORWARD_LDPATH "=";
-
-/* Main driver function to be called from main. */
-static int @KWSYS_NAMESPACE@_shared_forward_to_real(int argc, char** argv_in)
-{
- char const** argv = (char const**)argv_in;
- /* Get the directory containing this executable. */
- char self_path[KWSYS_SHARED_FORWARD_MAXPATH];
- if (kwsys_shared_forward_self_path(argv[0], self_path)) {
- /* Found this executable. Use it to get the library directory. */
- char exe[KWSYS_SHARED_FORWARD_MAXPATH];
- if (kwsys_shared_forward_get_settings(self_path,
- kwsys_shared_forward_ldpath, exe)) {
- /* Append the old runtime search path. */
- const char* old_ldpath = getenv(KWSYS_SHARED_FORWARD_LDPATH);
- if (old_ldpath) {
- strcat(kwsys_shared_forward_ldpath, kwsys_shared_forward_path_sep);
- strcat(kwsys_shared_forward_ldpath, old_ldpath);
- }
-
- /* Store the environment variable. */
- putenv(kwsys_shared_forward_ldpath);
-
-# if defined(KWSYS_SHARED_FORWARD_OPTION_COMMAND)
- /* Look for the command line replacement option. */
- if (argc > 1 &&
- strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_COMMAND) == 0) {
- if (argc > 2) {
- /* Use the command line given. */
- strcpy(exe, argv[2]);
- argv += 2;
- argc -= 2;
- } else {
- /* The option was not given an executable. */
- fprintf(stderr,
- "Option " KWSYS_SHARED_FORWARD_OPTION_COMMAND
- " must be followed by a command line.\n");
- return 1;
- }
- }
-# endif
-
-# if defined(KWSYS_SHARED_FORWARD_OPTION_PRINT)
- /* Look for the print command line option. */
- if (argc > 1 &&
- strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_PRINT) == 0) {
- fprintf(stdout, "%s\n", kwsys_shared_forward_ldpath);
- fprintf(stdout, "%s\n", exe);
- return 0;
- }
-# endif
-
-# if defined(KWSYS_SHARED_FORWARD_OPTION_LDD)
- /* Look for the ldd command line option. */
- if (argc > 1 && strcmp(argv[1], KWSYS_SHARED_FORWARD_OPTION_LDD) == 0) {
-# if defined(KWSYS_SHARED_FORWARD_LDD)
- /* Use the named ldd-like executable and arguments. */
- char const* ldd_argv[] = { KWSYS_SHARED_FORWARD_LDD, 0, 0 };
- ldd_argv[KWSYS_SHARED_FORWARD_LDD_N] = exe;
- kwsys_shared_forward_execvp(ldd_argv[0], ldd_argv);
-
- /* Report why execution failed. */
- kwsys_shared_forward_print_failure(ldd_argv);
- return 1;
-# else
- /* We have no ldd-like executable available on this platform. */
- fprintf(stderr, "No ldd-like tool is known to this executable.\n");
- return 1;
-# endif
- }
-# endif
-
- /* Replace this process with the real executable. */
- argv[0] = exe;
- kwsys_shared_forward_execvp(argv[0], argv);
-
- /* Report why execution failed. */
- kwsys_shared_forward_print_failure(argv);
- } else {
- /* Could not convert self path to the library directory. */
- }
- } else {
- /* Could not find this executable. */
- fprintf(stderr, "Error locating executable \"%s\".\n", argv[0]);
- }
-
- /* Avoid unused argument warning. */
- (void)argc;
-
- /* Exit with failure. */
- return 1;
-}
-
-/* Restore warning stack. */
-# if defined(__clang__) && defined(__has_warning)
-# if __has_warning("-Wcast-qual")
-# pragma clang diagnostic pop
-# endif
-# endif
-
-#else
-# error "@KWSYS_NAMESPACE@/SharedForward.h should be included only once."
-#endif
diff --git a/Source/kwsys/String.hxx.in b/Source/kwsys/String.hxx.in
deleted file mode 100644
index c36f4ce4a9..0000000000
--- a/Source/kwsys/String.hxx.in
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
-#ifndef @KWSYS_NAMESPACE@_String_hxx
-#define @KWSYS_NAMESPACE@_String_hxx
-
-#include <string>
-
-namespace @KWSYS_NAMESPACE@ {
-
-/** \class String
- * \brief Short-name version of the STL basic_string class template.
- *
- * The standard library "string" type is actually a typedef for
- * "basic_string<..long argument list..>". This string class is
- * simply a subclass of this type with the same interface so that the
- * name is shorter in debugging symbols and error messages.
- */
-class String : public std::string
-{
- /** The original string type. */
- typedef std::string stl_string;
-
-public:
- /** String member types. */
- typedef stl_string::value_type value_type;
- typedef stl_string::pointer pointer;
- typedef stl_string::reference reference;
- typedef stl_string::const_reference const_reference;
- typedef stl_string::size_type size_type;
- typedef stl_string::difference_type difference_type;
- typedef stl_string::iterator iterator;
- typedef stl_string::const_iterator const_iterator;
- typedef stl_string::reverse_iterator reverse_iterator;
- typedef stl_string::const_reverse_iterator const_reverse_iterator;
-
- /** String constructors. */
- String()
- : stl_string()
- {
- }
- String(const value_type* s)
- : stl_string(s)
- {
- }
- String(const value_type* s, size_type n)
- : stl_string(s, n)
- {
- }
- String(const stl_string& s, size_type pos = 0, size_type n = npos)
- : stl_string(s, pos, n)
- {
- }
-}; // End Class: String
-
-} // namespace @KWSYS_NAMESPACE@
-
-#endif
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 573cc6a568..3bb78696c2 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -3424,9 +3424,7 @@ bool SystemTools::SplitProgramPath(const std::string& in_name,
}
bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut,
- std::string& errorMsg, const char* exeName,
- const char* buildDir,
- const char* installPrefix)
+ std::string& errorMsg)
{
std::vector<std::string> failures;
std::string self = argv0 ? argv0 : "";
@@ -3434,34 +3432,9 @@ bool SystemTools::FindProgramPath(const char* argv0, std::string& pathOut,
SystemTools::ConvertToUnixSlashes(self);
self = SystemTools::FindProgram(self);
if (!SystemTools::FileIsExecutable(self)) {
- if (buildDir) {
- std::string intdir = ".";
-#ifdef CMAKE_INTDIR
- intdir = CMAKE_INTDIR;
-#endif
- self = buildDir;
- self += "/bin/";
- self += intdir;
- self += "/";
- self += exeName;
- self += SystemTools::GetExecutableExtension();
- }
- }
- if (installPrefix) {
- if (!SystemTools::FileIsExecutable(self)) {
- failures.push_back(self);
- self = installPrefix;
- self += "/bin/";
- self += exeName;
- }
- }
- if (!SystemTools::FileIsExecutable(self)) {
failures.push_back(self);
std::ostringstream msg;
msg << "Can not find the command line program ";
- if (exeName) {
- msg << exeName;
- }
msg << "\n";
if (argv0) {
msg << " argv[0] = \"" << argv0 << "\"\n";
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in
index 56b65fd201..729928e6f3 100644
--- a/Source/kwsys/SystemTools.hxx.in
+++ b/Source/kwsys/SystemTools.hxx.in
@@ -395,10 +395,7 @@ public:
* installPrefix is a possibly null pointer to the install directory.
*/
static bool FindProgramPath(const char* argv0, std::string& pathOut,
- std::string& errorMsg,
- const char* exeName = nullptr,
- const char* buildDir = nullptr,
- const char* installPrefix = nullptr);
+ std::string& errorMsg);
/**
* Given a path to a file or directory, convert it to a full path.
diff --git a/Source/kwsys/kwsysPlatformTests.cmake b/Source/kwsys/kwsysPlatformTests.cmake
index 89be4b8853..6c006bcd1a 100644
--- a/Source/kwsys/kwsysPlatformTests.cmake
+++ b/Source/kwsys/kwsysPlatformTests.cmake
@@ -8,9 +8,6 @@ macro(KWSYS_PLATFORM_TEST lang var description invert)
if(NOT DEFINED ${var}_COMPILED)
message(STATUS "${description}")
set(maybe_cxx_standard "")
- if(CMAKE_VERSION VERSION_LESS 3.8 AND CMAKE_CXX_STANDARD)
- set(maybe_cxx_standard "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}")
- endif()
try_compile(${var}_COMPILED
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/${KWSYS_PLATFORM_TEST_FILE_${lang}}
@@ -18,14 +15,16 @@ macro(KWSYS_PLATFORM_TEST lang var description invert)
CMAKE_FLAGS "-DLINK_LIBRARIES:STRING=${KWSYS_PLATFORM_TEST_LINK_LIBRARIES}"
${maybe_cxx_standard}
OUTPUT_VARIABLE OUTPUT)
- if(${var}_COMPILED)
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
- "${description} compiled with the following output:\n${OUTPUT}\n\n")
- else()
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
- "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
+ if(CMAKE_VERSION VERSION_LESS 3.26)
+ if(${var}_COMPILED)
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "${description} compiled with the following output:\n${OUTPUT}\n\n")
+ else()
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
+ endif()
endif()
if(${invert} MATCHES INVERT)
if(${var}_COMPILED)
@@ -67,19 +66,23 @@ macro(KWSYS_PLATFORM_TEST_RUN lang var description invert)
# Note that ${var} will be a 0 return value on success.
if(${var}_COMPILED)
- if(${var})
+ if(CMAKE_VERSION VERSION_LESS 3.26)
+ if(${var})
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "${description} compiled but failed to run with the following output:\n${OUTPUT}\n\n")
+ else()
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "${description} compiled and ran with the following output:\n${OUTPUT}\n\n")
+ endif()
+ endif()
+ else()
+ if(CMAKE_VERSION VERSION_LESS 3.26)
file(APPEND
${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
- "${description} compiled but failed to run with the following output:\n${OUTPUT}\n\n")
- else()
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
- "${description} compiled and ran with the following output:\n${OUTPUT}\n\n")
+ "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
endif()
- else()
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
- "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
set(${var} -1 CACHE INTERNAL "${description} failed to compile.")
endif()
@@ -188,14 +191,16 @@ macro(KWSYS_PLATFORM_INFO_TEST lang var description)
OUTPUT_VARIABLE OUTPUT
COPY_FILE ${KWSYS_PLATFORM_INFO_FILE}
)
- if(${var}_COMPILED)
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
- "${description} compiled with the following output:\n${OUTPUT}\n\n")
- else()
- file(APPEND
- ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
- "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
+ if(CMAKE_VERSION VERSION_LESS 3.26)
+ if(${var}_COMPILED)
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "${description} compiled with the following output:\n${OUTPUT}\n\n")
+ else()
+ file(APPEND
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "${description} failed to compile with the following output:\n${OUTPUT}\n\n")
+ endif()
endif()
if(${var}_COMPILED)
message(STATUS "${description} - compiled")
diff --git a/Source/kwsys/testDynamicLoader.cxx b/Source/kwsys/testDynamicLoader.cxx
index 806c01a0cf..a5095a5fc7 100644
--- a/Source/kwsys/testDynamicLoader.cxx
+++ b/Source/kwsys/testDynamicLoader.cxx
@@ -53,9 +53,9 @@ static std::string GetLibName(const char* lname, const char* subdir = nullptr)
slname += "/";
slname += subdir;
}
-#ifdef CMAKE_INTDIR
+#ifdef BUILD_CONFIG
slname += "/";
- slname += CMAKE_INTDIR;
+ slname += BUILD_CONFIG;
#endif
slname += "/";
slname += kwsys::DynamicLoader::LibPrefix();
diff --git a/Source/kwsys/testSharedForward.c.in b/Source/kwsys/testSharedForward.c.in
deleted file mode 100644
index e909458823..0000000000
--- a/Source/kwsys/testSharedForward.c.in
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
-#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__OpenBSD__)
-/* NOLINTNEXTLINE(bugprone-reserved-identifier) */
-# define _XOPEN_SOURCE 600
-#endif
-#if defined(CMAKE_INTDIR)
-# define CONFIG_DIR_PRE CMAKE_INTDIR "/"
-# define CONFIG_DIR_POST "/" CMAKE_INTDIR
-#else
-# define CONFIG_DIR_PRE ""
-# define CONFIG_DIR_POST ""
-#endif
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_DIR_BUILD "@EXEC_DIR@"
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_BUILD "." CONFIG_DIR_POST
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_PATH_INSTALL 0
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_BUILD \
- CONFIG_DIR_PRE "@KWSYS_NAMESPACE@TestProcess"
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_EXE_INSTALL \
- "@KWSYS_NAMESPACE@TestProcess"
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_COMMAND "--command"
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_PRINT "--print"
-#define @KWSYS_NAMESPACE@_SHARED_FORWARD_OPTION_LDD "--ldd"
-#if defined(CMAKE_INTDIR)
-# define @KWSYS_NAMESPACE@_SHARED_FORWARD_CONFIG_NAME CMAKE_INTDIR
-#endif
-#include <@KWSYS_NAMESPACE@/SharedForward.h>
-int main(int argc, char** argv)
-{
- return @KWSYS_NAMESPACE@_shared_forward_to_real(argc, argv);
-}
diff --git a/Tests/Architecture/CMakeLists.txt b/Tests/Architecture/CMakeLists.txt
index 96def003b9..3d10ee0a88 100644
--- a/Tests/Architecture/CMakeLists.txt
+++ b/Tests/Architecture/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(Architecture C)
function(test_for_xcode4 result_var)
diff --git a/Tests/ArgumentExpansion/CMakeLists.txt b/Tests/ArgumentExpansion/CMakeLists.txt
index da3bb4c902..9ab87b2cd9 100644
--- a/Tests/ArgumentExpansion/CMakeLists.txt
+++ b/Tests/ArgumentExpansion/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ArgumentExpansion)
diff --git a/Tests/BundleGeneratorTest/CMakeLists.txt b/Tests/BundleGeneratorTest/CMakeLists.txt
index cf7e2ce864..069fb77460 100644
--- a/Tests/BundleGeneratorTest/CMakeLists.txt
+++ b/Tests/BundleGeneratorTest/CMakeLists.txt
@@ -1,6 +1,6 @@
project(BundleGeneratorTest)
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Build a shared library and install it in lib/
add_library(Library SHARED Library.cxx)
diff --git a/Tests/BundleUtilities/CMakeLists.txt b/Tests/BundleUtilities/CMakeLists.txt
index 4a95e2fd4a..b53d499ec8 100644
--- a/Tests/BundleUtilities/CMakeLists.txt
+++ b/Tests/BundleUtilities/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(BundleUtilities)
if(CMAKE_GENERATOR STREQUAL "Xcode" AND
diff --git a/Tests/CFBundleTest/CMakeLists.txt b/Tests/CFBundleTest/CMakeLists.txt
index 5f2e8ec257..40dd887531 100644
--- a/Tests/CFBundleTest/CMakeLists.txt
+++ b/Tests/CFBundleTest/CMakeLists.txt
@@ -1,6 +1,6 @@
#this is adapted from FireBreath (http://www.firebreath.org)
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CFBundleTest)
diff --git a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt
index a6b3ffe101..96f553a03c 100644
--- a/Tests/CMakeCommands/add_compile_options/CMakeLists.txt
+++ b/Tests/CMakeCommands/add_compile_options/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
if(POLICY CMP0129)
cmake_policy(SET CMP0129 NEW)
diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt
index 72b3502ba8..0c1af9e731 100644
--- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt
+++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(target_compile_definitions)
diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
index 2e3760a634..dd4fe02d9d 100644
--- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
+++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
if(POLICY CMP0129)
cmake_policy(SET CMP0129 NEW)
diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt
index 3de9ef7e00..d5d4970ca1 100644
--- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt
+++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(target_include_directories)
diff --git a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt
index 52080bdc97..b2365ca92b 100644
--- a/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt
+++ b/Tests/CMakeCommands/target_link_libraries/CMakeLists.txt
@@ -1,6 +1,6 @@
# Using 2.8 will trigger a deprecation warning. In this case it's explicitly
# intentional since the tests checks various policy implementations prior to
-# 2.8.12
+# 3.5
cmake_minimum_required(VERSION 2.8)
if(POLICY CMP0129)
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 612d4b4d82..0fc3debc9f 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -30,6 +30,7 @@ set(CMakeLib_TESTS
testCMExtMemory.cxx
testCMExtAlgorithm.cxx
testCMExtEnumSet.cxx
+ testList.cxx
)
if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM)
list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx)
diff --git a/Tests/CMakeLib/testCTestResourceAllocator.cxx b/Tests/CMakeLib/testCTestResourceAllocator.cxx
index 72e06e5d40..3a2e524c9b 100644
--- a/Tests/CMakeLib/testCTestResourceAllocator.cxx
+++ b/Tests/CMakeLib/testCTestResourceAllocator.cxx
@@ -5,12 +5,16 @@
#include "cmCTestResourceAllocator.h"
#include "cmCTestResourceSpec.h"
-
-static const cmCTestResourceSpec spec{ { {
- /* clang-format off */
- { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } } },
- /* clang-format on */
-} } };
+#include "cmJSONState.h"
+
+static const cmCTestResourceSpec spec{
+ { {
+ /* clang-format off */
+ { "gpus", { { "0", 4 }, { "1", 8 }, { "2", 0 }, { "3", 8 } }, },
+ /* clang-format on */
+ } },
+ cmJSONState()
+};
static bool testInitializeFromResourceSpec()
{
diff --git a/Tests/CMakeLib/testCTestResourceSpec.cxx b/Tests/CMakeLib/testCTestResourceSpec.cxx
index b49f8ff2b4..4a0021f711 100644
--- a/Tests/CMakeLib/testCTestResourceSpec.cxx
+++ b/Tests/CMakeLib/testCTestResourceSpec.cxx
@@ -3,89 +3,72 @@
#include <vector>
#include "cmCTestResourceSpec.h"
+#include "cmJSONState.h"
struct ExpectedSpec
{
std::string Path;
- cmCTestResourceSpec::ReadFileResult ParseResult;
+ bool ParseResult;
cmCTestResourceSpec Expected;
};
static const std::vector<ExpectedSpec> expectedResourceSpecs = {
{ "spec1.json",
- cmCTestResourceSpec::ReadFileResult::READ_OK,
+ true,
{ { {
- { "gpus",
- {
- { "2", 4 },
- { "e", 1 },
- } },
- { "threads", {} },
- } } } },
- { "spec2.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} },
- { "spec3.json",
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
- {} },
- { "spec4.json",
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
- {} },
- { "spec5.json",
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
- {} },
- { "spec6.json",
- cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
- {} },
- { "spec7.json",
- cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE,
- {} },
- { "spec8.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec9.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec10.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec11.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec12.json", cmCTestResourceSpec::ReadFileResult::INVALID_ROOT, {} },
- { "spec13.json", cmCTestResourceSpec::ReadFileResult::JSON_PARSE_ERROR, {} },
- { "spec14.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} },
- { "spec15.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} },
- { "spec16.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} },
- { "spec17.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec18.json", cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, {} },
- { "spec19.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec20.json", cmCTestResourceSpec::ReadFileResult::READ_OK, {} },
- { "spec21.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec22.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec23.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec24.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec25.json",
- cmCTestResourceSpec::ReadFileResult::UNSUPPORTED_VERSION,
- {} },
- { "spec26.json",
- cmCTestResourceSpec::ReadFileResult::UNSUPPORTED_VERSION,
- {} },
- { "spec27.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec28.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec29.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec30.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec31.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec32.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec33.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec34.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec35.json", cmCTestResourceSpec::ReadFileResult::INVALID_VERSION, {} },
- { "spec36.json", cmCTestResourceSpec::ReadFileResult::NO_VERSION, {} },
- { "noexist.json", cmCTestResourceSpec::ReadFileResult::FILE_NOT_FOUND, {} },
+ { "gpus",
+ {
+ { "2", 4 },
+ { "e", 1 },
+ } },
+ { "threads", {} },
+ } },
+ cmJSONState() } },
+ { "spec2.json", true, {} },
+ { "spec3.json", false, {} },
+ { "spec4.json", false, {} },
+ { "spec5.json", false, {} },
+ { "spec6.json", false, {} },
+ { "spec7.json", false, {} },
+ { "spec8.json", false, {} },
+ { "spec9.json", false, {} },
+ { "spec10.json", false, {} },
+ { "spec11.json", false, {} },
+ { "spec12.json", false, {} },
+ { "spec13.json", false, {} },
+ { "spec14.json", true, {} },
+ { "spec15.json", true, {} },
+ { "spec16.json", true, {} },
+ { "spec17.json", false, {} },
+ { "spec18.json", false, {} },
+ { "spec19.json", false, {} },
+ { "spec20.json", true, {} },
+ { "spec21.json", false, {} },
+ { "spec22.json", false, {} },
+ { "spec23.json", false, {} },
+ { "spec24.json", false, {} },
+ { "spec25.json", false, {} },
+ { "spec26.json", false, {} },
+ { "spec27.json", false, {} },
+ { "spec28.json", false, {} },
+ { "spec29.json", false, {} },
+ { "spec30.json", false, {} },
+ { "spec31.json", false, {} },
+ { "spec32.json", false, {} },
+ { "spec33.json", false, {} },
+ { "spec34.json", false, {} },
+ { "spec35.json", false, {} },
+ { "spec36.json", false, {} },
+ { "noexist.json", false, {} },
};
-static bool testSpec(const std::string& path,
- cmCTestResourceSpec::ReadFileResult expectedResult,
+static bool testSpec(const std::string& path, bool expectedResult,
const cmCTestResourceSpec& expected)
{
cmCTestResourceSpec actual;
auto result = actual.ReadFromJSONFile(path);
if (result != expectedResult) {
- std::cout << "ReadFromJSONFile(\"" << path << "\") returned \""
- << cmCTestResourceSpec::ResultToString(result)
- << "\", should be \""
- << cmCTestResourceSpec::ResultToString(expectedResult) << "\""
- << std::endl;
+ std::cout << "ReadFromJSONFile(\"" << path << "\") failed \"" << std::endl;
return false;
}
diff --git a/Tests/CMakeLib/testJSONHelpers.cxx b/Tests/CMakeLib/testJSONHelpers.cxx
index 053c163ec9..50f0386567 100644
--- a/Tests/CMakeLib/testJSONHelpers.cxx
+++ b/Tests/CMakeLib/testJSONHelpers.cxx
@@ -10,6 +10,7 @@
#include <cm3p/json/value.h>
#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
#define ASSERT_TRUE(x) \
do { \
@@ -31,59 +32,65 @@ struct InheritedStruct : public ObjectStruct
std::string Field3;
};
-enum class ErrorCode
+namespace ErrorMessages {
+using ErrorGenerator =
+ std::function<void(const Json::Value*, cmJSONState* state)>;
+ErrorGenerator ErrorGeneratorBuilder(std::string errorMessage)
{
- Success,
- InvalidInt,
- InvalidBool,
- InvalidString,
- InvalidSubObject,
- InvalidObject,
- InvalidArray,
- MissingRequired,
+ return [errorMessage](const Json::Value* value, cmJSONState* state) -> void {
+ state->AddErrorAtValue(errorMessage, value);
+ };
+};
+ErrorGenerator InvalidArray = ErrorGeneratorBuilder("Invalid Array");
+ErrorGenerator MissingRequired = ErrorGeneratorBuilder("Missing Required");
+ErrorGenerator InvalidMap = ErrorGeneratorBuilder("Invalid Map");
+ErrorGenerator InvalidObject(JsonErrors::ObjectError /*errorType*/,
+ const Json::Value::Members& extraFields)
+{
+ return [extraFields](const Json::Value* value, cmJSONState* state) -> void {
+ state->AddErrorAtValue("Invalid Object", value);
+ };
+};
};
-using JSONHelperBuilder = cmJSONHelperBuilder<ErrorCode>;
+using JSONHelperBuilder = cmJSONHelperBuilder;
-auto const IntHelper =
- JSONHelperBuilder::Int(ErrorCode::Success, ErrorCode::InvalidInt, 1);
+auto const IntHelper = JSONHelperBuilder::Int(1);
auto const RequiredIntHelper =
- JSONHelperBuilder::Required<int>(ErrorCode::MissingRequired, IntHelper);
-auto const UIntHelper =
- JSONHelperBuilder::UInt(ErrorCode::Success, ErrorCode::InvalidInt, 1);
-auto const BoolHelper =
- JSONHelperBuilder::Bool(ErrorCode::Success, ErrorCode::InvalidBool, false);
-auto const StringHelper = JSONHelperBuilder::String(
- ErrorCode::Success, ErrorCode::InvalidString, "default");
+ JSONHelperBuilder::Required<int>(ErrorMessages::MissingRequired, IntHelper);
+auto const UIntHelper = JSONHelperBuilder::UInt(1);
+auto const BoolHelper = JSONHelperBuilder::Bool(false);
+auto const StringHelper = JSONHelperBuilder::String("default");
auto const RequiredStringHelper = JSONHelperBuilder::Required<std::string>(
- ErrorCode::MissingRequired, StringHelper);
+ ErrorMessages::MissingRequired, StringHelper);
auto const StringVectorHelper = JSONHelperBuilder::Vector<std::string>(
- ErrorCode::Success, ErrorCode::InvalidArray, StringHelper);
+ ErrorMessages::InvalidArray, StringHelper);
auto const StringVectorFilterHelper =
JSONHelperBuilder::VectorFilter<std::string>(
- ErrorCode::Success, ErrorCode::InvalidArray, StringHelper,
+ ErrorMessages::InvalidArray, StringHelper,
[](const std::string& value) { return value != "ignore"; });
-auto const StringMapHelper = JSONHelperBuilder::Map<std::string>(
- ErrorCode::Success, ErrorCode::InvalidObject, StringHelper);
+auto const StringMapHelper =
+ JSONHelperBuilder::Map<std::string>(ErrorMessages::InvalidMap, StringHelper);
auto const StringMapFilterHelper = JSONHelperBuilder::MapFilter<std::string>(
- ErrorCode::Success, ErrorCode::InvalidObject, StringHelper,
+ ErrorMessages::InvalidMap, StringHelper,
[](const std::string& key) { return key != "ignore"; });
auto const OptionalStringHelper =
- JSONHelperBuilder::Optional<std::string>(ErrorCode::Success, StringHelper);
+ JSONHelperBuilder::Optional<std::string>(StringHelper);
bool testInt()
{
Json::Value v(2);
+ cmJSONState state;
int i = 0;
- ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(IntHelper(i, &v, &state));
ASSERT_TRUE(i == 2);
i = 0;
v = Json::nullValue;
- ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::InvalidInt);
+ ASSERT_TRUE(!IntHelper(i, &v, &state));
i = 0;
- ASSERT_TRUE(IntHelper(i, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(IntHelper(i, nullptr, &state));
ASSERT_TRUE(i == 1);
return true;
@@ -92,16 +99,16 @@ bool testInt()
bool testUInt()
{
Json::Value v(2);
+ cmJSONState state;
unsigned int i = 0;
- ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(UIntHelper(i, &v, &state));
ASSERT_TRUE(i == 2);
-
i = 0;
v = Json::nullValue;
- ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::InvalidInt);
+ ASSERT_TRUE(!UIntHelper(i, &v, &state));
i = 0;
- ASSERT_TRUE(UIntHelper(i, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(UIntHelper(i, nullptr, &state));
ASSERT_TRUE(i == 1);
return true;
@@ -110,21 +117,22 @@ bool testUInt()
bool testBool()
{
Json::Value v(true);
+ cmJSONState state;
bool b = false;
- ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success);
+ ASSERT_TRUE(BoolHelper(b, &v, &state));
ASSERT_TRUE(b);
b = false;
v = false;
- ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success);
+ ASSERT_TRUE(BoolHelper(b, &v, &state));
ASSERT_TRUE(!b);
b = false;
v = 4;
- ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::InvalidBool);
+ ASSERT_TRUE(!BoolHelper(b, &v, &state));
b = true;
- ASSERT_TRUE(BoolHelper(b, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(BoolHelper(b, nullptr, &state));
ASSERT_TRUE(!b);
return true;
@@ -133,16 +141,17 @@ bool testBool()
bool testString()
{
Json::Value v("str");
+ cmJSONState state;
std::string str = "";
- ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::Success);
+ ASSERT_TRUE(StringHelper(str, &v, &state));
ASSERT_TRUE(str == "str");
str = "";
v = Json::nullValue;
- ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(!StringHelper(str, &v, &state));
str = "";
- ASSERT_TRUE(StringHelper(str, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(StringHelper(str, nullptr, &state));
ASSERT_TRUE(str == "default");
return true;
@@ -150,17 +159,15 @@ bool testString()
bool testObject()
{
- auto const subhelper =
- JSONHelperBuilder::Object<ObjectStruct>(ErrorCode::Success,
- ErrorCode::InvalidSubObject)
- .Bind("subfield"_s, &ObjectStruct::Field2, IntHelper);
- auto const helper = JSONHelperBuilder::Object<ObjectStruct>(
- ErrorCode::Success, ErrorCode::InvalidObject)
+ auto const subhelper = JSONHelperBuilder::Object<ObjectStruct>().Bind(
+ "subfield"_s, &ObjectStruct::Field2, IntHelper);
+ auto const helper = JSONHelperBuilder::Object<ObjectStruct>()
.Bind("field1"_s, &ObjectStruct::Field1, StringHelper)
.Bind("field2"_s, subhelper)
.Bind<std::string>("field3"_s, nullptr, StringHelper);
Json::Value v(Json::objectValue);
+ cmJSONState state;
v["field1"] = "Hello";
v["field2"] = Json::objectValue;
v["field2"]["subfield"] = 2;
@@ -168,38 +175,38 @@ bool testObject()
v["extra"] = "extra";
ObjectStruct s1;
- ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s1, &v, &state));
ASSERT_TRUE(s1.Field1 == "Hello");
ASSERT_TRUE(s1.Field2 == 2);
v["field2"]["subfield"] = "wrong";
ObjectStruct s2;
- ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt);
+ ASSERT_TRUE(!helper(s2, &v, &state));
v["field2"].removeMember("subfield");
ObjectStruct s3;
- ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidSubObject);
+ ASSERT_TRUE(!helper(s3, &v, &state));
v.removeMember("field2");
ObjectStruct s4;
- ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s4, &v, &state));
v["field2"] = Json::objectValue;
v["field2"]["subfield"] = 2;
v["field3"] = 3;
ObjectStruct s5;
- ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(!helper(s5, &v, &state));
v.removeMember("field3");
ObjectStruct s6;
- ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s6, &v, &state));
v = "Hello";
ObjectStruct s7;
- ASSERT_TRUE(helper(s7, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s7, &v, &state));
ObjectStruct s8;
- ASSERT_TRUE(helper(s8, nullptr) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s8, nullptr, &state));
return true;
}
@@ -207,47 +214,48 @@ bool testObject()
bool testObjectInherited()
{
auto const helper =
- JSONHelperBuilder::Object<InheritedStruct>(ErrorCode::Success,
- ErrorCode::InvalidObject)
+ JSONHelperBuilder::Object<InheritedStruct>(ErrorMessages::InvalidObject,
+ true)
.Bind("field1"_s, &InheritedStruct::Field1, StringHelper)
.Bind("field2"_s, &InheritedStruct::Field2, IntHelper)
.Bind("field3"_s, &InheritedStruct::Field3, StringHelper);
Json::Value v(Json::objectValue);
+ cmJSONState state;
v["field1"] = "Hello";
v["field2"] = 2;
v["field3"] = "world!";
v["extra"] = "extra";
InheritedStruct s1;
- ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s1, &v, &state));
ASSERT_TRUE(s1.Field1 == "Hello");
ASSERT_TRUE(s1.Field2 == 2);
ASSERT_TRUE(s1.Field3 == "world!");
v["field2"] = "wrong";
InheritedStruct s2;
- ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt);
+ ASSERT_TRUE(!helper(s2, &v, &state));
v.removeMember("field2");
InheritedStruct s3;
- ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s3, &v, &state));
v["field2"] = 2;
v["field3"] = 3;
InheritedStruct s4;
- ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(!helper(s4, &v, &state));
v.removeMember("field3");
InheritedStruct s5;
- ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s5, &v, &state));
v = "Hello";
InheritedStruct s6;
- ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s6, &v, &state));
InheritedStruct s7;
- ASSERT_TRUE(helper(s7, nullptr) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s7, nullptr, &state));
return true;
}
@@ -255,22 +263,23 @@ bool testObjectInherited()
bool testObjectNoExtra()
{
auto const helper = JSONHelperBuilder::Object<ObjectStruct>(
- ErrorCode::Success, ErrorCode::InvalidObject, false)
+ ErrorMessages::InvalidObject, false)
.Bind("field1"_s, &ObjectStruct::Field1, StringHelper)
.Bind("field2"_s, &ObjectStruct::Field2, IntHelper);
Json::Value v(Json::objectValue);
+ cmJSONState state;
v["field1"] = "Hello";
v["field2"] = 2;
ObjectStruct s1;
- ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s1, &v, &state));
ASSERT_TRUE(s1.Field1 == "Hello");
ASSERT_TRUE(s1.Field2 == 2);
v["extra"] = "world!";
ObjectStruct s2;
- ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!helper(s2, &v, &state));
return true;
}
@@ -278,31 +287,31 @@ bool testObjectNoExtra()
bool testObjectOptional()
{
auto const helper =
- JSONHelperBuilder::Object<ObjectStruct>(ErrorCode::Success,
- ErrorCode::InvalidObject)
+ JSONHelperBuilder::Object<ObjectStruct>(ErrorMessages::InvalidObject, true)
.Bind("field1"_s, &ObjectStruct::Field1, StringHelper, false)
.Bind("field2"_s, &ObjectStruct::Field2, IntHelper, false)
.Bind<std::string>("field3_s", nullptr, StringHelper, false);
Json::Value v(Json::objectValue);
+ cmJSONState state;
v["field1"] = "Hello";
v["field2"] = 2;
v["field3"] = "world!";
v["extra"] = "extra";
ObjectStruct s1;
- ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s1, &v, &state));
ASSERT_TRUE(s1.Field1 == "Hello");
ASSERT_TRUE(s1.Field2 == 2);
v = Json::objectValue;
ObjectStruct s2;
- ASSERT_TRUE(helper(s2, &v) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s2, &v, &state));
ASSERT_TRUE(s2.Field1 == "default");
ASSERT_TRUE(s2.Field2 == 1);
ObjectStruct s3;
- ASSERT_TRUE(helper(s3, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(helper(s3, nullptr, &state));
ASSERT_TRUE(s3.Field1 == "default");
ASSERT_TRUE(s3.Field2 == 1);
@@ -312,25 +321,26 @@ bool testObjectOptional()
bool testVector()
{
Json::Value v(Json::arrayValue);
+ cmJSONState state;
v.append("Hello");
v.append("world!");
v.append("ignore");
std::vector<std::string> l{ "default" };
std::vector<std::string> expected{ "Hello", "world!", "ignore" };
- ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::Success);
+ ASSERT_TRUE(StringVectorHelper(l, &v, &state));
ASSERT_TRUE(l == expected);
v[1] = 2;
l = { "default" };
- ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(!StringVectorHelper(l, &v, &state));
v = "Hello";
l = { "default" };
- ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidArray);
+ ASSERT_TRUE(!StringVectorHelper(l, &v, &state));
l = { "default" };
- ASSERT_TRUE(StringVectorHelper(l, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(StringVectorHelper(l, nullptr, &state));
ASSERT_TRUE(l.empty());
return true;
@@ -339,6 +349,7 @@ bool testVector()
bool testVectorFilter()
{
Json::Value v(Json::arrayValue);
+ cmJSONState state;
v.append("Hello");
v.append("world!");
v.append("ignore");
@@ -348,19 +359,19 @@ bool testVectorFilter()
"Hello",
"world!",
};
- ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::Success);
+ ASSERT_TRUE(StringVectorFilterHelper(l, &v, &state));
ASSERT_TRUE(l == expected);
v[1] = 2;
l = { "default" };
- ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidString);
+ ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state));
v = "Hello";
l = { "default" };
- ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidArray);
+ ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state));
l = { "default" };
- ASSERT_TRUE(StringVectorFilterHelper(l, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(StringVectorFilterHelper(l, nullptr, &state));
ASSERT_TRUE(l.empty());
return true;
@@ -372,20 +383,21 @@ bool testMap()
v["field1"] = "Hello";
v["field2"] = "world!";
v["ignore"] = "ignore";
+ cmJSONState state;
std::map<std::string, std::string> m{ { "key", "default" } };
std::map<std::string, std::string> expected{ { "field1", "Hello" },
{ "field2", "world!" },
{ "ignore", "ignore" } };
- ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::Success);
+ ASSERT_TRUE(StringMapHelper(m, &v, &state));
ASSERT_TRUE(m == expected);
v = Json::arrayValue;
m = { { "key", "default" } };
- ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!StringMapHelper(m, &v, &state));
m = { { "key", "default" } };
- ASSERT_TRUE(StringMapHelper(m, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(StringMapHelper(m, nullptr, &state));
ASSERT_TRUE(m.empty());
return true;
@@ -394,6 +406,7 @@ bool testMap()
bool testMapFilter()
{
Json::Value v(Json::objectValue);
+ cmJSONState state;
v["field1"] = "Hello";
v["field2"] = "world!";
v["ignore"] = "ignore";
@@ -401,15 +414,15 @@ bool testMapFilter()
std::map<std::string, std::string> m{ { "key", "default" } };
std::map<std::string, std::string> expected{ { "field1", "Hello" },
{ "field2", "world!" } };
- ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::Success);
+ ASSERT_TRUE(StringMapFilterHelper(m, &v, &state));
ASSERT_TRUE(m == expected);
v = Json::arrayValue;
m = { { "key", "default" } };
- ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::InvalidObject);
+ ASSERT_TRUE(!StringMapFilterHelper(m, &v, &state));
m = { { "key", "default" } };
- ASSERT_TRUE(StringMapFilterHelper(m, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(StringMapFilterHelper(m, nullptr, &state));
ASSERT_TRUE(m.empty());
return true;
@@ -418,13 +431,14 @@ bool testMapFilter()
bool testOptional()
{
Json::Value v = "Hello";
+ cmJSONState state;
cm::optional<std::string> str{ "default" };
- ASSERT_TRUE(OptionalStringHelper(str, &v) == ErrorCode::Success);
+ ASSERT_TRUE(OptionalStringHelper(str, &v, &state));
ASSERT_TRUE(str == "Hello");
str.emplace("default");
- ASSERT_TRUE(OptionalStringHelper(str, nullptr) == ErrorCode::Success);
+ ASSERT_TRUE(OptionalStringHelper(str, nullptr, &state));
ASSERT_TRUE(str == cm::nullopt);
return true;
@@ -433,25 +447,24 @@ bool testOptional()
bool testRequired()
{
Json::Value v = "Hello";
-
std::string str = "default";
int i = 1;
- ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::Success);
+ cmJSONState state;
+ ASSERT_TRUE(RequiredStringHelper(str, &v, &state));
ASSERT_TRUE(str == "Hello");
- ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::InvalidInt);
+ ASSERT_TRUE(!RequiredIntHelper(i, &v, &state));
v = 2;
str = "default";
i = 1;
- ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::InvalidString);
- ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::Success);
+ ASSERT_TRUE(!RequiredStringHelper(str, &v, &state));
+ ASSERT_TRUE(RequiredIntHelper(i, &v, &state));
ASSERT_TRUE(i == 2);
str = "default";
i = 1;
- ASSERT_TRUE(RequiredStringHelper(str, nullptr) ==
- ErrorCode::MissingRequired);
- ASSERT_TRUE(RequiredIntHelper(i, nullptr) == ErrorCode::MissingRequired);
+ ASSERT_TRUE(!RequiredStringHelper(str, nullptr, &state));
+ ASSERT_TRUE(!RequiredIntHelper(i, nullptr, &state));
return true;
}
diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx
new file mode 100644
index 0000000000..7294be0b31
--- /dev/null
+++ b/Tests/CMakeLib/testList.cxx
@@ -0,0 +1,995 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+
+#include "cmList.h"
+
+namespace {
+
+void checkResult(bool success)
+{
+ if (!success) {
+ std::cout << " => failed";
+ }
+ std::cout << std::endl;
+}
+
+bool testConstructors()
+{
+ std::cout << "testConstructors()";
+
+ bool result = true;
+
+ {
+ cmList list;
+ if (!list.empty() || list != cmList{}) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "aa;bb" };
+ if (list.size() != 2 || list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2("aa;bb");
+
+ if (list1.size() != 2 || list2.size() != 2 || list1 != list2) {
+ result = false;
+ }
+ if (list1.to_string() != "aa;bb") {
+ result = false;
+ }
+ if (list1.to_string() != list2.to_string()) {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "aa", "bb", "cc" };
+ cmList list(v.begin(), v.end());
+ if (list.size() != 3 || list.to_string() != "aa;bb;cc") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" };
+ cmList list1(values.begin(), values.end());
+ cmList list2(values.begin(), values.end(), cmList::ExpandElements::No);
+
+ if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb;;cc", "", "dd;ee" };
+ cmList list1(values.begin(), values.end(), cmList::ExpandElements::No,
+ cmList::EmptyElements::No);
+ cmList list2(values.begin(), values.end(), cmList::ExpandElements::No,
+ cmList::EmptyElements::Yes);
+ cmList list3(values.begin(), values.end(), cmList::ExpandElements::Yes,
+ cmList::EmptyElements::No);
+ cmList list4(values.begin(), values.end(), cmList::ExpandElements::Yes,
+ cmList::EmptyElements::Yes);
+
+ if (list1.size() != 2 || list1.to_string() != "aa;bb;;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;;cc;;dd;ee") {
+ result = false;
+ }
+ if (list3.size() != 5 || list3.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list4.size() != 7 || list4.to_string() != "aa;bb;;cc;;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa;bb", "cc", "dd;ee" };
+ cmList list1(values);
+ cmList list2(values, cmList::ExpandElements::No);
+
+ if (list1.size() != 5 || list1.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (list2.size() != 3 || list2.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> values{ "aa", "bb", "cc", "dd", "ee" };
+ cmList list(std::move(values));
+
+ if (list.size() != 5 || list.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ if (!values.empty()) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testAssign()
+{
+ std::cout << "testAssign()";
+
+ bool result = true;
+
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2{ "cc", "dd" };
+
+ list2 = list1;
+ if (list1.size() != 2 || list2.size() != 2 || list1 != list2) {
+ result = false;
+ }
+ if (list1.to_string() != "aa;bb") {
+ result = false;
+ }
+ if (list1.to_string() != list2.to_string()) {
+ result = false;
+ }
+ }
+ {
+ cmList list1{ "aa", "bb" };
+ cmList list2{ "cc", "dd" };
+
+ list2 = std::move(list1);
+ if (!list1.empty() || list2.size() != 2) {
+ result = false;
+ }
+ if (list2.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "aa", "bb" };
+ cmList list{ "cc", "dd" };
+
+ list = std::move(v);
+ if (!v.empty() || list.size() != 2) {
+ result = false;
+ }
+ if (list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "cc", "dd" };
+
+ list = "aa;bb";
+ if (list.size() != 2) {
+ result = false;
+ }
+ if (list.to_string() != "aa;bb") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testConversions()
+{
+ std::cout << "testConversions()";
+
+ bool result = true;
+
+ {
+ cmList list("a;b;c");
+ std::string s = list.to_string();
+
+ if (s != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v = list;
+
+ if (list.size() != 3 || v.size() != 3) {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v = std::move(list);
+
+ // Microsoft compiler is not able to handle correctly the move semantics
+ // so the initial list is not moved, so do not check its size...
+ if (v.size() != 3) {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c");
+ std::vector<std::string> v;
+
+ // compiler is not able to select the cmList conversion operator
+ // and the std::vector assignment operator using the move semantics
+ // v = std::move(list);
+ v = std::move(list.data());
+
+ if (!list.empty() || v.size() != 3) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testAccess()
+{
+ std::cout << "testAccess()";
+
+ bool result = true;
+
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.at(1) != "b") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.at(-3) != "a") {
+ result = false;
+ }
+ }
+ {
+ try {
+ cmList list{ "a", "b", "c" };
+ if (list.at(4) != "a") {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ try {
+ cmList list{ "a", "b", "c" };
+ if (list.at(-4) != "a") {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e" };
+ auto sublist = list.sublist(list.begin() + 1, list.begin() + 3);
+ if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e" };
+ auto sublist = list.sublist(1, 2);
+ if (sublist.size() != 2 || sublist != cmList{ "b", "c" }) {
+ result = false;
+ }
+
+ sublist = list.sublist(1, cmList::npos);
+ if (sublist.size() != 4 || sublist != cmList{ "b", "c", "d", "e" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ auto sublist = list.get_items({ 1, 3, 5 });
+ if (sublist.size() != 3 || sublist != cmList{ "b", "d", "f" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ auto sublist = list.get_items({ 1, -3, 5, -3 });
+ if (sublist.size() != 4 || sublist != cmList{ "b", "d", "f", "d" }) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+ try {
+ if (list.get_items({ 1, -3, 5, -3, 10 }).size() != 5) {
+ result = false;
+ }
+ } catch (std::out_of_range&) {
+ }
+ }
+ {
+ cmList list{ "a", "b", "c", "d", "e", "f" };
+
+ if (list.find("b") != 1) {
+ result = false;
+ }
+ if (list.find("x") != cmList::npos) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testModifiers()
+{
+ std::cout << "testModifiers()";
+
+ bool result = true;
+
+ {
+ cmList list{ "1;2;3;4;5" };
+
+ auto it = list.insert(list.begin() + 2, "6;7;8");
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+
+ auto it =
+ list.insert(list.begin() + 2, "6;7;8", cmList::ExpandElements::No);
+ if (list.size() != 6 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6;7;8") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ cmList v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ cmList v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, std::move(v));
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+
+ if (!v.empty()) {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v, cmList::ExpandElements::No);
+ if (list.size() != 7 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6;7") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v);
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6;;7", "8" };
+
+ auto it = list.insert(list.begin() + 2, v, cmList::EmptyElements::Yes);
+ if (list.size() != 9 || list.to_string() != "1;2;6;;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "1;2;3;4;5" };
+ std::vector<std::string> v{ "6", "7", "8" };
+
+ auto it = list.insert(list.begin() + 2, std::move(v));
+ if (list.size() != 8 || list.to_string() != "1;2;6;7;8;3;4;5") {
+ result = false;
+ }
+ if (*it != "6") {
+ result = false;
+ }
+
+ if (!v.empty()) {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testRemoveItems()
+{
+ std::cout << "testRemoveItems()";
+
+ bool result = true;
+
+ {
+ cmList list("a;b;c;d;e;f;g;h");
+
+ list.remove_items({ 1, 3, 5 });
+
+ if (list.size() != 5 || list.to_string() != "a;c;e;g;h") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;b;a;d;e;f");
+
+ list.remove_items({ "a", "b", "h" });
+
+ if (list.size() != 4 || list.to_string() != "c;d;e;f") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;d;e;f;g;h");
+ std::vector<cmList::index_type> remove{ 1, 3, 5 };
+
+ list.remove_items(remove.begin(), remove.end());
+
+ if (list.size() != 5 || list.to_string() != "a;c;e;g;h") {
+ result = false;
+ }
+ }
+ {
+ cmList list("a;b;c;b;a;d;e;f");
+ std::vector<std::string> remove{ "b", "a", "h" };
+
+ list.remove_items(remove.begin(), remove.end());
+
+ if (list.size() != 4 || list.to_string() != "c;d;e;f") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testRemoveDuplicates()
+{
+ std::cout << "testRemoveDuplicates()";
+
+ bool result = true;
+
+ {
+ cmList list("b;c;b;a;a;c;b;a;c;b");
+
+ list.remove_duplicates();
+
+ if (list.size() != 3 || list.to_string() != "b;c;a") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testFilter()
+{
+ std::cout << "testFilter()";
+
+ bool result = true;
+
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ list.filter("^A", cmList::FilterMode::INCLUDE);
+ if (list.size() != 2 || list.to_string() != "AA;Aa") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ list.filter("^A", cmList::FilterMode::EXCLUDE);
+ if (list.size() != 1 || list.to_string() != "aA") {
+ result = false;
+ }
+ }
+ {
+ cmList list{ "AA", "Aa", "aA" };
+
+ try {
+ list.filter("^(A", cmList::FilterMode::EXCLUDE);
+ if (list.size() != 1) {
+ result = false;
+ }
+ } catch (const std::invalid_argument&) {
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testReverse()
+{
+ std::cout << "testReverse()";
+
+ bool result = true;
+
+ {
+ cmList list{ "a", "b", "c" };
+ if (list.reverse().to_string() != "c;b;a") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testSort()
+{
+ std::cout << "testSort()";
+
+ bool result = true;
+
+ using SortConfiguration = cmList::SortConfiguration;
+
+ {
+ cmList list{ "A", "D", "C", "B", "A" };
+
+ list.sort();
+ if (list.to_string() != "A;A;B;C;D") {
+ result = false;
+ }
+
+ list.sort({ SortConfiguration::OrderMode::DESCENDING,
+ SortConfiguration::CompareMethod::DEFAULT,
+ SortConfiguration::CaseSensitivity::DEFAULT });
+ if (list.to_string() != "D;C;B;A;A") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "1.0", "1.1", "2.5", "10.2" };
+
+ list.sort(sortCfg);
+ if (list.to_string() != "1.0;1.1;10.2;2.5") {
+ result = false;
+ }
+
+ sortCfg.Compare = SortConfiguration::CompareMethod::NATURAL;
+ list.sort(sortCfg);
+ if (list.to_string() != "1.0;1.1;2.5;10.2") {
+ result = false;
+ }
+
+ sortCfg.Order = SortConfiguration::OrderMode::DESCENDING;
+ list.sort(sortCfg);
+ if (list.to_string() != "10.2;2.5;1.1;1.0") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "/zz/bb.cc", "/xx/yy/dd.cc", "/aa/cc.aa" };
+
+ list.sort(sortCfg);
+ if (list.to_string() != "/aa/cc.aa;/xx/yy/dd.cc;/zz/bb.cc") {
+ result = false;
+ }
+
+ sortCfg.Compare = SortConfiguration::CompareMethod::FILE_BASENAME;
+ if (list.sort(sortCfg).to_string() != "/zz/bb.cc;/aa/cc.aa;/xx/yy/dd.cc") {
+ result = false;
+ }
+ }
+ {
+ SortConfiguration sortCfg;
+ cmList list{ "c/B", "a/c", "B/a" };
+
+ if (list.sort().to_string() != "B/a;a/c;c/B") {
+ result = false;
+ }
+
+ sortCfg.Case = SortConfiguration::CaseSensitivity::INSENSITIVE;
+ if (list.sort(sortCfg).to_string() != "a/c;B/a;c/B") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testTransform()
+{
+ std::cout << "testTransform()";
+
+ bool result = true;
+
+ using AT = cmList::TransformSelector::AT;
+ using FOR = cmList::TransformSelector::FOR;
+ using REGEX = cmList::TransformSelector::REGEX;
+
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::APPEND, "-X");
+ if (list.to_string() != "AA-X;BB-X;CC-X;DD-X;EE-X") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::PREPEND, "X-");
+ if (list.to_string() != "X-AA;X-BB;X-CC;X-DD;X-EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER);
+ if (list.to_string() != "aa;bb;cc;dd;ee") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "aa", "bb", "cc", "dd", "ee" });
+
+ list.transform(cmList::TransformAction::TOUPPER);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ " AA", "BB ", " CC ", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::STRIP);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "$<CONFIG>AA", "BB$<OR>", "C$<AND>C", "$<OR>DD$<AND>",
+ "$<>E$<>E$<>" });
+
+ list.transform(cmList::TransformAction::GENEX_STRIP);
+ if (list.to_string() != "AA;BB;CC;DD;EE") {
+ result = false;
+ }
+ }
+ {
+ cmList list({ "ABC", "BBCB", "BCCCBC", "BCBCDD", "EBCBCEBC" });
+
+ list.transform(cmList::TransformAction::REPLACE, "^BC|BC$", "X");
+ if (list.to_string() != "AX;BBCB;XCCX;XXDD;EBCBCEX") {
+ result = false;
+ }
+ }
+ {
+ auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, 4 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector));
+ if (list.to_string() != "AA;bb;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto atSelector = cmList::TransformSelector::New<AT>({ 1, 2, -1 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(atSelector));
+ if (list.to_string() != "AA;bb;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto forSelector = cmList::TransformSelector::New<FOR>({ 1, 3 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector));
+ if (list.to_string() != "AA;bb;cc;dd;EE") {
+ result = false;
+ }
+ }
+ {
+ auto forSelector = cmList::TransformSelector::New<FOR>({ 0, 4, 2 });
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(forSelector));
+ if (list.to_string() != "aa;BB;cc;DD;ee") {
+ result = false;
+ }
+ }
+ {
+ auto regexSelector = cmList::TransformSelector::New<REGEX>("^(A|D|E)");
+ cmList list({ "AA", "BB", "CC", "DD", "EE" });
+
+ list.transform(cmList::TransformAction::TOLOWER, std::move(regexSelector));
+ if (list.to_string() != "aa;BB;CC;dd;ee") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+
+bool testStaticModifiers()
+{
+ std::cout << "testStaticModifiers()";
+
+ bool result = true;
+
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::assign("d;e", v);
+
+ if (v.size() != 2 || v[0] != "d" || v[1] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::append("d;;e", v);
+
+ if (v.size() != 5 || v[3] != "d" || v[4] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::append("d;;e", v, cmList::EmptyElements::Yes);
+
+ if (v.size() != 6 || v[3] != "d" || !v[4].empty() || v[5] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::prepend("d;e", v);
+
+ if (v.size() != 5 || v[0] != "d" || v[1] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::vector<std::string> v{ "a", "b", "c" };
+ cmList::prepend("d;;e", v, cmList::EmptyElements::Yes);
+
+ if (v.size() != 6 || v[0] != "d" || !v[1].empty() || v[2] != "e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::append("d;e", list);
+
+ if (list != "a;b;c;d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ cmList::append("d;e", list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::append("", list);
+
+ if (list != "a;b;c;") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v{ "d", "e" };
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "a;b;c;d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v;
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ std::vector<std::string> v{ "d", "e" };
+ cmList::append(v.begin(), v.end(), list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::prepend("d;e", list);
+
+ if (list != "d;e;a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ cmList::prepend("d;e", list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ cmList::prepend("", list);
+
+ if (list != ";a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v{ "d", "e" };
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "d;e;a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list{ "a;b;c" };
+ std::vector<std::string> v;
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "a;b;c") {
+ result = false;
+ }
+ }
+ {
+ std::string list;
+ std::vector<std::string> v{ "d", "e" };
+ cmList::prepend(v.begin(), v.end(), list);
+
+ if (list != "d;e") {
+ result = false;
+ }
+ }
+
+ checkResult(result);
+
+ return result;
+}
+}
+
+int testList(int /*unused*/, char* /*unused*/[])
+{
+ int result = 0;
+
+ if (!testConstructors()) {
+ result = 1;
+ }
+ if (!testAssign()) {
+ result = 1;
+ }
+ if (!testConversions()) {
+ result = 1;
+ }
+ if (!testAccess()) {
+ result = 1;
+ }
+ if (!testModifiers()) {
+ result = 1;
+ }
+ if (!testRemoveItems()) {
+ result = 1;
+ }
+ if (!testRemoveDuplicates()) {
+ result = 1;
+ }
+ if (!testFilter()) {
+ result = 1;
+ }
+ if (!testReverse()) {
+ result = 1;
+ }
+ if (!testSort()) {
+ result = 1;
+ }
+ if (!testTransform()) {
+ result = 1;
+ }
+ if (!testStaticModifiers()) {
+ result = 1;
+ }
+
+ return result;
+}
diff --git a/Tests/CMakeLib/testRST.expect b/Tests/CMakeLib/testRST.expect
index 5e3cdb1045..424b7d419a 100644
--- a/Tests/CMakeLib/testRST.expect
+++ b/Tests/CMakeLib/testRST.expect
@@ -26,10 +26,15 @@ Generator expression ``$<SOME_GENEX:...>`` with brackets and parameter.
Generator expression ``some genex`` with space and target.
Generator expression ``$<SOME_GENEX>`` with brackets, space, and target.
Generator expression ``$<SOME_GENEX:...>`` with brackets, parameter, space, and target.
-Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
+Inline cref ``Link Dest``.
+Inline cref ``Link_Dest_<Placeholder>``.
+Inline cref ``Link Text``.
+Inline cref ``Link_Text_<Placeholder>``.
+Inline link Link Dest.
Inline link Link Text.
Inline link Link Text <With \-escaped Brackets>.
Inline literal ``__`` followed by inline link Link Text.
+Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
First TOC entry.
@@ -46,7 +51,8 @@ Bracket Comment Content
Bracket Comment Content
]
-.. cmake:command:: some_cmd
+.. cmake:command::
+ some_cmd
Command some_cmd description.
@@ -54,7 +60,8 @@ Bracket Comment Content
Command other_cmd description.
-.. cmake:envvar:: some_var
+.. cmake:envvar::
+ some_var
Environment variable some_var description.
@@ -62,7 +69,8 @@ Bracket Comment Content
Environment variable other_var description.
-.. cmake:genex:: SOME_GENEX
+.. cmake:genex::
+ SOME_GENEX
Generator expression SOME_GENEX description.
@@ -70,7 +78,17 @@ Bracket Comment Content
Generator expression $<OTHER_GENEX> description.
-.. cmake:variable:: some_var
+.. cmake:signature::
+ some_command(SOME_SIGNATURE)
+
+ Command some_command SOME_SIGNATURE description.
+
+.. signature:: other_command(OTHER_SIGNATURE)
+
+ Command other_command OTHER_SIGNATURE description.
+
+.. cmake:variable::
+ some_var
Variable some_var description.
diff --git a/Tests/CMakeLib/testRST.rst b/Tests/CMakeLib/testRST.rst
index 4139801833..3a384077cf 100644
--- a/Tests/CMakeLib/testRST.rst
+++ b/Tests/CMakeLib/testRST.rst
@@ -33,10 +33,15 @@ Generator expression :genex:`$<SOME_GENEX:...>` with brackets and parameter.
Generator expression :genex:`some genex <SOME_GENEX>` with space and target.
Generator expression :genex:`$<SOME_GENEX> <SOME_GENEX>` with brackets, space, and target.
Generator expression :genex:`$<SOME_GENEX:...> <SOME_GENEX>` with brackets, parameter, space, and target.
-Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
+Inline cref :cref:`Link Dest`.
+Inline cref :cref:`Link_Dest_<Placeholder>`.
+Inline cref :cref:`Link Text <ExternalDest>`.
+Inline cref :cref:`Link_Text_<Placeholder> <ExternalDest>`.
+Inline link `Link Dest`_.
Inline link `Link Text <ExternalDest>`_.
Inline link `Link Text \<With \\-escaped Brackets\> <ExternalDest>`_.
Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
+Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
.. |not replaced| replace:: not replaced through toctree
.. |not replaced in literal| replace:: replaced in parsed literal
@@ -49,7 +54,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
.. cmake-module:: testRSTmod.cmake
-.. cmake:command:: some_cmd
+.. cmake:command::
+ some_cmd
Command some_cmd description.
@@ -57,7 +63,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
Command other_cmd description.
-.. cmake:envvar:: some_var
+.. cmake:envvar::
+ some_var
Environment variable some_var description.
@@ -65,7 +72,8 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
Environment variable other_var description.
-.. cmake:genex:: SOME_GENEX
+.. cmake:genex::
+ SOME_GENEX
Generator expression SOME_GENEX description.
@@ -73,7 +81,17 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
Generator expression $<OTHER_GENEX> description.
-.. cmake:variable:: some_var
+.. cmake:signature::
+ some_command(SOME_SIGNATURE)
+
+ Command some_command SOME_SIGNATURE description.
+
+.. signature:: other_command(OTHER_SIGNATURE)
+
+ Command other_command OTHER_SIGNATURE description.
+
+.. cmake:variable::
+ some_var
Variable some_var description.
diff --git a/Tests/CMakeLib/testSystemTools.cxx b/Tests/CMakeLib/testSystemTools.cxx
index 754205e46f..0f0c025d46 100644
--- a/Tests/CMakeLib/testSystemTools.cxx
+++ b/Tests/CMakeLib/testSystemTools.cxx
@@ -36,6 +36,53 @@ int testSystemTools(int /*unused*/, char* /*unused*/[])
"cmSystemTools::UpperCase");
// ----------------------------------------------------------------------
+ // Test cmSystemTools::VersionCompare
+ cmAssert(cmSystemTools::VersionCompareEqual("", ""),
+ "VersionCompareEqual empty string");
+ cmAssert(!cmSystemTools::VersionCompareGreater("", ""),
+ "VersionCompareGreater empty string");
+ cmAssert(cmSystemTools::VersionCompareEqual("1", "1a"),
+ "VersionCompareEqual letters");
+ cmAssert(!cmSystemTools::VersionCompareGreater("1", "1a"),
+ "VersionCompareGreater letters");
+ cmAssert(cmSystemTools::VersionCompareEqual("001", "1"),
+ "VersionCompareEqual leading zeros equal");
+ cmAssert(!cmSystemTools::VersionCompareGreater("001", "1"),
+ "VersionCompareGreater leading zeros equal");
+ cmAssert(!cmSystemTools::VersionCompareEqual("002", "1"),
+ "VersionCompareEqual leading zeros greater");
+ cmAssert(cmSystemTools::VersionCompareGreater("002", "1"),
+ "VersionCompareGreater leading zeros greater");
+ cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.3.1"),
+ "VersionCompareEqual components less");
+ cmAssert(!cmSystemTools::VersionCompareGreater("6.2.1", "6.3.1"),
+ "VersionCompareGreater components less");
+ cmAssert(!cmSystemTools::VersionCompareEqual("6.2.1", "6.2"),
+ "VersionCompareEqual different length");
+ cmAssert(cmSystemTools::VersionCompareGreater("6.2.1", "6.2"),
+ "VersionCompareGreater different length");
+ cmAssert(
+ !cmSystemTools::VersionCompareEqual(
+ "3.14159265358979323846264338327950288419716939937510582097494459230",
+ "3.14159265358979323846264338327950288419716939937510582097494459231"),
+ "VersionCompareEqual long number");
+ cmAssert(
+ !cmSystemTools::VersionCompareGreater(
+ "3.14159265358979323846264338327950288419716939937510582097494459230",
+ "3.14159265358979323846264338327950288419716939937510582097494459231"),
+ "VersionCompareGreater long number");
+ cmAssert(
+ !cmSystemTools::VersionCompareEqual(
+ "3.141592653589793238462643383279502884197169399375105820974944592307",
+ "3.14159265358979323846264338327950288419716939937510582097494459231"),
+ "VersionCompareEqual more digits");
+ cmAssert(
+ cmSystemTools::VersionCompareGreater(
+ "3.141592653589793238462643383279502884197169399375105820974944592307",
+ "3.14159265358979323846264338327950288419716939937510582097494459231"),
+ "VersionCompareGreater more digits");
+
+ // ----------------------------------------------------------------------
// Test cmSystemTools::strverscmp
cmAssert(cmSystemTools::strverscmp("", "") == 0, "strverscmp empty string");
cmAssert(cmSystemTools::strverscmp("abc", "") > 0,
diff --git a/Tests/CMakeLib/testVisualStudioSlnParser.cxx b/Tests/CMakeLib/testVisualStudioSlnParser.cxx
index c1bf3d41b2..3485bac4d5 100644
--- a/Tests/CMakeLib/testVisualStudioSlnParser.cxx
+++ b/Tests/CMakeLib/testVisualStudioSlnParser.cxx
@@ -80,7 +80,6 @@ int testVisualStudioSlnParser(int, char*[])
"cmsysProcessFwd9x",
"cmsysTestDynload",
"cmsysTestProcess",
- "cmsysTestSharedForward",
"cmsysTestsC",
"cmsysTestsCxx",
"cmsys_c",
diff --git a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file
index 395b953d86..1f148fc420 100644
--- a/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file
+++ b/Tests/CMakeLib/testVisualStudioSlnParser_data/valid.sln-file
@@ -21,7 +21,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ALL_BUILD", "ALL_BUILD.vcxp
{29D5FCAF-20D0-4DEF-8529-F035C249E996} = {29D5FCAF-20D0-4DEF-8529-F035C249E996}
{A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {A0421DCA-AC3E-42D0-94AC-379A21A1E591}
{C6AF7E57-CE57-4462-AE1D-BF520701480E} = {C6AF7E57-CE57-4462-AE1D-BF520701480E}
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {F2CAAAB3-9568-4284-B8E3-13955183A6D7}
{D8294E4A-03C5-43D7-AE35-15603F502DC0} = {D8294E4A-03C5-43D7-AE35-15603F502DC0}
{A4921D15-411F-436A-B6F3-F8381652A8E1} = {A4921D15-411F-436A-B6F3-F8381652A8E1}
{60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C}
@@ -220,12 +219,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestProcess", "Source\
{60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C}
EndProjectSection
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestSharedForward", "Source\kwsys\cmsysTestSharedForward.vcxproj", "{F2CAAAB3-9568-4284-B8E3-13955183A6D7}"
- ProjectSection(ProjectDependencies) = postProject
- {90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044}
- {60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {60BEB3AF-B4EF-4363-8747-C40177BC2D9C}
- EndProjectSection
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cmsysTestsC", "Source\kwsys\cmsysTestsC.vcxproj", "{D8294E4A-03C5-43D7-AE35-15603F502DC0}"
ProjectSection(ProjectDependencies) = postProject
{90BC31D7-A3E8-4F04-8049-2236C239A044} = {90BC31D7-A3E8-4F04-8049-2236C239A044}
@@ -528,14 +521,6 @@ Global
{C6AF7E57-CE57-4462-AE1D-BF520701480E}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
{C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
{C6AF7E57-CE57-4462-AE1D-BF520701480E}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.ActiveCfg = Debug|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Debug|x64.Build.0 = Debug|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.ActiveCfg = Release|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.Release|x64.Build.0 = Release|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.MinSizeRel|x64.Build.0 = MinSizeRel|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64
{D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.ActiveCfg = Debug|x64
{D8294E4A-03C5-43D7-AE35-15603F502DC0}.Debug|x64.Build.0 = Debug|x64
{D8294E4A-03C5-43D7-AE35-15603F502DC0}.Release|x64.ActiveCfg = Release|x64
@@ -667,7 +652,6 @@ Global
{29D5FCAF-20D0-4DEF-8529-F035C249E996} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
{A0421DCA-AC3E-42D0-94AC-379A21A1E591} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
{C6AF7E57-CE57-4462-AE1D-BF520701480E} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
- {F2CAAAB3-9568-4284-B8E3-13955183A6D7} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
{D8294E4A-03C5-43D7-AE35-15603F502DC0} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
{A4921D15-411F-436A-B6F3-F8381652A8E1} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
{60BEB3AF-B4EF-4363-8747-C40177BC2D9C} = {EF1DFA45-6F7A-4760-8EB5-69A8A221FC54}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index c22f70449d..e92d1c1c09 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -40,10 +40,12 @@ set(ENV{HOME} \"${TEST_HOME}\")
endif()
# Suppress generator deprecation warnings in test suite.
-if(CMAKE_GENERATOR MATCHES "^Visual Studio 11 2012")
- set(TEST_WARN_VS11_CODE "set(ENV{CMAKE_WARN_VS11} OFF)")
+if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 2008")
+ set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS9} OFF)")
+elseif(CMAKE_GENERATOR MATCHES "^Visual Studio 11 2012")
+ set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS11} OFF)")
else()
- set(TEST_WARN_VS11_CODE "")
+ set(TEST_WARN_VS_CODE "")
endif()
# 3.9 or later provides a definitive answer to whether we are multi-config
@@ -744,36 +746,6 @@ if(BUILD_TESTING)
ADD_LINK_FLAGS_TEST(mod_flags_config dll_flags_config)
ADD_LINK_FLAGS_TEST(exe_flags_config mod_flags_config)
- # If we are running right now with a Unix Makefiles or Ninja based generator,
- # build the "Simple" test with the ExtraGenerators, if available
- # This doesn't test whether the generated project files work (unfortunately),
- # mainly it tests that cmake doesn't crash when generating these project files.
- if(CMAKE_GENERATOR MATCHES "^(Unix Makefiles|Ninja)$"
- AND NOT "${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
- foreach(
- extraGenerator
- IN ITEMS
- "CodeBlocks"
- "CodeLite"
- "Eclipse CDT4"
- "Kate"
- "Sublime Text 2"
- )
- string(REPLACE " " "" extraGeneratorTestName "Simple_${extraGenerator}Generator")
- add_test(${extraGeneratorTestName} ${CMAKE_CTEST_COMMAND}
- --build-and-test
- "${CMake_SOURCE_DIR}/Tests/Simple"
- "${CMake_BINARY_DIR}/Tests/${extraGeneratorTestName}"
- --build-two-config
- --build-generator "${extraGenerator} - ${CMAKE_GENERATOR}"
- --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}"
- --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}"
- --build-project Simple
- --test-command Simple)
- list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${extraGeneratorTestName}")
- endforeach()
- endif()
-
# test for correct sub-project generation
# not implemented in Xcode or Ninja
if(NOT CMAKE_GENERATOR MATCHES "Xcode|Ninja")
@@ -1478,6 +1450,7 @@ if(BUILD_TESTING)
GnuTLS
GSL
GTK2
+ HDF5
Iconv
ICU
ImageMagick
@@ -1512,6 +1485,7 @@ if(BUILD_TESTING)
SQLite3
TIFF
Vulkan
+ wxWidgets
X11
XalanC
XercesC
diff --git a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
index c7e3105045..e6ed5598d1 100644
--- a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
if(POLICY CMP0129)
cmake_policy(SET CMP0129 NEW)
endif()
diff --git a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
index 2784e3bc5d..24a0a865e3 100644
--- a/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckCXXSymbolExists/CMakeLists.txt
@@ -9,7 +9,7 @@
project(CheckCXXSymbolExists CXX)
-cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../CheckSymbolExists")
diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
index 2e5d8d3b28..0c76158510 100644
--- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CheckLanguage NONE)
include(CheckLanguage)
diff --git a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt
index 4cbccd398c..6ecd194203 100644
--- a/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckStructHasMember/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CheckStructHasMember)
diff --git a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt
index 3d65b7ab18..b6ed9e961b 100644
--- a/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckSymbolExists/CMakeLists.txt
@@ -9,7 +9,7 @@
project(CheckSymbolExists C)
-cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}")
diff --git a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt
index 8f1378706b..18a1ff658b 100644
--- a/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt
+++ b/Tests/CMakeOnly/CompilerIdOBJC/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CompilerIdOBJC OBJC)
foreach(v
diff --git a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt
index 8f41db050e..76c1e4b0e4 100644
--- a/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt
+++ b/Tests/CMakeOnly/CompilerIdOBJCXX/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CompilerIdOBJCXX OBJCXX)
foreach(v
diff --git a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt
index d66eb06d76..77dadcf277 100644
--- a/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt
+++ b/Tests/CMakeOnly/LinkInterfaceLoop/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(LinkInterfaceLoop C)
# Add a shared library that incorrectly names itself as a
diff --git a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt
index 9f30c7dd59..664682598e 100644
--- a/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt
+++ b/Tests/CMakeOnly/MajorVersionSelection/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
if (NOT MAJOR_TEST_MODULE OR NOT MAJOR_TEST_VERSION)
message(FATAL_ERROR "test selection variables not set up")
diff --git a/Tests/CMakeOnly/TargetScope/CMakeLists.txt b/Tests/CMakeOnly/TargetScope/CMakeLists.txt
index faf2250992..3bcbb00a78 100644
--- a/Tests/CMakeOnly/TargetScope/CMakeLists.txt
+++ b/Tests/CMakeOnly/TargetScope/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(TargetScope NONE)
add_subdirectory(Sub)
diff --git a/Tests/CMakeOnly/find_library/CMakeLists.txt b/Tests/CMakeOnly/find_library/CMakeLists.txt
index b23d5e2ea4..2d487e3fff 100644
--- a/Tests/CMakeOnly/find_library/CMakeLists.txt
+++ b/Tests/CMakeOnly/find_library/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(FindLibraryTest NONE)
set(CMAKE_FIND_DEBUG_MODE 1)
diff --git a/Tests/CMakeOnly/find_path/CMakeLists.txt b/Tests/CMakeOnly/find_path/CMakeLists.txt
index bf4e350c7d..7cc08add91 100644
--- a/Tests/CMakeOnly/find_path/CMakeLists.txt
+++ b/Tests/CMakeOnly/find_path/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(FindPathTest NONE)
set(CMAKE_FIND_DEBUG_MODE 1)
diff --git a/Tests/CMakeTests/EndStuffTestScript.cmake b/Tests/CMakeTests/EndStuffTestScript.cmake
index e0d826d797..bd8924669e 100644
--- a/Tests/CMakeTests/EndStuffTestScript.cmake
+++ b/Tests/CMakeTests/EndStuffTestScript.cmake
@@ -22,7 +22,7 @@ elseif(testname STREQUAL bad_endfunction) # fail
do_end("endfunction()\n")
elseif(testname STREQUAL bad_endif) # fail
- do_end("cmake_minimum_required(VERSION 2.8.12)\nendif()\n")
+ do_end("cmake_minimum_required(VERSION 3.5)\nendif()\n")
elseif(testname STREQUAL endif_low_min_version) # fail
do_end("cmake_minimum_required(VERSION 1.2)\nendif()\n")
diff --git a/Tests/COnly/CMakeLists.txt b/Tests/COnly/CMakeLists.txt
index 1c24017b6c..728ec5b677 100644
--- a/Tests/COnly/CMakeLists.txt
+++ b/Tests/COnly/CMakeLists.txt
@@ -1,5 +1,5 @@
# a simple C only test case
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project (COnly C)
set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix")
@@ -7,11 +7,5 @@ add_library(testc1 STATIC libc1.c)
add_library(testc2 SHARED libc2.c)
add_executable (COnly conly.c foo.c foo.h)
target_link_libraries(COnly testc1 testc2)
-if(MSVC_VERSION AND NOT CMAKE_C_COMPILER_ID STREQUAL Clang OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
- set_target_properties(COnly PROPERTIES
- LINK_FLAGS " /NODEFAULTLIB:\"libcdg.lib\" /NODEFAULTLIB:\"libcmtg.lib\" /NODEFAULTLIB:\"foomsvcrt.lib\" /NODEFAULTLIB:\"libbar.lib\" /NODEFAULTLIB:\"libfooba.lib\"")
-endif()
-string(ASCII 35 32 67 77 97 107 101 ASCII_STRING)
-message(STATUS "String: ${ASCII_STRING}")
add_library(testCModule MODULE testCModule.c)
diff --git a/Tests/CPackComponents/CMakeLists.txt b/Tests/CPackComponents/CMakeLists.txt
index c1b348ef40..a886b3df60 100644
--- a/Tests/CPackComponents/CMakeLists.txt
+++ b/Tests/CPackComponents/CMakeLists.txt
@@ -4,7 +4,7 @@
# application (mylibapp). We create a binary installer that allows
# users to select which pieces will be installed: the example
# application, the library binaries, and/or the header file.
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CPackComponents)
# Create the mylib library
diff --git a/Tests/CPackTestAllGenerators/CMakeLists.txt b/Tests/CPackTestAllGenerators/CMakeLists.txt
index 95daabff0c..e7fed3b06e 100644
--- a/Tests/CPackTestAllGenerators/CMakeLists.txt
+++ b/Tests/CPackTestAllGenerators/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CPackTestAllGenerators)
add_subdirectory(../CTestTest/SmallAndFast SmallAndFast)
install(FILES RunCPack.cmake DESTINATION .)
diff --git a/Tests/CPackWiXGenerator/CMakeLists.txt b/Tests/CPackWiXGenerator/CMakeLists.txt
index 2249d703ae..33fdc5ef44 100644
--- a/Tests/CPackWiXGenerator/CMakeLists.txt
+++ b/Tests/CPackWiXGenerator/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CPackWiXGenerator)
diff --git a/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt b/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt
index ce6fac4352..79e968a581 100644
--- a/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt
+++ b/Tests/CTestCoverageCollectGCOV/TestProject/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(TestProject CXX)
diff --git a/Tests/CTestCoverageCollectGCOV/test.cmake.in b/Tests/CTestCoverageCollectGCOV/test.cmake.in
index 7c7a3e5bb4..aaf30704bc 100644
--- a/Tests/CTestCoverageCollectGCOV/test.cmake.in
+++ b/Tests/CTestCoverageCollectGCOV/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SOURCE_DIRECTORY "@CMake_SOURCE_DIR@/Tests/CTestCoverageCollectGCOV/TestProject")
set(CTEST_BINARY_DIRECTORY "@CMake_BINARY_DIR@/Tests/CTestCoverageCollectGCOV/TestProject")
set(CTEST_CMAKE_GENERATOR "@CMAKE_GENERATOR@")
diff --git a/Tests/CTestLimitDashJ/CMakeLists.txt b/Tests/CTestLimitDashJ/CMakeLists.txt
index d04b3adf45..5bb7369949 100644
--- a/Tests/CTestLimitDashJ/CMakeLists.txt
+++ b/Tests/CTestLimitDashJ/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CTestLimitDashJ NONE)
# This file demonstrates https://gitlab.kitware.com/cmake/cmake/-/issues/12904
diff --git a/Tests/CTestTest/SmallAndFast/CMakeLists.txt b/Tests/CTestTest/SmallAndFast/CMakeLists.txt
index 06cbafde23..d5b3b61a22 100644
--- a/Tests/CTestTest/SmallAndFast/CMakeLists.txt
+++ b/Tests/CTestTest/SmallAndFast/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(SmallAndFast)
include(CTest)
diff --git a/Tests/CTestTest2/test.cmake.in b/Tests/CTestTest2/test.cmake.in
index d5d4d2f39d..4f4f6cfb6c 100644
--- a/Tests/CTestTest2/test.cmake.in
+++ b/Tests/CTestTest2/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestBadExe/test.cmake.in b/Tests/CTestTestBadExe/test.cmake.in
index dd180f0fe7..e46f71bbaa 100644
--- a/Tests/CTestTestBadExe/test.cmake.in
+++ b/Tests/CTestTestBadExe/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestBadGenerator/test.cmake.in b/Tests/CTestTestBadGenerator/test.cmake.in
index ae6d0b578c..34003b43e2 100644
--- a/Tests/CTestTestBadGenerator/test.cmake.in
+++ b/Tests/CTestTestBadGenerator/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestChecksum/test.cmake.in b/Tests/CTestTestChecksum/test.cmake.in
index 3bac0e00b0..916bbbb088 100644
--- a/Tests/CTestTestChecksum/test.cmake.in
+++ b/Tests/CTestTestChecksum/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestCostSerial/test.cmake.in b/Tests/CTestTestCostSerial/test.cmake.in
index 1c46d4ca5a..0df9f378eb 100644
--- a/Tests/CTestTestCostSerial/test.cmake.in
+++ b/Tests/CTestTestCostSerial/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestCrash/CMakeLists.txt b/Tests/CTestTestCrash/CMakeLists.txt
index 663d2e45ca..c7e5b91d96 100644
--- a/Tests/CTestTestCrash/CMakeLists.txt
+++ b/Tests/CTestTestCrash/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestCrash)
include(CTest)
diff --git a/Tests/CTestTestCrash/test.cmake.in b/Tests/CTestTestCrash/test.cmake.in
index 916d4e9fc2..34c9f3e387 100644
--- a/Tests/CTestTestCrash/test.cmake.in
+++ b/Tests/CTestTestCrash/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestCycle/CMakeLists.txt b/Tests/CTestTestCycle/CMakeLists.txt
index 19f4dd7d10..4093111d2c 100644
--- a/Tests/CTestTestCycle/CMakeLists.txt
+++ b/Tests/CTestTestCycle/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestCycle)
include(CTest)
diff --git a/Tests/CTestTestCycle/test.cmake.in b/Tests/CTestTestCycle/test.cmake.in
index 507d46bc5f..78b0ebbd16 100644
--- a/Tests/CTestTestCycle/test.cmake.in
+++ b/Tests/CTestTestCycle/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestDepends/CMakeLists.txt b/Tests/CTestTestDepends/CMakeLists.txt
index 462ad8c7a8..5a011d0159 100644
--- a/Tests/CTestTestDepends/CMakeLists.txt
+++ b/Tests/CTestTestDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestDepends)
include(CTest)
diff --git a/Tests/CTestTestDepends/test.cmake.in b/Tests/CTestTestDepends/test.cmake.in
index 11bc92a250..ea01297295 100644
--- a/Tests/CTestTestDepends/test.cmake.in
+++ b/Tests/CTestTestDepends/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in b/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in
index 8eb808f7de..3aed1ab13c 100644
--- a/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in
+++ b/Tests/CTestTestEmptyBinaryDirectory/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_RUN_CURRENT_SCRIPT 0)
diff --git a/Tests/CTestTestFailure/CMakeLists.txt b/Tests/CTestTestFailure/CMakeLists.txt
index db14b3d924..b6c1e7a091 100644
--- a/Tests/CTestTestFailure/CMakeLists.txt
+++ b/Tests/CTestTestFailure/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestFailure)
include(CTest)
diff --git a/Tests/CTestTestFailure/testNoBuild.cmake.in b/Tests/CTestTestFailure/testNoBuild.cmake.in
index 47d254f567..505916e795 100644
--- a/Tests/CTestTestFailure/testNoBuild.cmake.in
+++ b/Tests/CTestTestFailure/testNoBuild.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestFailure/testNoExe.cmake.in b/Tests/CTestTestFailure/testNoExe.cmake.in
index 8496c80e1a..e3d7742a86 100644
--- a/Tests/CTestTestFailure/testNoExe.cmake.in
+++ b/Tests/CTestTestFailure/testNoExe.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestFdSetSize/test.cmake.in b/Tests/CTestTestFdSetSize/test.cmake.in
index bfe4459259..73b2cfa032 100644
--- a/Tests/CTestTestFdSetSize/test.cmake.in
+++ b/Tests/CTestTestFdSetSize/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.10)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt
index 7376a40e6a..24da9ca5c4 100644
--- a/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt
+++ b/Tests/CTestTestLaunchers/launcher_compiler_test_project/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(launcher_compiler_test_project)
diff --git a/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt
index b31f587796..72764ca966 100644
--- a/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt
+++ b/Tests/CTestTestLaunchers/launcher_custom_command_test_project/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(launcher_custom_command_test_project)
diff --git a/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt b/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt
index 38980aa7e0..7bf0362a26 100644
--- a/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt
+++ b/Tests/CTestTestLaunchers/launcher_linker_test_project/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(launcher_linker_test_project)
diff --git a/Tests/CTestTestLaunchers/test.cmake.in b/Tests/CTestTestLaunchers/test.cmake.in
index 2db1ddd1d1..c3edfd5883 100644
--- a/Tests/CTestTestLaunchers/test.cmake.in
+++ b/Tests/CTestTestLaunchers/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
set(TEST_SUCCESS TRUE)
diff --git a/Tests/CTestTestMissingDependsExe/CMakeLists.txt b/Tests/CTestTestMissingDependsExe/CMakeLists.txt
index 9826da661a..07df19464b 100644
--- a/Tests/CTestTestMissingDependsExe/CMakeLists.txt
+++ b/Tests/CTestTestMissingDependsExe/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CTestTestMissingDependsExe)
diff --git a/Tests/CTestTestParallel/CMakeLists.txt b/Tests/CTestTestParallel/CMakeLists.txt
index 819fee4ebe..7527202a8c 100644
--- a/Tests/CTestTestParallel/CMakeLists.txt
+++ b/Tests/CTestTestParallel/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestParallel)
include(CTest)
diff --git a/Tests/CTestTestParallel/test.cmake.in b/Tests/CTestTestParallel/test.cmake.in
index 517db726ec..d60d16f548 100644
--- a/Tests/CTestTestParallel/test.cmake.in
+++ b/Tests/CTestTestParallel/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestResourceLock/CMakeLists.txt b/Tests/CTestTestResourceLock/CMakeLists.txt
index 4bc4366c3a..683aba53d2 100644
--- a/Tests/CTestTestResourceLock/CMakeLists.txt
+++ b/Tests/CTestTestResourceLock/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestResourceLock)
include(CTest)
diff --git a/Tests/CTestTestResourceLock/test.cmake.in b/Tests/CTestTestResourceLock/test.cmake.in
index 826226de0b..dab26fceff 100644
--- a/Tests/CTestTestResourceLock/test.cmake.in
+++ b/Tests/CTestTestResourceLock/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestScheduler/CMakeLists.txt b/Tests/CTestTestScheduler/CMakeLists.txt
index a3f0f27cdc..91d565d402 100644
--- a/Tests/CTestTestScheduler/CMakeLists.txt
+++ b/Tests/CTestTestScheduler/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (CTestTestScheduler)
include (CTest)
diff --git a/Tests/CTestTestScheduler/test.cmake.in b/Tests/CTestTestScheduler/test.cmake.in
index 5dcfb635ad..3b03a7c452 100644
--- a/Tests/CTestTestScheduler/test.cmake.in
+++ b/Tests/CTestTestScheduler/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestSerialInDepends/CMakeLists.txt b/Tests/CTestTestSerialInDepends/CMakeLists.txt
index 90e50f9827..03ad4b3dcf 100644
--- a/Tests/CTestTestSerialInDepends/CMakeLists.txt
+++ b/Tests/CTestTestSerialInDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CTestTestSerialInDepends)
diff --git a/Tests/CTestTestSerialOrder/CMakeLists.txt b/Tests/CTestTestSerialOrder/CMakeLists.txt
index 69c11fcde1..d46d80e358 100644
--- a/Tests/CTestTestSerialOrder/CMakeLists.txt
+++ b/Tests/CTestTestSerialOrder/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CTestTestSerialOrder)
diff --git a/Tests/CTestTestSkipReturnCode/CMakeLists.txt b/Tests/CTestTestSkipReturnCode/CMakeLists.txt
index 26c4178fd9..1eeeec6ef1 100644
--- a/Tests/CTestTestSkipReturnCode/CMakeLists.txt
+++ b/Tests/CTestTestSkipReturnCode/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CTestTestSkipReturnCode)
include(CTest)
diff --git a/Tests/CTestTestSkipReturnCode/test.cmake.in b/Tests/CTestTestSkipReturnCode/test.cmake.in
index 2988d2fe95..b45e4a6672 100644
--- a/Tests/CTestTestSkipReturnCode/test.cmake.in
+++ b/Tests/CTestTestSkipReturnCode/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestStopTime/CMakeLists.txt b/Tests/CTestTestStopTime/CMakeLists.txt
index 08116e25b9..4f6e7958cd 100644
--- a/Tests/CTestTestStopTime/CMakeLists.txt
+++ b/Tests/CTestTestStopTime/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestStopTime)
include(CTest)
diff --git a/Tests/CTestTestStopTime/GetDate.cmake b/Tests/CTestTestStopTime/GetDate.cmake
index 64a4fb9b39..f8e40fcf80 100644
--- a/Tests/CTestTestStopTime/GetDate.cmake
+++ b/Tests/CTestTestStopTime/GetDate.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.11)
+cmake_minimum_required(VERSION 3.5)
macro(GET_DATE)
#
diff --git a/Tests/CTestTestStopTime/test.cmake.in b/Tests/CTestTestStopTime/test.cmake.in
index 3797d40eda..2d69f1d7f5 100644
--- a/Tests/CTestTestStopTime/test.cmake.in
+++ b/Tests/CTestTestStopTime/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestSubdir/CMakeLists.txt b/Tests/CTestTestSubdir/CMakeLists.txt
index 87c4604d8a..e6f3209236 100644
--- a/Tests/CTestTestSubdir/CMakeLists.txt
+++ b/Tests/CTestTestSubdir/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestSubdir)
include(CTest)
diff --git a/Tests/CTestTestSubdir/test.cmake.in b/Tests/CTestTestSubdir/test.cmake.in
index 3b1fb5fc86..8b8d85e201 100644
--- a/Tests/CTestTestSubdir/test.cmake.in
+++ b/Tests/CTestTestSubdir/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestTimeout/test.cmake.in b/Tests/CTestTestTimeout/test.cmake.in
index ce9c4979f0..9d9e430a09 100644
--- a/Tests/CTestTestTimeout/test.cmake.in
+++ b/Tests/CTestTestTimeout/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestUpload/CMakeLists.txt b/Tests/CTestTestUpload/CMakeLists.txt
index 5e02b2f4ac..5bf428d2a8 100644
--- a/Tests/CTestTestUpload/CMakeLists.txt
+++ b/Tests/CTestTestUpload/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestUpload)
add_executable (Sleep sleep.c)
diff --git a/Tests/CTestTestUpload/test.cmake.in b/Tests/CTestTestUpload/test.cmake.in
index 74fd1ecdf4..db428e98de 100644
--- a/Tests/CTestTestUpload/test.cmake.in
+++ b/Tests/CTestTestUpload/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestVerboseOutput/CMakeLists.txt b/Tests/CTestTestVerboseOutput/CMakeLists.txt
index 3792385842..d44e9fc5f3 100644
--- a/Tests/CTestTestVerboseOutput/CMakeLists.txt
+++ b/Tests/CTestTestVerboseOutput/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CTestTestVerboseOutput)
include(CTest)
diff --git a/Tests/CTestTestVerboseOutput/test.cmake.in b/Tests/CTestTestVerboseOutput/test.cmake.in
index 9c9a4dca83..b47383a693 100644
--- a/Tests/CTestTestVerboseOutput/test.cmake.in
+++ b/Tests/CTestTestVerboseOutput/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CTestTestZeroTimeout/CMakeLists.txt b/Tests/CTestTestZeroTimeout/CMakeLists.txt
index 2d404c8cbd..51ef80788d 100644
--- a/Tests/CTestTestZeroTimeout/CMakeLists.txt
+++ b/Tests/CTestTestZeroTimeout/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (CTestTestZeroTimeout)
include (CTest)
diff --git a/Tests/CTestTestZeroTimeout/test.cmake.in b/Tests/CTestTestZeroTimeout/test.cmake.in
index 50dbba03ff..e0dbbc614d 100644
--- a/Tests/CTestTestZeroTimeout/test.cmake.in
+++ b/Tests/CTestTestZeroTimeout/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_DASHBOARD_ROOT "@CMake_BINARY_DIR@/Tests/CTestTest")
diff --git a/Tests/CheckCompilerRelatedVariables/CMakeLists.txt b/Tests/CheckCompilerRelatedVariables/CMakeLists.txt
index 69fa4b6104..92593092f4 100644
--- a/Tests/CheckCompilerRelatedVariables/CMakeLists.txt
+++ b/Tests/CheckCompilerRelatedVariables/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CheckCompilerRelatedVariables)
diff --git a/Tests/CheckFortran.cmake b/Tests/CheckFortran.cmake
index 1e943a1d1c..850406bb0f 100644
--- a/Tests/CheckFortran.cmake
+++ b/Tests/CheckFortran.cmake
@@ -7,7 +7,7 @@ if(NOT DEFINED CMAKE_Fortran_COMPILER)
message(STATUS ${_desc})
file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CheckFortran)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CheckFortran/CMakeLists.txt"
- "cmake_minimum_required(VERSION 2.8.12)
+ "cmake_minimum_required(VERSION 3.5)
project(CheckFortran Fortran)
file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
\"set(CMAKE_Fortran_COMPILER \\\"\${CMAKE_Fortran_COMPILER}\\\")\\n\"
diff --git a/Tests/CompileDefinitions/CMakeLists.txt b/Tests/CompileDefinitions/CMakeLists.txt
index 8347d5a225..cd0a0b0da0 100644
--- a/Tests/CompileDefinitions/CMakeLists.txt
+++ b/Tests/CompileDefinitions/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(CompileDefinitions)
# Use compile flags to tell executables which config is built
diff --git a/Tests/Contracts/Trilinos/CMakeLists.txt b/Tests/Contracts/Trilinos/CMakeLists.txt
index 6cc2d09dd1..e23a643300 100644
--- a/Tests/Contracts/Trilinos/CMakeLists.txt
+++ b/Tests/Contracts/Trilinos/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(Trilinos)
include(ExternalProject)
diff --git a/Tests/Contracts/VTK/CMakeLists.txt b/Tests/Contracts/VTK/CMakeLists.txt
index 0d36323902..aee4dc6125 100644
--- a/Tests/Contracts/VTK/CMakeLists.txt
+++ b/Tests/Contracts/VTK/CMakeLists.txt
@@ -1,6 +1,6 @@
# The VTK external project for CMake
# ---------------------------------------------------------------------------
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(VTK)
include(ExternalProject)
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index db08076d26..aa25c4cb09 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -27,6 +27,9 @@ if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang")
# Only NVCC defines __CUDACC_DEBUG__ when compiling in debug mode.
add_cuda_test_macro(CudaOnly.GPUDebugFlag CudaOnlyGPUDebugFlag)
+ add_cuda_test_macro(CudaOnly.CUBIN CudaOnlyCUBIN)
+ add_cuda_test_macro(CudaOnly.Fatbin CudaOnlyFatbin)
+ add_cuda_test_macro(CudaOnly.OptixIR CudaOnlyOptixIR)
endif()
add_cuda_test_macro(CudaOnly.DeviceLTO CudaOnlyDeviceLTO)
diff --git a/Tests/CudaOnly/CUBIN/CMakeLists.txt b/Tests/CudaOnly/CUBIN/CMakeLists.txt
new file mode 100644
index 0000000000..81787e4c81
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.18)
+unset(ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}) # CUBIN needs true native arch
+project(CudaCUBIN LANGUAGES CUDA)
+
+set(CMAKE_CUDA_ARCHITECTURES all-major)
+
+# CUBIN needs the true native arch to be supported by the CUDA toolkit.
+set(unavailable_native_archs "${CMAKE_CUDA_ARCHITECTURES_NATIVE}")
+list(REMOVE_ITEM unavailable_native_archs ${CMAKE_CUDA_ARCHITECTURES_ALL})
+if(unavailable_native_archs)
+ add_executable(CudaOnlyCUBIN main_no_native_archs.cu)
+ return()
+endif()
+
+add_library(CudaCUBIN OBJECT kernelA.cu kernelB.cu kernelC.cu)
+set_property(TARGET CudaCUBIN PROPERTY CUDA_CUBIN_COMPILATION ON)
+set_property(TARGET CudaCUBIN PROPERTY CUDA_ARCHITECTURES native)
+
+add_executable(CudaOnlyCUBIN main.cu)
+target_compile_features(CudaOnlyCUBIN PRIVATE cuda_std_11)
+target_compile_definitions(CudaOnlyCUBIN PRIVATE "CUBIN_FILE_PATHS=\"$<JOIN:$<TARGET_OBJECTS:CudaCUBIN>,~_~>\"")
+
+find_package(CUDAToolkit REQUIRED)
+target_link_libraries(CudaOnlyCUBIN PRIVATE CUDA::cuda_driver)
+
+if(APPLE)
+ # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
+ set_property(TARGET CudaOnlyCUBIN PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
+endif()
diff --git a/Tests/CudaOnly/CUBIN/kernelA.cu b/Tests/CudaOnly/CUBIN/kernelA.cu
new file mode 100644
index 0000000000..fbe0d26d70
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/kernelA.cu
@@ -0,0 +1,7 @@
+
+__global__ void kernelA(float* r, float* x, float* y, float* z, int size)
+{
+ for (int i = threadIdx.x; i < size; i += blockDim.x) {
+ r[i] = x[i] * y[i] + z[i];
+ }
+}
diff --git a/Tests/CudaOnly/CUBIN/kernelB.cu b/Tests/CudaOnly/CUBIN/kernelB.cu
new file mode 100644
index 0000000000..74782532b9
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/kernelB.cu
@@ -0,0 +1,7 @@
+
+__global__ void kernelB(float* r, float* x, float* y, float* z, int size)
+{
+ for (int i = threadIdx.x; i < size; i += blockDim.x) {
+ r[i] = x[i] * y[i] + z[i];
+ }
+}
diff --git a/Tests/CudaOnly/CUBIN/kernelC.cu b/Tests/CudaOnly/CUBIN/kernelC.cu
new file mode 100644
index 0000000000..5f8a0ce93d
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/kernelC.cu
@@ -0,0 +1,7 @@
+
+__global__ void kernelC(float* r, float* x, float* y, float* z, int size)
+{
+ for (int i = threadIdx.x; i < size; i += blockDim.x) {
+ r[i] = x[i] * y[i] + z[i];
+ }
+}
diff --git a/Tests/CudaOnly/CUBIN/main.cu b/Tests/CudaOnly/CUBIN/main.cu
new file mode 100644
index 0000000000..581970a0c2
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/main.cu
@@ -0,0 +1,58 @@
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <cuda.h>
+
+#define GENERATED_HEADER(x) GENERATED_HEADER1(x)
+#define GENERATED_HEADER1(x) <x>
+
+static std::string input_paths = { CUBIN_FILE_PATHS };
+
+int main()
+{
+ const std::string delimiter = "~_~";
+ input_paths += delimiter;
+
+ size_t end = 0;
+ size_t previous_end = 0;
+ std::vector<std::string> actual_paths;
+ while ((end = input_paths.find(delimiter, previous_end)) !=
+ std::string::npos) {
+ actual_paths.emplace_back(
+ input_paths.substr(previous_end, end - previous_end));
+ previous_end = end + 3;
+ }
+
+ cuInit(0);
+ int count = 0;
+ cuDeviceGetCount(&count);
+ if (count == 0) {
+ std::cerr << "No CUDA devices found\n";
+ return 1;
+ }
+
+ CUdevice device;
+ cuDeviceGet(&device, 0);
+
+ CUcontext context;
+ cuCtxCreate(&context, 0, device);
+
+ CUmodule module;
+ for (auto p : actual_paths) {
+ if (p.find(".cubin") == std::string::npos) {
+ std::cout << p << " Doesn't have the .cubin suffix" << p << std::endl;
+ return 1;
+ }
+ std::cout << "trying to load cubin: " << p << std::endl;
+ CUresult result = cuModuleLoad(&module, p.c_str());
+ std::cout << "module pointer: " << module << '\n';
+ if (result != CUDA_SUCCESS || module == nullptr) {
+ std::cerr << "Failed to load the embedded cubin with error: "
+ << static_cast<unsigned int>(result) << '\n';
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/Tests/CudaOnly/CUBIN/main_no_native_archs.cu b/Tests/CudaOnly/CUBIN/main_no_native_archs.cu
new file mode 100644
index 0000000000..f8b643afbf
--- /dev/null
+++ b/Tests/CudaOnly/CUBIN/main_no_native_archs.cu
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+}
diff --git a/Tests/CudaOnly/Fatbin/CMakeLists.txt b/Tests/CudaOnly/Fatbin/CMakeLists.txt
new file mode 100644
index 0000000000..db0dc227e0
--- /dev/null
+++ b/Tests/CudaOnly/Fatbin/CMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.18)
+project(CudaFATBIN LANGUAGES CUDA)
+
+
+set(CMAKE_CUDA_ARCHITECTURES all-major)
+
+add_library(CudaFATBIN OBJECT
+${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelA.cu
+${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelB.cu
+${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelC.cu)
+
+set_property(TARGET CudaFATBIN PROPERTY CUDA_FATBIN_COMPILATION ON)
+
+# Will use `cuModuleLoadFatBinary` to load the fatbinaries
+add_executable(CudaOnlyFatbin main.cu)
+target_compile_features(CudaOnlyFatbin PRIVATE cuda_std_11)
+target_compile_definitions(CudaOnlyFatbin PRIVATE "FATBIN_FILE_PATHS=\"$<JOIN:$<TARGET_OBJECTS:CudaFATBIN>,~_~>\"")
+
+find_package(CUDAToolkit REQUIRED)
+target_link_libraries(CudaOnlyFatbin PRIVATE CUDA::cuda_driver)
+
+if(APPLE)
+ # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
+ set_property(TARGET CudaOnlyFatbin PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
+endif()
diff --git a/Tests/CudaOnly/Fatbin/main.cu b/Tests/CudaOnly/Fatbin/main.cu
new file mode 100644
index 0000000000..89af0e3a70
--- /dev/null
+++ b/Tests/CudaOnly/Fatbin/main.cu
@@ -0,0 +1,58 @@
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <cuda.h>
+
+#define GENERATED_HEADER(x) GENERATED_HEADER1(x)
+#define GENERATED_HEADER1(x) <x>
+
+static std::string input_paths = { FATBIN_FILE_PATHS };
+
+int main()
+{
+ const std::string delimiter = "~_~";
+ input_paths += delimiter;
+
+ size_t end = 0;
+ size_t previous_end = 0;
+ std::vector<std::string> actual_paths;
+ while ((end = input_paths.find(delimiter, previous_end)) !=
+ std::string::npos) {
+ actual_paths.emplace_back(
+ input_paths.substr(previous_end, end - previous_end));
+ previous_end = end + 3;
+ }
+
+ cuInit(0);
+ int count = 0;
+ cuDeviceGetCount(&count);
+ if (count == 0) {
+ std::cerr << "No CUDA devices found\n";
+ return 1;
+ }
+
+ CUdevice device;
+ cuDeviceGet(&device, 0);
+
+ CUcontext context;
+ cuCtxCreate(&context, 0, device);
+
+ CUmodule module;
+ for (auto p : actual_paths) {
+ if (p.find(".fatbin") == std::string::npos) {
+ std::cout << p << " Doesn't have the .fatbin suffix" << p << std::endl;
+ return 1;
+ }
+ std::cout << "trying to load fatbin: " << p << std::endl;
+ CUresult result = cuModuleLoad(&module, p.c_str());
+ std::cout << "module pointer: " << module << '\n';
+ if (result != CUDA_SUCCESS || module == nullptr) {
+ std::cerr << "Failed to load the embedded fatbin with error: "
+ << static_cast<unsigned int>(result) << '\n';
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/Tests/CudaOnly/OptixIR/CMakeLists.txt b/Tests/CudaOnly/OptixIR/CMakeLists.txt
new file mode 100644
index 0000000000..afeabdade7
--- /dev/null
+++ b/Tests/CudaOnly/OptixIR/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.18)
+project(CudaOptix LANGUAGES CUDA)
+
+
+set(CMAKE_CUDA_ARCHITECTURES all-major)
+
+add_library(CudaOptix OBJECT
+ ${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelA.cu
+ ${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelB.cu
+ ${CMAKE_CURRENT_SOURCE_DIR}/../CUBIN/kernelC.cu)
+
+if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.7.0")
+ set_property(TARGET CudaOptix PROPERTY CUDA_OPTIX_COMPILATION ON)
+endif()
+
+set_property(TARGET CudaOptix PROPERTY CUDA_ARCHITECTURES native)
+
+add_executable(CudaOnlyOptixIR main.cu)
+target_compile_features(CudaOnlyOptixIR PRIVATE cuda_std_11)
+
+if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.7.0")
+ target_compile_definitions(CudaOnlyOptixIR PRIVATE "OPTIX_FILE_PATHS=\"$<JOIN:$<TARGET_OBJECTS:CudaOptix>,~_~>\"")
+else()
+ target_compile_definitions(CudaOnlyOptixIR PRIVATE "OPTIX_FILE_PATHS=\"NO_OPTIX_SUPPORT\"")
+endif()
+
+find_package(CUDAToolkit REQUIRED)
+target_link_libraries(CudaOnlyOptixIR PRIVATE CUDA::cuda_driver)
+
+if(APPLE)
+ # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
+ set_property(TARGET CudaOnlyOptixIR PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
+endif()
diff --git a/Tests/CudaOnly/OptixIR/main.cu b/Tests/CudaOnly/OptixIR/main.cu
new file mode 100644
index 0000000000..c79829b297
--- /dev/null
+++ b/Tests/CudaOnly/OptixIR/main.cu
@@ -0,0 +1,53 @@
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <cuda.h>
+
+#define GENERATED_HEADER(x) GENERATED_HEADER1(x)
+#define GENERATED_HEADER1(x) <x>
+
+static std::string input_paths = { OPTIX_FILE_PATHS };
+
+int main()
+{
+ if (input_paths == "NO_OPTIX_SUPPORT") {
+ return 0;
+ }
+
+ const std::string delimiter = "~_~";
+ input_paths += delimiter;
+
+ size_t end = 0;
+ size_t previous_end = 0;
+ std::vector<std::string> actual_paths;
+ while ((end = input_paths.find(delimiter, previous_end)) !=
+ std::string::npos) {
+ actual_paths.emplace_back(
+ input_paths.substr(previous_end, end - previous_end));
+ previous_end = end + 3;
+ }
+
+ if (actual_paths.empty()) {
+ std::cerr << "Failed to parse OPTIX_FILE_PATHS" << std::endl;
+ return 1;
+ }
+
+ const std::uint32_t optix_magic_value = 0x7f4e43ed;
+ for (auto p : actual_paths) {
+ if (p.find(".optixir") == std::string::npos) {
+ std::cout << p << " Doesn't have the .optixir suffix" << p << std::endl;
+ return 1;
+ }
+ std::ifstream input(p, std::ios::binary);
+ std::uint32_t value;
+ input.read(reinterpret_cast<char*>(&value), sizeof(value));
+ if (value != optix_magic_value) {
+ std::cerr << p << " Doesn't look like an optix-ir file" << std::endl;
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/Tests/CudaOnly/RuntimeControls/verify_runtime.cmake b/Tests/CudaOnly/RuntimeControls/verify_runtime.cmake
index b313dac937..27fbe45874 100644
--- a/Tests/CudaOnly/RuntimeControls/verify_runtime.cmake
+++ b/Tests/CudaOnly/RuntimeControls/verify_runtime.cmake
@@ -7,7 +7,7 @@ file(GET_RUNTIME_DEPENDENCIES
EXECUTABLES ${EXEC_PATH}
)
-list(FILTER resolved_libs INCLUDE REGEX ".*cudart.*")
+list(FILTER resolved_libs INCLUDE REGEX ".*[Cc][Uu][Dd][Aa][Rr][Tt].*")
list(LENGTH resolved_libs has_cudart)
if(has_cudart EQUAL 0)
diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt
index fa06a94fa0..25df30075d 100644
--- a/Tests/CustomCommand/CMakeLists.txt
+++ b/Tests/CustomCommand/CMakeLists.txt
@@ -1,7 +1,7 @@
#
# Wrapping
#
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (CustomCommand)
add_subdirectory(GeneratedHeader)
diff --git a/Tests/CustomCommandByproducts/External/CMakeLists.txt b/Tests/CustomCommandByproducts/External/CMakeLists.txt
index feaa12ea4c..81e072bc45 100644
--- a/Tests/CustomCommandByproducts/External/CMakeLists.txt
+++ b/Tests/CustomCommandByproducts/External/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(External C)
add_library(ExternalLibrary STATIC ExternalLibrary.c)
diff --git a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
index 7697a9b727..531690ae16 100644
--- a/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
+++ b/Tests/CustomCommandWorkingDirectory/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(TestWorkingDir)
add_custom_command(
diff --git a/Tests/CxxDialect/CMakeLists.txt b/Tests/CxxDialect/CMakeLists.txt
index 8c903398cb..c88641b830 100644
--- a/Tests/CxxDialect/CMakeLists.txt
+++ b/Tests/CxxDialect/CMakeLists.txt
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
-cmake_policy(SET CMP0025 NEW)
+cmake_minimum_required(VERSION 3.5)
project(CxxDialect)
add_executable(use_typeof use_typeof.cxx)
diff --git a/Tests/CxxOnly/CMakeLists.txt b/Tests/CxxOnly/CMakeLists.txt
index 09689cb429..6cd3a8ecd6 100644
--- a/Tests/CxxOnly/CMakeLists.txt
+++ b/Tests/CxxOnly/CMakeLists.txt
@@ -1,5 +1,5 @@
# a simple CXX only test case
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project (CxxOnly CXX)
set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix")
diff --git a/Tests/Dependency/CMakeLists.txt b/Tests/Dependency/CMakeLists.txt
index 58d3fb7c51..cae108ea87 100644
--- a/Tests/Dependency/CMakeLists.txt
+++ b/Tests/Dependency/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project( Dependency )
# to test directories with only one character One was changed to 1
diff --git a/Tests/EmptyDepends/CMakeLists.txt b/Tests/EmptyDepends/CMakeLists.txt
index 272eff7fa8..5ba0b49ce9 100644
--- a/Tests/EmptyDepends/CMakeLists.txt
+++ b/Tests/EmptyDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(EmptyDepends)
include(CTest)
diff --git a/Tests/EnforceConfig.cmake.in b/Tests/EnforceConfig.cmake.in
index 7722d7d5fb..a652efcd0a 100644
--- a/Tests/EnforceConfig.cmake.in
+++ b/Tests/EnforceConfig.cmake.in
@@ -35,5 +35,8 @@ unset(ENV{CMAKE_GENERATOR_PLATFORM})
unset(ENV{CMAKE_GENERATOR_TOOLSET})
unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS})
+# Verify that our module implementations do not recurse too much.
+set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} 100)
+
@TEST_HOME_ENV_CODE@
-@TEST_WARN_VS11_CODE@
+@TEST_WARN_VS_CODE@
diff --git a/Tests/ExportImport/Import/try_compile/CMakeLists.txt b/Tests/ExportImport/Import/try_compile/CMakeLists.txt
index 813cf0666b..bb390f927c 100644
--- a/Tests/ExportImport/Import/try_compile/CMakeLists.txt
+++ b/Tests/ExportImport/Import/try_compile/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
find_package(testLibRequired 2.5 REQUIRED)
diff --git a/Tests/ExternalProject/CMakeLists.txt b/Tests/ExternalProject/CMakeLists.txt
index 81d31e77b1..d1ab7ace7a 100644
--- a/Tests/ExternalProject/CMakeLists.txt
+++ b/Tests/ExternalProject/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExternalProjectTest NONE)
if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12)
cmake_policy(SET CMP0114 NEW)
diff --git a/Tests/ExternalProject/Example/CMakeLists.txt b/Tests/ExternalProject/Example/CMakeLists.txt
index c3f2614e47..b6785a6191 100644
--- a/Tests/ExternalProject/Example/CMakeLists.txt
+++ b/Tests/ExternalProject/Example/CMakeLists.txt
@@ -1,5 +1,5 @@
# This is the canonical simplest ExternalProject example CMakeLists.txt file:
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExternalProjectExample NONE)
include(ExternalProject)
diff --git a/Tests/ExternalProjectLocal/CMakeLists.txt b/Tests/ExternalProjectLocal/CMakeLists.txt
index 57e81052bf..2dfc4252f2 100644
--- a/Tests/ExternalProjectLocal/CMakeLists.txt
+++ b/Tests/ExternalProjectLocal/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExternalProjectLocalTest NONE)
if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12)
cmake_policy(SET CMP0114 NEW)
diff --git a/Tests/ExternalProjectUpdate/CMakeLists.txt b/Tests/ExternalProjectUpdate/CMakeLists.txt
index 6f8a7b15bc..1b84ff37b0 100644
--- a/Tests/ExternalProjectUpdate/CMakeLists.txt
+++ b/Tests/ExternalProjectUpdate/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExternalProjectUpdateTest NONE)
if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12)
cmake_policy(SET CMP0114 NEW)
diff --git a/Tests/FindBLAS/Test/CMakeLists.txt b/Tests/FindBLAS/Test/CMakeLists.txt
index 14bf19f391..9909d2ea3c 100644
--- a/Tests/FindBLAS/Test/CMakeLists.txt
+++ b/Tests/FindBLAS/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindBLAS C)
include(CTest)
diff --git a/Tests/FindBZip2/Test/CMakeLists.txt b/Tests/FindBZip2/Test/CMakeLists.txt
index e9cb6187fa..136b3fdab9 100644
--- a/Tests/FindBZip2/Test/CMakeLists.txt
+++ b/Tests/FindBZip2/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindBZip2 C)
include(CTest)
diff --git a/Tests/FindBoost/Test/CMakeLists.txt b/Tests/FindBoost/Test/CMakeLists.txt
index f60ccfa009..39fa53206a 100644
--- a/Tests/FindBoost/Test/CMakeLists.txt
+++ b/Tests/FindBoost/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindBoost CXX)
include(CTest)
diff --git a/Tests/FindBoost/TestFail/CMakeLists.txt b/Tests/FindBoost/TestFail/CMakeLists.txt
index 7c14a59905..3db1a4f4d1 100644
--- a/Tests/FindBoost/TestFail/CMakeLists.txt
+++ b/Tests/FindBoost/TestFail/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindBoost CXX)
include(CTest)
diff --git a/Tests/FindBoost/TestHeaders/CMakeLists.txt b/Tests/FindBoost/TestHeaders/CMakeLists.txt
index d7be32703c..7dd12e9bb3 100644
--- a/Tests/FindBoost/TestHeaders/CMakeLists.txt
+++ b/Tests/FindBoost/TestHeaders/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindBoostHeaders CXX)
include(CTest)
diff --git a/Tests/FindDevIL/Test/CMakeLists.txt b/Tests/FindDevIL/Test/CMakeLists.txt
index c2c132223e..db80ccb85c 100644
--- a/Tests/FindDevIL/Test/CMakeLists.txt
+++ b/Tests/FindDevIL/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindDevIL C)
include(CTest)
diff --git a/Tests/FindGIF/Test/CMakeLists.txt b/Tests/FindGIF/Test/CMakeLists.txt
index 961e636faa..eb0291e99b 100644
--- a/Tests/FindGIF/Test/CMakeLists.txt
+++ b/Tests/FindGIF/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindGIF C)
include(CTest)
diff --git a/Tests/FindGLEW/Test/CMakeLists.txt b/Tests/FindGLEW/Test/CMakeLists.txt
index 954ee1092b..de8d97ded8 100644
--- a/Tests/FindGLEW/Test/CMakeLists.txt
+++ b/Tests/FindGLEW/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindGLEW LANGUAGES CXX)
include(CTest)
diff --git a/Tests/FindGSL/rng/CMakeLists.txt b/Tests/FindGSL/rng/CMakeLists.txt
index b15d6acab0..497b6c3855 100644
--- a/Tests/FindGSL/rng/CMakeLists.txt
+++ b/Tests/FindGSL/rng/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(FindGSL_rng CXX)
include(CTest)
diff --git a/Tests/FindGTK2/atk/CMakeLists.txt b/Tests/FindGTK2/atk/CMakeLists.txt
index 0392d88c48..b80c6fca92 100644
--- a/Tests/FindGTK2/atk/CMakeLists.txt
+++ b/Tests/FindGTK2/atk/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(atk C)
diff --git a/Tests/FindGTK2/atkmm/CMakeLists.txt b/Tests/FindGTK2/atkmm/CMakeLists.txt
index ec838deb95..cea87cdb32 100644
--- a/Tests/FindGTK2/atkmm/CMakeLists.txt
+++ b/Tests/FindGTK2/atkmm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(atkmm CXX)
diff --git a/Tests/FindGTK2/cairo/CMakeLists.txt b/Tests/FindGTK2/cairo/CMakeLists.txt
index 3652ad603b..42d371a241 100644
--- a/Tests/FindGTK2/cairo/CMakeLists.txt
+++ b/Tests/FindGTK2/cairo/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(cairo C)
diff --git a/Tests/FindGTK2/cairomm/CMakeLists.txt b/Tests/FindGTK2/cairomm/CMakeLists.txt
index cde0f42204..5d957eedee 100644
--- a/Tests/FindGTK2/cairomm/CMakeLists.txt
+++ b/Tests/FindGTK2/cairomm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(cairomm CXX)
diff --git a/Tests/FindGTK2/gdk/CMakeLists.txt b/Tests/FindGTK2/gdk/CMakeLists.txt
index 35ef337773..2b8f533b4e 100644
--- a/Tests/FindGTK2/gdk/CMakeLists.txt
+++ b/Tests/FindGTK2/gdk/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gdk C)
diff --git a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt
index ea1b05da66..3524f065db 100644
--- a/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt
+++ b/Tests/FindGTK2/gdk_pixbuf/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gdk_pixbuf C)
diff --git a/Tests/FindGTK2/gdkmm/CMakeLists.txt b/Tests/FindGTK2/gdkmm/CMakeLists.txt
index 72fc6f415f..be1ccebf90 100644
--- a/Tests/FindGTK2/gdkmm/CMakeLists.txt
+++ b/Tests/FindGTK2/gdkmm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gdkmm CXX)
diff --git a/Tests/FindGTK2/gio/CMakeLists.txt b/Tests/FindGTK2/gio/CMakeLists.txt
index 4835afa543..b420f48d26 100644
--- a/Tests/FindGTK2/gio/CMakeLists.txt
+++ b/Tests/FindGTK2/gio/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gio C)
diff --git a/Tests/FindGTK2/giomm/CMakeLists.txt b/Tests/FindGTK2/giomm/CMakeLists.txt
index b639979b5c..bae34ffa6e 100644
--- a/Tests/FindGTK2/giomm/CMakeLists.txt
+++ b/Tests/FindGTK2/giomm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(giomm CXX)
diff --git a/Tests/FindGTK2/glib/CMakeLists.txt b/Tests/FindGTK2/glib/CMakeLists.txt
index 536fc67d07..8efde3d00d 100644
--- a/Tests/FindGTK2/glib/CMakeLists.txt
+++ b/Tests/FindGTK2/glib/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(glib C)
diff --git a/Tests/FindGTK2/glibmm/CMakeLists.txt b/Tests/FindGTK2/glibmm/CMakeLists.txt
index 25d55184c5..f0785dc1a8 100644
--- a/Tests/FindGTK2/glibmm/CMakeLists.txt
+++ b/Tests/FindGTK2/glibmm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(glibmm CXX)
diff --git a/Tests/FindGTK2/gmodule/CMakeLists.txt b/Tests/FindGTK2/gmodule/CMakeLists.txt
index 2bfb81eeb7..9c686a679f 100644
--- a/Tests/FindGTK2/gmodule/CMakeLists.txt
+++ b/Tests/FindGTK2/gmodule/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gmodule C)
diff --git a/Tests/FindGTK2/gobject/CMakeLists.txt b/Tests/FindGTK2/gobject/CMakeLists.txt
index 11520f8a10..83d9546402 100644
--- a/Tests/FindGTK2/gobject/CMakeLists.txt
+++ b/Tests/FindGTK2/gobject/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gobject C)
diff --git a/Tests/FindGTK2/gthread/CMakeLists.txt b/Tests/FindGTK2/gthread/CMakeLists.txt
index 5ecfd9be6c..d97585c481 100644
--- a/Tests/FindGTK2/gthread/CMakeLists.txt
+++ b/Tests/FindGTK2/gthread/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gthread C)
diff --git a/Tests/FindGTK2/gtk/CMakeLists.txt b/Tests/FindGTK2/gtk/CMakeLists.txt
index 2c67619981..2b5a56dd42 100644
--- a/Tests/FindGTK2/gtk/CMakeLists.txt
+++ b/Tests/FindGTK2/gtk/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gtk C)
diff --git a/Tests/FindGTK2/gtkmm/CMakeLists.txt b/Tests/FindGTK2/gtkmm/CMakeLists.txt
index 3375a5583d..179f5dbf83 100644
--- a/Tests/FindGTK2/gtkmm/CMakeLists.txt
+++ b/Tests/FindGTK2/gtkmm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(gtkmm CXX)
diff --git a/Tests/FindGTK2/pango/CMakeLists.txt b/Tests/FindGTK2/pango/CMakeLists.txt
index bd6b13a6e9..e9d6b9d9c2 100644
--- a/Tests/FindGTK2/pango/CMakeLists.txt
+++ b/Tests/FindGTK2/pango/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(pango C)
diff --git a/Tests/FindGTK2/pangocairo/CMakeLists.txt b/Tests/FindGTK2/pangocairo/CMakeLists.txt
index 157b9c2fec..bbb1f279ff 100644
--- a/Tests/FindGTK2/pangocairo/CMakeLists.txt
+++ b/Tests/FindGTK2/pangocairo/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(pangocairo C)
diff --git a/Tests/FindGTK2/pangoft2/CMakeLists.txt b/Tests/FindGTK2/pangoft2/CMakeLists.txt
index 76966e7a97..801629cd20 100644
--- a/Tests/FindGTK2/pangoft2/CMakeLists.txt
+++ b/Tests/FindGTK2/pangoft2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(pangoft2 C)
diff --git a/Tests/FindGTK2/pangomm/CMakeLists.txt b/Tests/FindGTK2/pangomm/CMakeLists.txt
index 0bb49e2d06..853a1ddd23 100644
--- a/Tests/FindGTK2/pangomm/CMakeLists.txt
+++ b/Tests/FindGTK2/pangomm/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(pangomm CXX)
diff --git a/Tests/FindGTK2/pangoxft/CMakeLists.txt b/Tests/FindGTK2/pangoxft/CMakeLists.txt
index 7051d35133..f267d6c428 100644
--- a/Tests/FindGTK2/pangoxft/CMakeLists.txt
+++ b/Tests/FindGTK2/pangoxft/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(pangoxft C)
diff --git a/Tests/FindGTK2/sigc++/CMakeLists.txt b/Tests/FindGTK2/sigc++/CMakeLists.txt
index 9c1fff783e..f09ea66f1d 100644
--- a/Tests/FindGTK2/sigc++/CMakeLists.txt
+++ b/Tests/FindGTK2/sigc++/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(sigc++ CXX)
diff --git a/Tests/FindGTest/Test/CMakeLists.txt b/Tests/FindGTest/Test/CMakeLists.txt
index 9c1eb5e92b..582cbaa19e 100644
--- a/Tests/FindGTest/Test/CMakeLists.txt
+++ b/Tests/FindGTest/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindGTest CXX)
include(CTest)
diff --git a/Tests/FindGnuTLS/Test/CMakeLists.txt b/Tests/FindGnuTLS/Test/CMakeLists.txt
index c5a9819b2f..e17987daa0 100644
--- a/Tests/FindGnuTLS/Test/CMakeLists.txt
+++ b/Tests/FindGnuTLS/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindGnuTLS C)
include(CTest)
diff --git a/Tests/FindHDF5/CMakeLists.txt b/Tests/FindHDF5/CMakeLists.txt
new file mode 100644
index 0000000000..6bc551ac4d
--- /dev/null
+++ b/Tests/FindHDF5/CMakeLists.txt
@@ -0,0 +1,87 @@
+# These tests may be configured by cache entries:
+#
+# CMake_TEST_FindHDF5:BOOL=ON
+# Enable this directory.
+#
+# CMake_TEST_FindHDF5_<variant>_<lang>_COMPILER:FILEPATH=...
+# Enable testing for a variant/language combination with the given wrapper.
+# Variants: Serial, OpenMPI, MPICH
+# Languages: C, CXX, Fortran
+#
+# CMake_TEST_FindHDF5_<variant>_<lang>_COMPILER_EXPLICIT:BOOL=ON
+# Pass the above wrapper path to the test as HDF5_<lang>_COMPILER_EXECUTABLE.
+
+set(test_langs C CXX)
+if(CMAKE_Fortran_COMPILER)
+ list(APPEND test_langs Fortran)
+endif()
+
+# Test detection without any special hints.
+add_test(NAME FindHDF5.Default COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindHDF5/Test"
+ "${CMake_BINARY_DIR}/Tests/FindHDF5/Test-Default"
+ ${build_generator_args}
+ --build-project TestFindHDF5
+ --build-options ${build_options} -DTEST_SERIAL=1 "-DTEST_LANGS=${test_langs}"
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
+
+foreach(variant Serial OpenMPI MPICH)
+ set(wrapper "")
+ set(wrapper_langs "")
+ set(wrapper_as_compiler "")
+ foreach(lang IN LISTS test_langs)
+ if(CMake_TEST_FindHDF5_${variant}_${lang}_COMPILER)
+ list(APPEND wrapper_langs ${lang})
+ list(APPEND wrapper_as_compiler -DCMAKE_${lang}_COMPILER=${CMake_TEST_FindHDF5_${variant}_${lang}_COMPILER})
+ if(CMake_TEST_FindHDF5_${variant}_${lang}_COMPILER_EXPLICIT)
+ list(APPEND wrapper -DHDF5_${lang}_COMPILER_EXECUTABLE=${CMake_TEST_FindHDF5_${variant}_${lang}_COMPILER})
+ endif()
+ endif()
+ endforeach()
+
+ if(NOT wrapper_langs)
+ continue()
+ endif()
+
+ if(variant STREQUAL "Serial")
+ set(test_kind -DTEST_SERIAL=1)
+ else()
+ set(test_kind -DTEST_PARALLEL=1)
+ endif()
+
+ # Test detection using the HDF5 compiler wrappers as a reference.
+ add_test(NAME FindHDF5.${variant} COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindHDF5/Test"
+ "${CMake_BINARY_DIR}/Tests/FindHDF5/Test-${variant}"
+ ${build_generator_args}
+ --build-project TestFindHDF5
+ --build-options ${build_options} ${test_kind} "-DTEST_LANGS=${wrapper_langs}" ${wrapper}
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
+ if(CMake_TEST_FindHDF5_${variant}_ENVMOD)
+ set_property(TEST FindHDF5.${variant} PROPERTY ENVIRONMENT_MODIFICATION ${CMake_TEST_FindHDF5_${variant}_ENVMOD})
+ endif()
+
+ # Test detection using the HDF5 compiler wrappers as the compiler.
+ # Skip this if there are spaces in the path. The HDF5 wrappers do not like them.
+ if(NOT CMAKE_CURRENT_BINARY_DIR MATCHES " ")
+ add_test(NAME FindHDF5.${variant}-Wrapper COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindHDF5/Test"
+ "${CMake_BINARY_DIR}/Tests/FindHDF5/Test-${variant}-Wrapper"
+ ${build_generator_args}
+ --build-project TestFindHDF5
+ --build-options ${build_options} ${test_kind} "-DTEST_LANGS=${wrapper_langs}" -D TEST_WRAPPER_AS_COMPILER=1 ${wrapper_as_compiler}
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
+ if(CMake_TEST_FindHDF5_${variant}_ENVMOD)
+ set_property(TEST FindHDF5.${variant}-Wrapper PROPERTY ENVIRONMENT_MODIFICATION ${CMake_TEST_FindHDF5_${variant}_ENVMOD})
+ endif()
+ endif()
+endforeach()
diff --git a/Tests/FindHDF5/Test/CMakeLists.txt b/Tests/FindHDF5/Test/CMakeLists.txt
new file mode 100644
index 0000000000..da1b3690e3
--- /dev/null
+++ b/Tests/FindHDF5/Test/CMakeLists.txt
@@ -0,0 +1,106 @@
+cmake_minimum_required(VERSION 3.26)
+project(TestFindHDF5 LANGUAGES ${TEST_LANGS})
+include(CTest)
+message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
+
+if(TEST_PARALLEL)
+ set(HDF5_PREFER_PARALLEL 1)
+endif()
+
+find_package(HDF5 REQUIRED COMPONENTS ${TEST_LANGS} HL)
+
+set(variables HDF5_FOUND HDF5_VERSION)
+if(NOT TEST_WRAPPER_AS_COMPILER)
+ list(APPEND variables HDF5_INCLUDE_DIRS HDF5_LIBRARIES HDF5_HL_LIBRARIES)
+ foreach(lang ${TEST_LANGS})
+ list(APPEND variables
+ HDF5_${lang}_COMPILER_EXECUTABLE
+ HDF5_${lang}_INCLUDE_DIRS
+ HDF5_${lang}_LIBRARIES
+ HDF5_${lang}_HL_LIBRARIES
+ )
+ endforeach()
+ endif()
+foreach(var IN LISTS variables)
+ message(STATUS "${var}='${${var}}'")
+endforeach()
+foreach(var IN LISTS variables)
+ if(NOT DEFINED ${var})
+ message(SEND_ERROR "Variable '${var}' is not defined!")
+ endif()
+endforeach()
+
+set(targets HDF5::HDF5)
+if(CMAKE_C_COMPILER_LOADED)
+ list(APPEND targets hdf5::hdf5 hdf5::hdf5_hl)
+endif()
+if(CMAKE_CXX_COMPILER_LOADED)
+ list(APPEND targets hdf5::hdf5_cpp hdf5::hdf5_hl_cpp)
+endif()
+if(CMAKE_Fortran_COMPILER_LOADED)
+ list(APPEND targets hdf5::hdf5_fortran hdf5::hdf5_hl_fortran)
+endif()
+foreach(target IN LISTS targets)
+ if(NOT TARGET ${target})
+ message(SEND_ERROR "Target '${target}' not defined!")
+ endif()
+endforeach()
+
+message(STATUS "HDF5_IS_PARALLEL='${HDF5_IS_PARALLEL}'")
+if(TEST_SERIAL)
+ if(HDF5_IS_PARALLEL)
+ message(SEND_ERROR "HDF5_IS_PARALLEL is true in serial test.")
+ endif()
+endif()
+if(TEST_PARALLEL)
+ if(NOT HDF5_IS_PARALLEL)
+ message(SEND_ERROR "HDF5_IS_PARALLEL is false in parallel test.")
+ endif()
+endif()
+
+if(TEST_PARALLEL)
+ set(MPI_CXX_SKIP_MPICXX TRUE)
+ set(MPI_CXX_VALIDATE_SKIP_MPICXX TRUE)
+ find_package(MPI REQUIRED)
+ set(mpi_C_tgt MPI::MPI_C)
+ set(mpi_C_inc ${MPI_C_INCLUDE_PATH})
+ set(mpi_C_lib ${MPI_C_LINK_FLAGS} ${MPI_C_LIBRARIES})
+ set(mpi_Fortran_tgt MPI::MPI_Fortran)
+ set(mpi_Fortran_inc ${MPI_Fortran_INCLUDE_PATH})
+ set(mpi_Fortran_lib ${MPI_Fortran_LIBRARIES})
+endif()
+
+if(CMAKE_C_COMPILER_LOADED)
+ add_executable(test_C_tgt test_C.c)
+ target_link_libraries(test_C_tgt PRIVATE hdf5::hdf5 ${mpi_C_tgt})
+ add_test(NAME test_C_tgt COMMAND test_C_tgt test_C_tgt.h5)
+
+ add_executable(test_C_var test_C.c)
+ target_include_directories(test_C_var PRIVATE ${HDF5_C_INCLUDE_DIRS} ${mpi_C_inc})
+ target_link_libraries(test_C_var PRIVATE ${HDF5_C_LIBRARIES} ${mpi_C_lib})
+ add_test(NAME test_C_var COMMAND test_C_var test_C_var.h5)
+endif()
+
+if(CMAKE_CXX_COMPILER_LOADED)
+ add_executable(test_CXX_tgt test_CXX.cxx)
+ target_link_libraries(test_CXX_tgt PRIVATE hdf5::hdf5_cpp
+ hdf5::hdf5 # FIXME: hdf5::hdf5_cpp should link hdf5::hdf5 automatically.
+ ${mpi_C_tgt})
+ add_test(NAME test_CXX_tgt COMMAND test_CXX_tgt test_CXX_tgt.h5)
+
+ add_executable(test_CXX_var test_CXX.cxx)
+ target_include_directories(test_CXX_var PRIVATE ${HDF5_CXX_INCLUDE_DIRS} ${mpi_C_inc})
+ target_link_libraries(test_CXX_var PRIVATE ${HDF5_CXX_LIBRARIES} ${mpi_C_lib})
+ add_test(NAME test_CXX_var COMMAND test_CXX_var test_CXX_var.h5)
+endif()
+
+if(CMAKE_Fortran_COMPILER_LOADED)
+ add_executable(test_Fortran_tgt test_Fortran.f90)
+ target_link_libraries(test_Fortran_tgt PRIVATE hdf5::hdf5_fortran ${mpi_Fortran_tgt})
+ add_test(NAME test_Fortran_tgt COMMAND test_Fortran_tgt)
+
+ add_executable(test_Fortran_var test_Fortran.f90)
+ target_include_directories(test_Fortran_var PRIVATE ${HDF5_Fortran_INCLUDE_DIRS} ${mpi_Fortran_inc})
+ target_link_libraries(test_Fortran_var PRIVATE ${HDF5_Fortran_LIBRARIES} ${mpi_Fortran_lib})
+ add_test(NAME test_Fortran_var COMMAND test_Fortran_var)
+endif()
diff --git a/Tests/FindHDF5/Test/test_C.c b/Tests/FindHDF5/Test/test_C.c
new file mode 100644
index 0000000000..4c18364528
--- /dev/null
+++ b/Tests/FindHDF5/Test/test_C.c
@@ -0,0 +1,12 @@
+#include <hdf5.h>
+
+int main(int argc, const char* argv[])
+{
+ hid_t fid;
+ if (argc != 2) {
+ return 1;
+ }
+ fid = H5Fcreate(argv[1], H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+ H5Fclose(fid);
+ return 0;
+}
diff --git a/Tests/FindHDF5/Test/test_CXX.cxx b/Tests/FindHDF5/Test/test_CXX.cxx
new file mode 100644
index 0000000000..93fb462b65
--- /dev/null
+++ b/Tests/FindHDF5/Test/test_CXX.cxx
@@ -0,0 +1,14 @@
+#include <H5Cpp.h>
+
+#ifndef H5_NO_NAMESPACE
+using namespace H5;
+#endif
+
+int main(int argc, const char* argv[])
+{
+ if (argc != 2) {
+ return 1;
+ }
+ H5File f(argv[1], H5F_ACC_TRUNC);
+ return 0;
+}
diff --git a/Tests/FindHDF5/Test/test_Fortran.f90 b/Tests/FindHDF5/Test/test_Fortran.f90
new file mode 100644
index 0000000000..4c12c8ce02
--- /dev/null
+++ b/Tests/FindHDF5/Test/test_Fortran.f90
@@ -0,0 +1,6 @@
+program hdf5_hello
+ use hdf5
+ integer error
+ call h5open_f(error)
+ call h5close_f(error)
+end
diff --git a/Tests/FindICU/Test/CMakeLists.txt b/Tests/FindICU/Test/CMakeLists.txt
index 1247ac778f..066d1733d5 100644
--- a/Tests/FindICU/Test/CMakeLists.txt
+++ b/Tests/FindICU/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindICU LANGUAGES CXX)
include(CTest)
diff --git a/Tests/FindImageMagick/Test/CMakeLists.txt b/Tests/FindImageMagick/Test/CMakeLists.txt
index 618226004a..4e4f14dbbd 100644
--- a/Tests/FindImageMagick/Test/CMakeLists.txt
+++ b/Tests/FindImageMagick/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(FindImageMagick C CXX)
include(CTest)
diff --git a/Tests/FindJPEG/Test/CMakeLists.txt b/Tests/FindJPEG/Test/CMakeLists.txt
index 912c7a19b1..390b3a7471 100644
--- a/Tests/FindJPEG/Test/CMakeLists.txt
+++ b/Tests/FindJPEG/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindJPEG C)
include(CTest)
diff --git a/Tests/FindJsonCpp/Test/CMakeLists.txt b/Tests/FindJsonCpp/Test/CMakeLists.txt
index d1dc647b39..5f100ea5c5 100644
--- a/Tests/FindJsonCpp/Test/CMakeLists.txt
+++ b/Tests/FindJsonCpp/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindJsonCpp CXX)
include(CTest)
diff --git a/Tests/FindLAPACK/Test/CMakeLists.txt b/Tests/FindLAPACK/Test/CMakeLists.txt
index f5d5a7391d..ce9af9851b 100644
--- a/Tests/FindLAPACK/Test/CMakeLists.txt
+++ b/Tests/FindLAPACK/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindLAPACK C)
include(CTest)
diff --git a/Tests/FindLibLZMA/Test/CMakeLists.txt b/Tests/FindLibLZMA/Test/CMakeLists.txt
index c59dcdbdd6..438938e542 100644
--- a/Tests/FindLibLZMA/Test/CMakeLists.txt
+++ b/Tests/FindLibLZMA/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindLZMA C)
include(CTest)
diff --git a/Tests/FindLibXml2/Test/CMakeLists.txt b/Tests/FindLibXml2/Test/CMakeLists.txt
index 041cc132e7..9b866ab082 100644
--- a/Tests/FindLibXml2/Test/CMakeLists.txt
+++ b/Tests/FindLibXml2/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindLibXml2 C)
include(CTest)
diff --git a/Tests/FindLibXslt/Test/CMakeLists.txt b/Tests/FindLibXslt/Test/CMakeLists.txt
index e93266138d..32a157f660 100644
--- a/Tests/FindLibXslt/Test/CMakeLists.txt
+++ b/Tests/FindLibXslt/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindLibXslt C)
include(CTest)
diff --git a/Tests/FindMPI/CMakeLists.txt b/Tests/FindMPI/CMakeLists.txt
index 121d978a80..1bc12c8154 100644
--- a/Tests/FindMPI/CMakeLists.txt
+++ b/Tests/FindMPI/CMakeLists.txt
@@ -19,3 +19,6 @@ add_test(NAME FindMPI.Test COMMAND
-DMPI_TEST_Fortran=${CMake_TEST_FindMPI_FLAG_Fortran}
--test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
)
+if(CMake_TEST_FindMPI_ENVMOD)
+ set_property(TEST FindMPI.Test PROPERTY ENVIRONMENT_MODIFICATION ${CMake_TEST_FindMPI_ENVMOD})
+endif()
diff --git a/Tests/FindMatlab/basic_checks/CMakeLists.txt b/Tests/FindMatlab/basic_checks/CMakeLists.txt
index c0c752a0d1..e9b696c760 100644
--- a/Tests/FindMatlab/basic_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/basic_checks/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(basic_checks)
diff --git a/Tests/FindMatlab/components_checks/CMakeLists.txt b/Tests/FindMatlab/components_checks/CMakeLists.txt
index f5d4880609..efb99aefa9 100644
--- a/Tests/FindMatlab/components_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/components_checks/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(component_checks)
diff --git a/Tests/FindMatlab/failure_reports/CMakeLists.txt b/Tests/FindMatlab/failure_reports/CMakeLists.txt
index 4b092cd28f..45e48d7d24 100644
--- a/Tests/FindMatlab/failure_reports/CMakeLists.txt
+++ b/Tests/FindMatlab/failure_reports/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(failure_reports)
diff --git a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt
index bceeba1767..58db0ecea0 100644
--- a/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/no_implicit_link_checks/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(no_implicit_links_checks)
diff --git a/Tests/FindMatlab/r2018a_check/CMakeLists.txt b/Tests/FindMatlab/r2018a_check/CMakeLists.txt
index c732be1512..8b21888715 100644
--- a/Tests/FindMatlab/r2018a_check/CMakeLists.txt
+++ b/Tests/FindMatlab/r2018a_check/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(r2018a_checks)
diff --git a/Tests/FindMatlab/targets_checks/CMakeLists.txt b/Tests/FindMatlab/targets_checks/CMakeLists.txt
index 4af7cc3606..c709bbd90d 100644
--- a/Tests/FindMatlab/targets_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/targets_checks/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(targets_checks)
diff --git a/Tests/FindMatlab/versions_checks/CMakeLists.txt b/Tests/FindMatlab/versions_checks/CMakeLists.txt
index d015730b0b..f203f4b26e 100644
--- a/Tests/FindMatlab/versions_checks/CMakeLists.txt
+++ b/Tests/FindMatlab/versions_checks/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
enable_testing()
project(versions_checks)
diff --git a/Tests/FindODBC/Test/CMakeLists.txt b/Tests/FindODBC/Test/CMakeLists.txt
index a20c0f7071..1e3a2390d1 100644
--- a/Tests/FindODBC/Test/CMakeLists.txt
+++ b/Tests/FindODBC/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindODBC C)
include(CTest)
diff --git a/Tests/FindOpenAL/Test/CMakeLists.txt b/Tests/FindOpenAL/Test/CMakeLists.txt
index fa3e2638ee..6479df6420 100644
--- a/Tests/FindOpenAL/Test/CMakeLists.txt
+++ b/Tests/FindOpenAL/Test/CMakeLists.txt
@@ -12,3 +12,9 @@ add_executable(test_var main.cxx)
target_include_directories(test_var PRIVATE ${OPENAL_INCLUDE_DIR})
target_link_libraries(test_var PRIVATE ${OPENAL_LIBRARY})
add_test(NAME test_var COMMAND test_var)
+
+# OpenAL has been deprecated on macOS since Catalina (10.15)
+if(APPLE)
+ target_compile_options(test_tgt PRIVATE "-Wno-deprecated-declarations")
+ target_compile_options(test_var PRIVATE "-Wno-deprecated-declarations")
+endif()
diff --git a/Tests/FindOpenAL/Test/main.cxx b/Tests/FindOpenAL/Test/main.cxx
index bb45faf5b9..1396c60b32 100644
--- a/Tests/FindOpenAL/Test/main.cxx
+++ b/Tests/FindOpenAL/Test/main.cxx
@@ -1,5 +1,10 @@
-#include <AL/al.h>
-#include <AL/alc.h>
+#ifdef __APPLE__
+# include "OpenAL/al.h"
+# include "OpenAL/alc.h"
+#else
+# include <AL/al.h>
+# include <AL/alc.h>
+#endif
#include <stdio.h>
int main()
diff --git a/Tests/FindOpenCL/Test/CMakeLists.txt b/Tests/FindOpenCL/Test/CMakeLists.txt
index f8a65877b0..4a1f6bde60 100644
--- a/Tests/FindOpenCL/Test/CMakeLists.txt
+++ b/Tests/FindOpenCL/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindOpenCL C)
include(CTest)
diff --git a/Tests/FindOpenGL/Test/CMakeLists.txt b/Tests/FindOpenGL/Test/CMakeLists.txt
index 9004a9860b..7c805c0c5f 100644
--- a/Tests/FindOpenGL/Test/CMakeLists.txt
+++ b/Tests/FindOpenGL/Test/CMakeLists.txt
@@ -44,28 +44,115 @@ else()
add_test(NAME test_comp_glx_novnd COMMAND test_comp_glx_novnd)
endif()
-# EGL is only available on Linux+GLVND at present.
-if(OpenGL_TEST_VND)
- find_package(OpenGL COMPONENTS OpenGL EGL)
- if(OpenGL_EGL_FOUND)
- add_executable(test_comp_egl main.c)
- target_link_libraries(test_comp_egl PRIVATE OpenGL::OpenGL OpenGL::EGL)
- add_test(NAME test_comp_egl COMMAND test_comp_egl)
- # EGL-only code should not link to GLX.
- execute_process(COMMAND ldd test_comp_egl
- OUTPUT_VARIABLE LDD_OUT
- ERROR_VARIABLE LDD_ERR)
- if("${LDD_OUT}" MATCHES "GLX")
- message(FATAL_ERROR "EGL-only code links to GLX!")
- endif()
+find_package(OpenGL COMPONENTS OpenGL EGL)
+if(OpenGL_EGL_FOUND)
+ add_executable(test_comp_egl main.c)
+ target_link_libraries(test_comp_egl PRIVATE OpenGL::OpenGL OpenGL::EGL)
+ add_test(NAME test_comp_egl COMMAND test_comp_egl)
+ # EGL-only code should not link to GLX.
+ get_target_property(iface_libs OpenGL::EGL INTERFACE_LINK_LIBRARIES)
+ if(iface_libs MATCHES "GLX")
+ message(FATAL_ERROR "EGL-only code links to GLX!")
+ endif()
+endif()
+
+# all three COMPONENTS together.
+find_package(OpenGL COMPONENTS OpenGL EGL GLX)
+if(OpenGL_EGL_FOUND AND OpenGL_GLX_FOUND)
+ add_executable(test_comp_both main.c)
+ target_link_libraries(test_comp_both PRIVATE OpenGL::OpenGL OpenGL::EGL
+ OpenGL::GLX)
+ add_test(NAME test_comp_both COMMAND test_comp_both)
+endif()
+
+find_package(OpenGL COMPONENTS GLES2)
+if(OpenGL_GLES2_FOUND)
+ add_executable(test_comp_gles2 main_gles2.c)
+ target_link_libraries(test_comp_gles2 PRIVATE OpenGL::GLES2)
+ add_test(NAME test_comp_gles2 COMMAND test_comp_gles2)
+ # GLES2-only code should not link to OpenGL
+ get_target_property(iface_libs test_comp_gles2 LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES2-only code links to OpenGL!")
endif()
+endif()
+
+# GLES2 and EGL together.
+find_package(OpenGL COMPONENTS GLES2 EGL)
+if(OpenGL_GLES2_FOUND AND OpenGL_EGL_FOUND)
+ add_executable(test_comp_gles2_egl main_gles2.c)
+ target_link_libraries(test_comp_gles2_egl PRIVATE OpenGL::GLES2
+ OpenGL::EGL)
+ add_test(NAME test_comp_gles2_egl COMMAND test_comp_gles2_egl)
+ # GLES2-EGL-only code should not link to OpenGL or GLX
+ get_target_property(iface_libs test_comp_gles2_egl LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES2-only code links to OpenGL!")
+ endif()
+ if(iface_libs MATCHES "GLX")
+ message(FATAL_ERROR "GLES2-EGL-only code links to GLX!")
+ endif()
+endif()
- # all three COMPONENTS together.
- find_package(OpenGL COMPONENTS OpenGL EGL GLX)
- if(OpenGL_EGL_FOUND AND OpenGL_GLX_FOUND)
- add_executable(test_comp_both main.c)
- target_link_libraries(test_comp_both PRIVATE OpenGL::OpenGL OpenGL::EGL
- OpenGL::GLX)
- add_test(NAME test_comp_both COMMAND test_comp_both)
+# GLES2 and GLX together.
+find_package(OpenGL COMPONENTS GLES2 GLX)
+if(OpenGL_GLES2_FOUND AND OpenGL_GLX_FOUND)
+ add_executable(test_comp_gles2_glx main_gles2.c)
+ target_link_libraries(test_comp_gles2_glx PRIVATE OpenGL::GLES2
+ OpenGL::GLX)
+ add_test(NAME test_comp_gles2_glx COMMAND test_comp_gles2_glx)
+ # GLES2-GLX-only code should not link to OpenGL or EGL
+ get_target_property(iface_libs test_comp_gles2_glx LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES2-only code links to OpenGL!")
+ endif()
+ if(iface_libs MATCHES "EGL")
+ message(FATAL_ERROR "GLES2-GLX-only code links to EGL!")
+ endif()
+endif()
+
+find_package(OpenGL COMPONENTS GLES3)
+if(OpenGL_GLES3_FOUND)
+ add_executable(test_comp_gles3 main_gles3.c)
+ target_link_libraries(test_comp_gles3 PRIVATE OpenGL::GLES3)
+ add_test(NAME test_comp_gles3 COMMAND test_comp_gles3)
+ # GLES3-only code should not link to OpenGL.
+ get_target_property(iface_libs test_comp_gles3 LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES3-only code links to OpenGL!")
+ endif()
+endif()
+
+# GLES3 and EGL together.
+find_package(OpenGL COMPONENTS GLES3 EGL)
+if(OpenGL_GLES3_FOUND AND OpenGL_EGL_FOUND)
+ add_executable(test_comp_gles3_egl main_gles3.c)
+ target_link_libraries(test_comp_gles3_egl PRIVATE OpenGL::GLES3
+ OpenGL::EGL)
+ add_test(NAME test_comp_gles3_egl COMMAND test_comp_gles3_egl)
+ # GLES3-EGL-only code should not link to OpenGL or GLX
+ get_target_property(iface_libs test_comp_gles3_egl LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES3-only code links to OpenGL!")
+ endif()
+ if(iface_libs MATCHES "GLX")
+ message(FATAL_ERROR "GLES3-EGL-only code links to GLX!")
+ endif()
+endif()
+
+# GLES3 and GLX together.
+find_package(OpenGL COMPONENTS GLES3 GLX)
+if(OpenGL_GLES3_FOUND AND OpenGL_GLX_FOUND)
+ add_executable(test_comp_gles3_glx main_gles3.c)
+ target_link_libraries(test_comp_gles3_glx PRIVATE OpenGL::GLES3
+ OpenGL::GLX)
+ add_test(NAME test_comp_gles3_glx COMMAND test_comp_gles3_glx)
+ # GLESr-GLX-only code should not link to OpenGL or EGL
+ get_target_property(iface_libs test_comp_gles3_glx LINK_LIBRARIES)
+ if(iface_libs MATCHES "OpenGL::OpenGL")
+ message(FATAL_ERROR "GLES3-only code links to OpenGL!")
+ endif()
+ if(iface_libs MATCHES "EGL")
+ message(FATAL_ERROR "GLES3-GLX-only code links to EGL!")
endif()
endif()
diff --git a/Tests/FindOpenGL/Test/main_gles2.c b/Tests/FindOpenGL/Test/main_gles2.c
new file mode 100644
index 0000000000..52f5936089
--- /dev/null
+++ b/Tests/FindOpenGL/Test/main_gles2.c
@@ -0,0 +1,17 @@
+#ifdef _WIN32
+# error "GLES2 cannot be tested on WIN32 platforms."
+#endif
+#ifdef __APPLE__
+# error "GLES2 cannot be tested on macOS platform."
+#else
+# include <GLES2/gl2.h>
+#endif
+
+#include <stdio.h>
+
+int main()
+{
+ /* Reference a GL symbol without requiring a context at runtime. */
+ printf("&glGetString = %p\n", &glGetString);
+ return 0;
+}
diff --git a/Tests/FindOpenGL/Test/main_gles3.c b/Tests/FindOpenGL/Test/main_gles3.c
new file mode 100644
index 0000000000..875f73c992
--- /dev/null
+++ b/Tests/FindOpenGL/Test/main_gles3.c
@@ -0,0 +1,17 @@
+#ifdef _WIN32
+# error "GLES3 cannot be tested on WIN32 platforms."
+#endif
+#ifdef __APPLE__
+# error "GLES3 cannot be tested on macOS platform."
+#else
+# include <GLES3/gl3.h>
+#endif
+
+#include <stdio.h>
+
+int main()
+{
+ /* Reference a GL symbol without requiring a context at runtime. */
+ printf("&glGetString = %p\n", &glGetString);
+ return 0;
+}
diff --git a/Tests/FindOpenSP/Test/CMakeLists.txt b/Tests/FindOpenSP/Test/CMakeLists.txt
index d8992d9af5..266a459eb3 100644
--- a/Tests/FindOpenSP/Test/CMakeLists.txt
+++ b/Tests/FindOpenSP/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindOpenSP CXX)
include(CTest)
diff --git a/Tests/FindOpenSSL/rand/CMakeLists.txt b/Tests/FindOpenSSL/rand/CMakeLists.txt
index 520d5d2032..c4432219ff 100644
--- a/Tests/FindOpenSSL/rand/CMakeLists.txt
+++ b/Tests/FindOpenSSL/rand/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(FindOpenSSL_rand CXX)
include(CTest)
diff --git a/Tests/FindPNG/Test/CMakeLists.txt b/Tests/FindPNG/Test/CMakeLists.txt
index ad50011c75..66cdda19f2 100644
--- a/Tests/FindPNG/Test/CMakeLists.txt
+++ b/Tests/FindPNG/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindPNG C)
include(CTest)
diff --git a/Tests/FindPatch/CMakeLists.txt b/Tests/FindPatch/CMakeLists.txt
index 541f5bd150..65b778b952 100644
--- a/Tests/FindPatch/CMakeLists.txt
+++ b/Tests/FindPatch/CMakeLists.txt
@@ -4,5 +4,6 @@ add_test(NAME FindPatch.Test COMMAND
"${CMake_SOURCE_DIR}/Tests/FindPatch/Test"
"${CMake_BINARY_DIR}/Tests/FindPatch/Test"
${build_generator_args}
+ --build-project TestFindPatch
--build-options ${build_options}
)
diff --git a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
index 524be9205f..99823a60d6 100644
--- a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
+++ b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestArtifactsInteractive LANGUAGES C)
diff --git a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
index a0d8eb2f44..283aeec9b8 100644
--- a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
+++ b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestCustomFailureMessage LANGUAGES NONE)
diff --git a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
index 2164ac1ab9..0fb30368b0 100644
--- a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
+++ b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestCustomFailureMessage.Check LANGUAGES NONE)
diff --git a/Tests/FindPython/DifferentComponents/CMakeLists.txt b/Tests/FindPython/DifferentComponents/CMakeLists.txt
index 7476632a4e..e3e7b367ee 100644
--- a/Tests/FindPython/DifferentComponents/CMakeLists.txt
+++ b/Tests/FindPython/DifferentComponents/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestDifferentComponents LANGUAGES C)
diff --git a/Tests/FindPython/ExactVersion/CMakeLists.txt b/Tests/FindPython/ExactVersion/CMakeLists.txt
index 60abb26208..1bd94c47e0 100644
--- a/Tests/FindPython/ExactVersion/CMakeLists.txt
+++ b/Tests/FindPython/ExactVersion/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestExactVersion LANGUAGES C)
diff --git a/Tests/FindPython/Implementation/CMakeLists.txt b/Tests/FindPython/Implementation/CMakeLists.txt
index 592329b8cd..8086c16b66 100644
--- a/Tests/FindPython/Implementation/CMakeLists.txt
+++ b/Tests/FindPython/Implementation/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestImplementation${Python_REQUESTED_IMPLEMENTATION} LANGUAGES NONE)
diff --git a/Tests/FindPython/IronPython/CMakeLists.txt b/Tests/FindPython/IronPython/CMakeLists.txt
index 47ca022a9f..fd3182e1d2 100644
--- a/Tests/FindPython/IronPython/CMakeLists.txt
+++ b/Tests/FindPython/IronPython/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestIronPython LANGUAGES NONE)
diff --git a/Tests/FindPython/IronPython2/CMakeLists.txt b/Tests/FindPython/IronPython2/CMakeLists.txt
index fd9d947970..b623972753 100644
--- a/Tests/FindPython/IronPython2/CMakeLists.txt
+++ b/Tests/FindPython/IronPython2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestIronPython2 LANGUAGES NONE)
diff --git a/Tests/FindPython/MultiplePackages/CMakeLists.txt b/Tests/FindPython/MultiplePackages/CMakeLists.txt
index 5c85155bb5..48450352f0 100644
--- a/Tests/FindPython/MultiplePackages/CMakeLists.txt
+++ b/Tests/FindPython/MultiplePackages/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestMultiplePackages C)
diff --git a/Tests/FindPython/NumPy/CMakeLists.txt b/Tests/FindPython/NumPy/CMakeLists.txt
index 3e17f68952..9920336b77 100644
--- a/Tests/FindPython/NumPy/CMakeLists.txt
+++ b/Tests/FindPython/NumPy/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestNumPy LANGUAGES C)
diff --git a/Tests/FindPython/NumPyOnly/CMakeLists.txt b/Tests/FindPython/NumPyOnly/CMakeLists.txt
index bedc627e0c..9aa1bcf9e1 100644
--- a/Tests/FindPython/NumPyOnly/CMakeLists.txt
+++ b/Tests/FindPython/NumPyOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestNumPyOnly LANGUAGES C)
diff --git a/Tests/FindPython/PyPy/CMakeLists.txt b/Tests/FindPython/PyPy/CMakeLists.txt
index 4cf7c0ab75..dfc22d8aef 100644
--- a/Tests/FindPython/PyPy/CMakeLists.txt
+++ b/Tests/FindPython/PyPy/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPyPy LANGUAGES C)
diff --git a/Tests/FindPython/PyPy2/CMakeLists.txt b/Tests/FindPython/PyPy2/CMakeLists.txt
index ebfc9abd0c..5b7ce307b7 100644
--- a/Tests/FindPython/PyPy2/CMakeLists.txt
+++ b/Tests/FindPython/PyPy2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPyPy2 LANGUAGES C)
diff --git a/Tests/FindPython/PyPy3/CMakeLists.txt b/Tests/FindPython/PyPy3/CMakeLists.txt
index bf7cd61aeb..b702c994ce 100644
--- a/Tests/FindPython/PyPy3/CMakeLists.txt
+++ b/Tests/FindPython/PyPy3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPyPy3 LANGUAGES C)
diff --git a/Tests/FindPython/Python/CMakeLists.txt b/Tests/FindPython/Python/CMakeLists.txt
index 9bec22f767..85b1711381 100644
--- a/Tests/FindPython/Python/CMakeLists.txt
+++ b/Tests/FindPython/Python/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython LANGUAGES C)
diff --git a/Tests/FindPython/Python2/CMakeLists.txt b/Tests/FindPython/Python2/CMakeLists.txt
index 39577b2a2b..95ed495bd4 100644
--- a/Tests/FindPython/Python2/CMakeLists.txt
+++ b/Tests/FindPython/Python2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython2 LANGUAGES C)
diff --git a/Tests/FindPython/Python2Embedded/CMakeLists.txt b/Tests/FindPython/Python2Embedded/CMakeLists.txt
index a1036ce741..d9b2d220d7 100644
--- a/Tests/FindPython/Python2Embedded/CMakeLists.txt
+++ b/Tests/FindPython/Python2Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython2Embedded LANGUAGES C)
diff --git a/Tests/FindPython/Python2Fail/CMakeLists.txt b/Tests/FindPython/Python2Fail/CMakeLists.txt
index 989688f552..7a6520dfa8 100644
--- a/Tests/FindPython/Python2Fail/CMakeLists.txt
+++ b/Tests/FindPython/Python2Fail/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython2Fail C)
diff --git a/Tests/FindPython/Python2Module/CMakeLists.txt b/Tests/FindPython/Python2Module/CMakeLists.txt
index c9d46ac62f..7334d7af53 100644
--- a/Tests/FindPython/Python2Module/CMakeLists.txt
+++ b/Tests/FindPython/Python2Module/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython2Module LANGUAGES C)
diff --git a/Tests/FindPython/Python2SABIModule/CMakeLists.txt b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
index 4f01197aec..ffbaa3383e 100644
--- a/Tests/FindPython/Python2SABIModule/CMakeLists.txt
+++ b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython2SABIModule LANGUAGES C)
diff --git a/Tests/FindPython/Python3/CMakeLists.txt b/Tests/FindPython/Python3/CMakeLists.txt
index e40557dfc5..42d31f2758 100644
--- a/Tests/FindPython/Python3/CMakeLists.txt
+++ b/Tests/FindPython/Python3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython3 LANGUAGES C)
diff --git a/Tests/FindPython/Python3Embedded/CMakeLists.txt b/Tests/FindPython/Python3Embedded/CMakeLists.txt
index c45bd8c832..1d362bea66 100644
--- a/Tests/FindPython/Python3Embedded/CMakeLists.txt
+++ b/Tests/FindPython/Python3Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython3Embedded LANGUAGES C)
diff --git a/Tests/FindPython/Python3Fail/CMakeLists.txt b/Tests/FindPython/Python3Fail/CMakeLists.txt
index cd46c88642..5eca0cb25e 100644
--- a/Tests/FindPython/Python3Fail/CMakeLists.txt
+++ b/Tests/FindPython/Python3Fail/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython3Fail C)
diff --git a/Tests/FindPython/Python3Module/CMakeLists.txt b/Tests/FindPython/Python3Module/CMakeLists.txt
index ccc1fdb2a4..57c0ddf63d 100644
--- a/Tests/FindPython/Python3Module/CMakeLists.txt
+++ b/Tests/FindPython/Python3Module/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestPython3Module LANGUAGES C)
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
index 42d282d9a5..cb9d4d380e 100644
--- a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestRequiredArtifacts LANGUAGES C)
diff --git a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
index bb4f67cfd2..4d9c7c8206 100644
--- a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestRequiredArtifacts.Check LANGUAGES C)
diff --git a/Tests/FindPython/SOABI/CMakeLists.txt b/Tests/FindPython/SOABI/CMakeLists.txt
index 84f7362433..60399d3677 100644
--- a/Tests/FindPython/SOABI/CMakeLists.txt
+++ b/Tests/FindPython/SOABI/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestSOABI LANGUAGES C)
diff --git a/Tests/FindPython/VirtualEnv/CMakeLists.txt b/Tests/FindPython/VirtualEnv/CMakeLists.txt
index dae3282694..e2e5bd2b2f 100644
--- a/Tests/FindPython/VirtualEnv/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnv/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestVirtualEnv LANGUAGES NONE)
diff --git a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
index 23d208d4cb..2f7c0db0c3 100644
--- a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestVirtualEnvConda LANGUAGES NONE)
diff --git a/Tests/FindSDL/Test/CMakeLists.txt b/Tests/FindSDL/Test/CMakeLists.txt
index 61d4f4b334..0dd1047035 100644
--- a/Tests/FindSDL/Test/CMakeLists.txt
+++ b/Tests/FindSDL/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindSDL C)
include(CTest)
diff --git a/Tests/FindSQLite3/Test/CMakeLists.txt b/Tests/FindSQLite3/Test/CMakeLists.txt
index bcc6ebd204..43cdd37fa8 100644
--- a/Tests/FindSQLite3/Test/CMakeLists.txt
+++ b/Tests/FindSQLite3/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TestFindSQLite3 C)
include(CTest)
diff --git a/Tests/FindTIFF/Test/CMakeLists.txt b/Tests/FindTIFF/Test/CMakeLists.txt
index e235db3697..54404d0a99 100644
--- a/Tests/FindTIFF/Test/CMakeLists.txt
+++ b/Tests/FindTIFF/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindTIFF)
include(CTest)
diff --git a/Tests/FindThreads/C-only/CMakeLists.txt b/Tests/FindThreads/C-only/CMakeLists.txt
index ee2a5f991a..9bc50a48de 100644
--- a/Tests/FindThreads/C-only/CMakeLists.txt
+++ b/Tests/FindThreads/C-only/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(FindThreads_C-only C)
find_package(Threads REQUIRED)
diff --git a/Tests/FindThreads/CXX-only/CMakeLists.txt b/Tests/FindThreads/CXX-only/CMakeLists.txt
index 3c6cc1e55b..11ae60ccbd 100644
--- a/Tests/FindThreads/CXX-only/CMakeLists.txt
+++ b/Tests/FindThreads/CXX-only/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(FindThreads_CXX-only CXX)
find_package(Threads REQUIRED)
diff --git a/Tests/FindVulkan/Test/CMakeLists.txt b/Tests/FindVulkan/Test/CMakeLists.txt
index dfcfc156a7..7198d2249b 100644
--- a/Tests/FindVulkan/Test/CMakeLists.txt
+++ b/Tests/FindVulkan/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
cmake_policy(SET CMP0091 NEW)
project(TestFindVulkan C CXX)
include(CTest)
diff --git a/Tests/FindX11/Test/CMakeLists.txt b/Tests/FindX11/Test/CMakeLists.txt
index 18a73a335b..e39ffb1eff 100644
--- a/Tests/FindX11/Test/CMakeLists.txt
+++ b/Tests/FindX11/Test/CMakeLists.txt
@@ -32,10 +32,13 @@ test_x11_component(x11_components Xau)
test_x11_component(x11_components Xaw)
test_x11_component(x11_components xcb)
test_x11_component(x11_components X11_xcb)
+test_x11_component(x11_components xcb_cursor)
test_x11_component(x11_components xcb_icccm)
test_x11_component(x11_components xcb_randr)
+test_x11_component(x11_components xcb_shape)
test_x11_component(x11_components xcb_util)
test_x11_component(x11_components xcb_xfixes)
+test_x11_component(x11_components xcb_xrm)
test_x11_component(x11_components xcb_xtest)
test_x11_component(x11_components xcb_keysyms)
test_x11_component(x11_components xcb_xkb)
@@ -76,10 +79,13 @@ foreach(lib
Xaw
xcb
X11_xcb
+ xcb_cursor
xcb_icccm
xcb_randr
+ xcb_shape
xcb_util
xcb_xfixes
+ xcb_xrm
Xcomposite
Xdamage
Xdmcp
diff --git a/Tests/FindX11/Test/main.c b/Tests/FindX11/Test/main.c
index 653a2be2e3..5240de0f68 100644
--- a/Tests/FindX11/Test/main.c
+++ b/Tests/FindX11/Test/main.c
@@ -336,6 +336,22 @@ static void test_xcb(void)
xcb_disconnect(connection);
}
+# ifdef HAVE_xcb_cursor
+# include <xcb/xcb_cursor.h>
+
+static void test_xcb_cursor(void)
+{
+ int screen_nbr;
+ xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr);
+ xcb_screen_t* screen = xcb_aux_get_screen(conn, screen_nbr);
+ xcb_cursor_context_t* ctx;
+ xcb_cursor_context_new(connection, screen, &ctx);
+ xcb_cursor_context_free(ctx);
+ xcb_disconnect(connection);
+}
+
+# endif
+
# ifdef HAVE_xcb_randr
# include <xcb/randr.h>
@@ -350,6 +366,20 @@ static void test_xcb_randr(void)
# endif
+# ifdef HAVE_xcb_shape
+# include <xcb/shape.h>
+
+static void test_xcb_shape(void)
+{
+ int screen_nbr;
+ xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr);
+ xcb_shape_query_version_cookie_t cookie =
+ xcb_shape_query_version(connection);
+ xcb_disconnect(connection);
+}
+
+# endif
+
# ifdef HAVE_xcb_util
# include <xcb/xcb_aux.h>
@@ -376,6 +406,20 @@ static void test_xcb_xfixes(void)
# endif
+# ifdef HAVE_xcb_xrm
+# include <xcb/xcb_xrm.h>
+
+static void test_xcb_xrm(void)
+{
+ int screen_nbr;
+ xcb_connection_t* connection = xcb_connect(NULL, &screen_nbr);
+ xcb_xrm_database_t* db = xcb_xrm_database_from_default(connection);
+ xcb_xrm_database_free(db);
+ xcb_disconnect(connection);
+}
+
+# endif
+
# ifdef HAVE_xcb_xtest
# include <xcb/xtest.h>
@@ -496,15 +540,24 @@ int main(int argc, char* argv[])
#ifdef HAVE_xcb
test_xcb,
#endif
-#ifdef HAVE_xcb_util
+#ifdef HAVE_xcb_cursor
+ test_xcb_cursor,
+#endif
+#ifdef HAVE_xcb_randr
test_xcb_randr,
#endif
+#ifdef HAVE_xcb_shape
+ test_xcb_shape,
+#endif
#ifdef HAVE_xcb_util
test_xcb_util,
#endif
#ifdef HAVE_xcb_xfixes
test_xcb_xfixes,
#endif
+#ifdef HAVE_xcb_xrm
+ test_xcb_xrm,
+#endif
NULL,
};
diff --git a/Tests/FindXalanC/Test/CMakeLists.txt b/Tests/FindXalanC/Test/CMakeLists.txt
index a8c2a0a8ad..eb45802aa8 100644
--- a/Tests/FindXalanC/Test/CMakeLists.txt
+++ b/Tests/FindXalanC/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindXalanC CXX)
include(CTest)
diff --git a/Tests/FindXercesC/Test/CMakeLists.txt b/Tests/FindXercesC/Test/CMakeLists.txt
index 267c6a9076..38ae6e4050 100644
--- a/Tests/FindXercesC/Test/CMakeLists.txt
+++ b/Tests/FindXercesC/Test/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(TestFindXercesC CXX)
include(CTest)
diff --git a/Tests/FindwxWidgets/CMakeLists.txt b/Tests/FindwxWidgets/CMakeLists.txt
new file mode 100644
index 0000000000..b66f838166
--- /dev/null
+++ b/Tests/FindwxWidgets/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_test(NAME FindwxWidgets.Test COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindwxWidgets/Test"
+ "${CMake_BINARY_DIR}/Tests/FindwxWidgets/Test"
+ ${build_generator_args}
+ --build-project TestFindwxWidgets
+ --build-options ${build_options}
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
diff --git a/Tests/FindwxWidgets/Test/CMakeLists.txt b/Tests/FindwxWidgets/Test/CMakeLists.txt
new file mode 100644
index 0000000000..ecc9c369cc
--- /dev/null
+++ b/Tests/FindwxWidgets/Test/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.26)
+project(TestFindwxWidgets CXX)
+include(CTest)
+
+find_package(wxWidgets REQUIRED)
+
+add_executable(test_tgt main.cpp)
+target_link_libraries(test_tgt wxWidgets::wxWidgets)
+add_test(NAME test_tgt COMMAND test_tgt)
+
+add_executable(test_var main.cpp)
+target_link_libraries(test_var PRIVATE ${wxWidgets_LIBRARIES})
+target_link_directories(test_var PRIVATE ${wxWidgets_LIBRARY_DIRS})
+target_include_directories(test_var PRIVATE ${wxWidgets_INCLUDE_DIRS})
+target_compile_options(test_var PRIVATE ${wxWidgets_CONFIG_OPTIONS})
+target_compile_definitions(test_var PRIVATE ${wxWidgets_DEFINITIONS})
+add_test(NAME test_var COMMAND test_var)
diff --git a/Tests/FindwxWidgets/Test/main.cpp b/Tests/FindwxWidgets/Test/main.cpp
new file mode 100644
index 0000000000..0e576cfede
--- /dev/null
+++ b/Tests/FindwxWidgets/Test/main.cpp
@@ -0,0 +1,7 @@
+#include <wx/filefn.h>
+
+int main()
+{
+ wxGetCwd();
+ return 0;
+}
diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt
index 0fede25675..30ab16b51b 100644
--- a/Tests/Fortran/CMakeLists.txt
+++ b/Tests/Fortran/CMakeLists.txt
@@ -49,7 +49,7 @@ function(test_fortran_c_interface_module)
FortranCInterface_VERIFY()
FortranCInterface_VERIFY(CXX)
if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
- if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu")
+ if(NOT CMAKE_Fortran_COMPILER_ID MATCHES "SunPro|PathScale|Absoft|Fujitsu|LCC")
set(module_expected 1)
endif()
if(FortranCInterface_MODULE_FOUND OR module_expected)
diff --git a/Tests/FortranC/CMakeLists.txt b/Tests/FortranC/CMakeLists.txt
index 1403aa0d59..f5e056b05f 100644
--- a/Tests/FortranC/CMakeLists.txt
+++ b/Tests/FortranC/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(FortranC C Fortran)
# Skip this test for compilers not known to be compatible.
diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt
index fc71a18ccf..ed2a440d46 100644
--- a/Tests/FortranOnly/CMakeLists.txt
+++ b/Tests/FortranOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(FortranOnly Fortran)
message("CTEST_FULL_OUTPUT ")
@@ -152,13 +152,16 @@ if(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON AND
set_property(SOURCE preprocess3.f PROPERTY Fortran_PREPROCESS ON)
endif()
-# Test that neither the compiler nor CMake performs unnecessary preprocessing.
-add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f)
-target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense)
-set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF)
-add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f)
-target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense)
-set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF)
+# LCC < 1.24 has no way to disable Fortran preprocessor
+if(NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LCC" OR CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "1.24.00")
+ # Test that neither the compiler nor CMake performs unnecessary preprocessing.
+ add_library(no_preprocess_target_lower STATIC no_preprocess_target_lower.f)
+ target_compile_options(no_preprocess_target_lower PRIVATE -DINTEGER=nonsense)
+ set_property(TARGET no_preprocess_target_lower PROPERTY Fortran_PREPROCESS OFF)
+ add_library(no_preprocess_source_lower STATIC no_preprocess_source_lower.f)
+ target_compile_options(no_preprocess_source_lower PRIVATE -DINTEGER=nonsense)
+ set_property(SOURCE no_preprocess_source_lower.f PROPERTY Fortran_PREPROCESS OFF)
+endif()
# Test that we can explicitly not preprocess a target or source.
# This will not work on certain compilers due to either missing a
diff --git a/Tests/Framework/CMakeLists.txt b/Tests/Framework/CMakeLists.txt
index 287be949d7..629deebe5e 100644
--- a/Tests/Framework/CMakeLists.txt
+++ b/Tests/Framework/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(Framework)
add_library(foo SHARED
diff --git a/Tests/FunctionTest/CMakeLists.txt b/Tests/FunctionTest/CMakeLists.txt
index 0660d0fa3b..a5a8b115c2 100644
--- a/Tests/FunctionTest/CMakeLists.txt
+++ b/Tests/FunctionTest/CMakeLists.txt
@@ -1,5 +1,5 @@
# a simple C only test case
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (FunctionTest)
function(FAILED testname)
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt
index 3fb53d1bc8..ef115e60a0 100644
--- a/Tests/GeneratorExpression/CMakeLists.txt
+++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -200,6 +200,21 @@ set_property(TARGET importedFallback PROPERTY IMPORTED_LOCATION fallback_loc)
set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_DEBUG "" DEBUG)
set_property(TARGET importedFallback PROPERTY MAP_IMPORTED_CONFIG_RELEASE "")
+add_library(importedFallback2 SHARED IMPORTED)
+set_property(TARGET importedFallback2 PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_NOCONFIG noconfig_loc)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_NOCONFIG noconfig_imp)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_DEBUG debug_loc)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_DEBUG debug_imp)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION_RELEASE release_loc)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_RELEASE release_imp)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_LOCATION fallback_loc)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB fallback_imp)
+set_property(TARGET importedFallback2 PROPERTY IMPORTED_IMPLIB_SPECIAL special_imp)
+set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_NOCONFIG SPECIAL "")
+set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_DEBUG SPECIAL "")
+set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_RELEASE SPECIAL "")
+
add_library(importedFallback_genex STATIC IMPORTED)
set_property(TARGET importedFallback_genex PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET importedFallback_genex PROPERTY IMPORTED_LOCATION_RELEASE release_loc)
@@ -217,6 +232,7 @@ add_custom_target(check-part3 ALL
-Dconfig=$<CONFIGURATION>
-Dtest_imported_includes=$<TARGET_PROPERTY:imported4,INCLUDE_DIRECTORIES>
-Dtest_imported_fallback=$<STREQUAL:$<TARGET_FILE_NAME:importedFallback>,fallback_loc>
+ -Dtest_imported_fallback2=$<IF:$<OR:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_TAPI}>>>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,fallback_loc>>
-Dtest_imported_fallback_genex=$<STREQUAL:$<TARGET_PROPERTY:importedFallback_genex,INTERFACE_COMPILE_DEFINITIONS>,FOOBAR=1>
-Dtest_alias_file_exe=$<STREQUAL:$<TARGET_FILE:Alias::SomeExe>,$<TARGET_FILE:someexe>>
-Dtest_alias_file_lib=$<STREQUAL:$<TARGET_FILE:Alias::SomeLib>,$<TARGET_FILE:empty1>>
diff --git a/Tests/GeneratorExpression/check-part3.cmake b/Tests/GeneratorExpression/check-part3.cmake
index e1b1f9364e..7bb0d85ad2 100644
--- a/Tests/GeneratorExpression/check-part3.cmake
+++ b/Tests/GeneratorExpression/check-part3.cmake
@@ -19,6 +19,7 @@ else()
endif()
check(test_imported_fallback "1")
+check(test_imported_fallback2 "1")
check(test_imported_fallback_genex "1")
check(test_alias_file_exe "1")
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
index e4973b03b2..d4720be12c 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(SystemIncludeDirectories)
diff --git a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt
index 3b994a233b..6812267170 100644
--- a/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/TargetIncludeDirectories/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(TargetIncludeDirectories)
diff --git a/Tests/InterfaceLibrary/CMakeLists.txt b/Tests/InterfaceLibrary/CMakeLists.txt
index a302c7c287..d57eccc442 100644
--- a/Tests/InterfaceLibrary/CMakeLists.txt
+++ b/Tests/InterfaceLibrary/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(InterfaceLibrary)
diff --git a/Tests/JCTest/CMakeLists.txt b/Tests/JCTest/CMakeLists.txt
index b1206408d2..adbdf9a338 100644
--- a/Tests/JCTest/CMakeLists.txt
+++ b/Tests/JCTest/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(TestTime)
enable_testing()
add_executable(TestTime TestTime.cxx)
diff --git a/Tests/Java/CMakeLists.txt b/Tests/Java/CMakeLists.txt
index 1d8d7ac67b..c1c68177c7 100644
--- a/Tests/Java/CMakeLists.txt
+++ b/Tests/Java/CMakeLists.txt
@@ -1,6 +1,6 @@
project(hello Java)
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
set(CMAKE_VERBOSE_MAKEFILE 1)
include(CTest)
diff --git a/Tests/JavaJavah/CMakeLists.txt b/Tests/JavaJavah/CMakeLists.txt
index b56cc216ee..06fc06a88b 100644
--- a/Tests/JavaJavah/CMakeLists.txt
+++ b/Tests/JavaJavah/CMakeLists.txt
@@ -1,6 +1,6 @@
project(helloJavah Java CXX)
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
set(CMAKE_VERBOSE_MAKEFILE 1)
include(CTest)
diff --git a/Tests/JavaNativeHeaders/CMakeLists.txt b/Tests/JavaNativeHeaders/CMakeLists.txt
index 2471e013fd..8a2e4603bd 100644
--- a/Tests/JavaNativeHeaders/CMakeLists.txt
+++ b/Tests/JavaNativeHeaders/CMakeLists.txt
@@ -1,6 +1,6 @@
project(helloJavaNativeHeaders Java CXX)
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
set(CMAKE_VERBOSE_MAKEFILE 1)
include (CTest)
diff --git a/Tests/LinkDirectory/CMakeLists.txt b/Tests/LinkDirectory/CMakeLists.txt
index d9a8ac8e2b..2c2e488a3e 100644
--- a/Tests/LinkDirectory/CMakeLists.txt
+++ b/Tests/LinkDirectory/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(LinkDirectory C)
# Put the subproject source tree in our build tree so it can refer to
diff --git a/Tests/LinkDirectory/External/CMakeLists.txt b/Tests/LinkDirectory/External/CMakeLists.txt
index e2229299ed..431fd8977e 100644
--- a/Tests/LinkDirectory/External/CMakeLists.txt
+++ b/Tests/LinkDirectory/External/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(LinkDirectoryExternal C)
diff --git a/Tests/LinkFlags/CMakeLists.txt b/Tests/LinkFlags/CMakeLists.txt
index 31ff9b5cf3..c25c4b2bca 100644
--- a/Tests/LinkFlags/CMakeLists.txt
+++ b/Tests/LinkFlags/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(LinkFlags C)
string(TOUPPER "${TEST_CONFIG}" TEST_CONFIG_UPPER)
@@ -32,6 +32,11 @@ add_executable(LinkFlags_exe_config LinkFlagsExe.c)
set_property(TARGET LinkFlags_exe_config PROPERTY LINK_FLAGS_${TEST_CONFIG_UPPER} ${pre}BADFLAG_${TEST_CONFIG}${obj})
add_executable(LinkFlags LinkFlags.c)
+if("x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC")
+ set_property(TARGET LinkFlags PROPERTY
+ LINK_FLAGS "/NODEFAULTLIB:\"libcdg.lib\" /NODEFAULTLIB:\"libcmtg.lib\" /NODEFAULTLIB:\"foomsvcrt.lib\" /NODEFAULTLIB:\"libbar.lib\" /NODEFAULTLIB:\"libfooba.lib\""
+ )
+endif()
add_subdirectory(LinkerFlags)
add_subdirectory(LinkerFlagsConfig)
diff --git a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt
index cafa99b90b..3313c572b1 100644
--- a/Tests/LoadCommand/CMakeCommands/CMakeLists.txt
+++ b/Tests/LoadCommand/CMakeCommands/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CMAKE_LOADED_COMMANDS)
if (MUDSLIDE_TYPE MATCHES MUCHO)
diff --git a/Tests/LoadCommand/CMakeLists.txt b/Tests/LoadCommand/CMakeLists.txt
index e1c4998c69..c0dc24720e 100644
--- a/Tests/LoadCommand/CMakeLists.txt
+++ b/Tests/LoadCommand/CMakeLists.txt
@@ -1,4 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
+cmake_policy(SET CMP0031 OLD) # testing the old behavior
project(LoadCommand)
# set a definition
diff --git a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt
index dc029a4f7d..74a1f550e7 100644
--- a/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt
+++ b/Tests/LoadCommandOneConfig/CMakeCommands/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(CMAKE_LOADED_COMMANDS)
if (MUDSLIDE_TYPE MATCHES MUCHO)
diff --git a/Tests/LoadCommandOneConfig/CMakeLists.txt b/Tests/LoadCommandOneConfig/CMakeLists.txt
index fef4bb777a..35dc0fe594 100644
--- a/Tests/LoadCommandOneConfig/CMakeLists.txt
+++ b/Tests/LoadCommandOneConfig/CMakeLists.txt
@@ -1,4 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
+cmake_policy(SET CMP0031 OLD) # testing the old behavior
project(LoadCommand)
# set a definition
diff --git a/Tests/MFC/CMakeLists.txt b/Tests/MFC/CMakeLists.txt
index d17b955437..3f78a81113 100644
--- a/Tests/MFC/CMakeLists.txt
+++ b/Tests/MFC/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(mfc_driver)
include(CTest)
diff --git a/Tests/MFC/CMakeLists.txt.in b/Tests/MFC/CMakeLists.txt.in
index a600c63cbe..bae3d2fb19 100644
--- a/Tests/MFC/CMakeLists.txt.in
+++ b/Tests/MFC/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(mfc1)
macro(replace_flags var these those)
diff --git a/Tests/MFC/try_compile/CMakeLists.txt b/Tests/MFC/try_compile/CMakeLists.txt
index 768d2a639a..d22b8b6991 100644
--- a/Tests/MFC/try_compile/CMakeLists.txt
+++ b/Tests/MFC/try_compile/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(try_compile_mfc)
set(files
diff --git a/Tests/MSManifest/Subdir/CMakeLists.txt b/Tests/MSManifest/Subdir/CMakeLists.txt
index 3b4fccc1bd..68c66fe209 100644
--- a/Tests/MSManifest/Subdir/CMakeLists.txt
+++ b/Tests/MSManifest/Subdir/CMakeLists.txt
@@ -5,6 +5,11 @@ if(MSVC AND NOT MSVC_VERSION LESS 1400)
add_test(NAME MSManifest.Single COMMAND
${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSManifest>
-P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
+ add_executable(MSManifestNonIncremental main.c ${CMAKE_CURRENT_BINARY_DIR}/test.manifest)
+ set_property(TARGET MSManifestNonIncremental PROPERTY LINK_FLAGS "/INCREMENTAL:NO")
+ add_test(NAME MSManifest.Single.NonIncremental COMMAND
+ ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSManifestNonIncremental>
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
add_executable(MSManifestNone main.c)
set_property(TARGET MSManifestNone PROPERTY LINK_FLAGS "/MANIFEST:NO")
elseif(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang")
diff --git a/Tests/MSManifest/Subdir2/CMakeLists.txt b/Tests/MSManifest/Subdir2/CMakeLists.txt
index 0d960ad516..bbc70dcb1b 100644
--- a/Tests/MSManifest/Subdir2/CMakeLists.txt
+++ b/Tests/MSManifest/Subdir2/CMakeLists.txt
@@ -10,4 +10,14 @@ if((MSVC AND NOT MSVC_VERSION LESS 1400) OR (WIN32 AND CMAKE_C_COMPILER_ID MATCH
add_test(NAME MSManifest.Multiple COMMAND
${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSMultipleManifest>
-P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
+ if(MSVC AND NOT MSVC_VERSION LESS 1400)
+ add_executable(MSMultipleManifestNonIncremental main.c
+ ${CMAKE_CURRENT_BINARY_DIR}/test_manifest1.manifest
+ ${CMAKE_CURRENT_BINARY_DIR}/test_manifest2.manifest
+ ${CMAKE_CURRENT_BINARY_DIR}/test_manifest3.manifest)
+ set_property(TARGET MSMultipleManifestNonIncremental PROPERTY LINK_FLAGS "/INCREMENTAL:NO")
+ add_test(NAME MSManifest.Multiple.NonIncremental COMMAND
+ ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSMultipleManifestNonIncremental>
+ -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
+ endif()
endif()
diff --git a/Tests/MacRuntimePath/A/CMakeLists.txt b/Tests/MacRuntimePath/A/CMakeLists.txt
index c9d3f2c437..7af746c5f3 100644
--- a/Tests/MacRuntimePath/A/CMakeLists.txt
+++ b/Tests/MacRuntimePath/A/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(MacRuntimePath_A)
# a shared library
diff --git a/Tests/MacRuntimePath/B/CMakeLists.txt b/Tests/MacRuntimePath/B/CMakeLists.txt
index 85598c4185..e88433caef 100644
--- a/Tests/MacRuntimePath/B/CMakeLists.txt
+++ b/Tests/MacRuntimePath/B/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(MacRuntimePath_B)
include(${MacRuntimePath_B_BINARY_DIR}/../Root/lib/exp.cmake)
diff --git a/Tests/MakeClean/CMakeLists.txt b/Tests/MakeClean/CMakeLists.txt
index 809d65bcc0..b7b9602c2f 100644
--- a/Tests/MakeClean/CMakeLists.txt
+++ b/Tests/MakeClean/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(MakeClean)
# Build the to-clean project.
diff --git a/Tests/MissingSourceFile/CMakeLists.txt b/Tests/MissingSourceFile/CMakeLists.txt
index b4f00333fc..f4fd8b0fb5 100644
--- a/Tests/MissingSourceFile/CMakeLists.txt
+++ b/Tests/MissingSourceFile/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(MissingSourceFile C)
add_executable(MissingSourceFile DoesNotExist/MissingSourceFile.c)
diff --git a/Tests/ModuleDefinition/CMakeLists.txt b/Tests/ModuleDefinition/CMakeLists.txt
index 483bd8b219..49577a79b8 100644
--- a/Tests/ModuleDefinition/CMakeLists.txt
+++ b/Tests/ModuleDefinition/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ModuleDefinition C)
# Test .def file source recognition for DLLs.
diff --git a/Tests/NewlineArgs/CMakeLists.txt b/Tests/NewlineArgs/CMakeLists.txt
index 3e4b436f5b..c822113653 100644
--- a/Tests/NewlineArgs/CMakeLists.txt
+++ b/Tests/NewlineArgs/CMakeLists.txt
@@ -1,5 +1,5 @@
# a simple CXX only test case
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (NewlineArgs CXX)
add_definitions("-DTEST_FLAG_1
diff --git a/Tests/ObjectLibrary/CMakeLists.txt b/Tests/ObjectLibrary/CMakeLists.txt
index 06167a83f4..05a35bb268 100644
--- a/Tests/ObjectLibrary/CMakeLists.txt
+++ b/Tests/ObjectLibrary/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ObjectLibrary C)
add_subdirectory(A)
diff --git a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt
index fb0ebc0cc0..f19d5a47b9 100644
--- a/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt
+++ b/Tests/ObjectLibrary/ExportLanguages/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExportLanguages CXX)
add_library(ExportLanguagesA OBJECT a.cxx)
add_library(ExportLanguagesB STATIC a.c $<TARGET_OBJECTS:ExportLanguagesA>)
diff --git a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt
index 8544798599..093aca6988 100644
--- a/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt
+++ b/Tests/ObjectLibrary/ExportLanguages/ExportLanguagesTest/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(ExportLanguagesTest)
diff --git a/Tests/OutDir/CMakeLists.txt b/Tests/OutDir/CMakeLists.txt
index 8afe03618a..e7bc3abff3 100644
--- a/Tests/OutDir/CMakeLists.txt
+++ b/Tests/OutDir/CMakeLists.txt
@@ -7,7 +7,7 @@ if(_isMultiConfig)
string(TOUPPER "${config}" CONFIG)
list(APPEND configs "${CONFIG}")
endforeach()
- set(CMAKE_BUILD_TYPE)
+ unset(CMAKE_BUILD_TYPE CACHE)
elseif(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
diff --git a/Tests/PDBDirectoryAndName/CMakeLists.txt b/Tests/PDBDirectoryAndName/CMakeLists.txt
index 5aa2459f99..81207bcad6 100644
--- a/Tests/PDBDirectoryAndName/CMakeLists.txt
+++ b/Tests/PDBDirectoryAndName/CMakeLists.txt
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
-cmake_policy(SET CMP0054 NEW)
+cmake_minimum_required(VERSION 3.5)
project(PDBDirectoryAndName C)
# Make sure the proper compiler is in use.
diff --git a/Tests/Plugin/CMakeLists.txt b/Tests/Plugin/CMakeLists.txt
index c2f43cdee9..a62e53f605 100644
--- a/Tests/Plugin/CMakeLists.txt
+++ b/Tests/Plugin/CMakeLists.txt
@@ -1,5 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
-cmake_policy(SET CMP0054 NEW)
+cmake_minimum_required (VERSION 3.5)
project(Plugin)
# Test per-target output directory properties.
diff --git a/Tests/Plugin/PluginTest/CMakeLists.txt b/Tests/Plugin/PluginTest/CMakeLists.txt
index f00122dcbc..4100683f8e 100644
--- a/Tests/Plugin/PluginTest/CMakeLists.txt
+++ b/Tests/Plugin/PluginTest/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(PluginTest)
diff --git a/Tests/PositionIndependentTargets/CMakeLists.txt b/Tests/PositionIndependentTargets/CMakeLists.txt
index ff779d3933..4f7e285fc8 100644
--- a/Tests/PositionIndependentTargets/CMakeLists.txt
+++ b/Tests/PositionIndependentTargets/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(PositionIndependentTargets)
diff --git a/Tests/Preprocess/CMakeLists.txt b/Tests/Preprocess/CMakeLists.txt
index 84ca5e8a96..63a7b5e609 100644
--- a/Tests/Preprocess/CMakeLists.txt
+++ b/Tests/Preprocess/CMakeLists.txt
@@ -1,4 +1,6 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
+cmake_policy(SET CMP0043 OLD) # testing the old behavior
+
project(Preprocess)
# This test is meant both as a test and as a reference for supported
diff --git a/Tests/Qt4And5Automoc/CMakeLists.txt b/Tests/Qt4And5Automoc/CMakeLists.txt
index ad74961d9f..12dc99b2a4 100644
--- a/Tests/Qt4And5Automoc/CMakeLists.txt
+++ b/Tests/Qt4And5Automoc/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(Qt4And5Automoc)
diff --git a/Tests/Qt4Targets/CMakeLists.txt b/Tests/Qt4Targets/CMakeLists.txt
index 3ddc3459b6..83cd44f187 100644
--- a/Tests/Qt4Targets/CMakeLists.txt
+++ b/Tests/Qt4Targets/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(Qt4Targets)
diff --git a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt
index 65e2b64bfc..d0e96170b2 100644
--- a/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt
+++ b/Tests/Qt4Targets/IncrementalMoc/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(IncrementalMoc)
find_package(Qt4 REQUIRED)
diff --git a/Tests/QtAutogen/GlobalAutogenExecutable/CMakeLists.txt b/Tests/QtAutogen/GlobalAutogenExecutable/CMakeLists.txt
new file mode 100644
index 0000000000..34da744b7d
--- /dev/null
+++ b/Tests/QtAutogen/GlobalAutogenExecutable/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.26)
+project(GlobalAutogenExecutable)
+
+include("../AutogenCoreTest.cmake")
+
+set(test_automoc_path "global_automoc_exe_path")
+set(test_autouic_path "global_autouic_exe_path")
+set(test_autorcc_path "global_autorcc_exe_path")
+
+set(CMAKE_AUTOMOC_EXECUTABLE ${test_automoc_path})
+set(CMAKE_AUTOUIC_EXECUTABLE ${test_autouic_path})
+set(CMAKE_AUTORCC_EXECUTABLE ${test_autorcc_path})
+
+add_executable(autogen_test main.cpp)
+
+get_target_property(target_automoc_path autogen_test AUTOMOC_EXECUTABLE)
+get_target_property(target_autouic_path autogen_test AUTOUIC_EXECUTABLE)
+get_target_property(target_autorcc_path autogen_test AUTORCC_EXECUTABLE)
+
+if(NOT ${target_automoc_path} STREQUAL ${test_automoc_path})
+ message(FATAL_ERROR "CMAKE_AUTOMOC_EXECUTABLE not set")
+endif()
+
+if (NOT ${target_autouic_path} STREQUAL ${test_autouic_path})
+ message(FATAL_ERROR "CMAKE_AUTOUIC_EXECUTABLE not set")
+endif()
+
+if (NOT ${target_autorcc_path} STREQUAL ${test_autorcc_path})
+ message(FATAL_ERROR "CMAKE_AUTORCC_EXECUTABLE not set")
+endif()
diff --git a/Tests/QtAutogen/GlobalAutogenExecutable/main.cpp b/Tests/QtAutogen/GlobalAutogenExecutable/main.cpp
new file mode 100644
index 0000000000..f8b643afbf
--- /dev/null
+++ b/Tests/QtAutogen/GlobalAutogenExecutable/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+}
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/CMakeLists.txt b/Tests/QtAutogen/MocInterfaceMacroNames/CMakeLists.txt
new file mode 100644
index 0000000000..7744d78402
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/CMakeLists.txt
@@ -0,0 +1,61 @@
+cmake_minimum_required(VERSION 3.16)
+project(MocInterfaceMacroNames)
+
+include("../AutogenCoreTest.cmake")
+
+set(CMAKE_AUTOMOC ON)
+
+add_executable(dummy dummy.cpp)
+target_link_libraries(dummy PRIVATE static_lib interface_lib shared_lib)
+
+add_library(shared_lib SHARED shared_lib.cpp)
+set_target_properties(shared_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "SHARED_LIB_MACRO")
+
+add_library(interface_lib INTERFACE)
+set_target_properties(interface_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "INTERFACE_LIB_MACRO")
+
+add_library(static_lib STATIC static_lib.cpp)
+set_target_properties(static_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LIB_MACRO")
+
+set(AUTOGEN_INFO_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/dummy_autogen.dir/AutogenInfo.json")
+set(CHECK_AUTOGEN_JSON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CheckAutogenJson.cmake")
+message(STATUS "AutogenInfo.json: ${AUTOGEN_INFO_FILE}")
+
+add_custom_command(TARGET dummy POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -DFILE_PATH=${AUTOGEN_INFO_FILE} -P ${CHECK_AUTOGEN_JSON_PATH}
+)
+
+install(TARGETS shared_lib EXPORT shared_lib)
+install(TARGETS interface_lib EXPORT interface_lib)
+install(TARGETS static_lib EXPORT static_lib)
+
+install(EXPORT shared_lib FILE shared_libTargets.cmake DESTINATION lib/cmake/shared_lib)
+install(EXPORT interface_lib FILE interface_libTargets.cmake DESTINATION lib/cmake/interface_lib)
+install(EXPORT static_lib FILE static_libTargets.cmake DESTINATION lib/cmake/static_lib)
+
+set(CHECK_EXPORT_TARGETS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CheckExportTargets.cmake")
+set(EXPORT_FOLDER_PATH "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Export")
+
+add_custom_command(TARGET dummy POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -DFOLDER_PATH=${EXPORT_FOLDER_PATH} -P ${CHECK_EXPORT_TARGETS_PATH}
+)
+
+# check if INTERFACE_AUTOMOC_MACRO_NAMES were transferred to the *_link libraries correctly
+add_executable(dummy_link dummy.cpp)
+target_link_libraries(dummy_link PRIVATE static_link_lib interface_link_lib shared_link_lib)
+
+add_library(shared_link_lib SHARED shared_lib.cpp)
+target_link_libraries(shared_link_lib PUBLIC shared_lib)
+
+add_library(interface_link_lib INTERFACE)
+target_link_libraries(interface_link_lib INTERFACE interface_lib)
+
+add_library(static_link_lib STATIC static_lib.cpp)
+target_link_libraries(static_link_lib PUBLIC static_lib)
+
+set(AUTOGEN_INFO_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/dummy_link_autogen.dir/AutogenInfo.json")
+message(STATUS "AutogenInfo.json: ${AUTOGEN_INFO_FILE}")
+
+add_custom_command(TARGET dummy_link POST_BUILD
+ COMMAND ${CMAKE_COMMAND} -DFILE_PATH=${AUTOGEN_INFO_FILE} -P ${CHECK_AUTOGEN_JSON_PATH}
+)
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/CheckAutogenJson.cmake b/Tests/QtAutogen/MocInterfaceMacroNames/CheckAutogenJson.cmake
new file mode 100644
index 0000000000..338f345029
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/CheckAutogenJson.cmake
@@ -0,0 +1,27 @@
+
+
+set(expected_values "SHARED_LIB_MACRO" "INTERFACE_LIB_MACRO" "STATIC_LIB_MACRO")
+function(checkAutoMocMacroNames FILE_PATH)
+ message(STATUS "Checking for auto moc macro names in ${FILE_PATH}")
+ file(READ ${FILE_PATH} FILE_CONTENT)
+ string(JSON MOC_MACRO_NAMES_ARR GET ${FILE_CONTENT} MOC_MACRO_NAMES)
+ # get the length of MOC_MACRO_NAMES in JSON
+ string(JSON MOC_MACRO_NAMES_LENGTH LENGTH ${MOC_MACRO_NAMES_ARR})
+ if(${MOC_MACRO_NAMES_LENGTH} EQUAL 0)
+ message(FATAL_ERROR "MOC_MACRO_NAMES is empty")
+ endif()
+ message(STATUS "MOC_MACRO_NAMES: ${MOC_MACRO_NAMES_ARR}")
+
+ math(EXPR last_index "${MOC_MACRO_NAMES_LENGTH} - 1")
+ set(reverse_index ${last_index})
+ foreach(expected_value IN LISTS expected_values)
+ string(JSON element GET ${MOC_MACRO_NAMES_ARR} ${reverse_index})
+ # check if element equals to expected value
+ if(NOT ${element} STREQUAL ${expected_value})
+ message(FATAL_ERROR "MOC_MACRO_NAMES is expected to contain ${expected_value} but contains ${element}")
+ endif()
+ math(EXPR reverse_index "${reverse_index} - 1")
+ endforeach()
+endfunction()
+
+checkAutoMocMacroNames(${FILE_PATH})
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/CheckExportTargets.cmake b/Tests/QtAutogen/MocInterfaceMacroNames/CheckExportTargets.cmake
new file mode 100644
index 0000000000..9db23f62b8
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/CheckExportTargets.cmake
@@ -0,0 +1,45 @@
+
+set(TARGET_NAMES "static_lib" "shared_lib" "interface_lib")
+set(static_lib_FOUND "0")
+set(shared_lib_FOUND "0")
+set(interface_lib_FOUND "0")
+
+macro(checkExportTargets FOLDER_PATH)
+ message("Checking folder: ${FOLDER_PATH}")
+ file(GLOB sources_list LIST_DIRECTORIES true RELATIVE ${FOLDER_PATH} ${FOLDER_PATH}/*)
+ message("Found files and folders: ${sources_list}")
+ foreach(source ${sources_list})
+ set(SOURCE_ABS "${FOLDER_PATH}/${source}")
+ if(IS_DIRECTORY ${SOURCE_ABS})
+ message("Found subfolder: ${source}")
+ checkExportTargets(${SOURCE_ABS})
+ else()
+ message("Found file: ${source}")
+ foreach(TARGET_NAME ${TARGET_NAMES})
+ set(TARGETS_FILE "${TARGET_NAME}Targets.cmake")
+ if(${source} STREQUAL ${TARGETS_FILE})
+ message("Found ${TARGETS_FILE} in ${FOLDER_PATH}")
+ string(TOUPPER ${TARGET_NAME} TARGET_NAME_UPPER)
+ set(expected_macro "${TARGET_NAME_UPPER}_MACRO")
+ set(expected_string "INTERFACE_AUTOMOC_MACRO_NAMES \"${expected_macro}\"")
+ file(READ ${FOLDER_PATH}/${source} contents)
+ if (NOT contents MATCHES ${expected_string})
+ message(FATAL_ERROR "Expected ${expected_string} in ${FOLDER_PATH}/${source}")
+ else()
+ message("Found ${expected_string} in ${FOLDER_PATH}/${source}")
+ set(${TARGET_NAME}_FOUND "1")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ endforeach()
+endmacro()
+
+checkExportTargets(${FOLDER_PATH})
+
+foreach(TARGET_NAME ${TARGET_NAMES})
+ # check if the target found equals the expected value
+ if(NOT ${TARGET_NAME}_FOUND STREQUAL "1")
+ message(FATAL_ERROR "Did not find ${TARGET_NAME}Targets.cmake")
+ endif()
+endforeach()
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/dummy.cpp b/Tests/QtAutogen/MocInterfaceMacroNames/dummy.cpp
new file mode 100644
index 0000000000..f8b643afbf
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/dummy.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+}
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/shared_lib.cpp b/Tests/QtAutogen/MocInterfaceMacroNames/shared_lib.cpp
new file mode 100644
index 0000000000..3a5c48208f
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/shared_lib.cpp
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+ void foo()
+{
+}
diff --git a/Tests/QtAutogen/MocInterfaceMacroNames/static_lib.cpp b/Tests/QtAutogen/MocInterfaceMacroNames/static_lib.cpp
new file mode 100644
index 0000000000..3695dc91e3
--- /dev/null
+++ b/Tests/QtAutogen/MocInterfaceMacroNames/static_lib.cpp
@@ -0,0 +1,3 @@
+void foo()
+{
+}
diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake
index a3c57a5871..412d511349 100644
--- a/Tests/QtAutogen/Tests.cmake
+++ b/Tests/QtAutogen/Tests.cmake
@@ -4,8 +4,10 @@ ADD_AUTOGEN_TEST(AutogenOriginDependsOn)
ADD_AUTOGEN_TEST(AutogenTargetDepends)
ADD_AUTOGEN_TEST(Complex QtAutogen)
ADD_AUTOGEN_TEST(GlobalAutogenTarget)
+ADD_AUTOGEN_TEST(GlobalAutogenExecutable)
ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
ADD_AUTOGEN_TEST(ManySources manySources)
+ADD_AUTOGEN_TEST(MocInterfaceMacroNames)
ADD_AUTOGEN_TEST(MocOnly mocOnly)
ADD_AUTOGEN_TEST(MocOptions mocOptions)
ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
diff --git a/Tests/QtAutomocNoQt/CMakeLists.txt b/Tests/QtAutomocNoQt/CMakeLists.txt
index 655f12befb..4e2ceeb32b 100644
--- a/Tests/QtAutomocNoQt/CMakeLists.txt
+++ b/Tests/QtAutomocNoQt/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(QtAutomocNoQt)
diff --git a/Tests/ReturnTest/CMakeLists.txt b/Tests/ReturnTest/CMakeLists.txt
index 78e3fc1ca4..03a0f7aa63 100644
--- a/Tests/ReturnTest/CMakeLists.txt
+++ b/Tests/ReturnTest/CMakeLists.txt
@@ -1,5 +1,5 @@
# a simple C only test case
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project (ReturnTest)
function (FAILED testname)
diff --git a/Tests/RunCMake/AddRunCMakeTest.cmake b/Tests/RunCMake/AddRunCMakeTest.cmake
new file mode 100644
index 0000000000..c0c3bba822
--- /dev/null
+++ b/Tests/RunCMake/AddRunCMakeTest.cmake
@@ -0,0 +1,10 @@
+if(NOT DEFINED RunCMake_TEST_SUITE)
+ message("Usage: ${CMAKE_COMMAND} -DRunCMake_TEST_SUITE=<test name> -P Tests/RunCMake/AddRunCMakeTestSuite.cmake")
+else()
+ include("Source/CMakeVersion.cmake")
+ set(RunCMake_TEST_DIR "Tests/RunCMake/${RunCMake_TEST_SUITE}")
+ file(MAKE_DIRECTORY "${RunCMake_TEST_DIR}")
+ file(WRITE "${RunCMake_TEST_DIR}/CMakeLists.txt" "cmake_minimum_required(VERSION ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR})\nproject(\${RunCMake_TEST} NONE)\ninclude(\${RunCMake_TEST}.cmake)\n")
+ file(WRITE "${RunCMake_TEST_DIR}/RunCMakeTest.cmake" "include(RunCMake)\n\n# Write your test cases below, like so:\n#\n# run_cmake(TestCaseName)\n")
+ file(APPEND "Tests/RunCMake/CMakeLists.txt" "add_RunCMake_test(${RunCMake_TEST_SUITE})\n")
+endif()
diff --git a/Tests/RunCMake/AppleSilicon/RunCMakeTest.cmake b/Tests/RunCMake/AppleSilicon/RunCMakeTest.cmake
index 39e462e41f..58c50e0ea5 100644
--- a/Tests/RunCMake/AppleSilicon/RunCMakeTest.cmake
+++ b/Tests/RunCMake/AppleSilicon/RunCMakeTest.cmake
@@ -10,11 +10,17 @@ function(run_arch case)
run_cmake(${case})
unset(RunCMake_TEST_OPTIONS)
set(RunCMake_TEST_NO_CLEAN 1)
- run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
+ set(RunCMake_TEST_OUTPUT_MERGE 1)
+ run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug --verbose)
endfunction()
run_arch(default)
+if(RunCMake_GENERATOR MATCHES "Makefiles|Ninja")
+ run_arch(default-target-arm64 -DCMAKE_C_COMPILER_TARGET=arm64-apple-macosx)
+ run_arch(default-target-x86_64 -DCMAKE_C_COMPILER_TARGET=x86_64-apple-macosx)
+endif()
+
run_arch(arm64-var -DCMAKE_APPLE_SILICON_PROCESSOR=arm64)
run_arch(x86_64-var -DCMAKE_APPLE_SILICON_PROCESSOR=x86_64)
diff --git a/Tests/RunCMake/AppleSilicon/default-target-arm64-build-check.cmake b/Tests/RunCMake/AppleSilicon/default-target-arm64-build-check.cmake
new file mode 100644
index 0000000000..33ad9313e4
--- /dev/null
+++ b/Tests/RunCMake/AppleSilicon/default-target-arm64-build-check.cmake
@@ -0,0 +1,5 @@
+if(NOT actual_stdout MATCHES "[ -]-target=arm64-apple-macosx ")
+ set(RunCMake_TEST_FAILED "No -target=arm64-apple-macosx flag found!")
+elseif(actual_stdout MATCHES " (-arch +[^ ]*)")
+ set(RunCMake_TEST_FAILED "'${CMAKE_MATCH_1}' flag incorrectly found!")
+endif()
diff --git a/Tests/RunCMake/AppleSilicon/default-target-arm64.cmake b/Tests/RunCMake/AppleSilicon/default-target-arm64.cmake
new file mode 100644
index 0000000000..8c94020d77
--- /dev/null
+++ b/Tests/RunCMake/AppleSilicon/default-target-arm64.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
+ message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES is '${CMAKE_OSX_ARCHITECTURES}', not empty ''")
+endif()
+add_library(arm64 arm64.c)
diff --git a/Tests/RunCMake/AppleSilicon/default-target-x86_64-build-check.cmake b/Tests/RunCMake/AppleSilicon/default-target-x86_64-build-check.cmake
new file mode 100644
index 0000000000..9116ef4e72
--- /dev/null
+++ b/Tests/RunCMake/AppleSilicon/default-target-x86_64-build-check.cmake
@@ -0,0 +1,5 @@
+if(NOT actual_stdout MATCHES "[ -]-target=x86_64-apple-macosx ")
+ set(RunCMake_TEST_FAILED "No -target=x86_64-apple-macosx flag found!")
+elseif(actual_stdout MATCHES " (-arch +[^ ]*)")
+ set(RunCMake_TEST_FAILED "'${CMAKE_MATCH_1}' flag incorrectly found!")
+endif()
diff --git a/Tests/RunCMake/AppleSilicon/default-target-x86_64.cmake b/Tests/RunCMake/AppleSilicon/default-target-x86_64.cmake
new file mode 100644
index 0000000000..ded46b7968
--- /dev/null
+++ b/Tests/RunCMake/AppleSilicon/default-target-x86_64.cmake
@@ -0,0 +1,5 @@
+enable_language(C)
+if(NOT CMAKE_OSX_ARCHITECTURES STREQUAL "")
+ message(FATAL_ERROR "CMAKE_OSX_ARCHITECTURES is '${CMAKE_OSX_ARCHITECTURES}', not empty ''")
+endif()
+add_library(x86_64 x86_64.c)
diff --git a/Tests/RunCMake/AppleTextStubs/CMakeLists.txt b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt
new file mode 100644
index 0000000000..93ee9dfd5f
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/AppleTextStubs/Framework-export.cmake b/Tests/RunCMake/AppleTextStubs/Framework-export.cmake
new file mode 100644
index 0000000000..f75c6d1ed2
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Framework-export.cmake
@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY FRAMEWORK TRUE)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>)
+
+install(TARGETS foo EXPORT foo FRAMEWORK DESTINATION DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>")
+install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::)
+install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo)
+
+export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake)
diff --git a/Tests/RunCMake/AppleTextStubs/Framework-import.cmake b/Tests/RunCMake/AppleTextStubs/Framework-import.cmake
new file mode 100644
index 0000000000..e0001d0b84
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Framework-import.cmake
@@ -0,0 +1,62 @@
+enable_language(C)
+
+find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo-install::foo)
+
+get_property(is_framework TARGET foo-install::foo PROPERTY FRAMEWORK)
+if (NOT is_framework)
+ message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.")
+endif()
+get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS)
+if (CAMKE_TAPI AND NOT enable_exports)
+ message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CAMKE_TAPI AND NOT implib)
+ message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CAMKE_TAPI AND NOT implib MATCHES "foo.framework/Versions/A/foo.tbd$")
+ message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+ message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location MATCHES "foo.framework/Versions/A/foo$")
+ message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
+
+
+include(${foo_BUILD}/foo.cmake)
+
+add_executable(main2 main.c)
+target_link_libraries(main2 PRIVATE foo-build::foo)
+
+get_property(is_framework TARGET foo-build::foo PROPERTY FRAMEWORK)
+if (NOT is_framework)
+ message(SEND_ERROR "foo-build::foo: FRAMEWORK not set.")
+endif()
+get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS)
+if (CAMKE_TAPI AND NOT enable_exports)
+ message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CAMKE_TAPI AND NOT implib)
+ message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CAMKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo.tbd")
+ message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+ message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location STREQUAL "${foo_BUILD}/foo.framework/Versions/A/foo")
+ message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
diff --git a/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake b/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake
new file mode 100644
index 0000000000..e8a555773c
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Framework-install-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/Framework-Release-generated.cmake")
diff --git a/Tests/RunCMake/AppleTextStubs/Framework.cmake b/Tests/RunCMake/AppleTextStubs/Framework.cmake
new file mode 100644
index 0000000000..f99eb5e813
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Framework.cmake
@@ -0,0 +1,59 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY FRAMEWORK TRUE)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL")
+
+# LIBRARY and ARCHIVE should be ignored
+install(TARGETS foo FRAMEWORK DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2"
+ LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib"
+ ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev")
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+ set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+ if (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+ endif()
+endmacro()
+
+macro (CHECK_SYMLINK test_msg path)
+ if(NOT IS_SYMLINK "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n")
+ elseif (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a valid symlink\n")
+ endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_symlink("Public DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Public DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_FILE_NAME:foo>")
+check_file("Installed DULIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Public DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+ check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+ check_symlink("Public TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/Versions/A/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed Public TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/foo.framework/$<TARGET_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Framework-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/AppleTextStubs/Library-export.cmake b/Tests/RunCMake/AppleTextStubs/Library-export.cmake
new file mode 100644
index 0000000000..d2e09ea35d
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Library-export.cmake
@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY LIBRARY_OUTPUT_DIRECTORY $<CONFIG>)
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY $<CONFIG>)
+
+install(TARGETS foo EXPORT foo DESTINATION "${CMAKE_BINARY_DIR}/$<CONFIG>")
+install(EXPORT foo DESTINATION lib/foo NAMESPACE foo-install::)
+install(FILES foo-config.cmake.in RENAME foo-config.cmake DESTINATION lib/foo)
+
+export(TARGETS foo NAMESPACE foo-build:: FILE Release/foo.cmake)
diff --git a/Tests/RunCMake/AppleTextStubs/Library-import.cmake b/Tests/RunCMake/AppleTextStubs/Library-import.cmake
new file mode 100644
index 0000000000..9406aacac0
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Library-import.cmake
@@ -0,0 +1,54 @@
+enable_language(C)
+
+find_package(foo REQUIRED CONFIG NO_DEFAULT_PATH)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo-install::foo)
+
+get_property(enable_exports TARGET foo-install::foo PROPERTY ENABLE_EXPORTS)
+if (CMAKE_TAPI AND NOT enable_exports)
+ message(SEND_ERROR "foo-install::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-install::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CMAKE_TAPI AND NOT implib)
+ message(SEND_ERROR "foo-install::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CMAKE_TAPI AND NOT implib MATCHES "Release/libfoo.tbd$")
+ message(SEND_ERROR "foo-install::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-install::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+ message(SEND_ERROR "foo-install::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location MATCHES "Release/libfoo.dylib$")
+ message(SEND_ERROR "foo-install::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
+
+
+include(${foo_BUILD}/foo.cmake)
+
+add_executable(main2 main.c)
+target_link_libraries(main2 PRIVATE foo-build::foo)
+
+get_property(enable_exports TARGET foo-build::foo PROPERTY ENABLE_EXPORTS)
+if (CMAKE_TAPI AND NOT enable_exports)
+ message(SEND_ERROR "foo-build::foo: ENABLE_EXPORTS not set.")
+endif()
+
+get_property(implib TARGET foo-build::foo PROPERTY IMPORTED_IMPLIB_RELEASE)
+if (CMAKE_TAPI AND NOT implib)
+ message(SEND_ERROR "foo-build::foo: IMPORTED_IMPLIB_RELEASE not set.")
+endif()
+if (CMAKE_TAPI AND NOT implib STREQUAL "${foo_BUILD}/libfoo.tbd")
+ message(SEND_ERROR "foo-build::foo: ${implib}: wrong value for IMPORTED_IMPLIB_RELEASE.")
+endif()
+
+get_property(location TARGET foo-build::foo PROPERTY IMPORTED_LOCATION_RELEASE)
+if (NOT location)
+ message(SEND_ERROR "foo-build::foo: IMPORTED_LOCATION_RELEASE not set.")
+endif()
+if (NOT location STREQUAL "${foo_BUILD}/libfoo.dylib")
+ message(SEND_ERROR "foo-build::foo: ${location}: wrong value for IMPORTED_LOCATION_RELEASE.")
+endif()
diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake
new file mode 100644
index 0000000000..40ec0a6ffc
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs-install-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithOutputs-Release-generated.cmake")
diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake
new file mode 100644
index 0000000000..f61c8f2a7e
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/LibraryWithOutputs.cmake
@@ -0,0 +1,52 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/TBD/$<CONFIG>")
+set_property(TARGET foo PROPERTY ARCHIVE_OUTPUT_NAME "tbd")
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+ set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+ if (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+ endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+ check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+]])
+
+if (CMAKE_GENERATOR STREQUAL "Xcode")
+ # ARCHIVE outputs are ignored by this generator
+ string (APPEND GENERATE_CONTENT
+ "\n if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/$<CONFIG>\")
+ string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\")
+ endif()
+ if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"foo\")
+ string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\")
+ endif()\n")
+else()
+ string (APPEND GENERATE_CONTENT
+ "\n if (NOT \"$<TARGET_IMPORT_FILE_DIR:foo>\" STREQUAL \"${CMAKE_BINARY_DIR}/TBD/$<CONFIG>\")
+ string (APPEND RunCMake_TEST_FAILED \"Wrong directory for TBD file: \\\"$<TARGET_IMPORT_FILE_DIR:foo>\\\"\n\")
+ endif()
+ if (NOT \"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\" STREQUAL \"tbd\")
+ string (APPEND RunCMake_TEST_FAILED \"Wrong base name for TBD file: \\\"$<TARGET_IMPORT_FILE_BASE_NAME:foo>\\\"\n\")
+ endif()\n")
+endif()
+string (APPEND GENERATE_CONTENT "endif()\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithOutputs-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake
new file mode 100644
index 0000000000..af7328610b
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions-install-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/LibraryWithVersions-Release-generated.cmake")
diff --git a/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake
new file mode 100644
index 0000000000..28c175df31
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/LibraryWithVersions.cmake
@@ -0,0 +1,96 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+set_property (TARGET foo PROPERTY VERSION 2.5.0)
+set_property (TARGET foo PROPERTY SOVERSION 2.0.0)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL" COMPONENT default)
+
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev1" NAMELINK_SKIP COMPONENT default)
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev2" NAMELINK_ONLY COMPONENT default)
+
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL3"
+ COMPONENT lib3 NAMELINK_COMPONENT dev3)
+install(TARGETS foo ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL4"
+ COMPONENT lib4 NAMELINK_COMPONENT dev4)
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+ set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+cmake_policy (SET CMP0011 NEW)
+cmake_policy (SET CMP0057 NEW)
+
+macro (CHECK_FILE test_msg path)
+ if (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+ endif()
+endmacro()
+
+macro (CHECK_SYMLINK test_msg path)
+ if (NOT IS_SYMLINK "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" is not a symbolic link\n")
+ elseif (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not a valid symlink\n")
+ endif()
+endmacro()
+
+macro (CHECK_NOFILE test_msg path)
+ if (EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" was found\n")
+ endif()
+endmacro()
+
+macro (CHECK_INSTALLED test_msg dir file)
+ file(GLOB installed_files LIST_DIRECTORIES FALSE RELATIVE "${dir}" "${dir}/*")
+ if (NOT "${file}" IN_LIST installed_files)
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${dir}/${file}\" not found\n")
+ endif()
+endmacro()
+
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_symlink("Linkable DYLIB file" "$<TARGET_LINKER_LIBRARY_FILE:foo>")
+check_symlink("SONAME DYLIB file" "$<TARGET_SONAME_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_FILE_NAME:foo>")
+check_symlink("Installed Linkable DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_LIBRARY_FILE_NAME:foo>")
+check_symlink("Installed SONAME DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+ check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+ check_symlink("Linkable TBD file" "$<TARGET_LINKER_IMPORT_FILE:foo>")
+ check_symlink("SONAME TBD file" "$<TARGET_SONAME_IMPORT_FILE:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+ check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev1/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+
+ check_installed("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2" "$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+ check_nofile("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_nofile("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev2/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+ check_nofile("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL3/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed SONAME TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_SONAME_IMPORT_FILE_NAME:foo>")
+ check_symlink("Installed Linkable TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL4/$<TARGET_LINKER_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/LibraryWithVersions-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake b/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake
new file mode 100644
index 0000000000..9ccd685d03
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/RunCMakeTest.cmake
@@ -0,0 +1,58 @@
+include(RunCMake)
+
+function(build_project test)
+ if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+ endif()
+ run_cmake(${test})
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+ set(RunCMake_TEST_NO_CLEAN 1)
+
+ run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
+ if ("${ARGC}" GREATER "1")
+ # custom install step
+ cmake_language(CALL ${ARGV1})
+ else()
+ run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Release)
+ endif()
+endfunction()
+
+build_project(Simple)
+build_project(Framework)
+build_project(LibraryWithOutputs)
+
+
+function(LibraryWithVersions-install)
+ run_cmake_command(LibraryWithVersions-install-component-lib3 ${CMAKE_COMMAND} --install . --config Release --component lib3)
+ run_cmake_command(LibraryWithVersions-install-component-lib4 ${CMAKE_COMMAND} --install . --config Release --component lib4)
+ run_cmake_command(LibraryWithVersions-install-components-dev4 ${CMAKE_COMMAND} --install . --config Release --component dev4)
+ run_cmake_command(LibraryWithVersions-install ${CMAKE_COMMAND} --install . --config Release --component default)
+endfunction()
+
+build_project(LibraryWithVersions LibraryWithVersions-install)
+
+
+function(build_ExportImport_project test)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-export-build)
+ set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root)
+ if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+ endif()
+ run_cmake(${test}-export)
+ unset(RunCMake_TEST_OPTIONS)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ run_cmake_command(${test}-export-build ${CMAKE_COMMAND} --build . --config Release)
+ run_cmake_command(${test}-export-install ${CMAKE_COMMAND} --install . --prefix ${CMAKE_INSTALL_PREFIX} --config Release)
+
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-import-build)
+ set (foo_BUILD "${RunCMake_BINARY_DIR}/${test}-export-build")
+ if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ string (APPEND foo_BUILD "/Release")
+ endif()
+ run_cmake_with_options(${test}-import -Dfoo_DIR=${CMAKE_INSTALL_PREFIX}/lib/foo
+ -Dfoo_BUILD=${RunCMake_BINARY_DIR}/${test}-export-build/Release)
+ run_cmake_command(${test}-import-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+build_ExportImport_project(Library)
+build_ExportImport_project(Framework)
diff --git a/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake b/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake
new file mode 100644
index 0000000000..94054fad5f
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Simple-install-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/Simple-Release-generated.cmake")
diff --git a/Tests/RunCMake/AppleTextStubs/Simple.cmake b/Tests/RunCMake/AppleTextStubs/Simple.cmake
new file mode 100644
index 0000000000..9f6318c561
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/Simple.cmake
@@ -0,0 +1,41 @@
+enable_language(C)
+
+add_library(foo SHARED foo.c)
+set_property(TARGET foo PROPERTY ENABLE_EXPORTS TRUE)
+
+add_executable(main main.c)
+target_link_libraries(main PRIVATE foo)
+
+
+install(TARGETS foo DESTINATION "${CMAKE_BINARY_DIR}/INSTALL")
+
+install(TARGETS foo LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/lib"
+ ARCHIVE DESTINATION "${CMAKE_BINARY_DIR}/INSTALL2/dev")
+
+
+set (GENERATE_CONTENT "if (\"${CMAKE_TAPI}\")
+ set (APPLE_TEXT_STUBS_SUPPORTED TRUE)
+endif()\n\n")
+
+string (APPEND GENERATE_CONTENT [[
+macro (CHECK_FILE test_msg path)
+ if (NOT EXISTS "${path}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: \"${path}\" not found\n")
+ endif()
+endmacro()
+
+check_file("DYLIB file" "$<TARGET_FILE:foo>")
+check_file("executable file" "$<TARGET_FILE:main>")
+
+check_file("Installed DYLIB file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/lib/$<TARGET_FILE_NAME:foo>")
+
+if (APPLE_TEXT_STUBS_SUPPORTED)
+ check_file("TBD file" "$<TARGET_IMPORT_FILE:foo>")
+
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL/$<TARGET_IMPORT_FILE_NAME:foo>")
+ check_file("Installed TBD file" "${RunCMake_TEST_BINARY_DIR}/INSTALL2/dev/$<TARGET_IMPORT_FILE_NAME:foo>")
+endif()
+]])
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Simple-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in b/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in
new file mode 100644
index 0000000000..b0381387a6
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/foo-config.cmake.in
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/foo.cmake)
diff --git a/Tests/RunCMake/AppleTextStubs/foo.c b/Tests/RunCMake/AppleTextStubs/foo.c
new file mode 100644
index 0000000000..7f39d71755
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/foo.c
@@ -0,0 +1,5 @@
+
+int foo()
+{
+ return 0;
+}
diff --git a/Tests/RunCMake/AppleTextStubs/main.c b/Tests/RunCMake/AppleTextStubs/main.c
new file mode 100644
index 0000000000..dc5ce3d101
--- /dev/null
+++ b/Tests/RunCMake/AppleTextStubs/main.c
@@ -0,0 +1,7 @@
+
+extern int foo(void);
+
+int main()
+{
+ return foo();
+}
diff --git a/Tests/RunCMake/AutoExportDll/CMakeLists.txt b/Tests/RunCMake/AutoExportDll/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/AutoExportDll/CMakeLists.txt
+++ b/Tests/RunCMake/AutoExportDll/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/AutoExportDll/hello.cxx b/Tests/RunCMake/AutoExportDll/hello.cxx
index 74e7a4e948..35ccbb78f9 100644
--- a/Tests/RunCMake/AutoExportDll/hello.cxx
+++ b/Tests/RunCMake/AutoExportDll/hello.cxx
@@ -12,3 +12,12 @@ void hello()
}
void Hello::operator delete[](void*){};
void Hello::operator delete(void*){};
+
+#ifdef HELLO_VFTABLE
+HelloVFTable::HelloVFTable()
+{
+}
+HelloVFTable::~HelloVFTable()
+{
+}
+#endif
diff --git a/Tests/RunCMake/AutoExportDll/hello.h b/Tests/RunCMake/AutoExportDll/hello.h
index 7192f65af9..410ffabdda 100644
--- a/Tests/RunCMake/AutoExportDll/hello.h
+++ b/Tests/RunCMake/AutoExportDll/hello.h
@@ -16,3 +16,20 @@ public:
static void operator delete[](void*);
static void operator delete(void*);
};
+
+// In the MSVC ABI, a delegating constructor references the vftable.
+#if __cplusplus >= 201103L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201103L)
+# define HELLO_VFTABLE
+#endif
+#ifdef HELLO_VFTABLE
+class HelloVFTable
+{
+public:
+ HelloVFTable();
+ HelloVFTable(int)
+ : HelloVFTable()
+ {
+ }
+ virtual ~HelloVFTable();
+};
+#endif
diff --git a/Tests/RunCMake/AutoExportDll/say.cxx b/Tests/RunCMake/AutoExportDll/say.cxx
index 8fc768a368..a9459a9b23 100644
--- a/Tests/RunCMake/AutoExportDll/say.cxx
+++ b/Tests/RunCMake/AutoExportDll/say.cxx
@@ -53,5 +53,8 @@ int main()
#ifdef HAS_JUSTNOP
justnop();
#endif
+#ifdef HELLO_VFTABLE
+ HelloVFTable helloVFTable(1);
+#endif
return 0;
}
diff --git a/Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt b/Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt
new file mode 100644
index 0000000000..8d98f9debd
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocPredefs-build-stderr.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/Autogen/MocPredefs-check.cxx b/Tests/RunCMake/Autogen/MocPredefs-check.cxx
new file mode 100644
index 0000000000..2b4791fc49
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocPredefs-check.cxx
@@ -0,0 +1,60 @@
+#include <iostream>
+
+#include "check_predefs.h"
+
+#define TO_STRING(x) TO_STRING0(x)
+#define TO_STRING0(x) #x
+
+int main()
+{
+ int ret = 0;
+#if defined(__STRICT_ANSI__)
+# if !defined(CHECK___STRICT_ANSI__)
+ std::cout << "__STRICT_ANSI__: Expected " << TO_STRING(__STRICT_ANSI__)
+ << " but it is not defined.\n";
+ ret = 1;
+# elif __STRICT_ANSI__ != CHECK___STRICT_ANSI__
+ std::cout << "__STRICT_ANSI__: Expected " << TO_STRING(__STRICT_ANSI__)
+ << " but got: " << TO_STRING(CHECK___STRICT_ANSI__) << "\n";
+ ret = 1;
+# endif
+#elif defined(CHECK___STRICT_ANSI__)
+ std::cout << "__STRICT_ANSI__: Expected undefined but got: "
+ << TO_STRING(CHECK___STRICT_ANSI__) << "\n";
+ ret = 1;
+#endif
+
+#if defined(__cplusplus)
+# if !defined(CHECK___cplusplus)
+ std::cout << "__cplusplus: Expected " << TO_STRING(__cplusplus)
+ << " but it is not defined.\n";
+ ret = 1;
+# elif __cplusplus != CHECK___cplusplus
+ std::cout << "__cplusplus: Expected " << TO_STRING(__cplusplus)
+ << " but got: " << TO_STRING(CHECK___cplusplus) << "\n";
+ ret = 1;
+# endif
+#elif defined(CHECK___cplusplus)
+ std::cout << "__cplusplus: Expected undefined but got: "
+ << TO_STRING(CHECK___cplusplus) << "\n";
+ ret = 1;
+#endif
+
+#if defined(_MSVC_LANG)
+# if !defined(CHECK__MSVC_LANG)
+ std::cout << "_MSVC_LANG: Expected " << TO_STRING(_MSVC_LANG)
+ << " but it is not defined.\n";
+ ret = 1;
+# elif _MSVC_LANG != CHECK__MSVC_LANG
+ std::cout << "_MSVC_LANG: Expected " << TO_STRING(_MSVC_LANG)
+ << " but got: " << TO_STRING(CHECK__MSVC_LANG) << "\n";
+ ret = 1;
+# endif
+#elif defined(CHECK__MSVC_LANG)
+ std::cout << "_MSVC_LANG: Expected undefined but got: "
+ << TO_STRING(CHECK__MSVC_LANG) << "\n";
+ ret = 1;
+#endif
+
+ return ret;
+}
diff --git a/Tests/RunCMake/Autogen/MocPredefs-prefix.cmake b/Tests/RunCMake/Autogen/MocPredefs-prefix.cmake
new file mode 100644
index 0000000000..460a05d436
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocPredefs-prefix.cmake
@@ -0,0 +1,3 @@
+file(READ ${in} predefs)
+string(REGEX REPLACE "#define +" "#define CHECK_" predefs "${predefs}")
+file(WRITE ${out} "${predefs}")
diff --git a/Tests/RunCMake/Autogen/MocPredefs.cmake b/Tests/RunCMake/Autogen/MocPredefs.cmake
new file mode 100644
index 0000000000..8307e04cce
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocPredefs.cmake
@@ -0,0 +1,39 @@
+enable_language(CXX)
+
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+add_library(MocPredefs MocPredefs.cxx)
+
+if(NOT DEFINED CMAKE_CXX_COMPILER_PREDEFINES_COMMAND)
+ return()
+endif()
+
+if(CMAKE_CXX_COMPILER_ID STREQUAL "LCC" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "1.26")
+ return()
+endif()
+
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+ set(moc_predefs_h "moc_predefs_$<CONFIG>.h")
+ set(check_dir "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
+else()
+ set(moc_predefs_h "moc_predefs.h")
+ set(check_dir "${CMAKE_CURRENT_BINARY_DIR}")
+endif()
+
+add_custom_command(TARGET MocPredefs POST_BUILD VERBATIM COMMAND
+ ${CMAKE_COMMAND}
+ -Din=${CMAKE_CURRENT_BINARY_DIR}/MocPredefs_autogen/${moc_predefs_h}
+ -Dout=${check_dir}/check_predefs.h
+ -P${CMAKE_CURRENT_SOURCE_DIR}/MocPredefs-prefix.cmake
+ )
+
+add_executable(MocPredefs-check MocPredefs-check.cxx)
+target_include_directories(MocPredefs-check PRIVATE ${check_dir})
+add_dependencies(MocPredefs-check MocPredefs)
+
+add_custom_target(MocPredefs-run-check ALL VERBATIM COMMAND MocPredefs-check)
diff --git a/Tests/RunCMake/Autogen/MocPredefs.cxx b/Tests/RunCMake/Autogen/MocPredefs.cxx
new file mode 100644
index 0000000000..b27cec5f60
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocPredefs.cxx
@@ -0,0 +1,3 @@
+void MocPredefs()
+{
+}
diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
index e4c5811466..6a3c49b534 100644
--- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
@@ -15,4 +15,11 @@ if (DEFINED with_qt_version)
run_cmake(CMP0111-imported-target-full)
run_cmake(CMP0111-imported-target-libname)
run_cmake(CMP0111-imported-target-implib-only)
+
+ block()
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/MocPredefs-build)
+ run_cmake(MocPredefs)
+ set(RunCMake_TEST_NO_CLEAN 1)
+ run_cmake_command(MocPredefs-build ${CMAKE_COMMAND} --build . --config Debug)
+ endblock()
endif ()
diff --git a/Tests/RunCMake/BuildDepends/CMakeLists.txt b/Tests/RunCMake/BuildDepends/CMakeLists.txt
index 99f238b9ed..8eb57486ad 100644
--- a/Tests/RunCMake/BuildDepends/CMakeLists.txt
+++ b/Tests/RunCMake/BuildDepends/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/BundleUtilities/CMakeLists.txt b/Tests/RunCMake/BundleUtilities/CMakeLists.txt
index 6dd8cdf551..44025d3bd1 100644
--- a/Tests/RunCMake/BundleUtilities/CMakeLists.txt
+++ b/Tests/RunCMake/BundleUtilities/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.12)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake b/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake
index df28102f2a..a7b05d293e 100644
--- a/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake
+++ b/Tests/RunCMake/BundleUtilities/RunCMakeTest.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
include(RunCMake)
# TODO Migrate Tests/BundleUtilities here
diff --git a/Tests/RunCMake/Byproducts/CleanByproducts.cmake b/Tests/RunCMake/Byproducts/CleanByproducts.cmake
index 85d9582959..961deb95dc 100644
--- a/Tests/RunCMake/Byproducts/CleanByproducts.cmake
+++ b/Tests/RunCMake/Byproducts/CleanByproducts.cmake
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.10)
-project(CleanByproducts)
+enable_language(C)
+enable_language(CXX)
# Configurable parameters
set(TEST_CLEAN_NO_CUSTOM FALSE CACHE BOOL "Value for the CLEAN_NO_CUSTOM PROPERTY")
diff --git a/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake b/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake
index f42d8e49dc..3d861fbfe1 100644
--- a/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake
+++ b/Tests/RunCMake/CMP0004/CMP0004-NEW.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 2.8.4)
-
cmake_policy(SET CMP0004 NEW)
add_library(foo SHARED empty.cpp)
diff --git a/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake b/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake
index 3fa58b6b6a..32c147424e 100644
--- a/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake
+++ b/Tests/RunCMake/CMP0004/CMP0004-OLD.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 2.8.4)
-
cmake_policy(SET CMP0004 OLD)
add_library(foo SHARED empty.cpp)
diff --git a/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake b/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake
index 297047662c..b7cd7ff0b5 100644
--- a/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake
+++ b/Tests/RunCMake/CMP0004/CMP0004-policy-genex.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 2.8.4)
-
cmake_policy(SET CMP0004 NEW)
add_library(foo SHARED empty.cpp)
diff --git a/Tests/RunCMake/CMP0004/CMakeLists.txt b/Tests/RunCMake/CMP0004/CMakeLists.txt
index 12cd3c7757..93ee9dfd5f 100644
--- a/Tests/RunCMake/CMP0004/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0004/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.4)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0019/CMP0019-NEW-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt
index a446211dbe..dc0341453a 100644
--- a/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt
+++ b/Tests/RunCMake/CMP0019/CMP0019-OLD-stderr.txt
@@ -1,13 +1,6 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-+
-CMake Deprecation Warning at CMP0019-OLD.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMP0019-OLD\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0019 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt b/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt
index f7b9c0e889..6eee437b2e 100644
--- a/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0019/CMP0019-WARN-stderr.txt
@@ -1,11 +1,4 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-+
-CMake Warning \(dev\) in CMakeLists.txt:
+^CMake Warning \(dev\) in CMakeLists\.txt:
Policy CMP0019 is not set: Do not re-expand variables in include and link
information. Run "cmake --help-policy CMP0019" for policy details. Use
the cmake_policy command to set the policy and suppress this warning.
diff --git a/Tests/RunCMake/CMP0019/RunCMakeTest.cmake b/Tests/RunCMake/CMP0019/RunCMakeTest.cmake
index 119fc2b300..fcd080ff4a 100644
--- a/Tests/RunCMake/CMP0019/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0019/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0019-WARN)
run_cmake(CMP0019-OLD)
diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-NEW-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-link_libraries-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-NOWARN-static-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt
index 87404d3a97..c84a289c15 100644
--- a/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt
+++ b/Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt
@@ -1,11 +1,4 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-+
-CMake Warning \(dev\) in CMakeLists.txt:
+^CMake Warning \(dev\) in CMakeLists\.txt:
Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link
interface. Run "cmake --help-policy CMP0022" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
diff --git a/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt
index 5d757200bc..39a9511844 100644
--- a/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt
@@ -1,11 +1,4 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-+
-CMake Warning \(dev\) in CMakeLists.txt:
+^CMake Warning \(dev\) in CMakeLists\.txt:
Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link
interface. Run "cmake --help-policy CMP0022" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
diff --git a/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt b/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt
deleted file mode 100644
index 66a58fb827..0000000000
--- a/Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.$
diff --git a/Tests/RunCMake/CMP0022/RunCMakeTest.cmake b/Tests/RunCMake/CMP0022/RunCMakeTest.cmake
index 4c1099607a..ea956fc70d 100644
--- a/Tests/RunCMake/CMP0022/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0022/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0022-WARN)
run_cmake(CMP0022-WARN-tll)
diff --git a/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt
new file mode 100644
index 0000000000..259eabdbaf
--- /dev/null
+++ b/Tests/RunCMake/CMP0026/CMP0026-IMPORTED-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0026-IMPORTED.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0111 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0026/RunCMakeTest.cmake b/Tests/RunCMake/CMP0026/RunCMakeTest.cmake
index 047da283b9..6476176d51 100644
--- a/Tests/RunCMake/CMP0026/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0026/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0026-WARN)
run_cmake(CMP0026-OLD)
diff --git a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
index 5952279eb4..558fba3bf2 100644
--- a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
if(RunCMake_GENERATOR MATCHES "^Ninja")
# Detect ninja version so we know what tests can be supported.
diff --git a/Tests/RunCMake/CMP0038/RunCMakeTest.cmake b/Tests/RunCMake/CMP0038/RunCMakeTest.cmake
index fc3500addd..3e7b5f3b56 100644
--- a/Tests/RunCMake/CMP0038/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0038/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0038-WARN)
run_cmake(CMP0038-NEW)
diff --git a/Tests/RunCMake/CMP0039/RunCMakeTest.cmake b/Tests/RunCMake/CMP0039/RunCMakeTest.cmake
index 58e8ea91eb..ce7541aaf4 100644
--- a/Tests/RunCMake/CMP0039/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0039/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0039-WARN)
run_cmake(CMP0039-NEW)
diff --git a/Tests/RunCMake/CMP0040/RunCMakeTest.cmake b/Tests/RunCMake/CMP0040/RunCMakeTest.cmake
index 13160e3a40..e5e6c3759d 100644
--- a/Tests/RunCMake/CMP0040/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0040/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0040-OLD-missing-target)
run_cmake(CMP0040-NEW-missing-target)
diff --git a/Tests/RunCMake/CMP0041/RunCMakeTest.cmake b/Tests/RunCMake/CMP0041/RunCMakeTest.cmake
index f47bb2ee31..93378c20aa 100644
--- a/Tests/RunCMake/CMP0041/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0041/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
# Protect tests from running inside the default install prefix.
set(RunCMake_TEST_OPTIONS "-DCMAKE_INSTALL_PREFIX=${RunCMake_BINARY_DIR}/NotDefaultPrefix")
diff --git a/Tests/RunCMake/CMP0042/RunCMakeTest.cmake b/Tests/RunCMake/CMP0042/RunCMakeTest.cmake
index 3b226d74cb..6b2314542e 100644
--- a/Tests/RunCMake/CMP0042/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0042/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0042-OLD)
run_cmake(CMP0042-NEW)
diff --git a/Tests/RunCMake/CMP0043/RunCMakeTest.cmake b/Tests/RunCMake/CMP0043/RunCMakeTest.cmake
index 7f9572e5af..b940528a5a 100644
--- a/Tests/RunCMake/CMP0043/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0043/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
diff --git a/Tests/RunCMake/CMP0045/RunCMakeTest.cmake b/Tests/RunCMake/CMP0045/RunCMakeTest.cmake
index 7c0e8a2151..009d455cae 100644
--- a/Tests/RunCMake/CMP0045/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0045/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0045-OLD)
run_cmake(CMP0045-NEW)
diff --git a/Tests/RunCMake/CMP0046/RunCMakeTest.cmake b/Tests/RunCMake/CMP0046/RunCMakeTest.cmake
index 0a39c76596..86b749aacc 100644
--- a/Tests/RunCMake/CMP0046/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0046/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0046-OLD-missing-dependency)
run_cmake(CMP0046-NEW-missing-dependency)
diff --git a/Tests/RunCMake/CMP0049/RunCMakeTest.cmake b/Tests/RunCMake/CMP0049/RunCMakeTest.cmake
index a8aa9d9650..e71f31e87b 100644
--- a/Tests/RunCMake/CMP0049/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0049/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0049-OLD)
run_cmake(CMP0049-NEW)
diff --git a/Tests/RunCMake/CMP0050/RunCMakeTest.cmake b/Tests/RunCMake/CMP0050/RunCMakeTest.cmake
index b7de28491d..526a9aacbf 100644
--- a/Tests/RunCMake/CMP0050/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0050/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0050-OLD)
run_cmake(CMP0050-NEW)
diff --git a/Tests/RunCMake/CMP0051/RunCMakeTest.cmake b/Tests/RunCMake/CMP0051/RunCMakeTest.cmake
index 621192d9f6..955d8987ce 100644
--- a/Tests/RunCMake/CMP0051/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0051/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0051-OLD)
run_cmake(CMP0051-NEW)
diff --git a/Tests/RunCMake/CMP0053/RunCMakeTest.cmake b/Tests/RunCMake/CMP0053/RunCMakeTest.cmake
index 6521ac074d..de58c25723 100644
--- a/Tests/RunCMake/CMP0053/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0053/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0053-OLD)
run_cmake(CMP0053-NEW)
diff --git a/Tests/RunCMake/CMP0054/RunCMakeTest.cmake b/Tests/RunCMake/CMP0054/RunCMakeTest.cmake
index 2f2fb765da..fc031de9fb 100644
--- a/Tests/RunCMake/CMP0054/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0054/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0054-OLD)
run_cmake(CMP0054-NEW)
diff --git a/Tests/RunCMake/CMP0055/RunCMakeTest.cmake b/Tests/RunCMake/CMP0055/RunCMakeTest.cmake
index efcfcab674..33a5b4b3b7 100644
--- a/Tests/RunCMake/CMP0055/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0055/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0055-OLD-Out-of-Scope)
run_cmake(CMP0055-NEW-Out-of-Scope)
diff --git a/Tests/RunCMake/CMP0057/RunCMakeTest.cmake b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake
index 719e054a57..76eaca6e7f 100644
--- a/Tests/RunCMake/CMP0057/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0057/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0057-OLD)
run_cmake(CMP0057-WARN)
diff --git a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
index e2c280ee73..7230a07412 100644
--- a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
+++ b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
@@ -12,5 +12,5 @@
will ask the linker to search for these by library name.
Call Stack \(most recent call first\):
CMP0060-WARN-ON.cmake:[0-9]+ \(include\)
- CMakeLists.txt:4 \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/CMP0060/CMakeLists.txt b/Tests/RunCMake/CMP0060/CMakeLists.txt
index 291d34d43d..db6b701c04 100644
--- a/Tests/RunCMake/CMP0060/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0060/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.9)
-cmake_policy(VERSION 3.2)
+cmake_minimum_required(VERSION 3.2)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0060/RunCMakeTest.cmake b/Tests/RunCMake/CMP0060/RunCMakeTest.cmake
index 445156fa1a..b7eae5a46b 100644
--- a/Tests/RunCMake/CMP0060/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0060/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
function(run_cmake_CMP0060 CASE)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0060-${CASE}-build)
diff --git a/Tests/RunCMake/CMP0064/RunCMakeTest.cmake b/Tests/RunCMake/CMP0064/RunCMakeTest.cmake
index 26e0a91a74..4c68510eee 100644
--- a/Tests/RunCMake/CMP0064/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0064/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(CMP0064-OLD)
run_cmake(CMP0064-WARN)
diff --git a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
index e86b50eb16..1ca4605a7e 100644
--- a/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0065/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(OLDBad1)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "AIX")
diff --git a/Tests/RunCMake/CMP0081/CMakeLists.txt b/Tests/RunCMake/CMP0081/CMakeLists.txt
index ef2163c298..44025d3bd1 100644
--- a/Tests/RunCMake/CMP0081/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0081/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.12)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0102/CMakeLists.txt b/Tests/RunCMake/CMP0102/CMakeLists.txt
index ef2163c298..2632ffa91f 100644
--- a/Tests/RunCMake/CMP0102/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0102/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.16)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0106/CMakeLists.txt b/Tests/RunCMake/CMP0106/CMakeLists.txt
index eafa642358..0a96a26fdd 100644
--- a/Tests/RunCMake/CMP0106/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0106/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.17)
if (RunCMake_TEST STREQUAL "CMP0106-WARN-VTK")
project(VTK NONE)
else ()
diff --git a/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt
new file mode 100644
index 0000000000..bf7fb08492
--- /dev/null
+++ b/Tests/RunCMake/CMP0111/CMP0111-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0111-OLD.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0111 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
index 8b9031111d..67d00f75dd 100644
--- a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
+++ b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
@@ -3,11 +3,9 @@
noexist
- Tried extensions [^
-]*
- [^
-]*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMP0115-OLD\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt b/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt
index 7b100b6e4c..e79ca97be2 100644
--- a/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0115/CMP0115-WARN-stderr.txt
@@ -17,11 +17,9 @@ CMake Error at CMP0115\.cmake:[0-9]+ \(add_executable\):
noexist
- Tried extensions [^
-]*
- [^
-]*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMP0115-WARN\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt
index ec777f7634..acd145e113 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-stderr.txt
@@ -1,5 +1,5 @@
^prop: `0`
-CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test1-build/GeneratedMain\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt
index f7d9f6bff1..929dafd6ea 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt
index a876390394..6a096b802b 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test3b-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test3b-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt
index b750ae7e5b..e721876456 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4-stderr.txt
@@ -124,7 +124,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt)
@@ -132,7 +132,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt)
@@ -140,7 +140,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt)
@@ -148,7 +148,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt)
@@ -156,7 +156,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt
index 580f04f9c1..f358297970 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test4b-stderr.txt
@@ -124,7 +124,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt)
@@ -132,7 +132,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt)
@@ -140,7 +140,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt)
@@ -148,7 +148,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt)
@@ -156,7 +156,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test4b-build/Generated_with_full_path1\.txt|CMP0118-NEW-Test4b-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt
index e268a7ac36..0e27c1d336 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test5-stderr.txt
@@ -115,7 +115,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -123,7 +123,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -131,7 +131,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -139,7 +139,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -147,7 +147,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -155,7 +155,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -163,7 +163,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-NEW-Test5-build/Generated_with_full_path[2-3]\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt
index 08eb6826a2..4de6e76e14 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-stderr.txt
@@ -47,7 +47,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt
@@ -55,7 +55,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt
@@ -63,7 +63,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test7-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt
index b7c496c5b7..b7a170beb9 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-stderr.txt
@@ -47,7 +47,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt
@@ -55,7 +55,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt
@@ -63,7 +63,7 @@ Call Stack \(most recent call first\):
CMP0118-NEW-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-NEW-Test9-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
index 58144c8d8c..2af72a433c 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
@@ -1,5 +1,5 @@
^prop: `0`
-CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-build/GeneratedMain\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
index 1f1bc904ab..6109f65898 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
index 5c15f1203e..e5e97def75 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt
@@ -48,7 +48,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test11\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt
@@ -56,7 +56,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test11\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
index 12a913a9eb..f5b3d1a624 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
index 62db7ee4a8..a30bc84209 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt
@@ -48,7 +48,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test15\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt
@@ -56,7 +56,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test15\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
index 7f86d38b31..4f4fea3e06 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
index 4104fc0037..3c80531f17 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test3b-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
index 7a16d0b64c..9600feeb91 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt)
@@ -84,7 +84,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
index 5a5c4eceae..e6386605a1 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt)
@@ -84,7 +84,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test4b-build/Generated_with_full_path1\.txt|CMP0118-OLD-Test4b-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
index 12fa617ab4..18e6a8c715 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -84,7 +84,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -92,7 +92,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -100,7 +100,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-OLD-Test5-build/Generated_with_full_path[2-3]\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
index 7199f0441a..a60545f435 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
index 233fd8bdf6..fd496cbd22 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt
@@ -42,7 +42,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt
@@ -50,7 +50,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt
@@ -58,7 +58,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt
@@ -66,7 +66,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-build/Generated_source[2-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
index 4aed2edd1c..3505242f9c 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
index cea8c224d2..63a9341a05 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt
@@ -42,7 +42,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt
@@ -50,7 +50,7 @@ Call Stack \(most recent call first\):
CMP0118-OLD-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt
index e2a2cf5470..1d7cbde5ce 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-stderr.txt
@@ -1,5 +1,5 @@
^prop: `0`
-CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test1-build/GeneratedMain\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt
index bce7681313..f4c7d00d5d 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test10\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test10-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt
index 00c47e9e67..93d8b83d12 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-stderr.txt
@@ -63,7 +63,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt
@@ -71,7 +71,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test11\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt
@@ -79,7 +79,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test11\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test11\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test11-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt
index 5b7994c5f9..fdde792058 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-stderr.txt
@@ -40,7 +40,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test14\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test14-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt
index c975c235e9..9e6a4a5656 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-stderr.txt
@@ -63,7 +63,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt
@@ -71,7 +71,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test15\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt
@@ -79,7 +79,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test15\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test15\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test15-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt
index 142d8a0765..58ba79328f 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt
index d4ef667fc6..cdb7cb4a78 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test3b-stderr.txt
@@ -52,7 +52,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt)
@@ -60,7 +60,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt)
@@ -68,7 +68,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt)
@@ -76,7 +76,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test3b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test3b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test3b-build/Generated_with_full_path3\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt
index ceeb570edd..4cd401c4ba 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4-stderr.txt
@@ -169,7 +169,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt)
@@ -177,7 +177,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt)
@@ -185,7 +185,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt)
@@ -193,7 +193,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt)
@@ -201,7 +201,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt
index f8484d0b52..6acd3df827 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test4b-stderr.txt
@@ -169,7 +169,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt)
@@ -177,7 +177,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt)
@@ -185,7 +185,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt)
@@ -193,7 +193,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt)
@@ -201,7 +201,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test4b\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test4b\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test4b-build/Generated_with_full_path1\.txt|CMP0118-WARN-Test4b-build/Generated_with_relative_path1\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt
index 0556391efb..2805cbfe23 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test5-stderr.txt
@@ -154,7 +154,7 @@ Generated_with_full_source_path3\.txt: # 2a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 2b # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3a # GENERATED = `0`
Generated_with_full_source_path3\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -162,7 +162,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -170,7 +170,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -178,7 +178,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -186,7 +186,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -194,7 +194,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
@@ -202,7 +202,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test5\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test5\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/(Generated_with_full_source_path[1-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_relative_path[2-3]\.txt|CMP0118-WARN-Test5-build/Generated_with_full_path[2-3]\.txt)
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt
index 7d588a2d6a..6f29170bf6 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test6\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test6-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt
index 8421061ce4..cde1164a94 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-stderr.txt
@@ -57,7 +57,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt
@@ -65,7 +65,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt
@@ -73,7 +73,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt
@@ -81,7 +81,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt
@@ -89,7 +89,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test7\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test7\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test7-build/Generated_source[2-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt
index e0f17e675a..91c4c4c5a1 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-stderr.txt
@@ -34,7 +34,7 @@ Generated_source6\.txt: # 2a # GENERATED = `1`
Generated_source6\.txt: # 2b # GENERATED = `1`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test8\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test8-build/Generated_source4\.txt
diff --git a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt
index 80f3edf365..547e8207ad 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-stderr.txt
@@ -57,7 +57,7 @@ Generated_source6\.txt: # 2a # GENERATED = `0`
Generated_source6\.txt: # 2b # GENERATED = `0`
Generated_source6\.txt: # 3a # GENERATED = `0`
Generated_source6\.txt: # 3b # GENERATED = `0`
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt
@@ -65,7 +65,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt
@@ -73,7 +73,7 @@ Call Stack \(most recent call first\):
CMP0118-WARN-Test9\.cmake:[0-9]+ \(include\)
CMakeLists\.txt:[0-9]+ \(include\)
+
-CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(add_custom_target\):
+CMake Error at CMP0118-Common-Test9\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[ \t]*.*Tests/RunCMake/CMP0118/CMP0118-WARN-Test9-build/Generated_source[4-6]\.txt
diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
index d3aa546301..5e9cf6cd79 100644
--- a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
+++ b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
@@ -1,8 +1,5 @@
-^CMake Error at GenInSubdir-Common.cmake:[0-9]+ \(add_custom_target\):
+^CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\):
Cannot find source file:
[^
]*/Tests/RunCMake/CMP0118/GenInSubdir-OLD-build/GenInSubdir/sub.txt
-Call Stack \(most recent call first\):
- GenInSubdir-OLD.cmake:[0-9]+ \(include\)
- CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt
index 5eb8a34b60..2820ba3add 100644
--- a/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0118/GenInSubdir-WARN-stderr.txt
@@ -1,8 +1,5 @@
-^CMake Error at GenInSubdir-Common.cmake:[0-9]+ \(add_custom_target\):
+^CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\):
Cannot find source file:
[^
]*/Tests/RunCMake/CMP0118/GenInSubdir-WARN-build/GenInSubdir/sub.txt
-Call Stack \(most recent call first\):
- GenInSubdir-WARN.cmake:[0-9]+ \(include\)
- CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
index 1e7b127571..cb4ea46439 100644
--- a/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0121/CMP0121-ERANGE-WARN-stderr.txt
@@ -7,7 +7,7 @@ Call Stack \(most recent call first\):
CMP0121-ERANGE-WARN.cmake:2 \(include\)
CMakeLists.txt:3 \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
-
+.*
CMake Error at CMP0121-ERANGE-Common.cmake:3 \(list\):
list index: (-2147483643|2147483647) out of range \(-5, 4\)
Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/CMP0139/CMakeLists.txt b/Tests/RunCMake/CMP0139/CMakeLists.txt
index 18dfd2686f..5ff8d3e0ff 100644
--- a/Tests/RunCMake/CMP0139/CMakeLists.txt
+++ b/Tests/RunCMake/CMP0139/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.23)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 930122c961..f05f784d7d 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -15,6 +15,9 @@ macro(add_RunCMake_test test)
else()
set(Test_Dir ${test})
endif()
+ if(CMAKE_C_COMPILER_ID STREQUAL "LCC")
+ list(APPEND TEST_ARGS -DRunCMake_TEST_LCC=1)
+ endif()
add_test(NAME RunCMake.${test} COMMAND ${CMAKE_CMAKE_COMMAND}
-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR}
-DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig}
@@ -41,6 +44,11 @@ function(add_RunCMake_test_group test types)
file(REMOVE_RECURSE "${TEST_CONFIG_DIR}")
file(MAKE_DIRECTORY "${TEST_CONFIG_DIR}")
+ set(TEST_ARGS "")
+ if(CMAKE_C_COMPILER_ID STREQUAL "LCC")
+ list(APPEND TEST_ARGS -DRunCMake_TEST_LCC=1)
+ endif()
+
foreach(type IN LISTS types)
# generate prerequirements config file in cmake as ctest doesn't have as
# much system information so it is easier to set programs and environment
@@ -68,6 +76,7 @@ function(add_RunCMake_test_group test types)
-DRunCMake_MAKE_PROGRAM=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
-DRunCMake_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/${test}
-DRunCMake_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}/${type}/${test}
+ ${TEST_ARGS}
-Dconfig_file=${TEST_CONFIG_DIR}/${type}_config.cmake
-P "${CMAKE_CURRENT_SOURCE_DIR}/${test}/RunCMakeTest.cmake"
)
@@ -270,8 +279,6 @@ if(want_NoQt_test)
add_RunCMake_test(AutogenNoQt TEST_DIR Autogen)
endif()
-add_RunCMake_test(ArtifactOutputDirs)
-
if(NOT DEFINED CMake_TEST_BuildDepends_GNU_AS
AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "LCC")
AND CMAKE_GENERATOR MATCHES "^Ninja"
@@ -323,6 +330,10 @@ endif()
add_RunCMake_test(ExcludeFromAll)
add_RunCMake_test(ExportImport)
add_RunCMake_test(ExternalData)
+if(CMAKE_GENERATOR MATCHES "^(Unix Makefiles|Ninja)$"
+ AND NOT "${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ add_RunCMake_test(ExtraGenerators)
+endif()
add_RunCMake_test(FeatureSummary)
add_RunCMake_test(FPHSA)
if(CMAKE_USE_SYSTEM_JSONCPP)
@@ -353,6 +364,7 @@ add_RunCMake_test(GenEx-DEVICE_LINK)
add_RunCMake_test(GenEx-LINK_LIBRARY)
add_RunCMake_test(GenEx-LINK_GROUP)
add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
+add_RunCMake_test(GenEx-TARGET_IMPORT_FILE)
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-TARGET_PROPERTY)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
@@ -392,6 +404,7 @@ if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STRE
endif()
add_RunCMake_test(ScriptMode)
add_RunCMake_test(Swift -DCMAKE_Swift_COMPILER=${CMAKE_Swift_COMPILER} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
+add_RunCMake_test(TargetArtifacts -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
add_RunCMake_test(TargetObjects)
add_RunCMake_test(TargetProperties)
add_RunCMake_test(ToolchainFile)
@@ -511,6 +524,7 @@ add_RunCMake_test(BundleUtilities)
if(APPLE)
add_RunCMake_test(INSTALL_NAME_DIR)
add_RunCMake_test(MacOSVersions)
+ add_RunCMake_test(AppleTextStubs)
endif()
function(add_RunCMake_test_try_compile)
@@ -779,6 +793,7 @@ if(DEFINED CMake_COMPILER_FORCES_NEW_DTAGS)
endif()
add_RunCMake_test(file-GET_RUNTIME_DEPENDENCIES
-DCMake_INSTALL_NAME_TOOL_BUG=${CMake_INSTALL_NAME_TOOL_BUG}
+ -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
)
add_RunCMake_test(CPackCommandLine)
diff --git a/Tests/RunCMake/CMakePresets/Comment-stderr.txt b/Tests/RunCMake/CMakePresets/Comment-stderr.txt
index b3b6b66d9f..8619cf5fad 100644
--- a/Tests/RunCMake/CMakePresets/Comment-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/Comment-stderr.txt
@@ -1,7 +1,6 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/Comment: JSON parse error
-Errors:
-[^
+]*/Tests/RunCMake/CMakePresets/Comment:
+JSON Parse Error: [^
]*Comment\/CMakePresets.json:
\* Line 1, Column 1
Syntax error: value, object or array expected\.
diff --git a/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt b/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
index ea5f47f653..31ebbc1312 100644
--- a/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ConditionFuture: File version must be 3 or higher for condition support$
+]*/Tests/RunCMake/CMakePresets/ConditionFuture:
+File version must be 3 or higher for condition support$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
index 895afcb97c..2d5b477937 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance0-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/CyclicInheritance0: Cyclic preset inheritance$
+]*/Tests/RunCMake/CMakePresets/CyclicInheritance0:
+Cyclic preset inheritance for preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
index 1e59e92ed3..596fcf8c17 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance1-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/CyclicInheritance1: Cyclic preset inheritance$
+]*/Tests/RunCMake/CMakePresets/CyclicInheritance1:
+Cyclic preset inheritance for preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
index 56e630b526..a6b83c8136 100644
--- a/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/CyclicInheritance2-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/CyclicInheritance2: Cyclic preset inheritance$
+]*/Tests/RunCMake/CMakePresets/CyclicInheritance2:
+Cyclic preset inheritance for preset "CyclicInheritance0"$
diff --git a/Tests/RunCMake/CMakePresets/DuplicatePresets-stderr.txt b/Tests/RunCMake/CMakePresets/DuplicatePresets-stderr.txt
index c9361aea5e..ebb9a2d864 100644
--- a/Tests/RunCMake/CMakePresets/DuplicatePresets-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/DuplicatePresets-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/DuplicatePresets: Duplicate presets$
+]*/Tests/RunCMake/CMakePresets/DuplicatePresets:
+Duplicate preset: "DuplicatePresets"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
index 749d306de6..cb9e545c4a 100644
--- a/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyCacheKey-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyCacheKey: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/EmptyCacheKey:
+Invalid preset: "EmptyCacheKey"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt
index 723ac2161b..7774fee8c5 100644
--- a/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyEnv: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/EmptyEnv:
+Invalid macro expansion in "EmptyEnv"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
index 365f537dc3..bc0f8663e7 100644
--- a/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyEnvKey-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyEnvKey: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/EmptyEnvKey:
+Invalid preset: "EmptyEnvKey"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt
index 880cee6516..b3bfe21226 100644
--- a/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyPenv: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/EmptyPenv:
+Invalid macro expansion in "EmptyPenv"$
diff --git a/Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt
index 0d3c5001a1..6f8469022d 100644
--- a/Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt
@@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyPresetName: Invalid preset
-Errors:
-[^
-]*/EmptyPresetName/CMakePresets.json$
+]*/Tests/RunCMake/CMakePresets/EmptyPresetName:
+Error: @5,15: Invalid Preset Name
+ "name": "",
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
index 1d22b87232..8e19c693aa 100644
--- a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EnvCycle: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/EnvCycle:
+Invalid preset: "EnvCycle"
+Invalid macro expansion in "EnvCycle"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
index 3221345498..5c02d5b5e8 100644
--- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDeprecated:
+Invalid preset: "ErrorNoWarningDeprecated"$
diff --git a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
index d2ddb900de..4640b1ef31 100644
--- a/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ErrorNoWarningDev-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDev: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/ErrorNoWarningDev:
+Invalid preset: "ErrorNoWarningDev"$
diff --git a/Tests/RunCMake/CMP0004/CMP0004-WARN-stderr.txt b/Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake
index e69de29bb2..e69de29bb2 100644
--- a/Tests/RunCMake/CMP0004/CMP0004-WARN-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ExplicitNoTrace.cmake
diff --git a/Tests/RunCMake/CMakePresets/ExtraPresetField-stderr.txt b/Tests/RunCMake/CMakePresets/ExtraPresetField-stderr.txt
index 559e3c2bd2..2e94831c99 100644
--- a/Tests/RunCMake/CMakePresets/ExtraPresetField-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ExtraPresetField-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ExtraPresetField: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/ExtraPresetField:
+Error: @8,18: Invalid extra field "invalid" in Preset
+ "invalid": true
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/ExtraRootField-stderr.txt b/Tests/RunCMake/CMakePresets/ExtraRootField-stderr.txt
index bb281beed8..554cd4aa3d 100644
--- a/Tests/RunCMake/CMakePresets/ExtraRootField-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ExtraRootField-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ExtraRootField: Invalid root object$
+]*/Tests/RunCMake/CMakePresets/ExtraRootField:
+Error: @10,14: Invalid extra field "invalid" in root object
+ "invalid": true
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/ExtraVariableField-stderr.txt b/Tests/RunCMake/CMakePresets/ExtraVariableField-stderr.txt
index 9b346e78e8..5cb777c42f 100644
--- a/Tests/RunCMake/CMakePresets/ExtraVariableField-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/ExtraVariableField-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/ExtraVariableField: Invalid CMake variable definition$
+]*/Tests/RunCMake/CMakePresets/ExtraVariableField:
+Error: @11,22: Invalid extra field "invalid" in variable "EXTRA" for preset "ExtraVariableField"
+ "invalid": true
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/FileDirFuture-stderr.txt b/Tests/RunCMake/CMakePresets/FileDirFuture-stderr.txt
index ba85f0f42e..b49df79227 100644
--- a/Tests/RunCMake/CMakePresets/FileDirFuture-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/FileDirFuture-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/FileDirFuture: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/FileDirFuture:
+Invalid macro expansion in "FileDirFuture"$
diff --git a/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField-stderr.txt b/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField-stderr.txt
index 36123bdaca..ca481c6a02 100644
--- a/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField: File version must be 3 or higher for installDir preset support.$
+]*/Tests/RunCMake/CMakePresets/FuturePresetInstallDirField:
+File version must be 3 or higher for installDir preset support$
diff --git a/Tests/RunCMake/CMakePresets/FuturePresetToolchainField-stderr.txt b/Tests/RunCMake/CMakePresets/FuturePresetToolchainField-stderr.txt
index 9382423a35..7aedc6d330 100644
--- a/Tests/RunCMake/CMakePresets/FuturePresetToolchainField-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/FuturePresetToolchainField-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/FuturePresetToolchainField: File version must be 3 or higher for toolchainFile preset support.$
+]*/Tests/RunCMake/CMakePresets/FuturePresetToolchainField:
+File version must be 3 or higher for toolchainFile preset support$
diff --git a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt
index d8622f212e..598478fc66 100644
--- a/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/HighVersion-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/HighVersion: Unrecognized "version" field$
+]*/Tests/RunCMake/CMakePresets/HighVersion:
+Error: @2,14: Unrecognized "version" field
+ "version": 1000,
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/HostSystemNameFuture-stderr.txt b/Tests/RunCMake/CMakePresets/HostSystemNameFuture-stderr.txt
index 7f4bb9a243..a1dae4ce72 100644
--- a/Tests/RunCMake/CMakePresets/HostSystemNameFuture-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/HostSystemNameFuture-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/HostSystemNameFuture: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/HostSystemNameFuture:
+Invalid macro expansion in "HostSystemNameFuture"$
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
index 3343204ca2..fc6292d422 100644
--- a/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeCycle-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeCycle: Cyclic include among preset files$
+]*/Tests/RunCMake/CMakePresets/IncludeCycle:
+Cyclic include among preset files: [^
+]*/CMakeUserPresets.json$
diff --git a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
index 35aea4c9ca..1bc402a006 100644
--- a/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeCycle3Files-stderr.txt
@@ -1,2 +1,4 @@
-^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeCycle3Files: Cyclic include among preset files$
+CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/IncludeCycle3Files:
+Cyclic include among preset files: [^
+]*/CMakePresets.json
diff --git a/Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt
index 85a2d78903..fba5b01b74 100644
--- a/Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt
@@ -1,5 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeNotFound: File not found
-Errors:
-[^
-]*/IncludeNotFound/NotFound.json: Failed to read file$
+]*/Tests/RunCMake/CMakePresets/IncludeNotFound:
+File not found: [^
+]*/IncludeNotFound/NotFound.json$
diff --git a/Tests/RunCMake/CMakePresets/IncludeV3-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeV3-stderr.txt
index 1869b6db5f..7e975a7b11 100644
--- a/Tests/RunCMake/CMakePresets/IncludeV3-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeV3-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeV3: File version must be 4 or higher for include support$
+]*/Tests/RunCMake/CMakePresets/IncludeV3:
+File version must be 4 or higher for include support$
diff --git a/Tests/RunCMake/CMakePresets/IncludeV4V3-stderr.txt b/Tests/RunCMake/CMakePresets/IncludeV4V3-stderr.txt
index 89e3e3ddbf..f14c583eea 100644
--- a/Tests/RunCMake/CMakePresets/IncludeV4V3-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/IncludeV4V3-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeV4V3: File version must be 4 or higher for include support$
+]*/Tests/RunCMake/CMakePresets/IncludeV4V3:
+File version must be 4 or higher for include support$
diff --git a/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy-stderr.txt
index 4a4d4ce70c..5df075d789 100644
--- a/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidArchitectureStrategy:
+Error: @9,21: Invalid preset
+ "strategy": {}
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
index 97f3876737..216f308259 100644
--- a/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidInheritance-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidInheritance: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidInheritance:
+Invalid preset: "InvalidInheritance"$
diff --git a/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir-stderr.txt
index 2fe8c66e93..deb35b46d2 100644
--- a/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidPresetBinaryDir:
+Error: @7,20: "binaryDir" expected a string
+ "binaryDir": \[\]
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidPresetGenerator-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidPresetGenerator-stderr.txt
index 95728752f6..7f194123ff 100644
--- a/Tests/RunCMake/CMakePresets/InvalidPresetGenerator-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidPresetGenerator-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidPresetGenerator: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidPresetGenerator:
+Error: @6,20: "generator" expected a string
+ "generator": \[\],
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidPresetName-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidPresetName-stderr.txt
index 8f6ff7c1d8..4cceb73ad9 100644
--- a/Tests/RunCMake/CMakePresets/InvalidPresetName-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidPresetName-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidPresetName: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidPresetName:
+Error: @5,15: Invalid Preset Name
+ "name": \[\],
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidPresetVendor-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidPresetVendor-stderr.txt
index 89a424ab99..f92c48ec92 100644
--- a/Tests/RunCMake/CMakePresets/InvalidPresetVendor-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidPresetVendor-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidPresetVendor: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidPresetVendor:
+Error: @8,17: Invalid preset
+ "vendor": true
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidPresets-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidPresets-stderr.txt
index 2b0f5603e3..d7081afbd8 100644
--- a/Tests/RunCMake/CMakePresets/InvalidPresets-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidPresets-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidPresets: Invalid "configurePresets" field$
+]*/Tests/RunCMake/CMakePresets/InvalidPresets:
+Error: @3,23: Invalid "configurePresets" field
+ "configurePresets": {}
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
index 5b500e44e8..86cd861729 100644
--- a/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidRegex-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidRegex: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/InvalidRegex:
+Invalid preset: "InvalidRegex"
+Invalid macro expansion in "InvalidRegex"$
diff --git a/Tests/RunCMake/CMakePresets/InvalidRoot-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidRoot-stderr.txt
index e5c434d9bb..5f2dcc1b33 100644
--- a/Tests/RunCMake/CMakePresets/InvalidRoot-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidRoot-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidRoot: Invalid root object$
+]*/Tests/RunCMake/CMakePresets/InvalidRoot:
+Error: \@1\,1\: Invalid root object
+\[\]
+\^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy-stderr.txt
index fab3766200..d0974aca81 100644
--- a/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidToolsetStrategy:
+Error: @9,21: Invalid preset
+ "strategy": {}
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidVariableValue-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidVariableValue-stderr.txt
index 0ab07c3d0b..fdb7072c87 100644
--- a/Tests/RunCMake/CMakePresets/InvalidVariableValue-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidVariableValue-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidVariableValue: Invalid CMake variable definition$
+]*/Tests/RunCMake/CMakePresets/InvalidVariableValue:
+Error: @10,20: "value" expected a string
+ "value": \[\]
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidVariables-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidVariables-stderr.txt
index 6d9102a14b..f329d973d1 100644
--- a/Tests/RunCMake/CMakePresets/InvalidVariables-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidVariables-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidVariables: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/InvalidVariables:
+Error: @8,25: Invalid preset
+ "cacheVariables": \[\]
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidVendor-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidVendor-stderr.txt
index af923f0ba2..fc6534e2a5 100644
--- a/Tests/RunCMake/CMakePresets/InvalidVendor-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidVendor-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidVendor: Invalid root object$
+]*/Tests/RunCMake/CMakePresets/InvalidVendor:
+Error: @3,13: Invalid root object
+ "vendor": true,
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/InvalidVersion-stderr.txt b/Tests/RunCMake/CMakePresets/InvalidVersion-stderr.txt
index 7e0fcfdd21..97fe9d6e04 100644
--- a/Tests/RunCMake/CMakePresets/InvalidVersion-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/InvalidVersion-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/InvalidVersion: Invalid "version" field$
+]*/Tests/RunCMake/CMakePresets/InvalidVersion:
+Error: @2,14: Invalid "version" field
+ "version": "1.0",
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt b/Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt
index 89eff9fc5b..92a1b2b746 100644
--- a/Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt
@@ -1,9 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/JSONParseError: JSON parse error
-Errors:
-[^
-]*JSONParseError/CMakePresets.json:
-\* Line 1, Column 1
- Syntax error: value, object or array expected\.
-\* Line 1, Column 1
- A valid JSON document must be either an array or an object value\.$
+]*/Tests/RunCMake/CMakePresets/JSONParseError:
+A JSON document cannot be empty
diff --git a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt
index 92b3723e1e..e9331006d2 100644
--- a/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/LowVersion-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/LowVersion: Unrecognized "version" field$
+]*/Tests/RunCMake/CMakePresets/LowVersion:
+Error: @2,14: Unrecognized "version" field
+ "version": 0,
+ \^
diff --git a/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid-stderr.txt b/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid-stderr.txt
index 6548cafe21..30ab9ced74 100644
--- a/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid: Invalid "cmakeMinimumRequired" field$
+]*/Tests/RunCMake/CMakePresets/MinimumRequiredInvalid:
+Error: @3,27: Invalid "cmakeMinimumRequired"
+ "cmakeMinimumRequired": "3.18",
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/MinimumRequiredMajor-stderr.txt b/Tests/RunCMake/CMakePresets/MinimumRequiredMajor-stderr.txt
index 6036fe3ae3..c9001ea13b 100644
--- a/Tests/RunCMake/CMakePresets/MinimumRequiredMajor-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/MinimumRequiredMajor-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/MinimumRequiredMajor: "cmakeMinimumRequired" version too new$
+]*/Tests/RunCMake/CMakePresets/MinimumRequiredMajor:
+Error: @4,14: "cmakeMinimumRequired" major version 1000 must be less than [0-9]*
+ "major": 1000
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/MinimumRequiredMinor-stderr.txt b/Tests/RunCMake/CMakePresets/MinimumRequiredMinor-stderr.txt
index bdee4cdeb8..3911c84e84 100644
--- a/Tests/RunCMake/CMakePresets/MinimumRequiredMinor-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/MinimumRequiredMinor-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/MinimumRequiredMinor: "cmakeMinimumRequired" version too new$
+]*/Tests/RunCMake/CMakePresets/MinimumRequiredMinor:
+Error: @5,14: "cmakeMinimumRequired" minor version 1000 must be less than [0-9]*
+ "minor": 1000
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/MinimumRequiredPatch-stderr.txt b/Tests/RunCMake/CMakePresets/MinimumRequiredPatch-stderr.txt
index b5d3a393f0..e989fa8a7f 100644
--- a/Tests/RunCMake/CMakePresets/MinimumRequiredPatch-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/MinimumRequiredPatch-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/MinimumRequiredPatch: "cmakeMinimumRequired" version too new$
+]*/Tests/RunCMake/CMakePresets/MinimumRequiredPatch:
+Error: @6,14: "cmakeMinimumRequired" patch version 50000000 must be less than [0-9]*
+ "patch": 50000000
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/NoCMakePresets-stderr.txt b/Tests/RunCMake/CMakePresets/NoCMakePresets-stderr.txt
index c807ffc988..afc5887f34 100644
--- a/Tests/RunCMake/CMakePresets/NoCMakePresets-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoCMakePresets-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoCMakePresets: File not found$
+]*/Tests/RunCMake/CMakePresets/NoCMakePresets:
+File not found: [^
+]*/CMakePresets.json$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
index b525fc322c..bae979473d 100644
--- a/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoPresetBinaryDir-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoPresetBinaryDir: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/NoPresetBinaryDir:
+Preset "NoPresetBinaryDir" missing field "binaryDir"
+Invalid preset: "NoPresetBinaryDir"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
index 6c0c9f77ab..c7e5b5e23b 100644
--- a/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoPresetGenerator-stderr.txt
@@ -1,2 +1,4 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoPresetGenerator: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/NoPresetGenerator:
+Preset "NoPresetGenerator" missing field "generator"
+Invalid preset: "NoPresetGenerator"$
diff --git a/Tests/RunCMake/CMakePresets/NoPresetName-stderr.txt b/Tests/RunCMake/CMakePresets/NoPresetName-stderr.txt
index 0ee338a369..9aff07f45f 100644
--- a/Tests/RunCMake/CMakePresets/NoPresetName-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoPresetName-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoPresetName: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/NoPresetName:
+Error: @4,5: Missing required field "name" in Preset
+ {
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt b/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt
index 7dafe623df..8c7ca6e9f8 100644
--- a/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoSuchMacro: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/NoSuchMacro:
+Invalid macro expansion in "NoSuchMacro"$
diff --git a/Tests/RunCMake/CMakePresets/NoTrace.cmake b/Tests/RunCMake/CMakePresets/NoTrace.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/NoTrace.cmake
diff --git a/Tests/RunCMake/CMakePresets/NoVariableValue-stderr.txt b/Tests/RunCMake/CMakePresets/NoVariableValue-stderr.txt
index cdab32f7ba..630c288995 100644
--- a/Tests/RunCMake/CMakePresets/NoVariableValue-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoVariableValue-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoVariableValue: Invalid CMake variable definition$
+]*/Tests/RunCMake/CMakePresets/NoVariableValue:
+Error: @9,16: Missing required field "value" in variable "VAR" for preset "NoVariableValue"
+ "VAR": {}
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/NoVersion-stderr.txt b/Tests/RunCMake/CMakePresets/NoVersion-stderr.txt
index d4f07e499c..f7c95db9fc 100644
--- a/Tests/RunCMake/CMakePresets/NoVersion-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/NoVersion-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/NoVersion: No "version" field$
+]*/Tests/RunCMake/CMakePresets/NoVersion:
+No "version" field$
diff --git a/Tests/RunCMake/CMakePresets/PathListSepFuture-stderr.txt b/Tests/RunCMake/CMakePresets/PathListSepFuture-stderr.txt
index b961aaf459..7652ddc0e1 100644
--- a/Tests/RunCMake/CMakePresets/PathListSepFuture-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/PathListSepFuture-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/PathListSepFuture: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/PathListSepFuture:
+Invalid macro expansion in "PathListSepFuture"$
diff --git a/Tests/RunCMake/CMakePresets/PresetNotObject-stderr.txt b/Tests/RunCMake/CMakePresets/PresetNotObject-stderr.txt
index 6604a14c09..62e92485ac 100644
--- a/Tests/RunCMake/CMakePresets/PresetNotObject-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/PresetNotObject-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/PresetNotObject: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/PresetNotObject:
+Error: @4,5: Invalid Preset
+ \[\]
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index efa838e822..d67e8b14ee 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -150,6 +150,7 @@ run_cmake_presets(InvalidRegex)
set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
run_cmake_presets(ConditionFuture)
run_cmake_presets(SubConditionNull)
+run_cmake_presets(TraceNotSupported)
# Test cmakeMinimumRequired field
run_cmake_presets(MinimumRequiredInvalid)
@@ -326,6 +327,18 @@ set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Debug.json.in")
run_cmake_presets(NoDebug)
run_cmake_presets(Debug)
+# Test trace
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Trace.json.in")
+run_cmake_presets(NoTrace)
+run_cmake_presets(ExplicitNoTrace)
+run_cmake_presets(Trace)
+run_cmake_presets(TraceExpand)
+run_cmake_presets(TraceFormatJSON)
+run_cmake_presets(TraceFormatHuman)
+run_cmake_presets(TraceSource)
+run_cmake_presets(TraceRedirect)
+run_cmake_presets(TraceAll)
+
# Test ${hostSystemName} macro
set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/HostSystemName.json.in")
run_cmake_presets(HostSystemName)
diff --git a/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt b/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
index 42b74d6177..520b473e6a 100644
--- a/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/SubConditionNull: Invalid preset condition$
+]*/Tests/RunCMake/CMakePresets/SubConditionNull:
+Invalid condition for preset "SubConditionNull"$
diff --git a/Tests/RunCMake/CMakePresets/Trace-stderr.txt b/Tests/RunCMake/CMakePresets/Trace-stderr.txt
new file mode 100644
index 0000000000..5ed769cf55
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/Trace-stderr.txt
@@ -0,0 +1,4 @@
+^[^
+]*/Tests/RunCMake/CMakePresets/Trace/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.18 \)
+[^
+]*/Tests/RunCMake/CMakePresets/Trace/CMakeLists.txt\(2\): project\(\${RunCMake_TEST} NONE \)
diff --git a/Tests/RunCMake/CMakePresets/Trace.cmake b/Tests/RunCMake/CMakePresets/Trace.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/Trace.cmake
diff --git a/Tests/RunCMake/CMakePresets/Trace.json.in b/Tests/RunCMake/CMakePresets/Trace.json.in
new file mode 100644
index 0000000000..f50a6f25b3
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/Trace.json.in
@@ -0,0 +1,69 @@
+{
+ "version": 7,
+ "configurePresets": [
+ {
+ "name": "NoTrace",
+ "generator": "@RunCMake_GENERATOR@",
+ "binaryDir": "${sourceDir}/build"
+ },
+ {
+ "name": "ExplicitNoTrace",
+ "inherits": "NoTrace",
+ "trace": {
+ "mode": "off"
+ }
+ },
+ {
+ "name": "Trace",
+ "inherits": "NoTrace",
+ "trace": {
+ "mode": "on"
+ }
+ },
+ {
+ "name": "TraceExpand",
+ "inherits": "NoTrace",
+ "trace": {
+ "mode": "expand"
+ }
+ },
+ {
+ "name": "TraceFormatJSON",
+ "inherits": "NoTrace",
+ "trace": {
+ "format": "json-v1"
+ }
+ },
+ {
+ "name": "TraceFormatHuman",
+ "inherits": "NoTrace",
+ "trace": {
+ "format": "human"
+ }
+ },
+ {
+ "name": "TraceSource",
+ "inherits": "NoTrace",
+ "trace": {
+ "source": "TraceSourceFile.txt"
+ }
+ },
+ {
+ "name": "TraceRedirect",
+ "inherits": "NoTrace",
+ "trace": {
+ "redirect": "TraceRedirectFile.txt"
+ }
+ },
+ {
+ "name": "TraceAll",
+ "inherits": "NoTrace",
+ "trace": {
+ "mode": "expand",
+ "format": "json-v1",
+ "source": "TraceSourceFile.txt",
+ "redirect": "TraceRedirectFile.json"
+ }
+ }
+ ]
+}
diff --git a/Tests/RunCMake/CMakePresets/TraceAll.cmake b/Tests/RunCMake/CMakePresets/TraceAll.cmake
new file mode 100644
index 0000000000..9896ec0b29
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceAll.cmake
@@ -0,0 +1,4 @@
+include(${CMAKE_CURRENT_LIST_DIR}/TraceExpand.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/TraceFormatJSON.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/TraceSource.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/TraceRedirect.cmake)
diff --git a/Tests/RunCMake/CMakePresets/TraceExpand-stderr.txt b/Tests/RunCMake/CMakePresets/TraceExpand-stderr.txt
new file mode 100644
index 0000000000..7ee4fea2b8
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceExpand-stderr.txt
@@ -0,0 +1,4 @@
+^[^
+]*/Tests/RunCMake/CMakePresets/TraceExpand/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.18 \)
+[^
+]*/Tests/RunCMake/CMakePresets/TraceExpand/CMakeLists.txt\(2\): project\(TraceExpand NONE \)
diff --git a/Tests/RunCMake/CMakePresets/TraceExpand.cmake b/Tests/RunCMake/CMakePresets/TraceExpand.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceExpand.cmake
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatHuman-stderr.txt b/Tests/RunCMake/CMakePresets/TraceFormatHuman-stderr.txt
new file mode 100644
index 0000000000..1d3450daea
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceFormatHuman-stderr.txt
@@ -0,0 +1,4 @@
+^[^
+]*/Tests/RunCMake/CMakePresets/TraceFormatHuman/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.18 \)
+[^
+]*/Tests/RunCMake/CMakePresets/TraceFormatHuman/CMakeLists.txt\(2\): project\(\${RunCMake_TEST} NONE \)
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake b/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceFormatHuman.cmake
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatJSON-stderr.txt b/Tests/RunCMake/CMakePresets/TraceFormatJSON-stderr.txt
new file mode 100644
index 0000000000..edf044c655
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceFormatJSON-stderr.txt
@@ -0,0 +1,3 @@
+^{"version":{"major":1,"minor":2}}
+{"args":\["VERSION","3\.18"\],"cmd":"cmake_minimum_required","file":"[^"]*/Tests/RunCMake/CMakePresets/TraceFormatJSON/CMakeLists\.txt","frame":1,"global_frame":1,"line":1,"time":[0-9.]+}
+{"args":\["\${RunCMake_TEST}","NONE"\],"cmd":"project","file":"[^"]*/Tests/RunCMake/CMakePresets/TraceFormatJSON/CMakeLists\.txt","frame":1,"global_frame":1,"line":2,"time":[0-9.]+}
diff --git a/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake b/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceFormatJSON.cmake
diff --git a/Tests/RunCMake/CMakePresets/TraceNotSupported-result.txt b/Tests/RunCMake/CMakePresets/TraceNotSupported-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceNotSupported-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/TraceNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/TraceNotSupported-stderr.txt
new file mode 100644
index 0000000000..de19a8c36f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceNotSupported-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/TraceNotSupported:
+File version must be 7 or higher for trace preset support$
diff --git a/Tests/RunCMake/CMakePresets/TraceNotSupported.json.in b/Tests/RunCMake/CMakePresets/TraceNotSupported.json.in
new file mode 100644
index 0000000000..f3d3fbdb3a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceNotSupported.json.in
@@ -0,0 +1,13 @@
+{
+ "version": 6,
+ "configurePresets": [
+ {
+ "name": "TraceNotSupported",
+ "generator": "@RunCMake_GENERATOR@",
+ "binaryDir": "${sourceDir}/build",
+ "trace": {
+ "mode": "expand"
+ }
+ }
+ ]
+}
diff --git a/Tests/RunCMake/CMakePresets/TraceRedirect.cmake b/Tests/RunCMake/CMakePresets/TraceRedirect.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceRedirect.cmake
diff --git a/Tests/RunCMake/CMakePresets/TraceSource.cmake b/Tests/RunCMake/CMakePresets/TraceSource.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/TraceSource.cmake
diff --git a/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt b/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt
index f9481f091d..abdbb810c0 100644
--- a/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UnclosedMacro: Invalid macro expansion$
+]*/Tests/RunCMake/CMakePresets/UnclosedMacro:
+Invalid macro expansion in "UnclosedMacro"$
diff --git a/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy-stderr.txt b/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy-stderr.txt
index cf17881d7a..606376264c 100644
--- a/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/UnknownArchitectureStrategy:
+Error: @9,21: Invalid preset
+ "strategy": "unknown"
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy-stderr.txt b/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy-stderr.txt
index 8f9be2998d..55f9c7a779 100644
--- a/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/UnknownToolsetStrategy:
+Error: @9,21: Invalid preset
+ "strategy": "unknown"
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/UserDuplicateCross-stderr.txt b/Tests/RunCMake/CMakePresets/UserDuplicateCross-stderr.txt
index 125265f0a6..8b4139ffd9 100644
--- a/Tests/RunCMake/CMakePresets/UserDuplicateCross-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UserDuplicateCross-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UserDuplicateCross: Duplicate presets$
+]*/Tests/RunCMake/CMakePresets/UserDuplicateCross:
+Duplicate preset: "UserDuplicateCross"$
diff --git a/Tests/RunCMake/CMakePresets/UserDuplicateInUser-stderr.txt b/Tests/RunCMake/CMakePresets/UserDuplicateInUser-stderr.txt
index 1071b1775d..15c46f48fd 100644
--- a/Tests/RunCMake/CMakePresets/UserDuplicateInUser-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UserDuplicateInUser-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UserDuplicateInUser: Duplicate presets$
+]*/Tests/RunCMake/CMakePresets/UserDuplicateInUser:
+Duplicate preset: "UserDuplicateInUser"$
diff --git a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
index 5ad8b4b6b6..2ce1316413 100644
--- a/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/UserInheritance-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/UserInheritance: Inherited preset is unreachable from preset's file$
+]*/Tests/RunCMake/CMakePresets/UserInheritance:
+Inherited preset "UserInheritance" is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresets/VariableNotObject-stderr.txt b/Tests/RunCMake/CMakePresets/VariableNotObject-stderr.txt
index 8cacb0a26e..ce1aa20ef7 100644
--- a/Tests/RunCMake/CMakePresets/VariableNotObject-stderr.txt
+++ b/Tests/RunCMake/CMakePresets/VariableNotObject-stderr.txt
@@ -1,2 +1,5 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/VariableNotObject: Invalid CMake variable definition$
+]*/Tests/RunCMake/CMakePresets/VariableNotObject:
+Error: @9,16: Invalid CMake variable "VAR" for preset "VariableNotObject"
+ "VAR": \[\]
+ \^$
diff --git a/Tests/RunCMake/CMakePresets/validate_schema.py b/Tests/RunCMake/CMakePresets/validate_schema.py
index b2a67fc4b9..836147aab2 100644
--- a/Tests/RunCMake/CMakePresets/validate_schema.py
+++ b/Tests/RunCMake/CMakePresets/validate_schema.py
@@ -4,13 +4,13 @@ import os.path
import sys
-with open(sys.argv[1], "rb") as f:
- contents = json.loads(f.read().decode("utf-8-sig"))
+with open(sys.argv[1], "r", encoding="utf-8-sig") as f:
+ contents = json.load(f)
schema_file = os.path.join(
os.path.dirname(__file__),
"..", "..", "..", "Help", "manual", "presets", "schema.json")
-with open(schema_file) as f:
+with open(schema_file, "r", encoding="utf-8") as f:
schema = json.load(f)
jsonschema.validate(contents, schema)
diff --git a/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
index f08f4c11ba..aea1dcebd4 100644
--- a/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsBuild/ConditionFuture: File version must be 3 or higher for condition support$
+]*/Tests/RunCMake/CMakePresetsBuild/ConditionFuture:
+File version must be 3 or higher for condition support$
diff --git a/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable-build-x-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable-build-x-stderr.txt
index 05695d9f75..13b4158dca 100644
--- a/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable-build-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable-build-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable: Configure preset is unreachable from preset's file$
+]*/Tests/RunCMake/CMakePresetsBuild/ConfigurePresetUnreachable:
+Configure preset "x" is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-build-badConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-build-badConfigurePreset-stderr.txt
index 303632e54f..b62fd164fa 100644
--- a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-build-badConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-build-badConfigurePreset-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset: Invalid "configurePreset" field
+]*/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset:
+Invalid "configurePreset": "badConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-configure-default-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-configure-default-stderr.txt
index 303632e54f..b62fd164fa 100644
--- a/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-configure-default-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset-configure-default-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset: Invalid "configurePreset" field
+]*/Tests/RunCMake/CMakePresetsBuild/InvalidConfigurePreset:
+Invalid "configurePreset": "badConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
index fcb37bc439..9ce6ea5aad 100644
--- a/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset-build-noConfigurePreset-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset: Invalid preset
+]*/Tests/RunCMake/CMakePresetsBuild/NoConfigurePreset:
+Invalid preset: "noConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt
index d6ae62d6a4..48a5bd39a8 100644
--- a/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported-build-x-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported: File version must be 2 or higher for build and test preset support.
+]*Tests/RunCMake/CMakePresetsBuild/PresetsUnsupported:
+File version must be 2 or higher for build and test preset support$
diff --git a/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt
index 4c461e3f8b..38ec6bfc53 100644
--- a/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion: File version must be 6 or higher for package preset support$
+]*/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion:
+File version must be 6 or higher for package preset support$
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
index b814bbb94b..3f1a506571 100644
--- a/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/ConditionFuture: File version must be 3 or higher for condition support$
+]*/Tests/RunCMake/CMakePresetsTest/ConditionFuture:
+File version must be 3 or higher for condition support$
diff --git a/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable-test-x-stderr.txt
index d49148deb8..0be98efc30 100644
--- a/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable-test-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable-test-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable: Configure preset is unreachable from preset's file$
+]*/Tests/RunCMake/CMakePresetsTest/ConfigurePresetUnreachable:
+Configure preset "x" is unreachable from preset's file
diff --git a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-configure-default-stderr.txt b/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-configure-default-stderr.txt
index 3d7cdd075e..c427e4221b 100644
--- a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-configure-default-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-configure-default-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset: Invalid "configurePreset" field
+]*/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset:
+Invalid "configurePreset": "badConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-test-badConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-test-badConfigurePreset-stderr.txt
index 3d7cdd075e..c427e4221b 100644
--- a/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-test-badConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset-test-badConfigurePreset-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset: Invalid "configurePreset" field
+]*/Tests/RunCMake/CMakePresetsTest/InvalidConfigurePreset:
+Invalid "configurePreset": "badConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
index b167f68ef6..a70497f295 100644
--- a/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset-test-noConfigurePreset-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset: Invalid preset
+]*/Tests/RunCMake/CMakePresetsTest/NoConfigurePreset:
+Invalid preset: "noConfigurePreset"$
diff --git a/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt
index acd5785489..da94c3a027 100644
--- a/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported: File version must be 6 or higher for CTest JUnit output support$
+]*/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported:
+File version must be 6 or higher for CTest JUnit output support$
diff --git a/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt
index eb0ec1a225..74c0740018 100644
--- a/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/PresetsUnsupported-test-x-stderr.txt
@@ -1,2 +1,3 @@
CMake Error: Could not read presets from [^
-]*Tests/RunCMake/CMakePresetsTest/PresetsUnsupported: File version must be 2 or higher for build and test preset support.
+]*Tests/RunCMake/CMakePresetsTest/PresetsUnsupported:
+File version must be 2 or higher for build and test preset support
diff --git a/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt
index 90ea7c3b28..4deb755a9e 100644
--- a/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported: File version must be 5 or higher for testOutputTruncation preset support\.$
+]*/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported:
+File version must be 5 or higher for testOutputTruncation preset support$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt
index 22ca94d0da..33fe914149 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch: Invalid workflow steps$
+]*/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch:
+Invalid workflow step "default"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt
index cbfee5a5c2..776257bd93 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure: Invalid workflow steps$
+]*/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure:
+First workflow step "default" must be a configure step$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt
index 049ed6bc51..f34a6053a9 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps: Invalid workflow steps$
+]*/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps:
+No workflow steps specified for "default"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt
index c522b84d42..a9029e116b 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep: Invalid workflow steps$
+]*/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep:
+Invalid workflow step "default"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt
index b0ad7d530e..35eac16c38 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure: Invalid workflow steps$
+]*/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure:
+Configure workflow step "default" must be the first step
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt
index 425e7196f0..f0a36f898a 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep: Workflow step is unreachable from preset's file$
+]*/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep:
+Workflow step "default" is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt
index 5cf01aaa51..93e31eb99d 100644
--- a/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt
+++ b/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt
@@ -1,2 +1,3 @@
^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion: File version must be 6 or higher for workflow preset support$
+]*/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion:
+File version must be 6 or higher for workflow preset support$
diff --git a/Tests/RunCMake/CPack/CMakeLists.txt b/Tests/RunCMake/CPack/CMakeLists.txt
index c81b34ecd6..18a673e7d7 100644
--- a/Tests/RunCMake/CPack/CMakeLists.txt
+++ b/Tests/RunCMake/CPack/CMakeLists.txt
@@ -1,8 +1,4 @@
-cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
-
-if(POLICY CMP0129)
- cmake_policy(SET CMP0129 NEW)
-endif()
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "")
diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake
index ef4cf5ed10..ca02b76fdd 100644
--- a/Tests/RunCMake/CPack/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
include(RunCMake)
include("${RunCMake_SOURCE_DIR}/CPackTestHelpers.cmake")
diff --git a/Tests/RunCMake/CPackConfig/CMakeLists.txt b/Tests/RunCMake/CPackConfig/CMakeLists.txt
index 1e071ec527..2b3e1f9914 100644
--- a/Tests/RunCMake/CPackConfig/CMakeLists.txt
+++ b/Tests/RunCMake/CPackConfig/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST})
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt
index 89ff7c49fe..404e162d5e 100644
--- a/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt
+++ b/Tests/RunCMake/CPackInstallProperties/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CPackSymlinks/testcpacksym.tar b/Tests/RunCMake/CPackSymlinks/testcpacksym.tar
index c24af48a24..7cfcb363fa 100644
--- a/Tests/RunCMake/CPackSymlinks/testcpacksym.tar
+++ b/Tests/RunCMake/CPackSymlinks/testcpacksym.tar
Binary files differ
diff --git a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt
+++ b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt
+++ b/Tests/RunCMake/CSharpReferenceImport/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt
new file mode 100644
index 0000000000..06fce771bb
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0145-Dart-NEW\.cmake:[0-9]+ \(include\):
+ include could not find requested file:
+
+ Dart
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake
new file mode 100644
index 0000000000..5b14ecc3e2
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0145 NEW)
+include(Dart)
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake
new file mode 100644
index 0000000000..2f66c3f314
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0145 OLD)
+set(_FindDart_testing 1)
+include(Dart)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt
new file mode 100644
index 0000000000..5a751fc68b
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-WARN-stderr.txt
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\):
+ Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run
+ "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy
+ command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.
++
+CMake Warning \(dev\) at [^
+]*/Modules/Dart\.cmake:[0-9]+ \(message\):
+ Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run
+ "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy
+ command to set the policy and suppress this warning\.
+Call Stack \(most recent call first\):
+ CMP0145-Dart-WARN\.cmake:[0-9]+ \(include\)
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake b/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake
new file mode 100644
index 0000000000..1398dbe379
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-Dart-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0145.
+set(_FindDart_testing 1)
+include(Dart)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt
new file mode 100644
index 0000000000..b0456362f9
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0145-FindDart-NEW\.cmake:[0-9]+ \(include\):
+ include could not find requested file:
+
+ FindDart
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake
new file mode 100644
index 0000000000..c1227d69e8
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0145 NEW)
+include(FindDart)
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake
new file mode 100644
index 0000000000..b9f3c76708
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0145 OLD)
+set(_FindDart_testing 1)
+include(FindDart)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt
new file mode 100644
index 0000000000..d07623562c
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at CMP0145-FindDart-WARN\.cmake:[0-9]+ \(include\):
+ Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run
+ "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy
+ command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake
new file mode 100644
index 0000000000..59febdf34e
--- /dev/null
+++ b/Tests/RunCMake/CTest/CMP0145-FindDart-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0145.
+set(_FindDart_testing 1)
+include(FindDart)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/CTest/CMakeLists.txt b/Tests/RunCMake/CTest/CMakeLists.txt
index f1a83e8c4b..1319aecc3d 100644
--- a/Tests/RunCMake/CTest/CMakeLists.txt
+++ b/Tests/RunCMake/CTest/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
if(NOT NoProject)
project(${RunCMake_TEST} NONE)
endif()
diff --git a/Tests/RunCMake/CTest/RunCMakeTest.cmake b/Tests/RunCMake/CTest/RunCMakeTest.cmake
index b81f319f58..4c2c107647 100644
--- a/Tests/RunCMake/CTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTest/RunCMakeTest.cmake
@@ -39,3 +39,10 @@ endfunction()
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
run_SingleConfig()
endif()
+
+run_cmake(CMP0145-Dart-OLD)
+run_cmake(CMP0145-Dart-WARN)
+run_cmake(CMP0145-Dart-NEW)
+run_cmake(CMP0145-FindDart-OLD)
+run_cmake(CMP0145-FindDart-WARN)
+run_cmake(CMP0145-FindDart-NEW)
diff --git a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/CTestCommandLine/CMakeLists.txt
+++ b/Tests/RunCMake/CTestCommandLine/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CTestCommandLine/test.cmake.in b/Tests/RunCMake/CTestCommandLine/test.cmake.in
index b82968a9f5..11bede7215 100644
--- a/Tests/RunCMake/CTestCommandLine/test.cmake.in
+++ b/Tests/RunCMake/CTestCommandLine/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/CTestResourceAllocation/ctresalloc.cxx b/Tests/RunCMake/CTestResourceAllocation/ctresalloc.cxx
index 5c6c8d87b6..65f17b6e32 100644
--- a/Tests/RunCMake/CTestResourceAllocation/ctresalloc.cxx
+++ b/Tests/RunCMake/CTestResourceAllocation/ctresalloc.cxx
@@ -286,8 +286,7 @@ static int doVerify(int argc, char const* const* argv)
std::set<std::string> testNameSet(testNameList.begin(), testNameList.end());
cmCTestResourceSpec spec;
- if (spec.ReadFromJSONFile(resFile) !=
- cmCTestResourceSpec::ReadFileResult::READ_OK) {
+ if (spec.ReadFromJSONFile(resFile) != true) {
std::cout << "Could not read resource spec " << resFile << std::endl;
return 1;
}
diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in
index e9592f6d17..cfcf56d256 100644
--- a/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in
+++ b/Tests/RunCMake/CTestTimeoutAfterMatch/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(TimeoutAfterMatch NONE)
include(CTest)
add_test(NAME SleepFor1Second COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_SOURCE_DIR}/SleepFor1Second.cmake)
diff --git a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in
index d049c9ff0b..172d2c6bcf 100644
--- a/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in
+++ b/Tests/RunCMake/CTestTimeoutAfterMatch/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/CacheNewline/CacheNewline.cmake b/Tests/RunCMake/CacheNewline/CacheNewline.cmake
index 81851dbe01..418a8473e1 100644
--- a/Tests/RunCMake/CacheNewline/CacheNewline.cmake
+++ b/Tests/RunCMake/CacheNewline/CacheNewline.cmake
@@ -1,5 +1 @@
-cmake_minimum_required(VERSION 3.5)
-
-project(CacheNewlineTest NONE)
-
set(NEWLINE_VARIABLE "a\nb" CACHE STRING "Offending entry")
diff --git a/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt b/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt
index 30cb9aebf0..67eae6576c 100644
--- a/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt
+++ b/Tests/RunCMake/CheckCompilerFlag/CMakeLists.txt
@@ -1,9 +1,3 @@
cmake_minimum_required(VERSION 3.13)
-
-if(POLICY CMP0129)
- cmake_policy(SET CMP0129 NEW)
-endif()
-
project(${RunCMake_TEST} LANGUAGES NONE)
-
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt
index 4a13d29a0e..9f18d8d2a3 100644
--- a/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt
+++ b/Tests/RunCMake/CheckIPOSupported/CMakeLists.txt
@@ -1,7 +1,5 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.9)
project(${RunCMake_TEST} NONE)
-cmake_policy(SET CMP0069 NEW)
-
include(CheckIPOSupported)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CheckModules/CMakeLists.txt b/Tests/RunCMake/CheckModules/CMakeLists.txt
index 842c5cf523..93ee9dfd5f 100644
--- a/Tests/RunCMake/CheckModules/CMakeLists.txt
+++ b/Tests/RunCMake/CheckModules/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
-cmake_policy(SET CMP0054 NEW)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ClangTidy/CXX.cmake b/Tests/RunCMake/ClangTidy/CXX.cmake
index 2d22325ccd..3214122544 100644
--- a/Tests/RunCMake/ClangTidy/CXX.cmake
+++ b/Tests/RunCMake/ClangTidy/CXX.cmake
@@ -1,3 +1,3 @@
enable_language(CXX)
-set(CMAKE_CXX_CLANG_TIDY "${PSEUDO_TIDY}" -some -args)
+set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -some -args)
add_executable(main main.cxx)
diff --git a/Tests/RunCMake/CommandLine/CMakeLists.txt b/Tests/RunCMake/CommandLine/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/CommandLine/CMakeLists.txt
+++ b/Tests/RunCMake/CommandLine/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt
new file mode 100644
index 0000000000..c3329a0ed8
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Warning:
+ The "Visual Studio 9 2008" generator is deprecated and will be removed in a
+ future version of CMake.
+
+ Add CMAKE_WARN_VS9=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake
diff --git a/Tests/RunCMake/CommandLine/E_time-stdout.txt b/Tests/RunCMake/CommandLine/E_time-stdout.txt
index a51446a67e..1a5e13479b 100644
--- a/Tests/RunCMake/CommandLine/E_time-stdout.txt
+++ b/Tests/RunCMake/CommandLine/E_time-stdout.txt
@@ -1,3 +1,3 @@
^hello world
-Elapsed time: [^
+Elapsed time \(seconds\): [^
]*$
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 943be248ce..205949b568 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
include(RunCMake)
@@ -1101,6 +1101,13 @@ set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${P
run_cmake(ProfilingTest)
unset(RunCMake_TEST_OPTIONS)
+if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 2008")
+ run_cmake_with_options(DeprecateVS9-WARN-ON -DCMAKE_WARN_VS9=ON)
+ unset(ENV{CMAKE_WARN_VS9})
+ run_cmake(DeprecateVS9-WARN-ON)
+ run_cmake_with_options(DeprecateVS9-WARN-OFF -DCMAKE_WARN_VS9=OFF)
+endif()
+
if(RunCMake_GENERATOR MATCHES "^Visual Studio 11 2012")
run_cmake_with_options(DeprecateVS11-WARN-ON -DCMAKE_WARN_VS11=ON)
unset(ENV{CMAKE_WARN_VS11})
diff --git a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt
index 4fee9bc96f..b900686606 100644
--- a/Tests/RunCMake/CommandLine/trace-expand-stderr.txt
+++ b/Tests/RunCMake/CommandLine/trace-expand-stderr.txt
@@ -1,2 +1,2 @@
-^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \)
+^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \)
.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand NONE \)
diff --git a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt
index 74429b6433..88aad00698 100644
--- a/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt
+++ b/Tests/RunCMake/CommandLine/trace-expand-warn-uninitialized-stderr.txt
@@ -1,2 +1,2 @@
-^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \)
+^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \)
.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(trace-expand-warn-uninitialized NONE \)
diff --git a/Tests/RunCMake/CommandLine/trace-stderr.txt b/Tests/RunCMake/CommandLine/trace-stderr.txt
index 8e8ddfa59b..4bf3cffb43 100644
--- a/Tests/RunCMake/CommandLine/trace-stderr.txt
+++ b/Tests/RunCMake/CommandLine/trace-stderr.txt
@@ -1,2 +1,2 @@
-^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.0 \)
+^.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(1\): cmake_minimum_required\(VERSION 3.5 \)
.*/Tests/RunCMake/CommandLine/CMakeLists.txt\(2\): project\(\${RunCMake_TEST} NONE \)
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
index 982cb89024..4107aa4cc7 100644
--- a/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
+++ b/Tests/RunCMake/CommandLine/trace-try_compile-redirect.cmake
@@ -1,2 +1,2 @@
-cmake_minimum_required(VERSION 3.24)
-project(test C)
+cmake_policy(VERSION 3.24)
+enable_language(C)
diff --git a/Tests/RunCMake/CommandLine/trace-try_compile.cmake b/Tests/RunCMake/CommandLine/trace-try_compile.cmake
index 982cb89024..4107aa4cc7 100644
--- a/Tests/RunCMake/CommandLine/trace-try_compile.cmake
+++ b/Tests/RunCMake/CommandLine/trace-try_compile.cmake
@@ -1,2 +1,2 @@
-cmake_minimum_required(VERSION 3.24)
-project(test C)
+cmake_policy(VERSION 3.24)
+enable_language(C)
diff --git a/Tests/RunCMake/CommandLineTar/CMakeLists.txt b/Tests/RunCMake/CommandLineTar/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/CommandLineTar/CMakeLists.txt
+++ b/Tests/RunCMake/CommandLineTar/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt
index ebab7a357a..12a7fd463a 100644
--- a/Tests/RunCMake/CompatibleInterface/CMakeLists.txt
+++ b/Tests/RunCMake/CompatibleInterface/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake b/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake
index 64b52d9e1a..60a4246c30 100644
--- a/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake
+++ b/Tests/RunCMake/CompatibleInterface/DebugProperties.cmake
@@ -1,7 +1,4 @@
-
-cmake_minimum_required(VERSION 3.3)
-
-project(CompatibleInterface)
+enable_language(CXX)
include(GenerateExportHeader)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
diff --git a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/CompileDefinitions/CMakeLists.txt
+++ b/Tests/RunCMake/CompileDefinitions/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompileFeatures/CMakeLists.txt b/Tests/RunCMake/CompileFeatures/CMakeLists.txt
index 3482e6baf2..12a7fd463a 100644
--- a/Tests/RunCMake/CompileFeatures/CMakeLists.txt
+++ b/Tests/RunCMake/CompileFeatures/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompilerArgs/CMakeLists.txt b/Tests/RunCMake/CompilerArgs/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/CompilerArgs/CMakeLists.txt
+++ b/Tests/RunCMake/CompilerArgs/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompilerChange/CMakeLists.txt b/Tests/RunCMake/CompilerChange/CMakeLists.txt
index 14c47ad6bb..b41f3f323a 100644
--- a/Tests/RunCMake/CompilerChange/CMakeLists.txt
+++ b/Tests/RunCMake/CompilerChange/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
if(NOT RunCMake_TEST)
set(RunCMake_TEST "$ENV{RunCMake_TEST}") # needed when cache is deleted
endif()
diff --git a/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt
index 3313e31f63..544b65fceb 100644
--- a/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/C-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.*
diff --git a/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt
index 3313e31f63..544b65fceb 100644
--- a/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/C-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.*
diff --git a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/CompilerLauncher/CMakeLists.txt
+++ b/Tests/RunCMake/CompilerLauncher/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt
index 3313e31f63..a6e8b0aba6 100644
--- a/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/CUDA-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CUDA.*
diff --git a/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt
index 3313e31f63..a6e8b0aba6 100644
--- a/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/CUDA-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CUDA.*
diff --git a/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt
index 3313e31f63..082c7b575f 100644
--- a/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/CXX-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.*
diff --git a/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt
index 3313e31f63..082c7b575f 100644
--- a/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/CXX-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.*
diff --git a/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt
index 3313e31f63..9f8c7541c8 100644
--- a/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/Fortran-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=Fortran.*
diff --git a/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt
index 3313e31f63..9f8c7541c8 100644
--- a/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/Fortran-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=Fortran.*
diff --git a/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt
index 3313e31f63..354e317028 100644
--- a/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/HIP-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=HIP.*
diff --git a/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt
deleted file mode 100644
index 3313e31f63..0000000000
--- a/Tests/RunCMake/CompilerLauncher/HIP-env-launch-Build-stdout.txt
+++ /dev/null
@@ -1 +0,0 @@
-.*-E env USED_LAUNCHER=1.*
diff --git a/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt
new file mode 100644
index 0000000000..354e317028
--- /dev/null
+++ b/Tests/RunCMake/CompilerLauncher/HIP-launch-env-Build-stdout.txt
@@ -0,0 +1 @@
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=HIP.*
diff --git a/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt
index 3313e31f63..6b71839b41 100644
--- a/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/ISPC-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=ISPC.*
diff --git a/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt
index 3313e31f63..6b71839b41 100644
--- a/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/ISPC-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=ISPC.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt
index 3313e31f63..d2efd3d539 100644
--- a/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/OBJC-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt
deleted file mode 100644
index 3313e31f63..0000000000
--- a/Tests/RunCMake/CompilerLauncher/OBJC-env-launch-Build-stdout.txt
+++ /dev/null
@@ -1 +0,0 @@
-.*-E env USED_LAUNCHER=1.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt
new file mode 100644
index 0000000000..d2efd3d539
--- /dev/null
+++ b/Tests/RunCMake/CompilerLauncher/OBJC-launch-env-Build-stdout.txt
@@ -0,0 +1 @@
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt
index 3313e31f63..0082ab2b4b 100644
--- a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt
+++ b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt
deleted file mode 100644
index 3313e31f63..0000000000
--- a/Tests/RunCMake/CompilerLauncher/OBJCXX-env-launch-Build-stdout.txt
+++ /dev/null
@@ -1 +0,0 @@
-.*-E env USED_LAUNCHER=1.*
diff --git a/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt b/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt
new file mode 100644
index 0000000000..0082ab2b4b
--- /dev/null
+++ b/Tests/RunCMake/CompilerLauncher/OBJCXX-launch-env-Build-stdout.txt
@@ -0,0 +1 @@
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.*
diff --git a/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake b/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake
index e6a26052cc..b051a19db7 100644
--- a/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake
@@ -18,7 +18,7 @@ endfunction()
function(run_compiler_launcher_env lang)
string(REGEX REPLACE "-.*" "" core_lang "${lang}")
# Use the noop genexp $<PATH:...> genexp to validate genexp support.
- set(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1")
+ set(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1;TARGET_NAME=$<TARGET_PROPERTY:NAME>;LANGUAGE=$<COMPILE_LANGUAGE>")
run_compiler_launcher(${lang})
unset(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER})
endfunction()
diff --git a/Tests/RunCMake/Configure/CMakeLists.txt b/Tests/RunCMake/Configure/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/Configure/CMakeLists.txt
+++ b/Tests/RunCMake/Configure/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Cppcheck/CXX.cmake b/Tests/RunCMake/Cppcheck/CXX.cmake
index 3b79471276..7030c61b5e 100644
--- a/Tests/RunCMake/Cppcheck/CXX.cmake
+++ b/Tests/RunCMake/Cppcheck/CXX.cmake
@@ -1,3 +1,3 @@
enable_language(CXX)
-set(CMAKE_CXX_CPPCHECK "${PSEUDO_CPPCHECK}")
+set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>")
add_executable(main main.cxx)
diff --git a/Tests/RunCMake/Cpplint/CXX.cmake b/Tests/RunCMake/Cpplint/CXX.cmake
index 35f05ee91a..b58609cb86 100644
--- a/Tests/RunCMake/Cpplint/CXX.cmake
+++ b/Tests/RunCMake/Cpplint/CXX.cmake
@@ -1,3 +1,3 @@
enable_language(CXX)
-set(CMAKE_CXX_CPPLINT "${PSEUDO_CPPLINT}" --verbose=0 --linelength=80)
+set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --verbose=0 --linelength=80)
add_executable(main main.cxx)
diff --git a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
index 2d75985749..12a7fd463a 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
+++ b/Tests/RunCMake/CrosscompilingEmulator/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake b/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake
index 208ea20855..f3974eacf4 100644
--- a/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake
+++ b/Tests/RunCMake/DisallowedCommands/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
foreach(p
CMP0029
diff --git a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt
+++ b/Tests/RunCMake/ExcludeFromAll/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ExternalData/CMakeLists.txt b/Tests/RunCMake/ExternalData/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/ExternalData/CMakeLists.txt
+++ b/Tests/RunCMake/ExternalData/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt
new file mode 100644
index 0000000000..650be6430e
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies-stderr.txt
@@ -0,0 +1,10 @@
+^(CMake Deprecation Warning at Add_StepDependencies.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0114 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\))?$
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake
index 364bf9e526..02c7c8e349 100644
--- a/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake
+++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION ${CMAKE_VERSION})
+cmake_policy(VERSION ${CMAKE_VERSION})
if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12)
cmake_policy(SET CMP0114 NEW)
else()
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt
new file mode 100644
index 0000000000..c142541acd
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target-stderr.txt
@@ -0,0 +1,10 @@
+^(CMake Deprecation Warning at Add_StepDependencies_no_target.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0114 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\))?$
diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake
index da823cdeb6..31b7baf196 100644
--- a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake
+++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION ${CMAKE_VERSION})
+cmake_policy(VERSION ${CMAKE_VERSION})
if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12)
cmake_policy(SET CMP0114 NEW)
else()
diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt
index 2b0feb6e44..2428b8c5fa 100644
--- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-OLD-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Warning \(dev\) at [^
+^CMake Deprecation Warning at NO_DEPENDS-CMP0114-OLD.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0114 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Warning \(dev\) at [^
]*/Modules/ExternalProject.cmake:[0-9]+ \(message\):
Using NO_DEPENDS for "configure" step might break parallel builds
Call Stack \(most recent call first\):
diff --git a/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt
new file mode 100644
index 0000000000..6dd7cb0cc5
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/Steps-CMP0114-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at Steps-CMP0114-OLD.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0114 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake
index 2946c0b6cf..351d70f0e9 100644
--- a/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake
+++ b/Tests/RunCMake/ExternalProject/UsesTerminal-check.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
-
# If we are using the Ninja generator, we can check and verify that the
# USES_TERMINAL option actually works by examining the Ninja build file.
# This is the only way, since CMake doesn't offer a way to examine the
diff --git a/Tests/RunCMake/ExtraGenerators/CMakeLists.txt b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt
new file mode 100644
index 0000000000..93ee9dfd5f
--- /dev/null
+++ b/Tests/RunCMake/ExtraGenerators/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake b/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake
new file mode 100644
index 0000000000..fbef79c8e8
--- /dev/null
+++ b/Tests/RunCMake/ExtraGenerators/RunCMakeTest.cmake
@@ -0,0 +1,19 @@
+include(RunCMake)
+
+foreach(
+ extraGenerator
+ IN ITEMS
+ "CodeBlocks"
+ "CodeLite"
+ "Eclipse CDT4"
+ "Kate"
+ "Sublime Text 2"
+ )
+ block()
+ set(RunCMake_GENERATOR "${extraGenerator} - ${RunCMake_GENERATOR}")
+ set(RunCMake_TEST_VARIANT_DESCRIPTION ": ${RunCMake_GENERATOR}")
+ string(REPLACE " " "" extraGeneratorNoSpaces "${extraGenerator}")
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Simple-${extraGeneratorNoSpaces})
+ run_cmake(Simple)
+ endblock()
+endforeach()
diff --git a/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt
new file mode 100644
index 0000000000..e327a9f3ec
--- /dev/null
+++ b/Tests/RunCMake/ExtraGenerators/Simple-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning:
+ Support for "Extra Generators" like
+
+ [^
+]+
+
+ is deprecated and will be removed from a future version of CMake\. IDEs may
+ use the cmake-file-api\(7\) to view CMake-generated project build trees\.
diff --git a/Tests/RunCMake/ExtraGenerators/Simple.cmake b/Tests/RunCMake/ExtraGenerators/Simple.cmake
new file mode 100644
index 0000000000..d0770469bc
--- /dev/null
+++ b/Tests/RunCMake/ExtraGenerators/Simple.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+enable_language(CXX)
+
+add_subdirectory(../../Simple Simple)
diff --git a/Tests/RunCMake/FPHSA/CMakeLists.txt b/Tests/RunCMake/FPHSA/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/FPHSA/CMakeLists.txt
+++ b/Tests/RunCMake/FPHSA/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FeatureSummary/CMakeLists.txt b/Tests/RunCMake/FeatureSummary/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/FeatureSummary/CMakeLists.txt
+++ b/Tests/RunCMake/FeatureSummary/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake b/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake
new file mode 100644
index 0000000000..f8ee74995c
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/IgnoreToolchainFile.cmake
@@ -0,0 +1,2 @@
+set(ENV{CMAKE_TOOLCHAIN_FILE} path/to/somewhere/iDoNotExist.cmake)
+include(DownloadFile.cmake)
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
index bb27491b6b..378108991a 100644
--- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -7,6 +7,7 @@ run_cmake(DirectIgnoresDetails)
run_cmake(FirstDetailsWin)
run_cmake(DownloadTwice)
run_cmake(DownloadFile)
+run_cmake(IgnoreToolchainFile)
run_cmake(SameGenerator)
run_cmake(System)
run_cmake(VarDefinitions)
diff --git a/Tests/RunCMake/File_Archive/CMakeLists.txt b/Tests/RunCMake/File_Archive/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/File_Archive/CMakeLists.txt
+++ b/Tests/RunCMake/File_Archive/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/File_Generate/CMakeLists.txt b/Tests/RunCMake/File_Generate/CMakeLists.txt
index 3178de5834..eec672f8e2 100644
--- a/Tests/RunCMake/File_Generate/CMakeLists.txt
+++ b/Tests/RunCMake/File_Generate/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
if(NOT TEST_FILE)
set(TEST_FILE ${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt
index 2c385c48f6..b47a137c2f 100644
--- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt
+++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW-stderr.txt
@@ -1,11 +1,11 @@
-^CMake Error at SourceProperty-CMP0070-NEW.cmake:[0-9]+ \(add_library\):
+^CMake Error at SourceProperty-CMP0070-NEW.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
.*\/relative-output-NEW\.c
- Tried extensions \.c \.C.*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
-
-
++
CMake Generate step failed. Build files cannot be regenerated correctly.$
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW.cmake b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW.cmake
index d2b3e0c0a9..16d5563b97 100644
--- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW.cmake
+++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-NEW.cmake
@@ -1,5 +1,5 @@
enable_language(C)
-add_library(foo)
+add_library(foo empty.c)
cmake_policy(SET CMP0070 NEW)
file(GENERATE OUTPUT relative-output-NEW.c CONTENT "")
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt
index fcb53a781b..39735d717d 100644
--- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt
+++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD-stderr.txt
@@ -8,16 +8,15 @@
behavior and not rely on setting a policy to OLD.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
-
-
-CMake Error at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(add_library\):
++
+CMake Error at SourceProperty-CMP0070-OLD.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
.*\/relative-output-OLD\.c
- Tried extensions \.c \.C.*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)
-
-
++
CMake Generate step failed. Build files cannot be regenerated correctly.$
diff --git a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD.cmake b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD.cmake
index 48eae1e450..4f566b095e 100644
--- a/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD.cmake
+++ b/Tests/RunCMake/File_Generate/SourceProperty-CMP0070-OLD.cmake
@@ -1,5 +1,5 @@
enable_language(C)
-add_library(foo)
+add_library(foo empty.c)
cmake_policy(SET CMP0070 OLD)
file(GENERATE OUTPUT relative-output-OLD.c CONTENT "")
diff --git a/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake b/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake
index e0585eebc6..b20f824ab1 100644
--- a/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake
+++ b/Tests/RunCMake/FindGTK2/FindGTK2RunTwice.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.7)
-project(testFindGTK2 C)
+enable_language(C)
# First call
find_package(GTK2 REQUIRED)
diff --git a/Tests/RunCMake/FindLua/CMakeLists.txt b/Tests/RunCMake/FindLua/CMakeLists.txt
index a2c4d98126..e6c41a5cbb 100644
--- a/Tests/RunCMake/FindLua/CMakeLists.txt
+++ b/Tests/RunCMake/FindLua/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FindMatlab/CMakeLists.txt b/Tests/RunCMake/FindMatlab/CMakeLists.txt
index 1b9a957882..93ee9dfd5f 100644
--- a/Tests/RunCMake/FindMatlab/CMakeLists.txt
+++ b/Tests/RunCMake/FindMatlab/CMakeLists.txt
@@ -1,3 +1,3 @@
-
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FindMatlab/MatlabTest1.cmake b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake
index b4cc7415b1..8eaf90358b 100644
--- a/Tests/RunCMake/FindMatlab/MatlabTest1.cmake
+++ b/Tests/RunCMake/FindMatlab/MatlabTest1.cmake
@@ -1,7 +1,6 @@
-
-cmake_minimum_required (VERSION 2.8.12)
+enable_language(C)
+enable_language(CXX)
enable_testing()
-project(test_should_fail)
if(NOT "${matlab_root}" STREQUAL "")
set(Matlab_ROOT_DIR ${matlab_root})
diff --git a/Tests/RunCMake/FindMatlab/MatlabTest2.cmake b/Tests/RunCMake/FindMatlab/MatlabTest2.cmake
index 4295d3c730..95b1c2214d 100644
--- a/Tests/RunCMake/FindMatlab/MatlabTest2.cmake
+++ b/Tests/RunCMake/FindMatlab/MatlabTest2.cmake
@@ -1,6 +1,6 @@
-cmake_minimum_required (VERSION 2.8.12)
+enable_language(C)
+enable_language(CXX)
enable_testing()
-project(findmatlab_runcmake_test2)
if(NOT DEFINED matlab_required)
set(matlab_required REQUIRED)
diff --git a/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake
index deebf89d9f..45dc7993a6 100644
--- a/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindMatlab/RunCMakeTest.cmake
@@ -1,7 +1,5 @@
-
include(RunCMake)
-
if(NOT "${MCR_ROOT}" STREQUAL "")
if(NOT EXISTS "${MCR_ROOT}")
message(FATAL_ERROR "MCR does not exist ${MCR_ROOT}")
diff --git a/Tests/RunCMake/FindOpenSSL/version-exact.cmake b/Tests/RunCMake/FindOpenSSL/version-exact.cmake
index 29c2ce3713..11826cf41d 100644
--- a/Tests/RunCMake/FindOpenSSL/version-exact.cmake
+++ b/Tests/RunCMake/FindOpenSSL/version-exact.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.19...3.20)
-
find_package (OpenSSL REQUIRED COMPONENTS Crypto)
# Store version without a possibly trailing letter.
string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}")
diff --git a/Tests/RunCMake/FindOpenSSL/version-range.cmake b/Tests/RunCMake/FindOpenSSL/version-range.cmake
index 939003206f..f9689b641a 100644
--- a/Tests/RunCMake/FindOpenSSL/version-range.cmake
+++ b/Tests/RunCMake/FindOpenSSL/version-range.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.19...3.20)
-
find_package (OpenSSL REQUIRED COMPONENTS Crypto)
# Store version without a possibly trailing letter.
string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}")
diff --git a/Tests/RunCMake/FindOpenSSL/version.cmake b/Tests/RunCMake/FindOpenSSL/version.cmake
index d06cd1f2b5..3d151ab97e 100644
--- a/Tests/RunCMake/FindOpenSSL/version.cmake
+++ b/Tests/RunCMake/FindOpenSSL/version.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.19...3.20)
-
find_package (OpenSSL REQUIRED COMPONENTS Crypto)
# Store version without a possibly trailing letter.
string (REGEX MATCH "^([0-9.]+)" version "${OPENSSL_VERSION}")
diff --git a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/FindPkgConfig/CMakeLists.txt
+++ b/Tests/RunCMake/FindPkgConfig/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
index 69ab4da000..457747feb5 100644
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_IMPORTED_TARGET.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
-
-project(FindPkgConfig_IMPORTED_TARGET C)
+enable_language(C)
find_package(PkgConfig REQUIRED)
pkg_check_modules(NCURSES IMPORTED_TARGET QUIET ncurses)
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake
index f7a9815b1c..95a2e321d4 100644
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_VERSION_OPERATORS.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
-
-project(FindPkgConfig_IMPORTED_TARGET C)
+enable_language(C)
find_package(PkgConfig REQUIRED)
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake
index d0046caa11..15baa0d753 100644
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_cache_variables.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
-
find_package(PkgConfig REQUIRED)
pkg_check_modules(NCURSES QUIET ncurses)
diff --git a/Tests/RunCMake/FindSWIG/version-exact.cmake b/Tests/RunCMake/FindSWIG/version-exact.cmake
index ec3651fbaf..98903ffc13 100644
--- a/Tests/RunCMake/FindSWIG/version-exact.cmake
+++ b/Tests/RunCMake/FindSWIG/version-exact.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.18...3.19)
-
find_package (SWIG)
if (NOT SWIG_FOUND)
message (FATAL_ERROR "Failed to find SWIG")
diff --git a/Tests/RunCMake/FindSWIG/version-range.cmake b/Tests/RunCMake/FindSWIG/version-range.cmake
index 7ba1134473..e776961861 100644
--- a/Tests/RunCMake/FindSWIG/version-range.cmake
+++ b/Tests/RunCMake/FindSWIG/version-range.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.18...3.19)
-
find_package (SWIG)
if (NOT SWIG_FOUND)
message (FATAL_ERROR "Failed to find SWIG")
diff --git a/Tests/RunCMake/FindSWIG/version.cmake b/Tests/RunCMake/FindSWIG/version.cmake
index a4f1c390ef..b5ed6a71df 100644
--- a/Tests/RunCMake/FindSWIG/version.cmake
+++ b/Tests/RunCMake/FindSWIG/version.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required (VERSION 3.18...3.19)
-
find_package (SWIG 1.0)
if (NOT SWIG_FOUND)
message (FATAL_ERROR "Failed to find SWIG with version 1.0")
diff --git a/Tests/RunCMake/Framework/CMakeLists.txt b/Tests/RunCMake/Framework/CMakeLists.txt
index 6dd8cdf551..93ee9dfd5f 100644
--- a/Tests/RunCMake/Framework/CMakeLists.txt
+++ b/Tests/RunCMake/Framework/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Framework/FrameworkConsumption.cmake b/Tests/RunCMake/Framework/FrameworkConsumption.cmake
index 58b70a31c7..2180cf92dc 100644
--- a/Tests/RunCMake/Framework/FrameworkConsumption.cmake
+++ b/Tests/RunCMake/Framework/FrameworkConsumption.cmake
@@ -1,5 +1,3 @@
-
-cmake_minimum_required(VERSION 3.22...3.24)
enable_language(C)
# Create framework and ensure header is placed in Headers
@@ -24,10 +22,14 @@ set_target_properties(Gui2 PROPERTIES
)
add_executable(app2 main2.c)
-set_target_properties(Gui2 PROPERTIES
- PUBLIC_HEADER "${input_header}"
- FRAMEWORK TRUE
+set_target_properties(app2 PROPERTIES
RUNTIME_OUTPUT_DIRECTORY bin
)
target_link_libraries(app2 PRIVATE Gui2)
+
+
+# Same test with STATIC consumer
+add_library(Consumer STATIC consumer.c)
+
+target_link_libraries(Consumer PRIVATE Gui2)
diff --git a/Tests/RunCMake/Framework/FrameworkLayout.cmake b/Tests/RunCMake/Framework/FrameworkLayout.cmake
index 84012aa364..d09e8a0294 100644
--- a/Tests/RunCMake/Framework/FrameworkLayout.cmake
+++ b/Tests/RunCMake/Framework/FrameworkLayout.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
enable_language(C)
set(CMAKE_CONFIGURATION_TYPES "Debug" CACHE INTERNAL "Supported configuration types")
diff --git a/Tests/RunCMake/Framework/consumer.c b/Tests/RunCMake/Framework/consumer.c
new file mode 100644
index 0000000000..a578976498
--- /dev/null
+++ b/Tests/RunCMake/Framework/consumer.c
@@ -0,0 +1,9 @@
+
+#include <Gui2/Gui.h>
+
+int consumer()
+{
+ foo();
+
+ return 0;
+}
diff --git a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt
+++ b/Tests/RunCMake/GNUInstallDirs/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt
+++ b/Tests/RunCMake/GenEx-COMPILE_LANGUAGE/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in
index cc5ff54d14..d1cb61bc21 100644
--- a/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/ABSOLUTE_PATH.cmake.in
@@ -31,4 +31,28 @@ if (NOT output STREQUAL reference)
endif()
+######################################
+## tests with list of paths
+######################################
+unset (reference)
+foreach(item IN ITEMS "../../a/d" "/a/d/../e")
+ cmake_path(ABSOLUTE_PATH item BASE_DIRECTORY "/x/y/a/f")
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:ABSOLUTE_PATH,../../a/d;/a/d/../e,/x/y/a/f>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+unset (reference)
+foreach(item IN ITEMS "../../a/d" "/a/d/../e")
+ cmake_path(ABSOLUTE_PATH item BASE_DIRECTORY "/x/y/a/f" NORMALIZE)
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:ABSOLUTE_PATH,NORMALIZE,../../a/d;/a/d/../e,/x/y/a/f>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+
check_errors("PATH:ABSOLUTE_PATH" ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in b/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in
index ab967a2a8a..1955480e77 100644
--- a/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/APPEND.cmake.in
@@ -65,4 +65,39 @@ if (WIN32)
endif()
endif()
+
+######################################
+## tests with list of paths
+######################################
+unset(reference)
+foreach(item IN ITEMS "/a/b" "/x/y")
+ cmake_path (APPEND result "${item}" "c")
+ list(APPEND reference "${result}")
+endforeach()
+set(output "$<PATH:APPEND,/a/b;/x/y,c>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+unset(reference)
+foreach(item IN ITEMS "a" "c")
+ cmake_path (APPEND item "")
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:APPEND,a;c,>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+unset(reference)
+foreach(item IN ITEMS "a/" "c/")
+ cmake_path (APPEND item "/b")
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:APPEND,a/;c/,/b>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+
check_errors ("PATH:APPEND" ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in
index 41205fa674..29ebf165b1 100644
--- a/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/CMAKE_PATH.cmake.in
@@ -50,4 +50,46 @@ if (WIN32)
endif()
+######################################
+## tests with list of paths
+######################################
+set(reference "/x/y/z/../../a/d;/x/y/z/../../b/e")
+set(output "$<PATH:CMAKE_PATH,/x/y/z/../../a/d;/x/y/z/../../b/e>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+unset(reference)
+foreach(path IN ITEMS "/x/y/z/../../a/d" "/x/y/z/../../b/e")
+ cmake_path(SET result NORMALIZE "${path}")
+ list(APPEND reference "${result}")
+endforeach()
+set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x/y/z/../../a/d;/x/y/z/../../b/e>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+if (WIN32)
+ unset(reference)
+ foreach(path IN ITEMS "/x\\y/z\\..\\../a/d" "/x\\y/z\\..\\../b/e")
+ cmake_path(SET result "${path}")
+ list(APPEND reference "${result}")
+ endforeach()
+ set(output "$<PATH:CMAKE_PATH,/x\y/z\..\../a/d;/x\y/z\..\../b/e>")
+ if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+ endif()
+
+ unset(reference)
+ foreach(path IN ITEMS "/x\\y/z\\..\\../a/d" "/x\\y/z\\..\\../b/e")
+ cmake_path(SET result NORMALIZE "${path}")
+ list(APPEND reference "${result}")
+ endforeach()
+ set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x\y/z\..\../a/d;/x\y/z\..\../b/e>")
+ if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+ endif()
+endif()
+
+
check_errors("PATH:CMAKE_PATH" ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/CMakeLists.txt b/Tests/RunCMake/GenEx-PATH/CMakeLists.txt
index f9748e968b..5161b995a9 100644
--- a/Tests/RunCMake/GenEx-PATH/CMakeLists.txt
+++ b/Tests/RunCMake/GenEx-PATH/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.18...3.24)
+cmake_minimum_required(VERSION 3.18...3.25)
project(${RunCMake_TEST} NONE)
diff --git a/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in
index b58998cdba..e2acde4ea6 100644
--- a/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/GET_ITEM.cmake.in
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0140 NEW)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
@@ -308,4 +309,126 @@ if (NOT output STREQUAL reference)
endif()
+######################################
+## third, tests with list of paths
+######################################
+if (WIN32)
+ set (paths "C:/aa/bb/cc.ext1.ext2" "D:/xx/yy/zz.ext3.ext4")
+else()
+ set (paths "/aa/bb/cc.ext1.ext2" "/xx/yy/zz.ext3.ext4")
+endif()
+
+function (compute_reference action)
+ unset(reference)
+ foreach (path IN LISTS paths)
+ cmake_path(GET path ${ARGV} result)
+ list(APPEND reference "${result}")
+ endforeach()
+ if (reference STREQUAL "")
+ # define the list as 2 empty elements
+ set(reference ";")
+ endif()
+
+ return(PROPAGATE reference)
+endfunction()
+
+compute_reference(ROOT_NAME)
+if (WIN32)
+ set(output "$<PATH:GET_ROOT_NAME,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_ROOT_NAME,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "ROOT_NAME returns bad data: ${output}")
+endif()
+
+compute_reference(ROOT_DIRECTORY)
+if (WIN32)
+ set(output "$<PATH:GET_ROOT_DIRECTORY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_ROOT_DIRECTORY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "ROOT_DIRECTORY returns bad data: ${output}")
+endif()
+
+compute_reference(ROOT_PATH)
+if (WIN32)
+ set(output "$<PATH:GET_ROOT_PATH,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_ROOT_PATH,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "ROOT_PATH returns bad data: ${output}")
+endif()
+
+compute_reference(FILENAME)
+if (WIN32)
+ set(output "$<PATH:GET_FILENAME,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_FILENAME,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "FILENAME returns bad data: ${output}")
+endif()
+
+compute_reference(EXTENSION)
+if (WIN32)
+ set(output "$<PATH:GET_EXTENSION,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_EXTENSION,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION returns bad data: ${output}")
+endif()
+compute_reference(EXTENSION LAST_ONLY)
+if (WIN32)
+ set(output "$<PATH:GET_EXTENSION,LAST_ONLY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_EXTENSION,LAST_ONLY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION LAST_ONLY returns bad data: ${output}")
+endif()
+
+compute_reference(STEM)
+if (WIN32)
+ set(output "$<PATH:GET_STEM,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_STEM,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "STEM returns bad data: ${output}")
+endif()
+compute_reference(STEM LAST_ONLY)
+if (WIN32)
+ set(output "$<PATH:GET_STEM,LAST_ONLY,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_STEM,LAST_ONLY,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "STEM LAST_ONLY returns bad data: ${reference}")
+endif()
+
+compute_reference(RELATIVE_PART)
+if (WIN32)
+ set(output "$<PATH:GET_RELATIVE_PART,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_RELATIVE_PART,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "RELATIVE_PART returns bad data: ${output}")
+endif()
+
+compute_reference(PARENT_PATH)
+if (WIN32)
+ set(output "$<PATH:GET_PARENT_PATH,C:/aa/bb/cc.ext1.ext2;D:/xx/yy/zz.ext3.ext4>")
+else()
+ set (output "$<PATH:GET_PARENT_PATH,/aa/bb/cc.ext1.ext2;/xx/yy/zz.ext3.ext4>")
+endif()
+if (NOT output STREQUAL reference)
+ list (APPEND errors "PARENT_PATH returns bad data: ${output}")
+endif()
+
+
check_errors("PATH:GET..." ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in
index e6cc4a3868..81e4c0dd42 100644
--- a/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/NORMAL_PATH.cmake.in
@@ -40,4 +40,18 @@ if (WIN32)
endif()
+######################################
+## tests with list of paths
+######################################
+unset (reference)
+foreach(item IN ITEMS "a/./b/.." "x/.//y/z//..")
+ cmake_path(NORMAL_PATH item)
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:NORMAL_PATH,a/./b/..;x/.//y/z//..>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+
check_errors("PATH:NORMAL_PATH" ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in b/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in
index 11d73ad8f7..7670f4fd21 100644
--- a/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/RELATIVE_PATH.cmake.in
@@ -61,4 +61,18 @@ if (WIN32)
endif()
+######################################
+## tests with list of paths
+######################################
+unset (reference)
+foreach(item IN ITEMS "/a//d" "/a/b/e")
+ cmake_path(RELATIVE_PATH item BASE_DIRECTORY "/a/b/c")
+ list(APPEND reference "${item}")
+endforeach()
+set(output "$<PATH:RELATIVE_PATH,/a//d;/a/b/e,/a/b/c>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "'${output}' instead of '${reference}'")
+endif()
+
+
check_errors("PATH:RELATIVE_PATH" ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in
index cce4143591..a365efe578 100644
--- a/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/REMOVE_ITEM.cmake.in
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0140 NEW)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
@@ -62,4 +63,39 @@ if (NOT output STREQUAL reference)
endif()
+######################################
+## tests with list of paths
+######################################
+function (compute_reference action)
+ unset(reference)
+ foreach (path IN LISTS paths)
+ cmake_path(${action} path ${ARGN})
+ list(APPEND reference "${path}")
+ endforeach()
+
+ return(PROPAGATE reference)
+endfunction()
+
+set (paths "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REMOVE_FILENAME)
+set(output "$<PATH:REMOVE_FILENAME,a/b/c.e.f;g/h/i.j.k>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
+endif()
+
+set (paths "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REMOVE_EXTENSION)
+set(output "$<PATH:REMOVE_EXTENSION,a/b/c.e.f;g/h/i.j.k>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
+endif()
+
+set (reference "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REMOVE_EXTENSION LAST_ONLY)
+set(output "$<PATH:REMOVE_EXTENSION,LAST_ONLY,a/b/c.e.f;g/h/i.j.k>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
+endif()
+
+
check_errors("PATH:REMOVE..." ${errors})
diff --git a/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in b/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in
index 5bb04c3117..2d021528b7 100644
--- a/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in
+++ b/Tests/RunCMake/GenEx-PATH/REPLACE_ITEM.cmake.in
@@ -1,3 +1,4 @@
+cmake_policy(SET CMP0140 NEW)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
@@ -70,4 +71,39 @@ if (NOT output STREQUAL reference)
endif()
+######################################
+## tests with list of paths
+######################################
+function (compute_reference action new_value)
+ unset(reference)
+ foreach (path IN LISTS paths)
+ cmake_path(${action} path "${new_value}" ${ARGN})
+ list(APPEND reference "${path}")
+ endforeach()
+
+ return(PROPAGATE reference)
+endfunction()
+
+set (paths "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REPLACE_FILENAME "x.y")
+set(output "$<PATH:REPLACE_FILENAME,a/b/c.e.f;g/h/i.j.k,x.y>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
+endif()
+
+set (paths "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REPLACE_EXTENSION ".x")
+set(output "$<PATH:REPLACE_EXTENSION,a/b/c.e.f;g/h/i.j.k,.x>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
+endif()
+
+set (paths "a/b/c.e.f" "g/h/i.j.k")
+compute_reference(REPLACE_EXTENSION ".x" LAST_ONLY)
+set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,a/b/c.e.f;g/h/i.j.k,.x>")
+if (NOT output STREQUAL reference)
+ list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
+endif()
+
+
check_errors("PATH:REPLACE..." ${errors})
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt
index 4b3de84d94..93ee9dfd5f 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake
index 7eec527c8f..187e7d614e 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME-imported-target.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (GENERATE_CONTENT [[
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake
index 1963244fae..d550431e1e 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_BASE_NAME.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (GENERATE_CONTENT [[
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake
index cc9cd5a8f8..01926fe29d 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX-imported-target.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (win_platforms Windows CYGWIN MSYS)
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake
index edfb40c53b..a9a76bf549 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_PREFIX.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (win_platforms Windows CYGWIN MSYS)
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake
index 3ee42a5c12..ddf38874d8 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX-imported-target.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (win_platforms Windows CYGWIN MSYS)
diff --git a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake
index 1fe75d9516..9c5d9322ed 100644
--- a/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_FILE/TARGET_FILE_SUFFIX.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.14)
-
enable_language (C)
set (win_platforms Windows CYGWIN MSYS)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt
new file mode 100644
index 0000000000..93ee9dfd5f
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.5)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake
new file mode 100644
index 0000000000..04ff640942
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/RunCMakeTest.cmake
@@ -0,0 +1,21 @@
+include(RunCMake)
+
+cmake_policy(SET CMP0057 NEW)
+
+function(run_cmake_with_config test)
+ if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+ set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+ endif()
+ run_cmake(${test})
+endfunction()
+
+run_cmake(TARGET_LINKER_IMPORT_FILE-non-valid-target)
+run_cmake(TARGET_LINKER_LIBRARY_FILE-non-valid-target)
+run_cmake_with_config(TARGET_IMPORT_FILE)
+run_cmake_with_config(TARGET_IMPORT_FILE_SUFFIX)
+
+set (Windows_platforms Windows CYGWIN MSYS)
+if (NOT CMAKE_HOST_SYSTEM_NAME IN_LIST Windows_platforms)
+ run_cmake(TARGET_SONAME_IMPORT_FILE-non-valid-target)
+ run_cmake_with_config(TARGET_SONAME_IMPORT_FILE)
+endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake
new file mode 100644
index 0000000000..9a101fc704
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE-Release-generated.cmake")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake
new file mode 100644
index 0000000000..08765a68fe
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE.cmake
@@ -0,0 +1,47 @@
+enable_language(C)
+
+set (platforms_with_import Windows CYGWIN MSYS)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+ if (NOT "${value}" STREQUAL "${expected}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+ endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+add_executable (exec1 empty.c)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,>\")
+check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared1>,$<TARGET_LINKER_LIBRARY_FILE:shared1>>\")
+check_value (\"TARGET_IMPORT_FILE static library\" \"$<TARGET_IMPORT_FILE:static1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec1>\" \"\")\n")
+
+
+set(lib_with_import ${platforms_with_import})
+set(exec_with_import ${platforms_with_import})
+if (APPLE AND CMAKE_TAPI)
+ list(APPEND lib_with_import Darwin)
+endif()
+if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+ list(APPEND exec_with_import "AIX")
+endif()
+set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE)
+set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
+
+add_library (shared2 SHARED empty.c)
+add_executable (exec2 empty.c)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE shared library\" \"$<TARGET_IMPORT_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,>\")
+check_value (\"TARGET_LINKER_FILE shared library\" \"$<TARGET_LINKER_FILE:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${lib_with_import}>,$<TARGET_LINKER_IMPORT_FILE:shared2>,$<TARGET_LINKER_LIBRARY_FILE:shared2>>\")
+check_value (\"TARGET_IMPORT_FILE executable\" \"$<TARGET_IMPORT_FILE:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${exec_with_import}>,$<TARGET_LINKER_IMPORT_FILE:exec2>,>\")\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake
new file mode 100644
index 0000000000..2a1357aaa9
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-Release-generated.cmake")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake
new file mode 100644
index 0000000000..933471b651
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_IMPORT_FILE_SUFFIX.cmake
@@ -0,0 +1,44 @@
+enable_language (C)
+
+set (platforms_with_import Windows CYGWIN MSYS)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+ if (NOT "${value}" STREQUAL "${expected}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+ endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+add_executable (exec1 empty.c)
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared1>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared1>,>\")
+check_value (\"TARGET_FILE_SUFFIX static default\" \"$<TARGET_IMPORT_FILE_SUFFIX:static1>\" \"\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec1>\" \"\")\n")
+
+
+
+if (APPLE AND CMAKE_TAPI)
+ list(APPEND platforms_with_import Darwin)
+endif()
+if (CMAKE_SYSTEM_NAME STREQUAL "AIX")
+ list(APPEND platforms_with_import AIX)
+endif()
+set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS TRUE)
+set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
+
+add_library (shared2 SHARED empty.c)
+add_executable (exec2 empty.c)
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_IMPORT_FILE_SUFFIX executable default\" \"$<TARGET_IMPORT_FILE_SUFFIX:exec2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:exec2>,>\")
+check_value (\"TARGET_IMPORT_FILE_SUFFIX shared default\" \"$<TARGET_IMPORT_FILE_SUFFIX:shared2>\" \"$<IF:$<IN_LIST:$<PLATFORM_ID>,${platforms_with_import}>,$<TARGET_LINKER_IMPORT_FILE_SUFFIX:shared2>,>\")\n")
+
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_IMPORT_FILE_SUFFIX-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt
new file mode 100644
index 0000000000..8ba2223dfa
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_LINKER_IMPORT_FILE:exe1>
+
+ TARGET_LINKER_IMPORT_FILE is allowed only for libraries and executables
+ with ENABLE_EXPORTS.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake
new file mode 100644
index 0000000000..3f060cdc92
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_IMPORT_FILE-non-valid-target.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+add_executable(exe1 empty.c)
+
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+ CONTENT "[$<TARGET_LINKER_IMPORT_FILE:exe1>]"
+)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt
new file mode 100644
index 0000000000..06e7b3ad77
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_LINKER_LIBRARY_FILE:exe1>
+
+ TARGET_LINKER_LIBRARY_FILE is allowed only for libraries with
+ ENABLE_EXPORTS.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake
new file mode 100644
index 0000000000..bb95546633
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_LINKER_LIBRARY_FILE-non-valid-target.cmake
@@ -0,0 +1,9 @@
+
+enable_language(C)
+
+add_executable(exe1 empty.c)
+
+file(GENERATE
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+ CONTENT "[$<TARGET_LINKER_LIBRARY_FILE:exe1>]"
+)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake
new file mode 100644
index 0000000000..ab4443e10d
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-check.cmake
@@ -0,0 +1 @@
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-Release-generated.cmake")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt
new file mode 100644
index 0000000000..0640088a86
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_SONAME_IMPORT_FILE:static1>
+
+ TARGET_SONAME_IMPORT_FILE is allowed only for SHARED libraries.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake
new file mode 100644
index 0000000000..cc79580292
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE-non-valid-target.cmake
@@ -0,0 +1,8 @@
+enable_language(C)
+
+add_library (static1 STATIC empty.c)
+set_property (TARGET static1 PROPERTY VERSION 2.5.0)
+set_property (TARGET static1 PROPERTY SOVERSION 2.0.0)
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
+ CONTENT "[$<TARGET_SONAME_IMPORT_FILE:static1>]")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake
new file mode 100644
index 0000000000..02ba513759
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/TARGET_SONAME_IMPORT_FILE.cmake
@@ -0,0 +1,32 @@
+enable_language(C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+ if (NOT "${value}" STREQUAL "${expected}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+ endif()
+endmacro()
+]])
+
+add_library (shared1 SHARED empty.c)
+set_property (TARGET shared1 PROPERTY VERSION 2.5.0)
+set_property (TARGET shared1 PROPERTY SOVERSION 2.0.0)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared1>\" \"\")\n")
+
+
+
+add_library (shared2 SHARED empty.c)
+set_property(TARGET shared2 PROPERTY ENABLE_EXPORTS ON)
+set_property (TARGET shared2 PROPERTY VERSION 2.5.0)
+set_property (TARGET shared2 PROPERTY SOVERSION 2.0.0)
+
+
+string (APPEND GENERATE_CONTENT
+"\ncheck_value (\"TARGET_SONAME_IMPORT_FILE shared library\" \"$<TARGET_SONAME_IMPORT_FILE:shared2>\" \"$<$<BOOL:${CMAKE_TAPI}>:$<PATH:REPLACE_EXTENSION,LAST_ONLY,$<TARGET_SONAME_FILE:shared2>,.tbd>>\")\n")
+
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_SONAME_IMPORT_FILE-$<CONFIG>-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}")
diff --git a/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_IMPORT_FILE/empty.c
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake
index 212c03402c..eed194b3b9 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/ALIAS_GLOBAL.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.17)
-
add_library(lib-global SHARED IMPORTED GLOBAL)
add_library(alias-lib-global ALIAS lib-global)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt
index 26a73f974c..32d92d819d 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.10)
if(RunCMake_TEST STREQUAL "LOCATION")
cmake_minimum_required(VERSION 2.8.12) # Leave CMP0026 unset.
endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake
index e9855bed53..0f0c399c88 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/INCLUDE_DIRECTORIES.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.14)
enable_language(C)
add_library(foo1 STATIC empty.c)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt
index a4c8dcd1fb..fab2ce2d18 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/LOCATION-stderr.txt
@@ -1,3 +1,10 @@
+^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
CMake Warning \(dev\) in CMakeLists\.txt:
Policy CMP0026 is not set: Disallow use of the LOCATION target property.
Run "cmake --help-policy CMP0026" for policy details. Use the cmake_policy
diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake
deleted file mode 100644
index e19598e144..0000000000
--- a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/check.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-function(check_genex expected actual)
- if(NOT expected STREQUAL actual)
- string(APPEND RunCMake_TEST_FAILED "Expected DLLs:\n")
- foreach(dll IN LISTS expected)
- string(APPEND RunCMake_TEST_FAILED " ${dll}\n")
- endforeach()
- string(APPEND RunCMake_TEST_FAILED "Actual DLLs:\n")
- foreach(dll IN LISTS actual)
- string(APPEND RunCMake_TEST_FAILED " ${dll}\n")
- endforeach()
- endif()
- set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
-endfunction()
-
-include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake")
diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake
new file mode 100644
index 0000000000..6b05b4ede9
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared-check.cmake
@@ -0,0 +1,15 @@
+function(check_genex expected actual)
+ if(NOT expected STREQUAL actual)
+ string(APPEND RunCMake_TEST_FAILED "Expected items:\n")
+ foreach(item IN LISTS expected)
+ string(APPEND RunCMake_TEST_FAILED " ${item}\n")
+ endforeach()
+ string(APPEND RunCMake_TEST_FAILED "Actual items:\n")
+ foreach(item IN LISTS actual)
+ string(APPEND RunCMake_TEST_FAILED " ${item}\n")
+ endforeach()
+ endif()
+ set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+endfunction()
+
+include("${RunCMake_TEST_BINARY_DIR}/dlls.cmake")
diff --git a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake
index 806f0b60f2..c38fa39831 100644
--- a/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_RUNTIME_DLLS/shared.cmake
@@ -4,6 +4,10 @@ add_executable(exe main.c)
add_library(lib1 SHARED lib1.c)
add_library(lib2 SHARED lib2.c)
add_library(lib3 SHARED lib3.c)
+if(WIN32 OR CYGWIN)
+ set_property(TARGET lib3 PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/SomeSubDir/")
+endif()
+
add_library(static STATIC static.c)
add_library(imported SHARED IMPORTED)
set_property(TARGET imported PROPERTY IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/imported.dll")
@@ -26,9 +30,16 @@ if(WIN32 OR CYGWIN)
"$<TARGET_FILE:lib3>"
"$<TARGET_FILE:lib2>"
)
+ set(expected_dll_dirs
+ "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib2>>"
+ "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:imported>>"
+ "$<PATH:GET_PARENT_PATH,$<TARGET_FILE:lib3>>"
+ )
endif()
-set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\")\n")
+set(content "check_genex(\"${expected_dlls}\" \"$<TARGET_RUNTIME_DLLS:exe>\")
+check_genex(\"${expected_dll_dirs}\" \"$<TARGET_RUNTIME_DLL_DIRS:exe>\")\n")
+
set(condition)
get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(multi_config)
diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake
index bf9c302e05..3e35aa33fe 100644
--- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake
+++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake
@@ -100,7 +100,9 @@ if (WIN32 OR CYGWIN)
set(_platform Win32-Clang)
elseif(MSVC AND COMPILER_HAS_DEPRECATED)
set(_platform Win32)
- elseif((MINGW OR CYGWIN) AND COMPILER_HAS_DEPRECATED)
+ elseif(CYGWIN AND COMPILER_HAS_DEPRECATED)
+ set(_platform Cygwin)
+ elseif(MINGW AND COMPILER_HAS_DEPRECATED)
set(_platform MinGW)
else()
set(_platform WinEmpty)
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h
new file mode 100644
index 0000000000..dac4fda655
--- /dev/null
+++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libshared_export.h
@@ -0,0 +1,42 @@
+
+#ifndef LIBSHARED_EXPORT_H
+#define LIBSHARED_EXPORT_H
+
+#ifdef LIBSHARED_STATIC_DEFINE
+# define LIBSHARED_EXPORT
+# define LIBSHARED_NO_EXPORT
+#else
+# ifndef LIBSHARED_EXPORT
+# ifdef libshared_EXPORTS
+ /* We are building this library */
+# define LIBSHARED_EXPORT __declspec(dllexport)
+# else
+ /* We are using this library */
+# define LIBSHARED_EXPORT __declspec(dllimport)
+# endif
+# endif
+
+# ifndef LIBSHARED_NO_EXPORT
+# define LIBSHARED_NO_EXPORT
+# endif
+#endif
+
+#ifndef LIBSHARED_DEPRECATED
+# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__))
+#endif
+
+#ifndef LIBSHARED_DEPRECATED_EXPORT
+# define LIBSHARED_DEPRECATED_EXPORT LIBSHARED_EXPORT LIBSHARED_DEPRECATED
+#endif
+
+#ifndef LIBSHARED_DEPRECATED_NO_EXPORT
+# define LIBSHARED_DEPRECATED_NO_EXPORT LIBSHARED_NO_EXPORT LIBSHARED_DEPRECATED
+#endif
+
+#if 0 /* DEFINE_NO_DEPRECATED */
+# ifndef LIBSHARED_NO_DEPRECATED
+# define LIBSHARED_NO_DEPRECATED
+# endif
+#endif
+
+#endif /* LIBSHARED_EXPORT_H */
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h
new file mode 100644
index 0000000000..b6e2a4a880
--- /dev/null
+++ b/Tests/RunCMake/GenerateExportHeader/reference/Cygwin/libstatic_export.h
@@ -0,0 +1,42 @@
+
+#ifndef LIBSTATIC_EXPORT_H
+#define LIBSTATIC_EXPORT_H
+
+#ifdef LIBSTATIC_STATIC_DEFINE
+# define LIBSTATIC_EXPORT
+# define LIBSTATIC_NO_EXPORT
+#else
+# ifndef LIBSTATIC_EXPORT
+# ifdef libstatic_EXPORTS
+ /* We are building this library */
+# define LIBSTATIC_EXPORT
+# else
+ /* We are using this library */
+# define LIBSTATIC_EXPORT
+# endif
+# endif
+
+# ifndef LIBSTATIC_NO_EXPORT
+# define LIBSTATIC_NO_EXPORT
+# endif
+#endif
+
+#ifndef LIBSTATIC_DEPRECATED
+# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__))
+#endif
+
+#ifndef LIBSTATIC_DEPRECATED_EXPORT
+# define LIBSTATIC_DEPRECATED_EXPORT LIBSTATIC_EXPORT LIBSTATIC_DEPRECATED
+#endif
+
+#ifndef LIBSTATIC_DEPRECATED_NO_EXPORT
+# define LIBSTATIC_DEPRECATED_NO_EXPORT LIBSTATIC_NO_EXPORT LIBSTATIC_DEPRECATED
+#endif
+
+#if 0 /* DEFINE_NO_DEPRECATED */
+# ifndef LIBSTATIC_NO_DEPRECATED
+# define LIBSTATIC_NO_DEPRECATED
+# endif
+#endif
+
+#endif /* LIBSTATIC_EXPORT_H */
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h
index dac4fda655..3ba2d2eaca 100644
--- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h
+++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libshared_export.h
@@ -22,7 +22,7 @@
#endif
#ifndef LIBSHARED_DEPRECATED
-# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__))
+# define LIBSHARED_DEPRECATED __declspec(deprecated)
#endif
#ifndef LIBSHARED_DEPRECATED_EXPORT
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h
index b6e2a4a880..3c7e093abf 100644
--- a/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h
+++ b/Tests/RunCMake/GenerateExportHeader/reference/MinGW/libstatic_export.h
@@ -22,7 +22,7 @@
#endif
#ifndef LIBSTATIC_DEPRECATED
-# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__))
+# define LIBSTATIC_DEPRECATED __declspec(deprecated)
#endif
#ifndef LIBSTATIC_DEPRECATED_EXPORT
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h
index dac4fda655..3ba2d2eaca 100644
--- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h
+++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libshared_export.h
@@ -22,7 +22,7 @@
#endif
#ifndef LIBSHARED_DEPRECATED
-# define LIBSHARED_DEPRECATED __attribute__ ((__deprecated__))
+# define LIBSHARED_DEPRECATED __declspec(deprecated)
#endif
#ifndef LIBSHARED_DEPRECATED_EXPORT
diff --git a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h
index b6e2a4a880..3c7e093abf 100644
--- a/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h
+++ b/Tests/RunCMake/GenerateExportHeader/reference/Win32-Clang/libstatic_export.h
@@ -22,7 +22,7 @@
#endif
#ifndef LIBSTATIC_DEPRECATED
-# define LIBSTATIC_DEPRECATED __attribute__ ((__deprecated__))
+# define LIBSTATIC_DEPRECATED __declspec(deprecated)
#endif
#ifndef LIBSTATIC_DEPRECATED_EXPORT
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
index 2d545d9224..b139210d72 100644
--- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(BadIF)
run_cmake(BadCONFIG)
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt
new file mode 100644
index 0000000000..b2098bd425
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+ Generator
+
+ Visual Studio [^
+]+
+
+ given platform specification
+
+ Test Platform,nocomma
+
+ that contains a field after the first ',' with no '='\.$
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake
new file mode 100644
index 0000000000..2fc38e5c59
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldNoComma.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt
new file mode 100644
index 0000000000..654f9201c8
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+ Generator
+
+ Visual Studio [^
+]+
+
+ given platform specification
+
+ Test Platform,unknown=
+
+ that contains invalid field 'unknown='\.$
diff --git a/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake
new file mode 100644
index 0000000000..2fc38e5c59
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadFieldUnknown.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt
new file mode 100644
index 0000000000..1b7804da36
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+ Generator
+
+ Visual Studio [^
+]+
+
+ given platform specification with empty
+
+ version=
+
+ field\.$
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake
new file mode 100644
index 0000000000..2fc38e5c59
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionEmpty.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt
new file mode 100644
index 0000000000..d82eb0bbac
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+ Generator
+
+ Visual Studio [^
+]+
+
+ given platform specification with
+
+ version=1\.2\.3\.4
+
+ field, but no Windows SDK with that version was found\.$
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake
new file mode 100644
index 0000000000..2fc38e5c59
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionMissing.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt
new file mode 100644
index 0000000000..d3c62e3629
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform-stderr.txt
@@ -0,0 +1,19 @@
+^CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+ Generator
+
+ Visual Studio [^
+]+
+
+ given platform specification (containing a
+
+ version=8\.1
+
+ field\. The version field is not supported when targeting
+
+ Windows 8\.1(
+
+ with the Windows 8\.1 SDK installed\.)?|with
+
+ version=8\.1
+
+ field, but no Windows SDK with that version was found\.)$
diff --git a/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake
new file mode 100644
index 0000000000..2fc38e5c59
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/BadVersionPlatform.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt
+++ b/Tests/RunCMake/GeneratorPlatform/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
index a7519c373c..233eb0a2f0 100644
--- a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
@@ -26,3 +26,86 @@ else()
run_cmake(BadPlatformToolchain)
unset(RunCMake_TEST_OPTIONS)
endif()
+
+if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio (1[4567])( 20[0-9][0-9])?$")
+ unset(ENV{WindowsSDKVersion})
+
+ set(RunCMake_GENERATOR_PLATFORM "Test Platform,nocomma")
+ run_cmake(BadFieldNoComma)
+ set(RunCMake_GENERATOR_PLATFORM "Test Platform,unknown=")
+ run_cmake(BadFieldUnknown)
+ set(RunCMake_GENERATOR_PLATFORM "version=")
+ run_cmake(BadVersionEmpty)
+ set(RunCMake_GENERATOR_PLATFORM "version=1.2.3.4")
+ run_cmake(BadVersionMissing)
+ set(RunCMake_GENERATOR_PLATFORM "version=8.1")
+ run_cmake_with_options(BadVersionPlatform -DCMAKE_SYSTEM_VERSION=8.1)
+
+ if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio (1[45]) ")
+ set(expect_version "10.0")
+ set(RunCMake_GENERATOR_PLATFORM "version=${expect_version}")
+ set(RunCMake_TEST_VARIANT_DESCRIPTION "-${expect_version}")
+ run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0)
+ unset(RunCMake_GENERATOR_PLATFORM)
+ endif()
+
+ set(kits "")
+ cmake_host_system_information(RESULT kitsRoot10
+ QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Microsoft/Windows Kits/Installed Roots"
+ VALUE "KitsRoot10"
+ VIEW 64_32
+ ERROR_VARIABLE kitsRoot10Error
+ )
+ if(NOT kitsRoot10Error AND IS_DIRECTORY "${kitsRoot10}/include")
+ cmake_path(SET kitsInclude "${kitsRoot10}/include")
+ file(GLOB kits RELATIVE "${kitsInclude}" "${kitsInclude}/*/um/windows.h")
+ list(TRANSFORM kits REPLACE "/.*" "")
+ endif()
+ if(kits)
+ message(STATUS "Available Kits: ${kits}")
+ if(RunCMake_GENERATOR MATCHES "^Visual Studio 14 ")
+ set(kitMax 10.0.14393.0)
+ else()
+ set(kitMax "")
+ endif()
+ if(kitMax)
+ set(kitsIn "${kits}")
+ set(kits "")
+ foreach(kit IN LISTS kitsIn)
+ if(kit VERSION_LESS_EQUAL "${kitMax}")
+ list(APPEND kits "${kit}")
+ else()
+ message(STATUS "Excluding Kit ${kit} > ${kitMax}")
+ endif()
+ endforeach()
+ endif()
+ elseif(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 14 ")
+ message(FATAL_ERROR "Could not find any Windows SDKs to drive test cases.")
+ endif()
+
+ if(kits)
+ foreach(expect_version IN LISTS kits)
+ set(RunCMake_GENERATOR_PLATFORM "version=${expect_version}")
+ set(RunCMake_TEST_VARIANT_DESCRIPTION "-${expect_version}")
+ run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0)
+ unset(RunCMake_GENERATOR_PLATFORM)
+ endforeach()
+ foreach(expect_version IN LISTS kits)
+ set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMP0149-OLD-${expect_version}")
+ run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=${expect_version} -DCMAKE_POLICY_DEFAULT_CMP0149=OLD)
+ endforeach()
+ if(kits MATCHES "(^|;)([0-9.]+)$")
+ set(expect_version "${CMAKE_MATCH_2}")
+ foreach(test_version IN LISTS kits)
+ set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMP0149-NEW-${test_version}")
+ run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=${test_version} -DCMAKE_POLICY_DEFAULT_CMP0149=NEW)
+ endforeach()
+ endif()
+ foreach(expect_version IN LISTS kits)
+ set(RunCMake_TEST_VARIANT_DESCRIPTION "-WindowsSDKVersion-${expect_version}")
+ set(ENV{WindowsSDKVersion} "${expect_version}\\")
+ run_cmake_with_options(VersionExists -DCMAKE_SYSTEM_VERSION=10.0 -DCMAKE_POLICY_DEFAULT_CMP0149=NEW)
+ unset(ENV{WindowsSDKVersion})
+ endforeach()
+ endif()
+endif()
diff --git a/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake b/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake
new file mode 100644
index 0000000000..6c3c8e542b
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/VersionExists-check.cmake
@@ -0,0 +1,9 @@
+if(actual_stdout MATCHES "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='([^']+)'")
+ set(actual_version "${CMAKE_MATCH_1}")
+ if(NOT "${actual_version}" STREQUAL "${expect_version}")
+ set(RunCMake_TEST_FAILED "Actual SDK version '${actual_version}' did not match expected '${expect_version}'")
+ return()
+ endif()
+else()
+ set(RunCMake_TEST_FAILED "No CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION found in output.")
+endif()
diff --git a/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake b/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake
new file mode 100644
index 0000000000..5c30e2b616
--- /dev/null
+++ b/Tests/RunCMake/GeneratorPlatform/VersionExists.cmake
@@ -0,0 +1,5 @@
+cmake_policy(GET CMP0149 cmp0149)
+message(STATUS "CMP0149='${cmp0149}'")
+message(STATUS "CMAKE_SYSTEM_VERSION='${CMAKE_SYSTEM_VERSION}'")
+message(STATUS "ENV{WindowsSDKVersion}='$ENV{WindowsSDKVersion}'")
+message(STATUS "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION='${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}'")
diff --git a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/GeneratorToolset/CMakeLists.txt
+++ b/Tests/RunCMake/GeneratorToolset/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt
index 37747a1e47..d0aef2c476 100644
--- a/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt
+++ b/Tests/RunCMake/IfacePaths/BinInInstallPrefix-CMP0052-OLD-stderr.txt
@@ -1,6 +1,13 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_minimum_required\):
The OLD behavior for policy CMP0052 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/IfacePaths/CMakeLists.txt b/Tests/RunCMake/IfacePaths/CMakeLists.txt
index 5cd4825e66..0d707f077e 100644
--- a/Tests/RunCMake/IfacePaths/CMakeLists.txt
+++ b/Tests/RunCMake/IfacePaths/CMakeLists.txt
@@ -1,4 +1,8 @@
-cmake_minimum_required(VERSION 3.0)
+if(RunCMake_TEST MATCHES "-CMP0052")
+ cmake_minimum_required(VERSION 3.0)
+else()
+ cmake_minimum_required(VERSION 3.5)
+endif()
project(${RunCMake_TEST} NONE)
if(NOT TEST_FILE)
set(TEST_FILE ${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt
index 37747a1e47..4db8209bae 100644
--- a/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt
+++ b/Tests/RunCMake/IfacePaths/SrcInInstallPrefix-CMP0052-OLD-stderr.txt
@@ -1,6 +1,13 @@
-^CMake Deprecation Warning at CMakeLists.txt:1 \(cmake_minimum_required\):
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
The OLD behavior for policy CMP0052 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt
+++ b/Tests/RunCMake/IncludeWhatYouUse/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/IncludeWhatYouUse/CXX.cmake b/Tests/RunCMake/IncludeWhatYouUse/CXX.cmake
index 896930c16b..8780bb693b 100644
--- a/Tests/RunCMake/IncludeWhatYouUse/CXX.cmake
+++ b/Tests/RunCMake/IncludeWhatYouUse/CXX.cmake
@@ -1,3 +1,3 @@
enable_language(CXX)
-set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
add_executable(main main.cxx)
diff --git a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt
+++ b/Tests/RunCMake/InterfaceLibrary/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/InterfaceLibrary/genex_link.cmake b/Tests/RunCMake/InterfaceLibrary/genex_link.cmake
index 0dbf029fea..344586430a 100644
--- a/Tests/RunCMake/InterfaceLibrary/genex_link.cmake
+++ b/Tests/RunCMake/InterfaceLibrary/genex_link.cmake
@@ -1,7 +1,4 @@
-
-cmake_minimum_required(VERSION 2.8.12.20131125 FATAL_ERROR)
-
-project(genex_link)
+enable_language(CXX)
set(_main_cpp ${CMAKE_CURRENT_BINARY_DIR}/main.cpp)
file(WRITE ${_main_cpp}
diff --git a/Tests/RunCMake/InterfaceLibrary/global-interface-stderr.txt b/Tests/RunCMake/InterfaceLibrary/global-interface-stderr.txt
index 38585ebdd1..352bb68a09 100644
--- a/Tests/RunCMake/InterfaceLibrary/global-interface-stderr.txt
+++ b/Tests/RunCMake/InterfaceLibrary/global-interface-stderr.txt
@@ -3,7 +3,7 @@ CMake Error at global-interface.cmake:2 \(add_library\):
GLOBAL
- Tried extensions \.c \.C .*
-.*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake b/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake
index 575fcc6e1f..4a8ca37ba1 100644
--- a/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake
+++ b/Tests/RunCMake/InterfaceLibrary/invalid_name.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_policy(SET CMP0037 OLD)
add_library(if$ace INTERFACE)
add_library(iface::target INTERFACE)
diff --git a/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake b/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake
index ed81878cbe..eae8f57939 100644
--- a/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake
+++ b/Tests/RunCMake/InterfaceLibrary/no_shared_libs.cmake
@@ -1,5 +1,3 @@
-
-cmake_minimum_required(VERSION 2.8.12.20131009)
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
add_library(foo INTERFACE)
target_compile_definitions(foo INTERFACE FOO_DEFINE)
diff --git a/Tests/RunCMake/Languages/CMakeLists.txt b/Tests/RunCMake/Languages/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/Languages/CMakeLists.txt
+++ b/Tests/RunCMake/Languages/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt
index b7a07559ff..55aa1bbe79 100644
--- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt
+++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-iface-stderr.txt
@@ -1,6 +1,13 @@
-^CMake Deprecation Warning at CMP0028-OLD-iface.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+CMake Deprecation Warning at CMP0028-OLD-iface\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0028 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt
index 586a876a0a..f11d8f5767 100644
--- a/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt
+++ b/Tests/RunCMake/LinkItemValidation/CMP0028-OLD-stderr.txt
@@ -1,6 +1,13 @@
-^CMake Deprecation Warning at CMP0028-OLD.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMakeLists\.txt:3 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+CMake Deprecation Warning at CMP0028-OLD\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0028 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt
index 185cd91c10..6e1f8a2102 100644
--- a/Tests/RunCMake/LinkItemValidation/CMakeLists.txt
+++ b/Tests/RunCMake/LinkItemValidation/CMakeLists.txt
@@ -1,6 +1,6 @@
-cmake_minimum_required(VERSION 2.8.12)
-if(NOT RunCMake_TEST MATCHES "^CMP0028")
- cmake_minimum_required(VERSION 3.22)
+cmake_minimum_required(VERSION 3.5)
+if(RunCMake_TEST MATCHES "^CMP0028")
+ cmake_minimum_required(VERSION 2.8.12)
endif()
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE) # policy used at end of dir
diff --git a/Tests/RunCMake/LinkStatic/CMakeLists.txt b/Tests/RunCMake/LinkStatic/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/LinkStatic/CMakeLists.txt
+++ b/Tests/RunCMake/LinkStatic/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt
+++ b/Tests/RunCMake/LinkWhatYouUse/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt
index 3313e31f63..544b65fceb 100644
--- a/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/C-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.*
diff --git a/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt
index 3313e31f63..544b65fceb 100644
--- a/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/C-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=C.*
diff --git a/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt
index 3313e31f63..082c7b575f 100644
--- a/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/CXX-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.*
diff --git a/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt
index 3313e31f63..082c7b575f 100644
--- a/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/CXX-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=CXX.*
diff --git a/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt
index 3313e31f63..d2efd3d539 100644
--- a/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/OBJC-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.*
diff --git a/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt
index 3313e31f63..d2efd3d539 100644
--- a/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/OBJC-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJC.*
diff --git a/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt
index 3313e31f63..0082ab2b4b 100644
--- a/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/OBJCXX-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.*
diff --git a/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt b/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt
index 3313e31f63..0082ab2b4b 100644
--- a/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt
+++ b/Tests/RunCMake/LinkerLauncher/OBJCXX-launch-env-Build-stdout.txt
@@ -1 +1 @@
-.*-E env USED_LAUNCHER=1.*
+.*-E env USED_LAUNCHER=1 TARGET_NAME=main LANGUAGE=OBJCXX.*
diff --git a/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake b/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake
index 8f2bf63f4c..025f367844 100644
--- a/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake
+++ b/Tests/RunCMake/LinkerLauncher/RunCMakeTest.cmake
@@ -17,7 +17,8 @@ endfunction()
function(run_linker_launcher_env lang)
string(REGEX REPLACE "-.*" "" core_lang "${lang}")
- set(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER} "${CMAKE_COMMAND};-E;env;USED_LAUNCHER=1")
+ # Use the noop genexp $<PATH:...> genexp to validate genexp support.
+ set(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER} "$<PATH:CMAKE_PATH,${CMAKE_COMMAND}>;-E;env;USED_LAUNCHER=1;TARGET_NAME=$<TARGET_PROPERTY:NAME>;LANGUAGE=$<LINK_LANGUAGE>")
run_linker_launcher(${lang})
unset(ENV{CMAKE_${core_lang}_LINKER_LAUNCHER})
endfunction()
diff --git a/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt
new file mode 100644
index 0000000000..42742f73e4
--- /dev/null
+++ b/Tests/RunCMake/Make/CMP0113-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0113-OLD.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0113 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/Make/CMakeLists.txt b/Tests/RunCMake/Make/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/Make/CMakeLists.txt
+++ b/Tests/RunCMake/Make/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake b/Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake
index c5a859d877..fdf418f955 100644
--- a/Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake
+++ b/Tests/RunCMake/MaxRecursionDepth/RunCMakeTest.cmake
@@ -1,26 +1,42 @@
include(RunCMake)
include(RunCTest)
-function(run_cmake_recursive name)
- set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
- run_cmake(${name}-default)
- unset(RunCMake_TEST_OPTIONS)
- set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
- run_cmake(${name}-var)
- unset(RunCMake_TEST_OPTIONS)
- set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a)
- run_cmake(${name}-invalid-var)
- unset(RunCMake_TEST_OPTIONS)
+# Isolate this test from the caller's environment.
+unset(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH})
+function(run_cmake_recursive name)
+ run_cmake_with_options(${name}-default "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
run_cmake_command(${name}-default-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
+
+ set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} 5) # overridden, not used
+ run_cmake_with_options(${name}-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
+ run_cmake_with_options(${name}-invalid-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a)
run_cmake_command(${name}-var-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10 -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
run_cmake_command(${name}-invalid-var-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
+
+ set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} 10)
+ run_cmake_with_options(${name}-env "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
+ run_cmake_command(${name}-env-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
+
+ set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} a)
+ run_cmake_with_options(${name}-invalid-env "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
+ run_cmake_command(${name}-invalid-env-script ${CMAKE_COMMAND} "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -P "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
+
+ unset(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH})
endfunction()
function(run_ctest_recursive name)
run_ctest(${name}-default "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
run_ctest(${name}-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_ctest(${name}-invalid-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name} -DCMAKE_MAXIMUM_RECURSION_DEPTH=a)
+
+ set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} 10)
+ run_ctest(${name}-env "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
+
+ set(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH} a)
+ run_ctest(${name}-invalid-env "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=${name})
+
+ unset(ENV{CMAKE_MAXIMUM_RECURSION_DEPTH})
endfunction()
run_cmake_recursive(function)
@@ -32,12 +48,8 @@ run_cmake_recursive(variable_watch)
# We run these tests separately and only with a small limit because they are
# taxing and slow. The "implicit" and "invalid" cases are already thoroughly
# covered by the other tests above.
-set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=add_subdirectory -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
-run_cmake(add_subdirectory-var)
-unset(RunCMake_TEST_OPTIONS)
-set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=try_compile -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
-run_cmake(try_compile-var)
-unset(RunCMake_TEST_OPTIONS)
+run_cmake_with_options(add_subdirectory-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=add_subdirectory -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
+run_cmake_with_options(try_compile-var "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" -DTEST_NAME=try_compile -DCMAKE_MAXIMUM_RECURSION_DEPTH=10)
run_ctest_recursive(ctest_read_custom_files)
diff --git a/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-result.txt
new file mode 100644
index 0000000000..b57e2deb77
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-stderr.txt
new file mode 100644
index 0000000000..b664fa04f9
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env-stderr.txt
@@ -0,0 +1,34 @@
+^2
+3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:1 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
+ .*/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-env/test\.cmake:10 \(ctest_read_custom_files\)
+
+
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake
+Problem reading custom configuration: .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake$
diff --git a/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-result.txt
new file mode 100644
index 0000000000..b57e2deb77
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-stderr.txt
new file mode 100644
index 0000000000..7dbbb3e72d
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/ctest_read_custom_files-invalid-env-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:1 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ .*/Tests/RunCMake/MaxRecursionDepth/CTestCustom\.cmake:3 \(ctest_read_custom_files\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-default-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-default-script-stderr.txt
index b8557ab645..4e965e8eb6 100644
--- a/Tests/RunCMake/MaxRecursionDepth/find_package-default-script-stderr.txt
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-default-script-stderr.txt
@@ -1,5 +1,7 @@
[0-9]+
-CMake Error at .*/FindRecursivePackage\.cmake:1 \(message\):
- Maximum recursion depth of [0-9]+ exceeded
+CMake Error at [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
Call Stack \(most recent call first\):
- .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-default-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-default-stderr.txt
index 5d31e295b0..0119953adb 100644
--- a/Tests/RunCMake/MaxRecursionDepth/find_package-default-stderr.txt
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-default-stderr.txt
@@ -1,5 +1,5 @@
[0-9]+
-CMake Error at FindRecursivePackage\.cmake:1 \(message\):
- Maximum recursion depth of [0-9]+ exceeded
+CMake Error at FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
Call Stack \(most recent call first\):
- FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-stderr.txt
new file mode 100644
index 0000000000..53145515a5
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-env-script-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at .*/FindRecursivePackage\.cmake:1 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ .*/find_package\.cmake:2 \(find_package\)
+ .*/CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-env-stderr.txt
new file mode 100644
index 0000000000..b47a13a175
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-env-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at FindRecursivePackage\.cmake:1 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage\.cmake:3 \(find_package\)
+ find_package\.cmake:2 \(find_package\)
+ CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-stderr.txt
new file mode 100644
index 0000000000..4e965e8eb6
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-script-stderr.txt
@@ -0,0 +1,7 @@
+[0-9]+
+CMake Error at [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
+Call Stack \(most recent call first\):
+ [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-stderr.txt
new file mode 100644
index 0000000000..0119953adb
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-env-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
+Call Stack \(most recent call first\):
+ FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-script-stderr.txt
index b8557ab645..4e965e8eb6 100644
--- a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-script-stderr.txt
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-script-stderr.txt
@@ -1,5 +1,7 @@
[0-9]+
-CMake Error at .*/FindRecursivePackage\.cmake:1 \(message\):
- Maximum recursion depth of [0-9]+ exceeded
+CMake Error at [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
Call Stack \(most recent call first\):
- .*/FindRecursivePackage\.cmake:3 \(find_package\)
+ [^
+]*/Tests/RunCMake/MaxRecursionDepth/FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-stderr.txt
index 5d31e295b0..0119953adb 100644
--- a/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-stderr.txt
+++ b/Tests/RunCMake/MaxRecursionDepth/find_package-invalid-var-stderr.txt
@@ -1,5 +1,5 @@
[0-9]+
-CMake Error at FindRecursivePackage\.cmake:1 \(message\):
- Maximum recursion depth of [0-9]+ exceeded
+CMake Error at FindRecursivePackage.cmake:[0-9]+ \(find_package\):
+ find_package maximum nesting depth of [0-9]+ exceeded.
Call Stack \(most recent call first\):
- FindRecursivePackage\.cmake:3 \(find_package\)
+ FindRecursivePackage.cmake:[0-9]+ \(find_package\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/function-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/function-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/function-env-script-stderr.txt
new file mode 100644
index 0000000000..61304b13f7
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-env-script-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at .*/function\.cmake:2 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:4 \(recursive\)
+ .*/function\.cmake:7 \(recursive\)
+ .*/CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/function-env-stderr.txt
new file mode 100644
index 0000000000..54e72aff4b
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-env-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at function\.cmake:2 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:4 \(recursive\)
+ function\.cmake:7 \(recursive\)
+ CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-stderr.txt
new file mode 100644
index 0000000000..92de1fb3c7
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-script-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at .*/function\.cmake:2 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ .*/function\.cmake:4 \(recursive\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-stderr.txt
new file mode 100644
index 0000000000..5c25c4b129
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/function-invalid-env-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at function\.cmake:2 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ function\.cmake:4 \(recursive\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/include-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/include-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/include-env-script-stderr.txt
new file mode 100644
index 0000000000..f55f50536f
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-env-script-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at .*/include_recursive\.cmake:1 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include_recursive\.cmake:3 \(include\)
+ .*/include\.cmake:2 \(include\)
+ .*/CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/include-env-stderr.txt
new file mode 100644
index 0000000000..ff33985045
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-env-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at include_recursive\.cmake:1 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include_recursive\.cmake:3 \(include\)
+ include\.cmake:2 \(include\)
+ CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-stderr.txt
new file mode 100644
index 0000000000..0510e7c373
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-script-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at .*/include_recursive\.cmake:1 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ .*/include_recursive\.cmake:3 \(include\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-stderr.txt
new file mode 100644
index 0000000000..b1494a8e11
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/include-invalid-env-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at include_recursive\.cmake:1 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ include_recursive\.cmake:3 \(include\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/macro-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/macro-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/macro-env-script-stderr.txt
new file mode 100644
index 0000000000..142e068a08
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-env-script-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at .*/macro\.cmake:2 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:4 \(recursive\)
+ .*/macro\.cmake:7 \(recursive\)
+ .*/CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/macro-env-stderr.txt
new file mode 100644
index 0000000000..71de55376f
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-env-stderr.txt
@@ -0,0 +1,21 @@
+^3
+4
+5
+6
+7
+8
+9
+10
+CMake Error at macro\.cmake:2 \(message\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:4 \(recursive\)
+ macro\.cmake:7 \(recursive\)
+ CMakeLists\.txt:5 \(include\)$
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-stderr.txt
new file mode 100644
index 0000000000..c67be57d28
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-script-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at .*/macro\.cmake:2 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ .*/macro\.cmake:4 \(recursive\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-stderr.txt
new file mode 100644
index 0000000000..0b271627da
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/macro-invalid-env-stderr.txt
@@ -0,0 +1,5 @@
+[0-9]+
+CMake Error at macro\.cmake:2 \(message\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ macro\.cmake:4 \(recursive\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-stderr.txt
new file mode 100644
index 0000000000..52fedd314c
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-script-stderr.txt
@@ -0,0 +1,22 @@
+^4
+6
+8
+10
+CMake Error at .*/variable_watch\.cmake:[0-9]+ \(update_x\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ .*/variable_watch\.cmake:5 \(set\)
+ .*/variable_watch\.cmake:[0-9]+ \(update_x\)
+ .*/variable_watch\.cmake:5 \(set\)
+ .*/variable_watch\.cmake:[0-9]+ \(update_x\)
+ .*/variable_watch\.cmake:5 \(set\)
+ .*/variable_watch\.cmake:[0-9]+ \(update_x\)
+ .*/variable_watch\.cmake:5 \(set\)
+ .*/variable_watch\.cmake:[0-9]+ \(update_x\)
+ .*/variable_watch\.cmake:9 \(set\)
+ .*/CMakeLists\.txt:5 \(include\)
+
+
+CMake Error: Error in cmake code at
+Unknown:0:
+A command failed during the invocation of callback "update_x"\.$
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-stderr.txt
new file mode 100644
index 0000000000..1427f1df99
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-env-stderr.txt
@@ -0,0 +1,22 @@
+^4
+6
+8
+10
+CMake Error at variable_watch\.cmake:[0-9]+ \(update_x\):
+ Maximum recursion depth of 10 exceeded
+Call Stack \(most recent call first\):
+ variable_watch\.cmake:5 \(set\)
+ variable_watch\.cmake:[0-9]+ \(update_x\)
+ variable_watch\.cmake:5 \(set\)
+ variable_watch\.cmake:[0-9]+ \(update_x\)
+ variable_watch\.cmake:5 \(set\)
+ variable_watch\.cmake:[0-9]+ \(update_x\)
+ variable_watch\.cmake:5 \(set\)
+ variable_watch\.cmake:[0-9]+ \(update_x\)
+ variable_watch\.cmake:9 \(set\)
+ CMakeLists\.txt:5 \(include\)
+
+
+CMake Error: Error in cmake code at
+Unknown:0:
+A command failed during the invocation of callback "update_x"\.$
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-result.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-result.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-stderr.txt
new file mode 100644
index 0000000000..07deee2eaf
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-script-stderr.txt
@@ -0,0 +1,6 @@
+[0-9]+
+CMake Error at .*/variable_watch\.cmake:[0-9]+ \(update_x\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ .*/variable_watch\.cmake:5 \(set\)
+ .*/variable_watch\.cmake:[0-9]+ \(update_x\)
diff --git a/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-stderr.txt b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-stderr.txt
new file mode 100644
index 0000000000..b2395b30f3
--- /dev/null
+++ b/Tests/RunCMake/MaxRecursionDepth/variable_watch-invalid-env-stderr.txt
@@ -0,0 +1,6 @@
+[0-9]+
+CMake Error at variable_watch\.cmake:[0-9]+ \(update_x\):
+ Maximum recursion depth of [0-9]+ exceeded
+Call Stack \(most recent call first\):
+ variable_watch\.cmake:5 \(set\)
+ variable_watch\.cmake:[0-9]+ \(update_x\)
diff --git a/Tests/RunCMake/MultiLint/CXX.cmake b/Tests/RunCMake/MultiLint/CXX.cmake
index dc30146a6e..3e99e73754 100644
--- a/Tests/RunCMake/MultiLint/CXX.cmake
+++ b/Tests/RunCMake/MultiLint/CXX.cmake
@@ -1,6 +1,6 @@
enable_language(CXX)
-set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${PSEUDO_IWYU}" -some -args)
-set(CMAKE_CXX_CLANG_TIDY "${PSEUDO_TIDY}" -some -args)
-set(CMAKE_CXX_CPPLINT "${PSEUDO_CPPLINT}" --verbose=0 --linelength=80)
-set(CMAKE_CXX_CPPCHECK "${PSEUDO_CPPCHECK}")
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "$<1:${PSEUDO_IWYU}>" -some -args)
+set(CMAKE_CXX_CLANG_TIDY "$<1:${PSEUDO_TIDY}>" -some -args)
+set(CMAKE_CXX_CPPLINT "$<1:${PSEUDO_CPPLINT}>" --verbose=0 --linelength=80)
+set(CMAKE_CXX_CPPCHECK "$<1:${PSEUDO_CPPCHECK}>")
add_executable(main main.cxx)
diff --git a/Tests/RunCMake/Ninja/AssumedSources.cmake b/Tests/RunCMake/Ninja/AssumedSources.cmake
index d5364f0270..d68fca9997 100644
--- a/Tests/RunCMake/Ninja/AssumedSources.cmake
+++ b/Tests/RunCMake/Ninja/AssumedSources.cmake
@@ -1,6 +1,5 @@
-cmake_minimum_required(VERSION 3.8)
cmake_policy(SET CMP0118 NEW)
-project(AssumedSources)
+enable_language(C)
set_source_files_properties(
"${CMAKE_CURRENT_BINARY_DIR}/target.c"
diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt
index 9a606ee60a..6d340b0f11 100644
--- a/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt
+++ b/Tests/RunCMake/Ninja/CMP0058-OLD-by-stderr.txt
@@ -1,6 +1,15 @@
-^CMake Deprecation Warning at CMP0058-OLD-by.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMP0058-OLD-by\.cmake:[0-9] \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9] \(include\)
++
+CMake Deprecation Warning at CMP0058-OLD-by\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0058 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake b/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake
index 92a3a0fb57..45e5aa3721 100644
--- a/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake
+++ b/Tests/RunCMake/Ninja/CMP0058-OLD-by.cmake
@@ -1,3 +1,4 @@
+cmake_policy(VERSION 3.2)
cmake_policy(SET CMP0058 OLD)
set(byproducts BYPRODUCTS byproduct1a byproduct1b)
include(CMP0058-common.cmake)
diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt
index ba6e5da4e0..834c781354 100644
--- a/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt
+++ b/Tests/RunCMake/Ninja/CMP0058-OLD-no-stderr.txt
@@ -1,6 +1,15 @@
-^CMake Deprecation Warning at CMP0058-OLD-no.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMP0058-OLD-no\.cmake:[0-9] \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9] \(include\)
++
+CMake Deprecation Warning at CMP0058-OLD-no\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0058 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake b/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake
index 0326e07662..388e0184c4 100644
--- a/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake
+++ b/Tests/RunCMake/Ninja/CMP0058-OLD-no.cmake
@@ -1,2 +1,3 @@
+cmake_policy(VERSION 3.2)
cmake_policy(SET CMP0058 OLD)
include(CMP0058-common.cmake)
diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt
new file mode 100644
index 0000000000..2927f527b3
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CMP0058-WARN-by-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMP0058-WARN-by\.cmake:[0-9] \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9] \(include\)$
diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake b/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake
index 6128167967..6f5484a051 100644
--- a/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake
+++ b/Tests/RunCMake/Ninja/CMP0058-WARN-by.cmake
@@ -1,2 +1,3 @@
+cmake_policy(VERSION 3.2)
set(byproducts BYPRODUCTS byproduct1a byproduct1b)
include(CMP0058-common.cmake)
diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt
index 439a2d9bfd..1ffb4169d0 100644
--- a/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt
+++ b/Tests/RunCMake/Ninja/CMP0058-WARN-no-stderr.txt
@@ -1,4 +1,13 @@
-^CMake Warning \(dev\):
+^CMake Deprecation Warning at CMP0058-WARN-no\.cmake:[0-9] \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9] \(include\)
++
+CMake Warning \(dev\):
Policy CMP0058 is not set: Ninja requires custom command byproducts to be
explicit. Run "cmake --help-policy CMP0058" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
diff --git a/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake b/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake
index 7bc66ef284..714ae6443b 100644
--- a/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake
+++ b/Tests/RunCMake/Ninja/CMP0058-WARN-no.cmake
@@ -1 +1,2 @@
+cmake_policy(VERSION 3.2)
include(CMP0058-common.cmake)
diff --git a/Tests/RunCMake/Ninja/CMakeLists.txt b/Tests/RunCMake/Ninja/CMakeLists.txt
index 2a0591e74d..8eb57486ad 100644
--- a/Tests/RunCMake/Ninja/CMakeLists.txt
+++ b/Tests/RunCMake/Ninja/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/Ninja/CommandConcat.cmake b/Tests/RunCMake/Ninja/CommandConcat.cmake
index 790cf9d255..7d6faf5d4b 100644
--- a/Tests/RunCMake/Ninja/CommandConcat.cmake
+++ b/Tests/RunCMake/Ninja/CommandConcat.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.7)
-project(concat_cmd NONE)
set(output1 ${CMAKE_BINARY_DIR}/out1.txt)
set(output2 ${CMAKE_BINARY_DIR}/out2.txt)
file(REMOVE ${output1} ${output2})
diff --git a/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake b/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake
new file mode 100644
index 0000000000..fefd86a1e3
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandExplicitDepends.cmake
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 3.26)
+project(CustomCommandExplicitDepends C)
+
+add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-option.h"
+ COMMAND "${CMAKE_COMMAND}" -E touch
+ "${CMAKE_CURRENT_BINARY_DIR}/command-option.h"
+ COMMENT "Creating command-option.h"
+ DEPENDS_EXPLICIT_ONLY
+)
+
+set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY ON)
+add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h"
+ COMMAND "${CMAKE_COMMAND}" -E touch
+ "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h"
+ COMMENT "Creating command-variable-on.h"
+)
+
+set(CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY OFF)
+add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h"
+ COMMAND "${CMAKE_COMMAND}" -E touch
+ "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h"
+ COMMENT "Creating command-variable-off.h"
+)
+
+add_library(dep SHARED dep.c)
+
+add_library(top SHARED
+ top.c
+ "${CMAKE_CURRENT_BINARY_DIR}/command-option.h"
+ "${CMAKE_CURRENT_BINARY_DIR}/command-variable-on.h"
+ "${CMAKE_CURRENT_BINARY_DIR}/command-variable-off.h"
+)
+target_link_libraries(top PRIVATE dep)
diff --git a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake
index 8e01c8c657..e04ac21e5c 100644
--- a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake
+++ b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello NONE)
-
add_custom_command(
OUTPUT hello.copy.c
COMMAND "${CMAKE_COMMAND}" -E copy
diff --git a/Tests/RunCMake/Ninja/Executable.cmake b/Tests/RunCMake/Ninja/Executable.cmake
index 4e17d68898..2b6a61bf4e 100644
--- a/Tests/RunCMake/Ninja/Executable.cmake
+++ b/Tests/RunCMake/Ninja/Executable.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello C)
+enable_language(C)
add_executable(hello hello.c)
include(CheckOutput.cmake)
include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/LooseObjectDepends.cmake b/Tests/RunCMake/Ninja/LooseObjectDepends.cmake
index 360c7ba13c..90f824978a 100644
--- a/Tests/RunCMake/Ninja/LooseObjectDepends.cmake
+++ b/Tests/RunCMake/Ninja/LooseObjectDepends.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.8)
-project(LooseObjectDepends C)
+enable_language(C)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/command.h"
diff --git a/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
index 505f750942..9615c5696f 100644
--- a/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
+++ b/Tests/RunCMake/Ninja/PreventConfigureFileDupBuildRule.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
-project(Test LANGUAGES C)
+enable_language(C)
configure_file(PreventConfigureFileDupBuildRule.cmake PreventTargetAliasesDupBuildRule.cmake @ONLY)
add_subdirectory(SubDirConfigureFileDup)
diff --git a/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake b/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake
index da6f86a733..81eb731e76 100644
--- a/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake
+++ b/Tests/RunCMake/Ninja/PreventTargetAliasesDupBuildRule.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
-project(Test LANGUAGES C)
+enable_language(C)
# fake launcher executable
set(input_launcher_executable ${CMAKE_CURRENT_BINARY_DIR}/fake_launcher_executable)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 520d9c7ee9..619e94a234 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -191,6 +191,38 @@ function (run_LooseObjectDepends)
endfunction ()
run_LooseObjectDepends()
+function (run_CustomCommandExplictDepends)
+ set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CustomCommandExplicitDepends-build)
+ run_cmake(CustomCommandExplicitDepends)
+
+ set(DEP_LIB "${RunCMake_TEST_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}dep${CMAKE_SHARED_LIBRARY_SUFFIX}")
+
+ run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-option.h")
+ if (EXISTS "${DEP_LIB}")
+ message(FATAL_ERROR
+ "The `dep` library was created when requesting a custom command to be "
+ "generated; this should no longer be necessary when passing "
+ "DEPENDS_EXPLICIT_ONLY option.")
+ endif ()
+
+ run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-variable-on.h")
+ if (EXISTS "${DEP_LIB}")
+ message(FATAL_ERROR
+ "The `dep` library was created when requesting a custom command to be "
+ "generated; this should no longer be necessary when setting "
+ "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY variable to ON.")
+ endif ()
+
+ run_ninja("${RunCMake_TEST_BINARY_DIR}" "command-variable-off.h")
+ if (NOT EXISTS "${DEP_LIB}")
+ message(FATAL_ERROR
+ "The `dep` library was not created when requesting a custom command to be "
+ "generated; this should be necessary when setting "
+ "CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY variable to OFF.")
+ endif ()
+endfunction ()
+run_CustomCommandExplictDepends()
+
function (run_AssumedSources)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/AssumedSources-build)
run_cmake(AssumedSources)
diff --git a/Tests/RunCMake/Ninja/SharedLib.cmake b/Tests/RunCMake/Ninja/SharedLib.cmake
index 1a78390e32..c295c16ae9 100644
--- a/Tests/RunCMake/Ninja/SharedLib.cmake
+++ b/Tests/RunCMake/Ninja/SharedLib.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello C)
+enable_language(C)
add_library(greeting SHARED greeting.c)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(hello hello_with_greeting.c)
diff --git a/Tests/RunCMake/Ninja/StaticLib.cmake b/Tests/RunCMake/Ninja/StaticLib.cmake
index 0f815ae5e5..dab674281a 100644
--- a/Tests/RunCMake/Ninja/StaticLib.cmake
+++ b/Tests/RunCMake/Ninja/StaticLib.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello C)
+enable_language(C)
add_definitions(-DGREETING_STATIC)
add_library(greeting STATIC greeting.c)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/Tests/RunCMake/Ninja/SubDirPrefix.cmake b/Tests/RunCMake/Ninja/SubDirPrefix.cmake
index 30ad1e6073..49d075fba7 100644
--- a/Tests/RunCMake/Ninja/SubDirPrefix.cmake
+++ b/Tests/RunCMake/Ninja/SubDirPrefix.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello C)
+enable_language(C)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
add_subdirectory(SubDirPrefix)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/Tests/RunCMake/Ninja/TwoLibs.cmake b/Tests/RunCMake/Ninja/TwoLibs.cmake
index 666452f340..10ac5a6acc 100644
--- a/Tests/RunCMake/Ninja/TwoLibs.cmake
+++ b/Tests/RunCMake/Ninja/TwoLibs.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
-project(hello C)
+enable_language(C)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib-static")
diff --git a/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt
new file mode 100644
index 0000000000..f5bce43c15
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix-all-ninja-stdout.txt
@@ -0,0 +1 @@
+tgt has been built
diff --git a/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake
new file mode 100644
index 0000000000..c4e0587441
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/OutputPathPrefix.cmake
@@ -0,0 +1 @@
+add_custom_target(tgt ALL COMMAND ${CMAKE_COMMAND} -E echo "tgt has been built")
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index c040e8f1ee..47f5eeed22 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -459,6 +459,15 @@ set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_C
run_cmake(CompileCommands)
unset(RunCMake_TEST_OPTIONS)
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/OutputPathPrefix-build)
+run_cmake_with_options(OutputPathPrefix "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=OutputPathPrefix-build")
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR})
+set(RunCMake_TEST_NO_CLEAN 1)
+run_cmake_command(OutputPathPrefix-all-ninja "${RunCMake_MAKE_PROGRAM}" -f OutputPathPrefix-build/build.ninja OutputPathPrefix-build/all)
+run_cmake_command(OutputPathPrefix-clean-ninja "${RunCMake_MAKE_PROGRAM}" -f OutputPathPrefix-build/build.ninja OutputPathPrefix-build/clean)
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
# CudaSimple uses separable compilation, which is currently only supported on NVCC.
if(CMake_TEST_CUDA)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CudaSimple-build)
diff --git a/Tests/RunCMake/ObjectLibrary/MissingSource-stderr.txt b/Tests/RunCMake/ObjectLibrary/MissingSource-stderr.txt
index 5c7882d688..05eb42d5ea 100644
--- a/Tests/RunCMake/ObjectLibrary/MissingSource-stderr.txt
+++ b/Tests/RunCMake/ObjectLibrary/MissingSource-stderr.txt
@@ -3,7 +3,7 @@ CMake Error at MissingSource.cmake:1 \(add_library\):
missing.c
- Tried extensions \.c \.C .*
-.*
-Call Stack \(most recent call first\):
+ Tried extensions ([^
+]+
+)+Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt b/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt
index 2897109554..3e470a29cb 100644
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.14)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
index 75c26a7a17..6027f0339b 100644
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required(VERSION 3.14)
-project(Minimal NONE)
-
#
# list of targets to test. to add a target: put its files in the data
# subdirectory and add it to this list... we run each target's
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt
index 2897109554..3e470a29cb 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.14)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
index df4ef1fcb4..fa7bf072d4 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required(VERSION 3.14)
-project(Minimal NONE)
-
#
# list of targets to test. to add a target: put its files in the data
# subdirectory and add it to this list... we run each target's
diff --git a/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake b/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake
index abd27f4cd8..da608b3729 100644
--- a/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PolicyScope/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(NotClosed)
run_cmake(NotOpened)
diff --git a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt
index c3922d623f..42b0577061 100644
--- a/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt
+++ b/Tests/RunCMake/PositionIndependentCode/CMakeLists.txt
@@ -1,5 +1,4 @@
-
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
# MSVC creates extra targets which pollute the stderr unless we set this.
diff --git a/Tests/RunCMake/README.rst b/Tests/RunCMake/README.rst
index ebe40cf480..d8cae8bcc8 100644
--- a/Tests/RunCMake/README.rst
+++ b/Tests/RunCMake/README.rst
@@ -82,3 +82,10 @@ match the regular expression are not run. For example::
This will only run subtests in ``RunCMake.Example`` that start with
``example``.
+
+To speed up the process of creating a new ``RunCMake`` test, you can run a
+script that will automatically perform steps 1 through 4 for you::
+
+ cmake -DRunCMake_TEST_SUITE=<test suite name> -P Tests/RunCMake/AddRunCMakeTestSuite.cmake
+
+Be sure to run this from the top-level CMake source directory.
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index faff95767c..8e85f6cf46 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -11,8 +11,12 @@ foreach(
endforeach()
function(run_cmake test)
- if(DEFINED ENV{RunCMake_TEST_FILTER} AND NOT test MATCHES "$ENV{RunCMake_TEST_FILTER}")
- return()
+ if(DEFINED ENV{RunCMake_TEST_FILTER})
+ set(test_and_variant "${test}${RunCMake_TEST_VARIANT_DESCRIPTION}")
+ if(NOT test_and_variant MATCHES "$ENV{RunCMake_TEST_FILTER}")
+ return()
+ endif()
+ unset(test_and_variant)
endif()
set(top_src "${RunCMake_SOURCE_DIR}")
@@ -94,7 +98,7 @@ function(run_cmake test)
if(APPLE)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0025=NEW)
endif()
- if(NOT RunCMake_TEST_NO_CMP0129 AND CMAKE_C_COMPILER_ID STREQUAL "LCC")
+ if(RunCMake_TEST_LCC AND NOT RunCMake_TEST_NO_CMP0129)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0129=NEW)
endif()
if(RunCMake_MAKE_PROGRAM)
@@ -187,6 +191,18 @@ function(run_cmake test)
"|[^\n]*Bullseye Testing Technology"
")[^\n]*\n)+"
)
+ if(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION)
+ string(REGEX REPLACE [[
+^CMake Deprecation Warning at [^
+]*CMakeLists.txt:1 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+]] "" actual_stderr "${actual_stderr}")
+ endif()
foreach(o IN ITEMS stdout stderr config)
string(REGEX REPLACE "\r\n" "\n" actual_${o} "${actual_${o}}")
string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_${o} "${actual_${o}}")
diff --git a/Tests/RunCMake/Swift/CMakeLists.txt b/Tests/RunCMake/Swift/CMakeLists.txt
index 74b3ff8de3..77030d63b2 100644
--- a/Tests/RunCMake/Swift/CMakeLists.txt
+++ b/Tests/RunCMake/Swift/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.15)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt b/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt
index 874bdc73c2..16d2e21386 100644
--- a/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt
+++ b/Tests/RunCMake/Swift/SwiftMultiArch-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at SwiftMultiArch.cmake:3 \(project\):
+^CMake Error at SwiftMultiArch.cmake:2 \(enable_language\):
multiple values for CMAKE_OSX_ARCHITECTURES not supported with Swift
Call Stack \(most recent call first\):
CMakeLists.txt:3
diff --git a/Tests/RunCMake/Swift/SwiftMultiArch.cmake b/Tests/RunCMake/Swift/SwiftMultiArch.cmake
index 5fdb6888f9..b59bb62db8 100644
--- a/Tests/RunCMake/Swift/SwiftMultiArch.cmake
+++ b/Tests/RunCMake/Swift/SwiftMultiArch.cmake
@@ -1,4 +1,2 @@
-cmake_minimum_required(VERSION 3.15.1)
set(CMAKE_OSX_ARCHITECTURES "armv7;arm64;i386;x86_64")
-project(SwiftMultiArch
- LANGUAGES Swift)
+enable_language(Swift)
diff --git a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt
index e16faea539..919a1685ca 100644
--- a/Tests/RunCMake/SymlinkTrees/CMakeLists.txt
+++ b/Tests/RunCMake/SymlinkTrees/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
message(STATUS "source: '${CMAKE_SOURCE_DIR}'")
diff --git a/Tests/RunCMake/Syntax/RunCMakeTest.cmake b/Tests/RunCMake/Syntax/RunCMakeTest.cmake
index f0c287cdb9..f56ac64753 100644
--- a/Tests/RunCMake/Syntax/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Syntax/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(BOM-UTF-8)
run_cmake(BOM-UTF-16-LE)
diff --git a/Tests/RunCMake/Syntax/String1-stderr.txt b/Tests/RunCMake/Syntax/String1-stderr.txt
index 07e98dae04..382ea005bb 100644
--- a/Tests/RunCMake/Syntax/String1-stderr.txt
+++ b/Tests/RunCMake/Syntax/String1-stderr.txt
@@ -1,3 +1,3 @@
-^
+^'
1 \${var} 4
- $
+ '$
diff --git a/Tests/RunCMake/Syntax/String1.cmake b/Tests/RunCMake/Syntax/String1.cmake
index a94c9ffdb4..20ed3c119b 100644
--- a/Tests/RunCMake/Syntax/String1.cmake
+++ b/Tests/RunCMake/Syntax/String1.cmake
@@ -1,3 +1,3 @@
-message("
+message("'
1 \${var} 4
- ")
+ '")
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt
index b309c3baf6..c7238df5b2 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace0-stderr.txt
@@ -1,7 +1,7 @@
-CMake Error at UnterminatedBrace0.cmake:2 \(set\):
+CMake Error at UnterminatedBrace0.cmake:1 \(set\):
Syntax error in cmake code at
- .*/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake:2
+ .*/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake:1
when parsing string
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake
index 0da12902a9..09af0cea65 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace0.cmake
@@ -1,2 +1 @@
-cmake_minimum_required(VERSION 3.0)
set(var "${")
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt
index ffe0e2a9e0..3d88f36158 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace1-stderr.txt
@@ -1,7 +1,7 @@
-CMake Warning \(dev\) at UnterminatedBrace1.cmake:3 \(set\):
+CMake Warning \(dev\) at UnterminatedBrace1.cmake:2 \(set\):
Syntax error in cmake code at
- .*/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake:3
+ .*/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake:2
when parsing string
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake
index 93fba3413e..8b40b19df9 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace1.cmake
@@ -1,3 +1,2 @@
-cmake_minimum_required(VERSION 3.0)
cmake_policy(SET CMP0010 OLD)
set(var "${")
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt b/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt
index b332d34b20..0d76251bf6 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace2-stderr.txt
@@ -1,7 +1,7 @@
-CMake Error at UnterminatedBrace2.cmake:4 \(set\):
+CMake Error at UnterminatedBrace2.cmake:3 \(set\):
Syntax error in cmake code at
- .*/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake:4
+ .*/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake:3
when parsing string
diff --git a/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake b/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake
index a650e5b728..30d4d4d46d 100644
--- a/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake
+++ b/Tests/RunCMake/Syntax/UnterminatedBrace2.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
cmake_policy(SET CMP0010 OLD)
cmake_policy(SET CMP0053 NEW)
set(var "${")
diff --git a/Tests/RunCMake/ArtifactOutputDirs/CMakeLists.txt b/Tests/RunCMake/TargetArtifacts/CMakeLists.txt
index ab1a20c7eb..ab1a20c7eb 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/CMakeLists.txt
+++ b/Tests/RunCMake/TargetArtifacts/CMakeLists.txt
diff --git a/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt
new file mode 100644
index 0000000000..b375da6f4a
--- /dev/null
+++ b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION-build-stdout.txt
@@ -0,0 +1,2 @@
+.*exA_name="(libexA\.so\.2|libexA\.2\.dylib|(lib|cyg|msys-|)exA-2\.dll)"
+.*exB_name="(libexB\.so\.2|libexB\.2\.dylib|(lib|cyg|msys-|)exB-2\.dll)"
diff --git a/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake
new file mode 100644
index 0000000000..82eca0b6f3
--- /dev/null
+++ b/Tests/RunCMake/TargetArtifacts/DLL-SOVERSION.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+add_library(exA SHARED dll.c)
+set_target_properties(exA PROPERTIES
+ SOVERSION 2
+ DLL_NAME_WITH_SOVERSION 1
+ )
+
+set(CMAKE_DLL_NAME_WITH_SOVERSION 1)
+add_library(exB SHARED dll.c)
+set_property(TARGET exB PROPERTY SOVERSION 2)
+
+add_custom_target(checkNames ALL
+ COMMAND ${CMAKE_COMMAND} -E echo exA_name="$<TARGET_FILE_NAME:exA>"
+ COMMAND ${CMAKE_COMMAND} -E echo exB_name="$<TARGET_FILE_NAME:exB>"
+ VERBATIM
+ )
+add_dependencies(checkNames exA exB)
diff --git a/Tests/RunCMake/ArtifactOutputDirs/ArtifactOutputDirs.cmake b/Tests/RunCMake/TargetArtifacts/OutputDirs.cmake
index d0accd7dea..d0accd7dea 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/ArtifactOutputDirs.cmake
+++ b/Tests/RunCMake/TargetArtifacts/OutputDirs.cmake
diff --git a/Tests/RunCMake/ArtifactOutputDirs/RunCMakeTest.cmake b/Tests/RunCMake/TargetArtifacts/RunCMakeTest.cmake
index 1bf84380d0..de69936ed7 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/RunCMakeTest.cmake
+++ b/Tests/RunCMake/TargetArtifacts/RunCMakeTest.cmake
@@ -2,18 +2,18 @@ include(RunCMake)
function(run_cmake_and_verify_after_build case)
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${case}-build")
- file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
- file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
- set(RunCMake_TEST_NO_CLEAN 1)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
else()
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
endif()
run_cmake(${case})
+ set(RunCMake_TEST_NO_CLEAN 1)
+ set(RunCMake_TEST_OUTPUT_MERGE 1)
run_cmake_command("${case}-build" ${CMAKE_COMMAND} --build .)
- unset(RunCMake_TEST_NO_CLEAN)
- unset(RunCMake_TEST_BINARY_DIR)
endfunction()
-run_cmake_and_verify_after_build(ArtifactOutputDirs)
+if(NOT CMAKE_SYSTEM_NAME STREQUAL "AIX")
+ run_cmake_and_verify_after_build(DLL-SOVERSION)
+endif()
+run_cmake_and_verify_after_build(OutputDirs)
diff --git a/Tests/RunCMake/ArtifactOutputDirs/check.cmake b/Tests/RunCMake/TargetArtifacts/check.cmake
index ca37eba531..ca37eba531 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/check.cmake
+++ b/Tests/RunCMake/TargetArtifacts/check.cmake
diff --git a/Tests/RunCMake/TargetArtifacts/dll.c b/Tests/RunCMake/TargetArtifacts/dll.c
new file mode 100644
index 0000000000..31e1dbfdf2
--- /dev/null
+++ b/Tests/RunCMake/TargetArtifacts/dll.c
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+ void dll(void)
+{
+}
diff --git a/Tests/RunCMake/ArtifactOutputDirs/lib.c b/Tests/RunCMake/TargetArtifacts/lib.c
index 22373f1940..22373f1940 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/lib.c
+++ b/Tests/RunCMake/TargetArtifacts/lib.c
diff --git a/Tests/RunCMake/ArtifactOutputDirs/main.c b/Tests/RunCMake/TargetArtifacts/main.c
index 8488f4e58f..8488f4e58f 100644
--- a/Tests/RunCMake/ArtifactOutputDirs/main.c
+++ b/Tests/RunCMake/TargetArtifacts/main.c
diff --git a/Tests/RunCMake/TargetProperties/CMakeLists.txt b/Tests/RunCMake/TargetProperties/CMakeLists.txt
index 44b5d30c03..26f536be57 100644
--- a/Tests/RunCMake/TargetProperties/CMakeLists.txt
+++ b/Tests/RunCMake/TargetProperties/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST})
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake b/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake
index 7744ee850c..b588ce03d1 100644
--- a/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ToolchainFile/RunCMakeTest.cmake
@@ -14,6 +14,7 @@ run_cmake_toolchain(LinkFlagsInit)
run_cmake_toolchain(CMP0126-NEW)
run_cmake_toolchain(CMP0126-OLD)
run_cmake_toolchain(CMP0126-WARN)
+run_cmake_toolchain(SetCrossCompiling)
function(run_IncludeDirectories)
run_cmake_toolchain(IncludeDirectories)
diff --git a/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt
new file mode 100644
index 0000000000..6642cf6578
--- /dev/null
+++ b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at [^
+]*/Modules/CMakeDetermineSystem.cmake:[0-9]+ \(message\):
+ CMAKE_CROSSCOMPILING has been set by the project, toolchain file, or user\.
+ CMake is resetting it to false because CMAKE_SYSTEM_NAME was not set\. To
+ indicate cross compilation, only CMAKE_SYSTEM_NAME needs to be set\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(project\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/ToolchainFile/SetCrossCompiling-toolchain.cmake b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-toolchain.cmake
new file mode 100644
index 0000000000..170e26cab2
--- /dev/null
+++ b/Tests/RunCMake/ToolchainFile/SetCrossCompiling-toolchain.cmake
@@ -0,0 +1 @@
+set(CMAKE_CROSSCOMPILING TRUE)
diff --git a/Tests/RunCMake/ToolchainFile/SetCrossCompiling.cmake b/Tests/RunCMake/ToolchainFile/SetCrossCompiling.cmake
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/Tests/RunCMake/ToolchainFile/SetCrossCompiling.cmake
diff --git a/Tests/RunCMake/VS10Project/CustomCommandParallel-check.cmake b/Tests/RunCMake/VS10Project/CustomCommandParallel-check.cmake
new file mode 100644
index 0000000000..4c8ad0007f
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/CustomCommandParallel-check.cmake
@@ -0,0 +1,52 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+ set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+ return()
+endif()
+
+set(found_CustomBuild_cmp0147_new 0)
+set(found_CustomBuild_cmp0147_old 0)
+set(found_CustomBuild_CMakeLists 0)
+set(found_BuildInParallel_cmp0147_new 0)
+set(found_BuildInParallel_cmp0147_old 0)
+set(found_BuildInParallel_CMakeLists 0)
+set(in_CustomBuild "")
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+ if(line MATCHES [[<CustomBuild Include=".*\\cmp0147-old\.txt\.rule">]])
+ set(found_CustomBuild_cmp0147_old 1)
+ set(in_CustomBuild "cmp0147_old")
+ endif()
+ if(line MATCHES [[<CustomBuild Include=".*\\cmp0147-new\.txt\.rule">]])
+ set(found_CustomBuild_cmp0147_new 1)
+ set(in_CustomBuild "cmp0147_new")
+ endif()
+ if(line MATCHES [[<CustomBuild Include=".*\\CMakeLists\.txt">]])
+ set(found_CustomBuild_CMakeLists 1)
+ set(in_CustomBuild "CMakeLists")
+ endif()
+ if(line MATCHES [[</CustomBuild>]])
+ set(in_CustomBuild "")
+ endif()
+ if(line MATCHES [[<BuildInParallel .*>true</BuildInParallel>]] AND in_CustomBuild)
+ set(found_BuildInParallel_${in_CustomBuild} 1)
+ endif()
+endforeach()
+if(NOT found_CustomBuild_cmp0147_new)
+ string(APPEND RunCMake_TEST_FAILED "CustomBuild for cmp0147-new.txt.rule not found in\n ${vcProjectFile}\n")
+endif()
+if(NOT found_CustomBuild_cmp0147_old)
+ string(APPEND RunCMake_TEST_FAILED "CustomBuild for cmp0147-old.txt.rule not found in\n ${vcProjectFile}\n")
+endif()
+if(NOT found_CustomBuild_CMakeLists)
+ string(APPEND RunCMake_TEST_FAILED "CustomBuild for CMakeLists.txt not found in\n ${vcProjectFile}\n")
+endif()
+if(NOT found_BuildInParallel_cmp0147_new)
+ string(APPEND RunCMake_TEST_FAILED "BuildInParallel for cmp0147-new.txt.rule not found in\n ${vcProjectFile}\n")
+endif()
+if(found_BuildInParallel_cmp0147_old)
+ string(APPEND RunCMake_TEST_FAILED "BuildInParallel for cmp0147-old.txt.rule incorrectly found in\n ${vcProjectFile}\n")
+endif()
+if(found_BuildInParallel_CMakeLists)
+ string(APPEND RunCMake_TEST_FAILED "BuildInParallel for CMakeLists.txt incorrectly found in\n ${vcProjectFile}\n")
+endif()
diff --git a/Tests/RunCMake/VS10Project/CustomCommandParallel.cmake b/Tests/RunCMake/VS10Project/CustomCommandParallel.cmake
new file mode 100644
index 0000000000..784fc685b5
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/CustomCommandParallel.cmake
@@ -0,0 +1,5 @@
+cmake_policy(VERSION 3.26) # CMP0147 left unset
+add_custom_command(OUTPUT "cmp0147-old.txt" COMMAND echo)
+cmake_policy(SET CMP0147 NEW)
+add_custom_command(OUTPUT "cmp0147-new.txt" COMMAND echo)
+add_custom_target(foo DEPENDS "cmp0147-old.txt" "cmp0147-new.txt")
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index ed74896370..669049a737 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -8,6 +8,9 @@ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREA
endif()
run_cmake(CustomCommandGenex)
+if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 1[1-5] ")
+ run_cmake(CustomCommandParallel)
+endif()
run_cmake(VsCsharpSourceGroup)
run_cmake(VsCSharpCompilerOpts)
run_cmake(ExplicitCMakeLists)
diff --git a/Tests/RunCMake/VSSolution/CMakeLists.txt b/Tests/RunCMake/VSSolution/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/VSSolution/CMakeLists.txt
+++ b/Tests/RunCMake/VSSolution/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake b/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake
index 7a000ee9ac..133dbe1641 100644
--- a/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VisibilityPreset/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
run_cmake(PropertyTypo)
run_cmake(CMP0063-OLD)
diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake
index bd914f821e..623ef2adde 100644
--- a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake
+++ b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake
@@ -1,7 +1,5 @@
-cmake_minimum_required(VERSION 3.22)
-
# a simple CSharp only test case
-project (DotNetSdk CSharp)
+enable_language(CSharp)
set(CMAKE_DOTNET_TARGET_FRAMEWORK net472)
set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk")
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt
index 872338d3c6..1d0bd703cf 100644
--- a/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt
+++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} CXX)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake b/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake
index 1f3c19d0c9..68372a126e 100644
--- a/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake
+++ b/Tests/RunCMake/XcodeProject/BundleLinkBundle.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.23)
-
-project(BundleLinkBundle CXX)
+enable_language(CXX)
add_subdirectory(lib_bundle)
diff --git a/Tests/RunCMake/XcodeProject/CMakeLists.txt b/Tests/RunCMake/XcodeProject/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/XcodeProject/CMakeLists.txt
+++ b/Tests/RunCMake/XcodeProject/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake b/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake
index 3d8fa17920..234ceef8b1 100644
--- a/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake
+++ b/Tests/RunCMake/XcodeProject/DeploymentTarget.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
-project(DeploymentTarget C)
+enable_language(C)
# using Xcode 7.1 SDK versions for deployment targets
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake
index a9ea717df3..b42e933d67 100644
--- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake
+++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase.cmake
@@ -45,7 +45,6 @@ endforeach()
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ExternalFrameworks/CMakeLists.txt
[[
-cmake_minimum_required(VERSION 3.18)
project(ExternalFrameworks)
add_library(staticFrameworkExt STATIC func6.c)
add_library(sharedFrameworkExt SHARED func7.c)
diff --git a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake
index e72bf4dc9c..ab64db7662 100644
--- a/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake
+++ b/Tests/RunCMake/XcodeProject/LinkBinariesBuildPhase_Funcs.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.18...3.19)
macro(returnOnError errorMsg)
if(NOT "${errorMsg}" STREQUAL "")
diff --git a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
index bc14874ad2..a9fafd22b7 100644
--- a/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeBundles.cmake
@@ -1,6 +1,5 @@
# check if Xcode and CMake have the same understanding of Bundle layout
-cmake_minimum_required(VERSION 3.3)
enable_language(C)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
index 8426148b8d..f8eccc76a7 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombined.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
-
-project(IOSInstallCombined CXX)
+enable_language(CXX)
set(maybe_armv7 armv7)
set(maybe_i386 i386)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
index 7d14d195fa..e719428c0a 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedPrune.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
-
-project(XcodeIOSInstallCombinedPrune CXX)
+enable_language(CXX)
if(XCODE_VERSION VERSION_GREATER_EQUAL 9)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10)
diff --git a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
index 5177ec2c84..cb22e5184f 100644
--- a/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeIOSInstallCombinedSingleArch.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
-
-project(XcodeIOSInstallCombinedSingleArch CXX)
+enable_language(CXX)
set(iphoneos_arch armv7)
if(XCODE_VERSION VERSION_GREATER_EQUAL 14)
diff --git a/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake b/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake
index 75da7b1c44..fccd4c6a84 100644
--- a/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeInstallIOS.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
-
-project(XcodeInstallIOS)
+enable_language(CXX)
set(XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
diff --git a/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake b/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake
index a1064f45dc..b334b7ddd3 100644
--- a/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeMultiplatform.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
enable_language(CXX)
set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME ON)
diff --git a/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake b/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake
index 4840276ae8..8f540460a4 100644
--- a/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeObjcFlags.cmake
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-project(objctest LANGUAGES C OBJC)
+enable_language(C)
+enable_language(OBJC)
include(CheckOBJCCompilerFlag)
check_objc_compiler_flag(-fobjc-arc HAVE_OBJC_ARC)
diff --git a/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake b/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake
index 0ad942f28f..193860c230 100644
--- a/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeObjcxxFlags.cmake
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-project(objcxxtest LANGUAGES CXX OBJCXX)
+enable_language(CXX)
+enable_language(OBJCXX)
include(CheckOBJCXXCompilerFlag)
check_objcxx_compiler_flag(-fobjc-arc HAVE_OBJC_ARC)
diff --git a/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake b/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake
index 3ca24af8db..5e66e827e9 100644
--- a/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeObjectLibsInTwoProjects.cmake
@@ -1,5 +1 @@
-cmake_minimum_required (VERSION 3.19)
-
-project (test_xcode_fail NONE)
-
add_subdirectory(subproject_two_object_libs)
diff --git a/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake b/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake
index 44052f035b..80107cbbc1 100644
--- a/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeRemoveExcessiveISystem.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required (VERSION 3.14)
if(NOT "$ENV{CMAKE_OSX_ARCHITECTURES}" MATCHES "[;$]")
set(USE_SWIFT 1)
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake
index 2fe5a9f839..ff3419cfd0 100644
--- a/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeSchemaGeneration.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.7)
-
-project(XcodeSchemaGeneration CXX)
+enable_language(CXX)
add_executable(foo main.cpp)
diff --git a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
index 267e3797aa..05a89763de 100644
--- a/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
@@ -1,9 +1,7 @@
-cmake_minimum_required(VERSION 3.7)
+enable_language(CXX)
set(CMAKE_XCODE_GENERATE_SCHEME ON)
-project(XcodeSchemaProperty CXX)
-
function(create_scheme_for_variable variable)
set(CMAKE_XCODE_SCHEME_${variable} ON)
add_executable(${variable} main.cpp)
diff --git a/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake b/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake
index e83d7f3cd8..55dd1a75e3 100644
--- a/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeTbdStub.cmake
@@ -1,2 +1 @@
-cmake_minimum_required(VERSION 3.3)
find_package(ZLIB REQUIRED)
diff --git a/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake b/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake
index 58d26164f6..bd8995be9d 100644
--- a/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake
+++ b/Tests/RunCMake/XcodeProject/XcodeXCConfig.cmake
@@ -1,6 +1,4 @@
-cmake_minimum_required(VERSION 3.23)
-
-project(XcodeXCConfig C)
+enable_language(C)
set(CMAKE_XCODE_XCCONFIG "$<IF:$<CONFIG:Debug>,XcodeXCConfig.global.debug.xcconfig,XcodeXCConfig.global.release.xcconfig>")
diff --git a/Tests/RunCMake/add_custom_command/CMakeLists.txt b/Tests/RunCMake/add_custom_command/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/add_custom_command/CMakeLists.txt
+++ b/Tests/RunCMake/add_custom_command/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake b/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake
index 65b72506e5..d8a5d86b57 100644
--- a/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake
+++ b/Tests/RunCMake/add_custom_command/WorkingDirectory.cmake
@@ -2,7 +2,7 @@ add_custom_target(mkdir COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURR
add_custom_command(
OUTPUT out.txt
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/PrintDir.cmake
- WORKING_DIRECTORY ${CMAKE_CFG_INTDIR}
+ WORKING_DIRECTORY $<CONFIG>
)
set_property(SOURCE out.txt PROPERTY SYMBOLIC 1)
add_custom_target(drive ALL DEPENDS out.txt)
diff --git a/Tests/RunCMake/add_custom_target/CMakeLists.txt b/Tests/RunCMake/add_custom_target/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/add_custom_target/CMakeLists.txt
+++ b/Tests/RunCMake/add_custom_target/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/add_dependencies/CMakeLists.txt b/Tests/RunCMake/add_dependencies/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/add_dependencies/CMakeLists.txt
+++ b/Tests/RunCMake/add_dependencies/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake b/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake
index f0e4069eda..547c1017ad 100644
--- a/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake
+++ b/Tests/RunCMake/add_dependencies/ReadOnlyProperty.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.7)
-project(ReadOnlyProperty C)
+enable_language(C)
add_library(a a.c)
diff --git a/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake b/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake
index 45b3974420..2f883af9e2 100644
--- a/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake
+++ b/Tests/RunCMake/add_dependencies/RetrieveDependencies.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.7)
-project(RetrieveDependencies C)
+enable_language(C)
add_library(a a.c)
diff --git a/Tests/RunCMake/add_executable/CMakeLists.txt b/Tests/RunCMake/add_executable/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/add_executable/CMakeLists.txt
+++ b/Tests/RunCMake/add_executable/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/add_library/CMakeLists.txt b/Tests/RunCMake/add_library/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/add_library/CMakeLists.txt
+++ b/Tests/RunCMake/add_library/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake b/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake
index c77b43cd51..f7c551d19c 100644
--- a/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake
+++ b/Tests/RunCMake/add_link_options/LINKER_expansion-list.cmake
@@ -11,7 +11,7 @@ string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_C_CREATE_SHARED_LIBRARY "${CMAK
add_library(example SHARED LinkOptionsLib.c)
# use LAUNCH facility to dump linker command
-set_property(TARGET example PROPERTY RULE_LAUNCH_LINK "\"${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/dump${CMAKE_EXECUTABLE_SUFFIX}\"")
+set_property(TARGET example PROPERTY RULE_LAUNCH_LINK "\"$<TARGET_FILE:dump>\"")
add_dependencies (example dump)
diff --git a/Tests/RunCMake/add_subdirectory/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/CMakeLists.txt
index 47d249cce0..b5d72625df 100644
--- a/Tests/RunCMake/add_subdirectory/CMakeLists.txt
+++ b/Tests/RunCMake/add_subdirectory/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
# Have to set policy here due to policy scope
if(DEFINED CMP0082_VALUE)
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt
new file mode 100644
index 0000000000..46b32d1426
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-AlphaNumeric-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-AlphaNumeric.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt
new file mode 100644
index 0000000000..906f318d36
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-BracketArgument-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-BracketArgument.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt
new file mode 100644
index 0000000000..65818fae27
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-EscapedSpecialChars-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-EscapedSpecialChars.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt
new file mode 100644
index 0000000000..db88659dfb
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialChars-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt
new file mode 100644
index 0000000000..42cc02aa1a
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-FormerInvalidSpecialCharsMC-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Deprecation Warning at CMP0110-OLD-FormerInvalidSpecialChars.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMP0110-OLD-FormerInvalidSpecialCharsMC.cmake:[0-9]+ \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt
new file mode 100644
index 0000000000..6a39db67ff
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-LeadingAndTrailingWhitespace-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-LeadingAndTrailingWhitespace.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt
new file mode 100644
index 0000000000..d5dcada0b4
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-OtherSpecialChars-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-OtherSpecialChars.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt
new file mode 100644
index 0000000000..69cd304b27
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-Quote-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-Quote.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt
new file mode 100644
index 0000000000..e601bfc814
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-Semicolon-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-Semicolon.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt
new file mode 100644
index 0000000000..618d2b19ed
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-Space-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-Space.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt
new file mode 100644
index 0000000000..ad67c08d61
--- /dev/null
+++ b/Tests/RunCMake/add_test/CMP0110-OLD-ValidSpecialChars-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0110-OLD-ValidSpecialChars.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0110 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/alias_targets/CMakeLists.txt b/Tests/RunCMake/alias_targets/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/alias_targets/CMakeLists.txt
+++ b/Tests/RunCMake/alias_targets/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/alias_targets/invalid-name.cmake b/Tests/RunCMake/alias_targets/invalid-name.cmake
index 01983e54f1..e2ebbfa05f 100644
--- a/Tests/RunCMake/alias_targets/invalid-name.cmake
+++ b/Tests/RunCMake/alias_targets/invalid-name.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_policy(SET CMP0037 OLD)
enable_language(CXX)
add_library(foo empty.cpp)
diff --git a/Tests/RunCMake/build_command/CMakeLists.txt b/Tests/RunCMake/build_command/CMakeLists.txt
index f1a83e8c4b..1319aecc3d 100644
--- a/Tests/RunCMake/build_command/CMakeLists.txt
+++ b/Tests/RunCMake/build_command/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
if(NOT NoProject)
project(${RunCMake_TEST} NONE)
endif()
diff --git a/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt b/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt
deleted file mode 100644
index 7d40dcb047..0000000000
--- a/Tests/RunCMake/cmake_minimum_required/Before2812-stderr.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-^CMake Deprecation Warning at Before2812.cmake:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
-+
-CMake Deprecation Warning at Before2812.cmake:2 \(cmake_policy\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
-+
-CMake Deprecation Warning at Before2812.cmake:6 \(cmake_policy\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
-
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt b/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt
new file mode 100644
index 0000000000..79812352fa
--- /dev/null
+++ b/Tests/RunCMake/cmake_minimum_required/Before3_5-stderr.txt
@@ -0,0 +1,26 @@
+^CMake Deprecation Warning at Before3_5\.cmake:1 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
++
+CMake Deprecation Warning at Before3_5\.cmake:2 \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
++
+CMake Deprecation Warning at Before3_5\.cmake:6 \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/cmake_minimum_required/Before2812.cmake b/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake
index 220e359d4d..220e359d4d 100644
--- a/Tests/RunCMake/cmake_minimum_required/Before2812.cmake
+++ b/Tests/RunCMake/cmake_minimum_required/Before3_5.cmake
diff --git a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt
index 667561ea79..8eb57486ad 100644
--- a/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt
+++ b/Tests/RunCMake/cmake_minimum_required/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt
index 81d26d260d..3eb980a58b 100644
--- a/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt
+++ b/Tests/RunCMake/cmake_minimum_required/CompatBefore24-stderr.txt
@@ -1,9 +1,9 @@
-^CMake Deprecation Warning at CompatBefore24.cmake:1 \(cmake_minimum_required\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
+^CMake Deprecation Warning at CompatBefore24\.cmake:1 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
diff --git a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
index 3a959bb2f9..36c44cbc71 100644
--- a/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_minimum_required/RunCMakeTest.cmake
@@ -4,7 +4,7 @@ run_cmake(Before24)
run_cmake(CompatBefore24)
run_cmake(Future)
run_cmake(PolicyBefore24)
-run_cmake(Before2812)
+run_cmake(Before3_5)
run_cmake(Range)
run_cmake(RangeBad)
run_cmake(Unknown)
diff --git a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt
index 6dd8cdf551..93ee9dfd5f 100644
--- a/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt
+++ b/Tests/RunCMake/cmake_parse_arguments/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/cmake_path/call-cmake_path.cmake b/Tests/RunCMake/cmake_path/call-cmake_path.cmake
index 70fd6f53f2..655115f58b 100644
--- a/Tests/RunCMake/cmake_path/call-cmake_path.cmake
+++ b/Tests/RunCMake/cmake_path/call-cmake_path.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.18...3.19)
-
# define input variable
set (path "")
diff --git a/Tests/RunCMake/configure_file/CMakeLists.txt b/Tests/RunCMake/configure_file/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/configure_file/CMakeLists.txt
+++ b/Tests/RunCMake/configure_file/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/continue/CMakeLists.txt b/Tests/RunCMake/continue/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/continue/CMakeLists.txt
+++ b/Tests/RunCMake/continue/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt
index af70ac325b..f5d57e9b2a 100644
--- a/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt
+++ b/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD-stderr.txt
@@ -1,2 +1,9 @@
-^(Error\(s\) when building project
+^CMake Deprecation Warning at [^
+]*/Tests/RunCMake/ctest_build/BuildFailure-CMP0061-OLD/test\.cmake:[0-9]+ \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++(Error\(s\) when building project
)?ctest_build returned zero$
diff --git a/Tests/RunCMake/ctest_build/CMakeLists.txt.in b/Tests/RunCMake/ctest_build/CMakeLists.txt.in
index 4a067fa507..0a59940fe2 100644
--- a/Tests/RunCMake/ctest_build/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_build/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
@CASE_CMAKELISTS_PREFIX_CODE@
project(CTestBuild@CASE_NAME@ @LANG@)
include(CTest)
diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
index 6f1b4b6f47..12525f2e4e 100644
--- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
@@ -35,7 +35,9 @@ endif()
run_ctest(BuildFailure)
if (RunCMake_GENERATOR MATCHES "Makefiles")
- set(CASE_TEST_PREFIX_CODE "")
+ set(CASE_TEST_PREFIX_CODE [[
+cmake_policy(VERSION 3.2)
+]])
run_ctest(BuildFailure-CMP0061-OLD)
endif()
endfunction()
diff --git a/Tests/RunCMake/ctest_build/test.cmake.in b/Tests/RunCMake/ctest_build/test.cmake.in
index 9f7fa13e07..f92568fcf1 100644
--- a/Tests/RunCMake/ctest_build/test.cmake.in
+++ b/Tests/RunCMake/ctest_build/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
@CASE_TEST_PREFIX_CODE@
set(CTEST_SITE "test-site")
diff --git a/Tests/RunCMake/ctest_cmake_error/test.cmake.in b/Tests/RunCMake/ctest_cmake_error/test.cmake.in
index 0648e7c351..711a77fd49 100644
--- a/Tests/RunCMake/ctest_cmake_error/test.cmake.in
+++ b/Tests/RunCMake/ctest_cmake_error/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in
index 2fb21d45d8..2860b5c46d 100644
--- a/Tests/RunCMake/ctest_configure/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_configure/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestConfigure@CASE_NAME@ NONE)
include(CTest)
add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_configure/test.cmake.in b/Tests/RunCMake/ctest_configure/test.cmake.in
index 72d199acb0..5935809d97 100644
--- a/Tests/RunCMake/ctest_configure/test.cmake.in
+++ b/Tests/RunCMake/ctest_configure/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in
index 1babd72d91..da7ec1a606 100644
--- a/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_coverage/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestCoverage@CASE_NAME@ NONE)
include(CTest)
add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_coverage/test.cmake.in b/Tests/RunCMake/ctest_coverage/test.cmake.in
index 1788e6647c..f5a12236b0 100644
--- a/Tests/RunCMake/ctest_coverage/test.cmake.in
+++ b/Tests/RunCMake/ctest_coverage/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in
index 68a0fcb8bc..1de338b4f2 100644
--- a/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_memcheck/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(CTestTestMemcheck@CASE_NAME@ NONE)
include(CTest)
diff --git a/Tests/RunCMake/ctest_memcheck/test.cmake.in b/Tests/RunCMake/ctest_memcheck/test.cmake.in
index eedf080b78..af995fc0e3 100644
--- a/Tests/RunCMake/ctest_memcheck/test.cmake.in
+++ b/Tests/RunCMake/ctest_memcheck/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
# Settings:
set(CTEST_SITE "@SITE@")
diff --git a/Tests/RunCMake/ctest_start/CMakeLists.txt.in b/Tests/RunCMake/ctest_start/CMakeLists.txt.in
index 913239c295..e497a7d995 100644
--- a/Tests/RunCMake/ctest_start/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_start/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestStart@CASE_NAME@ NONE)
include(CTest)
add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_start/test.cmake.in b/Tests/RunCMake/ctest_start/test.cmake.in
index 4063ece663..82acc19686 100644
--- a/Tests/RunCMake/ctest_start/test.cmake.in
+++ b/Tests/RunCMake/ctest_start/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_submit/test.cmake.in b/Tests/RunCMake/ctest_submit/test.cmake.in
index 35cd16a980..0f4885f994 100644
--- a/Tests/RunCMake/ctest_submit/test.cmake.in
+++ b/Tests/RunCMake/ctest_submit/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/ctest_test/CMakeLists.txt.in b/Tests/RunCMake/ctest_test/CMakeLists.txt.in
index e61b556688..8eb422d10e 100644
--- a/Tests/RunCMake/ctest_test/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_test/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestTest@CASE_NAME@ NONE)
include(CTest)
add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_test/test.cmake.in b/Tests/RunCMake/ctest_test/test.cmake.in
index 36b1dbdf03..16dde1c69e 100644
--- a/Tests/RunCMake/ctest_test/test.cmake.in
+++ b/Tests/RunCMake/ctest_test/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
@CASE_TEST_PREFIX_CODE@
set(CTEST_SITE "test-site")
diff --git a/Tests/RunCMake/ctest_update/CMakeLists.txt.in b/Tests/RunCMake/ctest_update/CMakeLists.txt.in
index ecf0e54eaa..f816fac07d 100644
--- a/Tests/RunCMake/ctest_update/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_update/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestTest@CASE_NAME@ NONE)
include(CTest)
@CASE_CMAKELISTS_SUFFIX_CODE@
diff --git a/Tests/RunCMake/ctest_update/test.cmake.in b/Tests/RunCMake/ctest_update/test.cmake.in
index 01aab26bb9..faaf9b489c 100644
--- a/Tests/RunCMake/ctest_update/test.cmake.in
+++ b/Tests/RunCMake/ctest_update/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
@CASE_TEST_PREFIX_CODE@
set(CTEST_SITE "test-site")
diff --git a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in
index 1fab71be5e..21e5273568 100644
--- a/Tests/RunCMake/ctest_upload/CMakeLists.txt.in
+++ b/Tests/RunCMake/ctest_upload/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(CTestUpload@CASE_NAME@ NONE)
include(CTest)
add_test(NAME RunCMakeVersion COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/ctest_upload/test.cmake.in b/Tests/RunCMake/ctest_upload/test.cmake.in
index f13bdd11da..a546d38c4b 100644
--- a/Tests/RunCMake/ctest_upload/test.cmake.in
+++ b/Tests/RunCMake/ctest_upload/test.cmake.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
set(CTEST_SITE "test-site")
set(CTEST_BUILD_NAME "test-build-name")
diff --git a/Tests/RunCMake/export/CMakeLists.txt b/Tests/RunCMake/export/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/export/CMakeLists.txt
+++ b/Tests/RunCMake/export/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
index 43b406bbaa..a68607e6b5 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake
@@ -9,6 +9,10 @@ function(run_install_test case)
run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
# Check "all" components.
set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-all)
+ set(maybe_stderr "${case}-all-stderr-${CMAKE_C_COMPILER_ID}.txt")
+ if(EXISTS "${RunCMake_SOURCE_DIR}/${maybe_stderr}")
+ set(RunCMake-stderr-file "${maybe_stderr}")
+ endif()
run_cmake_command(${case}-all ${CMAKE_COMMAND} --install . --prefix ${CMAKE_INSTALL_PREFIX} --config Debug)
endfunction()
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
index cb0e534a35..10b7b82bce 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
@@ -1,19 +1,29 @@
+if(CMAKE_C_COMPILER_ID STREQUAL "Borland")
+ # Borland upper-cases dll names referenced in import libraries.
+ set(conflict_dll [[CONFLICT\.DLL]])
+ set(unresolved_dll [[UNRESOLVED\.DLL]])
+else()
+ set(conflict_dll [[conflict\.dll]])
+ set(unresolved_dll [[unresolved\.dll]])
+endif()
+
set(_check
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\.conflict/\.\./(lib)?libdir\.dll]=]
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\.search/(lib)?search\.dll]=]
+ [=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?MixedCase\.dll]=]
[=[[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?testlib\.dll]=]
)
check_contents(deps/deps1.txt "^${_check}$")
check_contents(deps/deps2.txt "^${_check}$")
check_contents(deps/deps3.txt "^${_check}$")
set(_check
- [=[(lib)?unresolved\.dll]=]
+ "(lib)?${unresolved_dll}"
)
check_contents(deps/udeps1.txt "^${_check}$")
check_contents(deps/udeps2.txt "^${_check}$")
check_contents(deps/udeps3.txt "^${_check}$")
set(_check
- "^(lib)?conflict\\.dll:[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?conflict\\.dll\n$"
+ "^(lib)?${conflict_dll}:[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/\\.conflict/(lib)?conflict\\.dll;[^;]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-build/root-all/bin/(lib)?conflict\\.dll\n$"
)
check_contents(deps/cdeps1.txt "${_check}")
check_contents(deps/cdeps2.txt "${_check}")
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt
new file mode 100644
index 0000000000..607e4b8e82
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-Borland.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+ file Multiple conflicting paths found for PATH\.DLL:
+
+ [^
+]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test1/path\.dll
+ [^
+]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test2/path\.dll$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt
new file mode 100644
index 0000000000..fea1083a53
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-Borland.txt
@@ -0,0 +1,4 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+ file Could not resolve runtime dependencies:
+
+ UNRESOLVED\.DLL$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake
index 9160ce5f20..aad9077553 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows.cmake
@@ -8,6 +8,7 @@ set(testlib_names
search
unresolved
conflict
+ MixedCase
)
file(REMOVE "${CMAKE_BINARY_DIR}/testlib.c")
@@ -34,7 +35,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/testlib_noconflict.c" "__declspec(dllimport) ext
add_library(testlib_noconflict SHARED "${CMAKE_BINARY_DIR}/testlib_noconflict.c")
target_link_libraries(testlib_noconflict PRIVATE libdir)
-install(TARGETS testlib libdir_postexcluded libdir conflict testlib_noconflict DESTINATION bin)
+install(TARGETS testlib libdir_postexcluded libdir conflict MixedCase testlib_noconflict DESTINATION bin)
install(TARGETS libdir search_postexcluded search DESTINATION bin/.search) # Prefixing with "." ensures it is the first item after list(SORT)
install(TARGETS testlib_conflict conflict DESTINATION bin/.conflict)
@@ -61,6 +62,7 @@ install(CODE [[
"^(lib)?search\\.dll$"
"^(lib)?unresolved\\.dll$"
"^(lib)?conflict\\.dll$"
+ "^(lib)?mixedcase\\.dll$"
"^kernel32\\.dll$"
PRE_EXCLUDE_REGEXES ".*"
POST_INCLUDE_REGEXES
@@ -68,6 +70,7 @@ install(CODE [[
"^.*/(lib)?libdir\\.dll$"
"^.*/(lib)?search\\.dll$"
"^.*/(lib)?conflict\\.dll$"
+ "^.*/(lib)?mixedcase\\.dll$"
POST_EXCLUDE_REGEXES ".*"
DIRECTORIES
"${CMAKE_INSTALL_PREFIX}/bin/.search"
diff --git a/Tests/RunCMake/file/CMakeLists.txt b/Tests/RunCMake/file/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/file/CMakeLists.txt
+++ b/Tests/RunCMake/file/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/file/REAL_PATH.cmake b/Tests/RunCMake/file/REAL_PATH.cmake
index 0b5d3c0fd1..9c5d4ea0ee 100644
--- a/Tests/RunCMake/file/REAL_PATH.cmake
+++ b/Tests/RunCMake/file/REAL_PATH.cmake
@@ -34,3 +34,19 @@ file(REAL_PATH "~/test.txt" real_path EXPAND_TILDE)
if (NOT real_path STREQUAL "${HOME_DIR}/test.txt")
message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}/test.txt\"")
endif()
+
+if (WIN32)
+ cmake_policy(SET CMP0139 NEW)
+
+ set(in "${CMAKE_CURRENT_BINARY_DIR}/AbC.TxT")
+
+ file(REMOVE "${in}")
+ file(TOUCH "${in}")
+
+ string(TOLOWER "${in}" low)
+ file(REAL_PATH "${low}" out)
+
+ if(NOT "${out}" PATH_EQUAL "${in}")
+ message(SEND_ERROR "real path is \"${out}\", should be \"${in}\"")
+ endif()
+endif()
diff --git a/Tests/RunCMake/find_dependency/CMakeLists.txt b/Tests/RunCMake/find_dependency/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_dependency/CMakeLists.txt
+++ b/Tests/RunCMake/find_dependency/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/find_file/CMakeLists.txt b/Tests/RunCMake/find_file/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_file/CMakeLists.txt
+++ b/Tests/RunCMake/find_file/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/find_library/CMakeLists.txt b/Tests/RunCMake/find_library/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_library/CMakeLists.txt
+++ b/Tests/RunCMake/find_library/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt
new file mode 100644
index 0000000000..3ccd101bda
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive-stderr.txt
@@ -0,0 +1,45 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake
new file mode 100644
index 0000000000..f0853eec4b
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseInsensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+cmake_policy(SET CMP0144 NEW)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt
new file mode 100644
index 0000000000..3ccd101bda
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive-stderr.txt
@@ -0,0 +1,45 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake
new file mode 100644
index 0000000000..f0853eec4b
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-NEW-CaseSensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+cmake_policy(SET CMP0144 NEW)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt
new file mode 100644
index 0000000000..3274b82b71
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive-stderr.txt
@@ -0,0 +1,45 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake
new file mode 100644
index 0000000000..f1560a9126
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseInsensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+cmake_policy(SET CMP0144 OLD)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt
new file mode 100644
index 0000000000..c02a797f32
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive-stderr.txt
@@ -0,0 +1,45 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake
new file mode 100644
index 0000000000..f1560a9126
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-OLD-CaseSensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+cmake_policy(SET CMP0144 OLD)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt
new file mode 100644
index 0000000000..3540dc9165
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive-stderr.txt
@@ -0,0 +1,63 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
++
+CMake Warning \(dev\) at CMP0144-common.cmake:[0-9]+ \(find_package\):
+ Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT
+ variables. Run "cmake --help-policy CMP0144" for policy details\. Use the
+ cmake_policy command to set the policy and suppress this warning\.
+
+ CMake variable FOO_ROOT is set to:
+
+ [^
+]*/Tests/RunCMake/find_package/PackageRoot/foo/cmake_root
+
+ For compatibility, find_package is ignoring the variable, but code in a
+ \.cmake module might still use it\.
+Call Stack \(most recent call first\):
+ CMP0144-common.cmake:[0-9]+ \(RunTestCase\)
+ CMP0144-WARN-CaseInsensitive.cmake:[0-9]+ \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers. Use -Wno-dev to suppress it.
++
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake
new file mode 100644
index 0000000000..10f6b66d45
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseInsensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+# (do not set CMP0144)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt
new file mode 100644
index 0000000000..3ccd101bda
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed-stderr.txt
@@ -0,0 +1,45 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/cmake_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/cmake_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/cmake_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/cmake_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/cmake_root/bin/foo.exe
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: <base>/foo/env_root/include/foo.h
+FOO_TEST_FILE_ZOT: <base>/foo/env_root/include/zot/zot.h
+FOO_TEST_PATH_FOO: <base>/foo/env_root/include
+FOO_TEST_PATH_ZOT: <base>/foo/env_root/include/zot
+FOO_TEST_PROG_FOO: <base>/foo/env_root/bin/foo.exe
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake
new file mode 100644
index 0000000000..10f6b66d45
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-Mixed.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+# (do not set CMP0144)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt
new file mode 100644
index 0000000000..b828f058f6
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive-stderr.txt
@@ -0,0 +1,68 @@
+^----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: '<base>/foo/env_root'
++
+CMake Warning \(dev\) at CMP0144-common.cmake:[0-9]+ \(find_package\):
+ Policy CMP0144 is not set: find_package uses upper-case <PACKAGENAME>_ROOT
+ variables. Run "cmake --help-policy CMP0144" for policy details\. Use the
+ cmake_policy command to set the policy and suppress this warning\.
+
+ CMake variable FOO_ROOT is set to:
+
+ [^
+]*/Tests/RunCMake/find_package/PackageRoot/foo/cmake_root
+
+ Environment variable FOO_ROOT is set to:
+
+ [^
+]*/Tests/RunCMake/find_package/PackageRoot/foo/env_root
+
+ For compatibility, find_package is ignoring the variable, but code in a
+ \.cmake module might still use it\.
+Call Stack \(most recent call first\):
+ CMP0144-common.cmake:[0-9]+ \(RunTestCase\)
+ CMP0144-WARN-CaseSensitive.cmake:[0-9]+ \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers. Use -Wno-dev to suppress it.
++
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : '<base>/foo/cmake_root'
+ENV{FOO_ROOT}: ''
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------
+FOO_ROOT : ''
+ENV{FOO_ROOT}: '<base>/foo/env_root'
+
+find_package\(Foo\)
+FOO_TEST_FILE_FOO: FOO_TEST_FILE_FOO-NOTFOUND
+FOO_TEST_FILE_ZOT: FOO_TEST_FILE_ZOT-NOTFOUND
+FOO_TEST_PATH_FOO: FOO_TEST_PATH_FOO-NOTFOUND
+FOO_TEST_PATH_ZOT: FOO_TEST_PATH_ZOT-NOTFOUND
+FOO_TEST_PROG_FOO: FOO_TEST_PROG_FOO-NOTFOUND
+
+----------$
diff --git a/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake
new file mode 100644
index 0000000000..10f6b66d45
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-WARN-CaseSensitive.cmake
@@ -0,0 +1,3 @@
+cmake_policy(VERSION 3.26)
+# (do not set CMP0144)
+include(CMP0144-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0144-common.cmake b/Tests/RunCMake/find_package/CMP0144-common.cmake
new file mode 100644
index 0000000000..58eccca815
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0144-common.cmake
@@ -0,0 +1,78 @@
+# (includer selects CMP0144)
+list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/PackageRoot)
+set(PackageRoot_BASE ${CMAKE_CURRENT_SOURCE_DIR}/PackageRoot)
+
+function(PrintPath label path)
+ string(REPLACE "${PackageRoot_BASE}" "<base>" out "${path}")
+ message("${label} ${out}")
+endfunction()
+
+macro(RunTestCase)
+ message("----------")
+ PrintPath("FOO_ROOT :" "'${FOO_ROOT}'")
+ PrintPath("ENV{FOO_ROOT}:" "'$ENV{FOO_ROOT}'")
+ message("")
+
+ find_package(Foo)
+ message("find_package(Foo)")
+ PrintPath("FOO_TEST_FILE_FOO:" "${FOO_TEST_FILE_FOO}")
+ PrintPath("FOO_TEST_FILE_ZOT:" "${FOO_TEST_FILE_ZOT}")
+ PrintPath("FOO_TEST_PATH_FOO:" "${FOO_TEST_PATH_FOO}")
+ PrintPath("FOO_TEST_PATH_ZOT:" "${FOO_TEST_PATH_ZOT}")
+ PrintPath("FOO_TEST_PROG_FOO:" "${FOO_TEST_PROG_FOO}")
+ message("")
+
+ unset(FOO_ROOT)
+ unset(ENV{FOO_ROOT})
+ if(NOT CMAKE_HOST_WIN32)
+ unset(Foo_ROOT)
+ unset(ENV{Foo_ROOT})
+ endif()
+ unset(FOO_TEST_FILE_FOO)
+ unset(FOO_TEST_FILE_ZOT)
+ unset(FOO_TEST_PATH_FOO)
+ unset(FOO_TEST_PATH_ZOT)
+ unset(FOO_TEST_PROG_FOO)
+ unset(FOO_TEST_FILE_FOO CACHE)
+ unset(FOO_TEST_FILE_ZOT CACHE)
+ unset(FOO_TEST_PATH_FOO CACHE)
+ unset(FOO_TEST_PATH_ZOT CACHE)
+ unset(FOO_TEST_PROG_FOO CACHE)
+endmacro()
+
+RunTestCase()
+
+set(FOO_ROOT ${PackageRoot_BASE}/foo/cmake_root)
+set(ENV{FOO_ROOT} ${PackageRoot_BASE}/foo/env_root)
+if(RunCMake_TEST MATCHES "CaseSensitive")
+ if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed")
+ set(Foo_ROOT "${FOO_ROOT}")
+ set(ENV{Foo_ROOT} "$ENV{FOO_ROOT}")
+ else()
+ set(Foo_ROOT ${PackageRoot_BASE}/does_not_exist)
+ set(ENV{Foo_ROOT} ${PackageRoot_BASE}/does_not_exist)
+ endif()
+endif()
+RunTestCase()
+
+set(FOO_ROOT ${PackageRoot_BASE}/foo/cmake_root)
+if(RunCMake_TEST MATCHES "CaseSensitive")
+ if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed")
+ set(Foo_ROOT "${FOO_ROOT}")
+ else()
+ set(Foo_ROOT ${PackageRoot_BASE}/does_not_exist)
+ endif()
+endif()
+RunTestCase()
+
+set(ENV{FOO_ROOT} ${PackageRoot_BASE}/foo/env_root)
+if(RunCMake_TEST MATCHES "CaseSensitive")
+ if(RunCMake_TEST STREQUAL "CMP0144-WARN-CaseSensitive-Mixed")
+ set(ENV{Foo_ROOT} "$ENV{FOO_ROOT}")
+ else()
+ set(ENV{Foo_ROOT} ${PackageRoot_BASE}/does_not_exist)
+ endif()
+endif()
+RunTestCase()
+
+message("----------")
diff --git a/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt
new file mode 100644
index 0000000000..8249211c47
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0145-NEW-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning at CMP0145-NEW\.cmake:[0-9]+ \(find_package\):
+ No "FindDart\.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0145-NEW.cmake b/Tests/RunCMake/find_package/CMP0145-NEW.cmake
new file mode 100644
index 0000000000..f3e7bef591
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0145-NEW.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0145 NEW)
+set(_FindDart_testing TRUE)
+find_package(Dart MODULE)
+
+if(_FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake erroneously included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0145-OLD.cmake b/Tests/RunCMake/find_package/CMP0145-OLD.cmake
new file mode 100644
index 0000000000..9a73f687db
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0145-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0145 OLD)
+set(_FindDart_testing TRUE)
+find_package(Dart MODULE)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt
new file mode 100644
index 0000000000..36c66ec177
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0145-WARN-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at CMP0145-WARN\.cmake:[0-9]+ \(find_package\):
+ Policy CMP0145 is not set: The Dart and FindDart modules are removed\. Run
+ "cmake --help-policy CMP0145" for policy details\. Use the cmake_policy
+ command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0145-WARN.cmake b/Tests/RunCMake/find_package/CMP0145-WARN.cmake
new file mode 100644
index 0000000000..76da752e08
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0145-WARN.cmake
@@ -0,0 +1,6 @@
+set(_FindDart_testing TRUE)
+find_package(Dart MODULE)
+
+if(NOT _FindDart_included)
+ message(FATAL_ERROR "FindDart.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0146-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0146-NEW-stderr.txt
new file mode 100644
index 0000000000..01628526d8
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0146-NEW-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning at CMP0146-NEW\.cmake:[0-9]+ \(find_package\):
+ No "FindCUDA\.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0146-NEW.cmake b/Tests/RunCMake/find_package/CMP0146-NEW.cmake
new file mode 100644
index 0000000000..b373227f49
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0146-NEW.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0146 NEW)
+set(_FindCUDA_testing TRUE)
+find_package(CUDA MODULE)
+
+if(_FindCUDA_included)
+ message(FATAL_ERROR "FindCUDA.cmake erroneously included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0146-OLD.cmake b/Tests/RunCMake/find_package/CMP0146-OLD.cmake
new file mode 100644
index 0000000000..77cd1f528d
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0146-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0146 OLD)
+set(_FindCUDA_testing TRUE)
+find_package(CUDA MODULE)
+
+if(NOT _FindCUDA_included)
+ message(FATAL_ERROR "FindCUDA.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt
new file mode 100644
index 0000000000..2cd9c5fd28
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0146-WARN-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at CMP0146-WARN\.cmake:[0-9]+ \(find_package\):
+ Policy CMP0146 is not set: The FindCUDA module is removed\. Run "cmake
+ --help-policy CMP0146" for policy details\. Use the cmake_policy command to
+ set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0146-WARN.cmake b/Tests/RunCMake/find_package/CMP0146-WARN.cmake
new file mode 100644
index 0000000000..276daf2c0f
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0146-WARN.cmake
@@ -0,0 +1,6 @@
+set(_FindCUDA_testing TRUE)
+find_package(CUDA MODULE)
+
+if(NOT _FindCUDA_included)
+ message(FATAL_ERROR "FindCUDA.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0147-NEW-result.txt b/Tests/RunCMake/find_package/CMP0147-NEW-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-NEW-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt
new file mode 100644
index 0000000000..1caf6c592e
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-NEW-stderr.txt
@@ -0,0 +1,7 @@
+^CMake Error at [^
+]*/Modules/FindCUDA.cmake:[0-9]+ \(message\):
+ The FindCUDA module does not work in Visual Studio with policy CMP0147\.
+Call Stack \(most recent call first\):
+ CMP0147-common\.cmake:[0-9]+ \(find_package\)
+ CMP0147-NEW\.cmake:[0-9]+ \(include\)
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/find_package/CMP0147-NEW.cmake b/Tests/RunCMake/find_package/CMP0147-NEW.cmake
new file mode 100644
index 0000000000..0ca5b6c999
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0147 NEW)
+include(CMP0147-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0147-OLD.cmake b/Tests/RunCMake/find_package/CMP0147-OLD.cmake
new file mode 100644
index 0000000000..61ecee5106
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0147 OLD)
+include(CMP0147-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0147-WARN.cmake b/Tests/RunCMake/find_package/CMP0147-WARN.cmake
new file mode 100644
index 0000000000..d5f3e91db1
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-WARN.cmake
@@ -0,0 +1,2 @@
+# leave CMP0147 unset
+include(CMP0147-common.cmake)
diff --git a/Tests/RunCMake/find_package/CMP0147-common.cmake b/Tests/RunCMake/find_package/CMP0147-common.cmake
new file mode 100644
index 0000000000..68a86eedab
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0147-common.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0146 OLD)
+set(_FindCUDA_testing TRUE)
+find_package(CUDA MODULE)
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Interp-NEW-stderr.txt
new file mode 100644
index 0000000000..68b40f7f54
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Interp-NEW-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning at CMP0148-Interp-NEW\.cmake:[0-9]+ \(find_package\):
+ No "FindPythonInterp\.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-NEW.cmake b/Tests/RunCMake/find_package/CMP0148-Interp-NEW.cmake
new file mode 100644
index 0000000000..ccd04f55f2
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Interp-NEW.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 NEW)
+set(_FindPythonInterp_testing TRUE)
+find_package(PythonInterp MODULE)
+
+if(_FindPythonInterp_included)
+ message(FATAL_ERROR "FindPythonInterp.cmake erroneously included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-OLD.cmake b/Tests/RunCMake/find_package/CMP0148-Interp-OLD.cmake
new file mode 100644
index 0000000000..1879d19053
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Interp-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 OLD)
+set(_FindPythonInterp_testing TRUE)
+find_package(PythonInterp MODULE)
+
+if(NOT _FindPythonInterp_included)
+ message(FATAL_ERROR "FindPythonInterp.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt
new file mode 100644
index 0000000000..2666c22cd1
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Interp-WARN-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(find_package\):
+ Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
+ are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use
+ the cmake_policy command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Interp-WARN.cmake b/Tests/RunCMake/find_package/CMP0148-Interp-WARN.cmake
new file mode 100644
index 0000000000..53b60af26e
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Interp-WARN.cmake
@@ -0,0 +1,6 @@
+set(_FindPythonInterp_testing TRUE)
+find_package(PythonInterp MODULE)
+
+if(NOT _FindPythonInterp_included)
+ message(FATAL_ERROR "FindPythonInterp.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Libs-NEW-stderr.txt
new file mode 100644
index 0000000000..d92b434247
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Libs-NEW-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning at CMP0148-Libs-NEW\.cmake:[0-9]+ \(find_package\):
+ No "FindPythonLibs\.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-NEW.cmake b/Tests/RunCMake/find_package/CMP0148-Libs-NEW.cmake
new file mode 100644
index 0000000000..2ef8c467c7
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Libs-NEW.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 NEW)
+set(_FindPythonLibs_testing TRUE)
+find_package(PythonLibs MODULE)
+
+if(_FindPythonLibs_included)
+ message(FATAL_ERROR "FindPythonLibs.cmake erroneously included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-OLD.cmake b/Tests/RunCMake/find_package/CMP0148-Libs-OLD.cmake
new file mode 100644
index 0000000000..06fd6a6d6b
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Libs-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 OLD)
+set(_FindPythonLibs_testing TRUE)
+find_package(PythonLibs MODULE)
+
+if(NOT _FindPythonLibs_included)
+ message(FATAL_ERROR "FindPythonLibs.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt
new file mode 100644
index 0000000000..5210e514ad
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Libs-WARN-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(find_package\):
+ Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
+ are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use
+ the cmake_policy command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/find_package/CMP0148-Libs-WARN.cmake b/Tests/RunCMake/find_package/CMP0148-Libs-WARN.cmake
new file mode 100644
index 0000000000..9bbe066229
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0148-Libs-WARN.cmake
@@ -0,0 +1,6 @@
+set(_FindPythonLibs_testing TRUE)
+find_package(PythonLibs MODULE)
+
+if(NOT _FindPythonLibs_included)
+ message(FATAL_ERROR "FindPythonLibs.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMakeLists.txt b/Tests/RunCMake/find_package/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_package/CMakeLists.txt
+++ b/Tests/RunCMake/find_package/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index fa41fc1807..006757af12 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -36,6 +36,23 @@ run_cmake(WrongVersionConfig)
run_cmake(CMP0084-OLD)
run_cmake(CMP0084-WARN)
run_cmake(CMP0084-NEW)
+run_cmake(CMP0145-OLD)
+run_cmake(CMP0145-WARN)
+run_cmake(CMP0145-NEW)
+run_cmake(CMP0146-OLD)
+run_cmake(CMP0146-WARN)
+run_cmake(CMP0146-NEW)
+if(RunCMake_GENERATOR MATCHES "Visual Studio")
+ run_cmake(CMP0147-OLD)
+ run_cmake(CMP0147-WARN)
+ run_cmake(CMP0147-NEW)
+endif()
+run_cmake(CMP0148-Interp-OLD)
+run_cmake(CMP0148-Interp-WARN)
+run_cmake(CMP0148-Interp-NEW)
+run_cmake(CMP0148-Libs-OLD)
+run_cmake(CMP0148-Libs-WARN)
+run_cmake(CMP0148-Libs-NEW)
run_cmake(WrongVersionRange)
run_cmake(EmptyVersionRange)
run_cmake(VersionRangeWithEXACT)
@@ -55,6 +72,17 @@ run_cmake(REGISTRY_VIEW-no-view)
run_cmake(REGISTRY_VIEW-wrong-view)
run_cmake(REGISTRY_VIEW-propagated)
+if(CMAKE_HOST_WIN32)
+ run_cmake(CMP0144-WARN-CaseInsensitive)
+ run_cmake(CMP0144-OLD-CaseInsensitive)
+ run_cmake(CMP0144-NEW-CaseInsensitive)
+else()
+ run_cmake(CMP0144-WARN-CaseSensitive)
+ run_cmake(CMP0144-WARN-CaseSensitive-Mixed)
+ run_cmake(CMP0144-OLD-CaseSensitive)
+ run_cmake(CMP0144-NEW-CaseSensitive)
+endif()
+
file(
GLOB SearchPaths_TEST_CASE_LIST
LIST_DIRECTORIES TRUE
diff --git a/Tests/RunCMake/find_path/CMakeLists.txt b/Tests/RunCMake/find_path/CMakeLists.txt
index ef2163c298..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_path/CMakeLists.txt
+++ b/Tests/RunCMake/find_path/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt
new file mode 100644
index 0000000000..fa767b925f
--- /dev/null
+++ b/Tests/RunCMake/find_program/CMP0109-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0109-OLD.cmake:[0-9]+ \(cmake_policy\):
+ The OLD behavior for policy CMP0109 will be removed from a future version
+ of CMake.
+
+ The cmake-policies\(7\) manual explains that the OLD behaviors of all
+ policies are deprecated and that a policy should be set to OLD only under
+ specific short-term circumstances. Projects should be ported to the NEW
+ behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/find_program/CMakeLists.txt b/Tests/RunCMake/find_program/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/find_program/CMakeLists.txt
+++ b/Tests/RunCMake/find_program/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/get_filename_component/CMakeLists.txt b/Tests/RunCMake/get_filename_component/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/get_filename_component/CMakeLists.txt
+++ b/Tests/RunCMake/get_filename_component/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/get_property/CMakeLists.txt b/Tests/RunCMake/get_property/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/get_property/CMakeLists.txt
+++ b/Tests/RunCMake/get_property/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/if/CMakeLists.txt b/Tests/RunCMake/if/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/if/CMakeLists.txt
+++ b/Tests/RunCMake/if/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/include/CMP0024-NEW-stderr.txt b/Tests/RunCMake/include/CMP0024-NEW-stderr.txt
index 0fdb3ca7f1..26ced801b1 100644
--- a/Tests/RunCMake/include/CMP0024-NEW-stderr.txt
+++ b/Tests/RunCMake/include/CMP0024-NEW-stderr.txt
@@ -1,8 +1,9 @@
-CMake Error at subdir2/CMakeLists.txt:2 \(include\):
+^CMake Error at subdir2/CMakeLists\.txt:2 \(include\):
The file
- .*/Tests/RunCMake/include/CMP0024-NEW-build/subdir1/theTargets.cmake
+ [^
+]*/Tests/RunCMake/include/CMP0024-NEW-build/subdir1/theTargets\.cmake
- was generated by the export\(\) command. It may not be used as the argument
- to the include\(\) command. Use ALIAS targets instead to refer to targets by
- alternative names.
+ was generated by the export\(\) command\. It may not be used as the argument
+ to the include\(\) command\. Use ALIAS targets instead to refer to targets by
+ alternative names\.
diff --git a/Tests/RunCMake/include/CMP0024-NEW.cmake b/Tests/RunCMake/include/CMP0024-NEW.cmake
index 0e03d2af85..783cf78e5d 100644
--- a/Tests/RunCMake/include/CMP0024-NEW.cmake
+++ b/Tests/RunCMake/include/CMP0024-NEW.cmake
@@ -1,8 +1,6 @@
enable_language(CXX)
-cmake_policy(SET CMP0024 NEW)
-
add_library(foo SHARED empty.cpp)
add_subdirectory(subdir1)
diff --git a/Tests/RunCMake/include/CMP0024-WARN-stderr.txt b/Tests/RunCMake/include/CMP0024-WARN-stderr.txt
index 9c79007058..8472f41e20 100644
--- a/Tests/RunCMake/include/CMP0024-WARN-stderr.txt
+++ b/Tests/RunCMake/include/CMP0024-WARN-stderr.txt
@@ -1,14 +1,24 @@
-CMake Warning \(dev\) at subdir2/CMakeLists.txt:2 \(include\):
+^CMake Deprecation Warning at CMP0024-WARN\.cmake:[0-9]+ \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Warning \(dev\) at subdir2/CMakeLists\.txt:2 \(include\):
Policy CMP0024 is not set: Disallow include export result. Run "cmake
- --help-policy CMP0024" for policy details. Use the cmake_policy command to
- set the policy and suppress this warning.
+ --help-policy CMP0024" for policy details\. Use the cmake_policy command to
+ set the policy and suppress this warning\.
The file
- .*/Tests/RunCMake/include/CMP0024-WARN-build/subdir1/theTargets.cmake
+ [^
+]*/Tests/RunCMake/include/CMP0024-WARN-build/subdir1/theTargets\.cmake
- was generated by the export\(\) command. It should not be used as the
- argument to the include\(\) command. Use ALIAS targets instead to refer to
- targets by alternative names.
+ was generated by the export\(\) command\. It should not be used as the
+ argument to the include\(\) command\. Use ALIAS targets instead to refer to
+ targets by alternative names\.
-This warning is for project developers. Use -Wno-dev to suppress it.
+This warning is for project developers\. Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/include/CMP0024-WARN.cmake b/Tests/RunCMake/include/CMP0024-WARN.cmake
index 783cf78e5d..f267a5ab21 100644
--- a/Tests/RunCMake/include/CMP0024-WARN.cmake
+++ b/Tests/RunCMake/include/CMP0024-WARN.cmake
@@ -1,3 +1,4 @@
+cmake_policy(VERSION 2.8.12) # Leave CMP0024 unset.
enable_language(CXX)
diff --git a/Tests/RunCMake/include/CMP0146-NEW-name-result.txt b/Tests/RunCMake/include/CMP0146-NEW-name-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-name-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0146-NEW-name-stderr.txt b/Tests/RunCMake/include/CMP0146-NEW-name-stderr.txt
new file mode 100644
index 0000000000..7d9e7d8450
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-name-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0146-NEW-name\.cmake:[0-9]+ \(include\):
+ include could not find requested file:
+
+ FindCUDA
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0146-NEW-name.cmake b/Tests/RunCMake/include/CMP0146-NEW-name.cmake
new file mode 100644
index 0000000000..feedc6fa99
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-name.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0146 NEW)
+include(FindCUDA)
diff --git a/Tests/RunCMake/include/CMP0146-NEW-path-result.txt b/Tests/RunCMake/include/CMP0146-NEW-path-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-path-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0146-NEW-path-stderr.txt b/Tests/RunCMake/include/CMP0146-NEW-path-stderr.txt
new file mode 100644
index 0000000000..916672b4c7
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-path-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/Modules/FindCUDA.cmake:[0-9]+ \(message\):
+ The FindCUDA module has been removed by policy CMP0146\.
+Call Stack \(most recent call first\):
+ CMP0146-NEW-path\.cmake:[0-9]+ \(include\)
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0146-NEW-path.cmake b/Tests/RunCMake/include/CMP0146-NEW-path.cmake
new file mode 100644
index 0000000000..6768d4dd8e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-NEW-path.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0146 NEW)
+include(${CMAKE_ROOT}/Modules/FindCUDA.cmake)
diff --git a/Tests/RunCMake/include/CMP0146-OLD.cmake b/Tests/RunCMake/include/CMP0146-OLD.cmake
new file mode 100644
index 0000000000..654cdf7b67
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0146 OLD)
+set(_FindCUDA_testing 1)
+include(FindCUDA)
+
+if(NOT _FindCUDA_included)
+ message(FATAL_ERROR "FindCUDA.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0146-WARN-stderr.txt b/Tests/RunCMake/include/CMP0146-WARN-stderr.txt
new file mode 100644
index 0000000000..aaaf1dcf0b
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at CMP0146-WARN\.cmake:[0-9]+ \(include\):
+ Policy CMP0146 is not set: The FindCUDA module is removed\. Run "cmake
+ --help-policy CMP0146" for policy details\. Use the cmake_policy command to
+ set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0146-WARN.cmake b/Tests/RunCMake/include/CMP0146-WARN.cmake
new file mode 100644
index 0000000000..bce1ae8514
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0146-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0146.
+set(_FindCUDA_testing 1)
+include(FindCUDA)
+
+if(NOT _FindCUDA_included)
+ message(FATAL_ERROR "FindCUDA.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-name-result.txt b/Tests/RunCMake/include/CMP0148-Interp-NEW-name-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-name-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-name-stderr.txt b/Tests/RunCMake/include/CMP0148-Interp-NEW-name-stderr.txt
new file mode 100644
index 0000000000..8627c2e619
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-name-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0148-Interp-NEW-name\.cmake:[0-9]+ \(include\):
+ include could not find requested file:
+
+ FindPythonInterp
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-name.cmake b/Tests/RunCMake/include/CMP0148-Interp-NEW-name.cmake
new file mode 100644
index 0000000000..7d73dc4e8f
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-name.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0148 NEW)
+include(FindPythonInterp)
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-path-result.txt b/Tests/RunCMake/include/CMP0148-Interp-NEW-path-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-path-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-path-stderr.txt b/Tests/RunCMake/include/CMP0148-Interp-NEW-path-stderr.txt
new file mode 100644
index 0000000000..0b0f29d5b9
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-path-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/Modules/FindPythonInterp.cmake:[0-9]+ \(message\):
+ The FindPythonInterp module has been removed by policy CMP0148\.
+Call Stack \(most recent call first\):
+ CMP0148-Interp-NEW-path\.cmake:[0-9]+ \(include\)
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0148-Interp-NEW-path.cmake b/Tests/RunCMake/include/CMP0148-Interp-NEW-path.cmake
new file mode 100644
index 0000000000..1f8630e6ca
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-NEW-path.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0148 NEW)
+include(${CMAKE_ROOT}/Modules/FindPythonInterp.cmake)
diff --git a/Tests/RunCMake/include/CMP0148-Interp-OLD.cmake b/Tests/RunCMake/include/CMP0148-Interp-OLD.cmake
new file mode 100644
index 0000000000..8905221093
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 OLD)
+set(_FindPythonInterp_testing 1)
+include(FindPythonInterp)
+
+if(NOT _FindPythonInterp_included)
+ message(FATAL_ERROR "FindPythonInterp.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt b/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt
new file mode 100644
index 0000000000..5bd6935f6b
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at CMP0148-Interp-WARN\.cmake:[0-9]+ \(include\):
+ Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
+ are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use
+ the cmake_policy command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Interp-WARN.cmake b/Tests/RunCMake/include/CMP0148-Interp-WARN.cmake
new file mode 100644
index 0000000000..18f302ddbf
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Interp-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0148.
+set(_FindPythonInterp_testing 1)
+include(FindPythonInterp)
+
+if(NOT _FindPythonInterp_included)
+ message(FATAL_ERROR "FindPythonInterp.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-name-result.txt b/Tests/RunCMake/include/CMP0148-Libs-NEW-name-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-name-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-name-stderr.txt b/Tests/RunCMake/include/CMP0148-Libs-NEW-name-stderr.txt
new file mode 100644
index 0000000000..4e1f97c2cf
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-name-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0148-Libs-NEW-name\.cmake:[0-9]+ \(include\):
+ include could not find requested file:
+
+ FindPythonLibs
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-name.cmake b/Tests/RunCMake/include/CMP0148-Libs-NEW-name.cmake
new file mode 100644
index 0000000000..c9877bc087
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-name.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0148 NEW)
+include(FindPythonLibs)
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-path-result.txt b/Tests/RunCMake/include/CMP0148-Libs-NEW-path-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-path-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-path-stderr.txt b/Tests/RunCMake/include/CMP0148-Libs-NEW-path-stderr.txt
new file mode 100644
index 0000000000..5f1037d4dd
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-path-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/Modules/FindPythonLibs.cmake:[0-9]+ \(message\):
+ The FindPythonLibs module has been removed by policy CMP0148\.
+Call Stack \(most recent call first\):
+ CMP0148-Libs-NEW-path\.cmake:[0-9]+ \(include\)
+ CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0148-Libs-NEW-path.cmake b/Tests/RunCMake/include/CMP0148-Libs-NEW-path.cmake
new file mode 100644
index 0000000000..29b315f53d
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-NEW-path.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0148 NEW)
+include(${CMAKE_ROOT}/Modules/FindPythonLibs.cmake)
diff --git a/Tests/RunCMake/include/CMP0148-Libs-OLD.cmake b/Tests/RunCMake/include/CMP0148-Libs-OLD.cmake
new file mode 100644
index 0000000000..b69e160ddc
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0148 OLD)
+set(_FindPythonLibs_testing 1)
+include(FindPythonLibs)
+
+if(NOT _FindPythonLibs_included)
+ message(FATAL_ERROR "FindPythonLibs.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt b/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt
new file mode 100644
index 0000000000..c24abd495b
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at CMP0148-Libs-WARN\.cmake:[0-9]+ \(include\):
+ Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
+ are removed\. Run "cmake --help-policy CMP0148" for policy details\. Use
+ the cmake_policy command to set the policy and suppress this warning\.
+
+Call Stack \(most recent call first\):
+ CMakeLists\.txt:[0-9]+ \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/include/CMP0148-Libs-WARN.cmake b/Tests/RunCMake/include/CMP0148-Libs-WARN.cmake
new file mode 100644
index 0000000000..d8dc00c529
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0148-Libs-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0148.
+set(_FindPythonLibs_testing 1)
+include(FindPythonLibs)
+
+if(NOT _FindPythonLibs_included)
+ message(FATAL_ERROR "FindPythonLibs.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMakeLists.txt b/Tests/RunCMake/include/CMakeLists.txt
index 4b3de84d94..93ee9dfd5f 100644
--- a/Tests/RunCMake/include/CMakeLists.txt
+++ b/Tests/RunCMake/include/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/include/EmptyString-stderr.txt b/Tests/RunCMake/include/EmptyString-stderr.txt
index 006c647c66..dab16ea4df 100644
--- a/Tests/RunCMake/include/EmptyString-stderr.txt
+++ b/Tests/RunCMake/include/EmptyString-stderr.txt
@@ -1,5 +1,5 @@
-CMake Warning \(dev\) at EmptyString.cmake:1 \(include\):
+^CMake Warning \(dev\) at EmptyString\.cmake:1 \(include\):
include\(\) given empty file name \(ignored\).
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
-This warning is for project developers. Use -Wno-dev to suppress it.
+ CMakeLists\.txt:3 \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/include/EmptyStringOptional-stderr.txt b/Tests/RunCMake/include/EmptyStringOptional-stderr.txt
index b61c679128..1b763f2c01 100644
--- a/Tests/RunCMake/include/EmptyStringOptional-stderr.txt
+++ b/Tests/RunCMake/include/EmptyStringOptional-stderr.txt
@@ -1,5 +1,5 @@
-CMake Warning \(dev\) at EmptyStringOptional.cmake:1 \(include\):
+^CMake Warning \(dev\) at EmptyStringOptional\.cmake:1 \(include\):
include\(\) given empty file name \(ignored\).
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
-This warning is for project developers. Use -Wno-dev to suppress it.
+ CMakeLists\.txt:3 \(include\)
+This warning is for project developers\. Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/include/ExportExportInclude-stderr.txt b/Tests/RunCMake/include/ExportExportInclude-stderr.txt
index 6d5c02f196..22bf7a1228 100644
--- a/Tests/RunCMake/include/ExportExportInclude-stderr.txt
+++ b/Tests/RunCMake/include/ExportExportInclude-stderr.txt
@@ -1,6 +1,7 @@
-CMake Error at ExportExportInclude.cmake:6 \(include\):
+^CMake Error at ExportExportInclude.cmake:6 \(include\):
include could not find requested file:
- .*/Tests/RunCMake/include/ExportExportInclude-build/theTargets.cmake
+ [^
+]*/Tests/RunCMake/include/ExportExportInclude-build/theTargets\.cmake
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/include/IncludeIsDirectory-stderr.txt b/Tests/RunCMake/include/IncludeIsDirectory-stderr.txt
index 5735c29136..6ce934d679 100644
--- a/Tests/RunCMake/include/IncludeIsDirectory-stderr.txt
+++ b/Tests/RunCMake/include/IncludeIsDirectory-stderr.txt
@@ -1,6 +1,7 @@
-CMake Error at IncludeIsDirectory.cmake:1 \(include\):
+^CMake Error at IncludeIsDirectory.cmake:1 \(include\):
include requested file is a directory:
- .*/Tests/RunCMake/include/IncludeIsDirectory-build
+ [^
+]*/Tests/RunCMake/include/IncludeIsDirectory-build
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/include/IncludeMalformed-stderr.txt b/Tests/RunCMake/include/IncludeMalformed-stderr.txt
index fc755495cf..e34e9bbcc3 100644
--- a/Tests/RunCMake/include/IncludeMalformed-stderr.txt
+++ b/Tests/RunCMake/include/IncludeMalformed-stderr.txt
@@ -1,13 +1,12 @@
-CMake Error at malformedInclude.cmake:1:
- Parse error. Function missing ending "\)". End of file reached.
+^CMake Error at malformedInclude\.cmake:1:
+ Parse error\. Function missing ending "\)"\. End of file reached\.
Call Stack \(most recent call first\):
- IncludeMalformed.cmake:1 \(include\)
- CMakeLists.txt:3 \(include\)
-
-
-CMake Error at IncludeMalformed.cmake:1 \(include\):
+ IncludeMalformed\.cmake:1 \(include\)
+ CMakeLists\.txt:3 \(include\)
++
+CMake Error at IncludeMalformed\.cmake:1 \(include\):
include could not load requested file:
- malformedInclude.cmake
+ malformedInclude\.cmake
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/include/RunCMakeTest.cmake b/Tests/RunCMake/include/RunCMakeTest.cmake
index 8fb7201384..685173ecdb 100644
--- a/Tests/RunCMake/include/RunCMakeTest.cmake
+++ b/Tests/RunCMake/include/RunCMakeTest.cmake
@@ -7,3 +7,17 @@ run_cmake(CMP0024-NEW)
run_cmake(ExportExportInclude)
run_cmake(IncludeIsDirectory)
run_cmake(IncludeMalformed)
+
+run_cmake(CMP0146-OLD)
+run_cmake(CMP0146-WARN)
+run_cmake(CMP0146-NEW-name)
+run_cmake(CMP0146-NEW-path)
+
+run_cmake(CMP0148-Interp-OLD)
+run_cmake(CMP0148-Interp-WARN)
+run_cmake(CMP0148-Interp-NEW-name)
+run_cmake(CMP0148-Interp-NEW-path)
+run_cmake(CMP0148-Libs-OLD)
+run_cmake(CMP0148-Libs-WARN)
+run_cmake(CMP0148-Libs-NEW-name)
+run_cmake(CMP0148-Libs-NEW-path)
diff --git a/Tests/RunCMake/include_directories/CMakeLists.txt b/Tests/RunCMake/include_directories/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/include_directories/CMakeLists.txt
+++ b/Tests/RunCMake/include_directories/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/include_external_msproject/CMakeLists.txt b/Tests/RunCMake/include_external_msproject/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/include_external_msproject/CMakeLists.txt
+++ b/Tests/RunCMake/include_external_msproject/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt
index de0b70ffc3..6b4c4b0eb8 100644
--- a/Tests/RunCMake/install/CMP0062-OLD-stderr.txt
+++ b/Tests/RunCMake/install/CMP0062-OLD-stderr.txt
@@ -1,10 +1,19 @@
-^CMake Deprecation Warning at CMP0062-OLD.cmake:[0-9]+ \(cmake_policy\):
+^CMake Deprecation Warning at CMP0062-OLD\.cmake:[0-9]+ \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Deprecation Warning at CMP0062-OLD\.cmake:[0-9]+ \(cmake_policy\):
The OLD behavior for policy CMP0062 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
specific short-term circumstances. Projects should be ported to the NEW
behavior and not rely on setting a policy to OLD.
Call Stack \(most recent call first\):
- CMakeLists.txt:[0-9]+ \(include\)$
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/install/CMakeLists.txt b/Tests/RunCMake/install/CMakeLists.txt
index 6dd8cdf551..93ee9dfd5f 100644
--- a/Tests/RunCMake/install/CMakeLists.txt
+++ b/Tests/RunCMake/install/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index 477ffe06ca..efafdd1383 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.4)
include(RunCMake)
# Function to build and install a project. The latter step *-check.cmake
@@ -122,6 +121,10 @@ run_install_test(FILES-OPTIONAL)
run_install_test(DIRECTORY-OPTIONAL)
run_install_test(TARGETS-Defaults)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ run_install_test(TARGETS-NAMELINK-No-Tweak)
+endif()
+
set(RunCMake_TEST_OPTIONS
"-DCMAKE_INSTALL_BINDIR:PATH=mybin"
"-DCMAKE_INSTALL_LIBDIR:PATH=mylib"
@@ -167,7 +170,6 @@ unset(RunCMake_TEST_OPTIONS)
run_install_test(Deprecated)
run_install_test(PRE_POST_INSTALL_SCRIPT)
-run_install_test(SCRIPT)
run_install_test(TARGETS-CONFIGURATIONS)
run_install_test(DIRECTORY-PATTERN)
run_install_test(TARGETS-Parts)
@@ -175,6 +177,10 @@ run_install_test(FILES-PERMISSIONS)
run_install_test(TARGETS-RPATH)
run_install_test(InstallRequiredSystemLibraries)
+set(RunCMake_TEST_OPTIONS "-DCMAKE_POLICY_DEFAULT_CMP0087:STRING=NEW")
+run_install_test(SCRIPT)
+unset(RunCMake_TEST_OPTIONS)
+
if(UNIX)
run_install_test(DIRECTORY-symlink-clobber)
endif()
diff --git a/Tests/RunCMake/install/SCRIPT-all-check.cmake b/Tests/RunCMake/install/SCRIPT-all-check.cmake
index 48d8e1aec6..b9c38e3d42 100644
--- a/Tests/RunCMake/install/SCRIPT-all-check.cmake
+++ b/Tests/RunCMake/install/SCRIPT-all-check.cmake
@@ -1 +1 @@
-check_installed([[^empty1.txt;empty2.txt$]])
+check_installed([[^empty1.txt;empty2.txt;empty3.cmake;empty3.txt;empty4.cmake;empty4.txt$]])
diff --git a/Tests/RunCMake/install/SCRIPT.cmake b/Tests/RunCMake/install/SCRIPT.cmake
index f857b54371..ee0c80ec96 100644
--- a/Tests/RunCMake/install/SCRIPT.cmake
+++ b/Tests/RunCMake/install/SCRIPT.cmake
@@ -1,4 +1,10 @@
install(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/empty3.cmake ${CMAKE_CURRENT_SOURCE_DIR}/empty4.cmake
+ DESTINATION .
+ )
+install(
SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/install_script.cmake"
CODE "write_empty_file(empty2.txt)"
+ SCRIPT "$<INSTALL_PREFIX>/empty3.cmake"
+ CODE [[include($<INSTALL_PREFIX>/empty4.cmake)]]
)
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt
index 138a69d09f..7d1477f923 100644
--- a/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt
+++ b/Tests/RunCMake/install/TARGETS-Defaults-Cache-stderr.txt
@@ -1,11 +1,11 @@
^CMake Warning \(dev\) at TARGETS-Defaults-Cache.cmake:[0-9]+ \(install\):
Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION.
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
CMake Warning \(dev\) at TARGETS-Defaults-Cache.cmake:[0-9]+ \(install\):
Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION.
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt
index 5f5698676c..5600801713 100644
--- a/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt
+++ b/Tests/RunCMake/install/TARGETS-Defaults-stderr.txt
@@ -1,11 +1,11 @@
^CMake Warning \(dev\) at TARGETS-Defaults.cmake:[0-9]+ \(install\):
Target lib3 has PRIVATE_HEADER files but no PRIVATE_HEADER DESTINATION.
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.
+
CMake Warning \(dev\) at TARGETS-Defaults.cmake:[0-9]+ \(install\):
Target lib4 has PUBLIC_HEADER files but no PUBLIC_HEADER DESTINATION.
Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
+ CMakeLists.txt:[0-9]+ \(include\)
This warning is for project developers. Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
index cba04b2664..c72e405a0a 100644
--- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-new_rpath.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.14)
enable_language(C)
# test matrix
diff --git a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
index 43ae787082..891b1ac974 100644
--- a/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
+++ b/Tests/RunCMake/install/TARGETS-FILE_RPATH_CHANGE-old_rpath.cmake
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.14)
enable_language(C)
add_library(utils SHARED obj1.c)
diff --git a/Tests/RunCMake/install/TARGETS-NAMELINK-No-Tweak.cmake b/Tests/RunCMake/install/TARGETS-NAMELINK-No-Tweak.cmake
new file mode 100644
index 0000000000..879f4b8455
--- /dev/null
+++ b/Tests/RunCMake/install/TARGETS-NAMELINK-No-Tweak.cmake
@@ -0,0 +1,20 @@
+enable_language(C)
+
+add_library(foo SHARED obj1.c)
+set_target_properties(foo PROPERTIES
+ VERSION 1.0
+ SOVERSION 1
+ INSTALL_RPATH "$ORIGIN"
+ )
+install(TARGETS foo DESTINATION lib)
+
+# Replace the .so "namelink" symlink with a linker script.
+# It is no longer a symlink, so any install tweaks would break.
+# This verifies that no install tweaks are added for the namelink.
+set(linker_script "INPUT($<TARGET_SONAME_FILE_NAME:foo>)")
+add_custom_command(TARGET foo POST_BUILD
+ COMMAND "${CMAKE_COMMAND}" -E remove "$<TARGET_LINKER_FILE:foo>"
+ COMMAND "${CMAKE_COMMAND}" -E echo "${linker_script}" > "$<TARGET_LINKER_FILE:foo>"
+ COMMENT "Generating linker script: '${linker_script}' as file $<TARGET_LINKER_FILE:foo>"
+ VERBATIM
+ )
diff --git a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt
index fe65fd3006..26dcd42bb2 100644
--- a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt
+++ b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-all-stderr.txt
@@ -1,5 +1,6 @@
^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-all\.cmake:5 \(install\):
- install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\. The
- NAMELINK_COMPONENT option may be specified only following LIBRARY\.
+ install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE
+ group\. The NAMELINK_COMPONENT option may be specified only following
+ LIBRARY or ARCHIVE\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt
index 60f52c4889..8aed62b6b9 100644
--- a/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt
+++ b/Tests/RunCMake/install/TARGETS-NAMELINK_COMPONENT-bad-exc-stderr.txt
@@ -1,5 +1,6 @@
^CMake Error at TARGETS-NAMELINK_COMPONENT-bad-exc\.cmake:5 \(install\):
- install TARGETS given NAMELINK_COMPONENT option not in LIBRARY group\. The
- NAMELINK_COMPONENT option may be specified only following LIBRARY\.
+ install TARGETS given NAMELINK_COMPONENT option not in LIBRARY or ARCHIVE
+ group\. The NAMELINK_COMPONENT option may be specified only following
+ LIBRARY or ARCHIVE\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/install/TARGETS-RPATH.cmake b/Tests/RunCMake/install/TARGETS-RPATH.cmake
index b75deffff5..3e182f8cbf 100644
--- a/Tests/RunCMake/install/TARGETS-RPATH.cmake
+++ b/Tests/RunCMake/install/TARGETS-RPATH.cmake
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.9)
-
+cmake_policy(SET CMP0068 NEW)
enable_language(C)
set(CMAKE_BUILD_WITH_INSTALL_RPATH 1)
diff --git a/Tests/RunCMake/install/empty3.cmake b/Tests/RunCMake/install/empty3.cmake
new file mode 100644
index 0000000000..18c2ac5f9e
--- /dev/null
+++ b/Tests/RunCMake/install/empty3.cmake
@@ -0,0 +1 @@
+write_empty_file(empty3.txt)
diff --git a/Tests/RunCMake/install/empty4.cmake b/Tests/RunCMake/install/empty4.cmake
new file mode 100644
index 0000000000..026a4d220e
--- /dev/null
+++ b/Tests/RunCMake/install/empty4.cmake
@@ -0,0 +1 @@
+write_empty_file(empty4.txt)
diff --git a/Tests/RunCMake/list/CMakeLists.txt b/Tests/RunCMake/list/CMakeLists.txt
index 4b3de84d94..93ee9dfd5f 100644
--- a/Tests/RunCMake/list/CMakeLists.txt
+++ b/Tests/RunCMake/list/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt
index 9103bd2d41..5dd76f778a 100644
--- a/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt
+++ b/Tests/RunCMake/list/GET-CMP0007-WARN-stderr.txt
@@ -1,13 +1,13 @@
-^CMake Deprecation Warning at GET-CMP0007-WARN.cmake:1 \(cmake_policy\):
- Compatibility with CMake < 2.8.12 will be removed from a future version of
- CMake.
+^CMake Deprecation Warning at GET-CMP0007-WARN\.cmake:1 \(cmake_policy\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
- Update the VERSION argument <min> value or use a ...<max> suffix to tell
- CMake that the project does not need compatibility with older versions.
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)
+
-CMake Warning \(dev\) at GET-CMP0007-WARN.cmake:4 \(list\):
+CMake Warning \(dev\) at GET-CMP0007-WARN\.cmake:4 \(list\):
Policy CMP0007 is not set: list command no longer ignores empty elements.
Run "cmake --help-policy CMP0007" for policy details. Use the cmake_policy
command to set the policy and suppress this warning. List has value =
diff --git a/Tests/RunCMake/math/CMakeLists.txt b/Tests/RunCMake/math/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/math/CMakeLists.txt
+++ b/Tests/RunCMake/math/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/message/CMakeLists.txt b/Tests/RunCMake/message/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/message/CMakeLists.txt
+++ b/Tests/RunCMake/message/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/message/warnmessage-rootdir.cmake b/Tests/RunCMake/message/warnmessage-rootdir.cmake
index f82efb909b..f400079777 100644
--- a/Tests/RunCMake/message/warnmessage-rootdir.cmake
+++ b/Tests/RunCMake/message/warnmessage-rootdir.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.15)
-
# Generating the backtrace for this warning message used to trigger a
# spurious assertion when the current directory is the root directory
message(WARNING "We expect to see this warning message")
diff --git a/Tests/RunCMake/no_install_prefix/CMakeLists.txt b/Tests/RunCMake/no_install_prefix/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/no_install_prefix/CMakeLists.txt
+++ b/Tests/RunCMake/no_install_prefix/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/project/CMP0048-NEW-stderr.txt b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt
new file mode 100644
index 0000000000..ad75f4386c
--- /dev/null
+++ b/Tests/RunCMake/project/CMP0048-NEW-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.$
diff --git a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt
index 695fb70e9b..376249b3fe 100644
--- a/Tests/RunCMake/project/CMP0048-OLD-stderr.txt
+++ b/Tests/RunCMake/project/CMP0048-OLD-stderr.txt
@@ -1,6 +1,13 @@
-^CMake Deprecation Warning at CMP0048-OLD.cmake:1 \(cmake_policy\):
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.
++
+CMake Deprecation Warning at CMP0048-OLD\.cmake:1 \(cmake_policy\):
The OLD behavior for policy CMP0048 will be removed from a future version
- of CMake.
+ of CMake\.
The cmake-policies\(7\) manual explains that the OLD behaviors of all
policies are deprecated and that a policy should be set to OLD only under
diff --git a/Tests/RunCMake/project/CMakeLists.txt b/Tests/RunCMake/project/CMakeLists.txt
index fdcaee9c86..a0ee6d7a0d 100644
--- a/Tests/RunCMake/project/CMakeLists.txt
+++ b/Tests/RunCMake/project/CMakeLists.txt
@@ -1,5 +1,9 @@
-if(NOT "x${RunCMake_TEST}" STREQUAL "xNoMinimumRequired")
+if("x${RunCMake_TEST}" STREQUAL "xNoMinimumRequired")
+ # No cmake_minimum_required(VERSION)
+elseif(RunCMake_TEST MATCHES "^CMP0048")
cmake_minimum_required(VERSION 2.8.12)
+else()
+ cmake_minimum_required(VERSION 3.5)
endif()
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt
index fc1a02b439..c72534fe1a 100644
--- a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt
+++ b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Warning \(dev\) in CMakeLists.txt:
+^CMake Warning \(dev\) in CMakeLists\.txt:
No project\(\) command is present. The top-level CMakeLists.txt file must
contain a literal, direct call to the project\(\) command. Add a line of
code such as
@@ -8,5 +8,12 @@
near the top of the file, but after cmake_minimum_required\(\).
CMake is pretending there is a "project\(Project\)" command on the first
- line.
-This warning is for project developers. Use -Wno-dev to suppress it.$
+ line\.
+This warning is for project developers. Use -Wno-dev to suppress it\.
++
+CMake Deprecation Warning at CMakeLists\.txt:1 \(cmake_minimum_required\):
+ Compatibility with CMake < 3\.5 will be removed from a future version of
+ CMake\.
+
+ Update the VERSION argument <min> value or use a \.\.\.<max> suffix to tell
+ CMake that the project does not need compatibility with older versions\.$
diff --git a/Tests/RunCMake/separate_arguments/CMakeLists.txt b/Tests/RunCMake/separate_arguments/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/separate_arguments/CMakeLists.txt
+++ b/Tests/RunCMake/separate_arguments/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/set/CMakeLists.txt b/Tests/RunCMake/set/CMakeLists.txt
index 4b3de84d94..93ee9dfd5f 100644
--- a/Tests/RunCMake/set/CMakeLists.txt
+++ b/Tests/RunCMake/set/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/set/ParentPulling.cmake b/Tests/RunCMake/set/ParentPulling.cmake
index 2614533f4e..5649cb2fd1 100644
--- a/Tests/RunCMake/set/ParentPulling.cmake
+++ b/Tests/RunCMake/set/ParentPulling.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
-project(Minimal NONE)
-
function(test_set)
set(blah "value2")
message("before PARENT_SCOPE blah=${blah}")
diff --git a/Tests/RunCMake/set/ParentPullingRecursive.cmake b/Tests/RunCMake/set/ParentPullingRecursive.cmake
index a3e29f5b87..4d50561e68 100644
--- a/Tests/RunCMake/set/ParentPullingRecursive.cmake
+++ b/Tests/RunCMake/set/ParentPullingRecursive.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
-project(Minimal NONE)
-
function(report where)
message("----------")
message("variable values at ${where}:")
diff --git a/Tests/RunCMake/set_property/CMakeLists.txt b/Tests/RunCMake/set_property/CMakeLists.txt
index 18dfd2686f..93ee9dfd5f 100644
--- a/Tests/RunCMake/set_property/CMakeLists.txt
+++ b/Tests/RunCMake/set_property/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.2)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/string/CMakeLists.txt b/Tests/RunCMake/string/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/string/CMakeLists.txt
+++ b/Tests/RunCMake/string/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/string/RegexClear.cmake b/Tests/RunCMake/string/RegexClear.cmake
index d5edaacd6e..4abe25ef85 100644
--- a/Tests/RunCMake/string/RegexClear.cmake
+++ b/Tests/RunCMake/string/RegexClear.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required (VERSION 3.0)
-project (RegexClear C)
-
function (output_results msg)
message("results from: ${msg}")
message("CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--")
diff --git a/Tests/RunCMake/string/RegexMultiMatchClear.cmake b/Tests/RunCMake/string/RegexMultiMatchClear.cmake
index 80b6b3c7b4..788ba5edd3 100644
--- a/Tests/RunCMake/string/RegexMultiMatchClear.cmake
+++ b/Tests/RunCMake/string/RegexMultiMatchClear.cmake
@@ -1,6 +1,3 @@
-cmake_minimum_required (VERSION 3.0)
-project (RegexClear NONE)
-
function (output_results msg)
message("results from: ${msg}")
message("CMAKE_MATCH_0: -->${CMAKE_MATCH_0}<--")
diff --git a/Tests/RunCMake/target_compile_features/CMakeLists.txt b/Tests/RunCMake/target_compile_features/CMakeLists.txt
index 2897109554..93ee9dfd5f 100644
--- a/Tests/RunCMake/target_compile_features/CMakeLists.txt
+++ b/Tests/RunCMake/target_compile_features/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
index 316b74b131..4a0f068605 100644
--- a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
+++ b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.16...3.17)
-
enable_language(C)
add_library (func SHARED func.c)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake
index 22d3df7380..6610d401e8 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANGUAGE/genex.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.16...3.17)
-
enable_language(C)
enable_language(CXX)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake
index 9feccd0747..e2bd6691a6 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LANG_AND_ID/genex.cmake
@@ -1,6 +1,3 @@
-
-cmake_minimum_required(VERSION 3.16...3.17)
-
enable_language(C)
enable_language(CXX)
diff --git a/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake b/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake
index 6c72546418..a5bf2fbb55 100644
--- a/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake
+++ b/Tests/RunCMake/target_link_libraries/CMP0023-WARN-2.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.11)
+cmake_policy(VERSION 2.8.11)
project(CMP0022-WARN)
add_library(foo SHARED empty_vs6_1.cpp)
diff --git a/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake b/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake
index dfdf70b274..d6f34a1d8d 100644
--- a/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake
+++ b/Tests/RunCMake/target_link_libraries/CMP0023-WARN.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.11)
+cmake_policy(VERSION 2.8.11)
project(CMP0022-WARN)
add_library(foo SHARED empty_vs6_1.cpp)
diff --git a/Tests/RunCMake/target_link_libraries/CMakeLists.txt b/Tests/RunCMake/target_link_libraries/CMakeLists.txt
index 667561ea79..8eb57486ad 100644
--- a/Tests/RunCMake/target_link_libraries/CMakeLists.txt
+++ b/Tests/RunCMake/target_link_libraries/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
index 189592d764..7c5d77d74f 100644
--- a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
@@ -1,4 +1,5 @@
include(RunCMake)
+set(RunCMake_IGNORE_POLICY_VERSION_DEPRECATION ON)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
diff --git a/Tests/RunCMake/target_sources/CMP0076-WARN.cmake b/Tests/RunCMake/target_sources/CMP0076-WARN.cmake
index 2e07331120..20f1d5e349 100644
--- a/Tests/RunCMake/target_sources/CMP0076-WARN.cmake
+++ b/Tests/RunCMake/target_sources/CMP0076-WARN.cmake
@@ -1,5 +1,3 @@
-cmake_minimum_required(VERSION 3.12)
-
add_library(publiclib)
add_subdirectory(CMP0076-WARN)
diff --git a/Tests/RunCMake/target_sources/CMakeLists.txt b/Tests/RunCMake/target_sources/CMakeLists.txt
index 727f93a925..296fdda5aa 100644
--- a/Tests/RunCMake/target_sources/CMakeLists.txt
+++ b/Tests/RunCMake/target_sources/CMakeLists.txt
@@ -1,3 +1,3 @@
cmake_minimum_required(VERSION 3.11)
project(${RunCMake_TEST} LANGUAGES NONE)
-include(${RunCMake_TEST}.cmake)
+include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)
diff --git a/Tests/RunCMake/target_sources/FileSetDirect-result.txt b/Tests/RunCMake/target_sources/FileSetDirect-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetDirect-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt b/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt
new file mode 100644
index 0000000000..06458b9800
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetDirect-stderr.txt
@@ -0,0 +1,12 @@
+CMake Error at FileSetDirect.cmake:3 \(add_library\):
+ Cannot find source file:
+
+ FILE_SET
+
+ Tried extensions ([^
+]+
+)+
+ Hint: the FILE_SET keyword may only appear after a visibility specifier or
+ another FILE_SET within the target_sources\(\) command.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/target_sources/FileSetDirect.cmake b/Tests/RunCMake/target_sources/FileSetDirect.cmake
new file mode 100644
index 0000000000..9f412c833b
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetDirect.cmake
@@ -0,0 +1,3 @@
+enable_language(C)
+
+add_library(lib1 STATIC empty.c FILE_SET h1.h TYPE HEADERS)
diff --git a/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt b/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt
index 9a2ca6ae59..2b5db43124 100644
--- a/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt
+++ b/Tests/RunCMake/target_sources/FileSetFileNoExist-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at FileSetFileNoExist\.cmake:[0-9]+ \(add_library\):
+^CMake Error at FileSetFileNoExist\.cmake:[0-9]+ \(target_sources\):
Cannot find source file:
[^
diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt b/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt
new file mode 100644
index 0000000000..01db0022bf
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax-stderr.txt
@@ -0,0 +1,12 @@
+CMake Error at FileSetWrongSyntax.cmake:4 \(target_sources\):
+ Cannot find source file:
+
+ FILE_SET
+
+ Tried extensions ([^
+]+
+)+
+ Hint: the FILE_SET keyword may only appear after a visibility specifier or
+ another FILE_SET within the target_sources\(\) command.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake b/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake
new file mode 100644
index 0000000000..709fb23b14
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetWrongSyntax.cmake
@@ -0,0 +1,4 @@
+enable_language(C)
+
+add_library(lib1 STATIC)
+target_sources(lib1 PRIVATE empty.c FILE_SET h1.h TYPE HEADERS)
diff --git a/Tests/RunCMake/target_sources/MissingSource-result.txt b/Tests/RunCMake/target_sources/MissingSource-result.txt
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/Tests/RunCMake/target_sources/MissingSource-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/MissingSource-stderr.txt b/Tests/RunCMake/target_sources/MissingSource-stderr.txt
new file mode 100644
index 0000000000..b4c81c2e98
--- /dev/null
+++ b/Tests/RunCMake/target_sources/MissingSource-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at MissingSource\.cmake:[0-9]+ \(target_sources\):
+ Cannot find source file:
+
+ missing\.txt
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/target_sources/MissingSource.cmake b/Tests/RunCMake/target_sources/MissingSource.cmake
new file mode 100644
index 0000000000..2bb71e4935
--- /dev/null
+++ b/Tests/RunCMake/target_sources/MissingSource.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0115 NEW)
+add_custom_target(foo)
+target_sources(foo PRIVATE missing.txt)
diff --git a/Tests/RunCMake/target_sources/OriginDebug-stderr.txt b/Tests/RunCMake/target_sources/OriginDebug-stderr.txt
index 502d5f1536..fd797cf7de 100644
--- a/Tests/RunCMake/target_sources/OriginDebug-stderr.txt
+++ b/Tests/RunCMake/target_sources/OriginDebug-stderr.txt
@@ -1,4 +1,4 @@
-CMake Debug Log at OriginDebug.cmake:13 \(add_library\):
+CMake Debug Log at OriginDebug\.cmake:10 \(add_library\):
Used sources for target OriginDebug:
\* .*Tests/RunCMake/target_sources/empty_2.cpp
@@ -6,7 +6,7 @@ CMake Debug Log at OriginDebug.cmake:13 \(add_library\):
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
.*
-CMake Debug Log at OriginDebug.cmake:16 \(set_property\):
+CMake Debug Log at OriginDebug\.cmake:13 \(set_property\):
Used sources for target OriginDebug:
\* .*Tests/RunCMake/target_sources/empty_3.cpp
@@ -14,7 +14,7 @@ CMake Debug Log at OriginDebug.cmake:16 \(set_property\):
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
.*
-CMake Debug Log at OriginDebug.cmake:20 \(target_sources\):
+CMake Debug Log at OriginDebug\.cmake:17 \(target_sources\):
Used sources for target OriginDebug:
\* .*Tests/RunCMake/target_sources/empty_4.cpp
@@ -22,7 +22,7 @@ CMake Debug Log at OriginDebug.cmake:20 \(target_sources\):
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
.*
-CMake Debug Log at OriginDebug.cmake:14 \(target_link_libraries\):
+CMake Debug Log at OriginDebug\.cmake:11 \(target_link_libraries\):
Used sources for target OriginDebug:
\* .*Tests/RunCMake/target_sources/empty_1.cpp
diff --git a/Tests/RunCMake/target_sources/OriginDebug.cmake b/Tests/RunCMake/target_sources/OriginDebug.cmake
index d40a1d82a9..e2d847793e 100644
--- a/Tests/RunCMake/target_sources/OriginDebug.cmake
+++ b/Tests/RunCMake/target_sources/OriginDebug.cmake
@@ -1,7 +1,4 @@
-
-cmake_minimum_required(VERSION 3.0)
-
-project(OriginDebug)
+enable_language(CXX)
set(CMAKE_DEBUG_TARGET_PROPERTIES SOURCES)
diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
index 7c67c3f47b..90915cd759 100644
--- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
@@ -9,6 +9,7 @@ run_cmake(OriginDebug)
run_cmake(CMP0026-LOCATION)
run_cmake(CMP0076-OLD)
run_cmake(CMP0076-WARN)
+run_cmake(MissingSource)
run_cmake(RelativePathInInterface)
run_cmake(RelativePathInSubdirGenEx)
run_cmake(RelativePathInSubdirInterface)
@@ -43,6 +44,8 @@ run_cmake(FileSetNoExistInstall)
run_cmake(FileSetDirectories)
run_cmake(FileSetCustomTarget)
run_cmake(FileSetBadName)
+run_cmake(FileSetWrongSyntax)
+run_cmake(FileSetDirect)
if(APPLE)
run_cmake(FileSetFramework)
endif()
diff --git a/Tests/RunCMake/try_compile/CMP0056.cmake b/Tests/RunCMake/try_compile/CMP0056.cmake
index 2ab79d5dde..634576e273 100644
--- a/Tests/RunCMake/try_compile/CMP0056.cmake
+++ b/Tests/RunCMake/try_compile/CMP0056.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_policy(VERSION 3.1)
enable_language(C)
set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
if(BORLAND)
diff --git a/Tests/RunCMake/try_compile/CMakeLists.txt b/Tests/RunCMake/try_compile/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/try_compile/CMakeLists.txt
+++ b/Tests/RunCMake/try_compile/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/try_run/CMakeLists.txt b/Tests/RunCMake/try_run/CMakeLists.txt
index e93f0b69e4..e6c41a5cbb 100644
--- a/Tests/RunCMake/try_run/CMakeLists.txt
+++ b/Tests/RunCMake/try_run/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} C)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/variable_watch/CMakeLists.txt b/Tests/RunCMake/variable_watch/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/variable_watch/CMakeLists.txt
+++ b/Tests/RunCMake/variable_watch/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/while/CMakeLists.txt b/Tests/RunCMake/while/CMakeLists.txt
index 74b3ff8de3..93ee9dfd5f 100644
--- a/Tests/RunCMake/while/CMakeLists.txt
+++ b/Tests/RunCMake/while/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
diff --git a/Tests/StagingPrefix/Consumer/CMakeLists.txt b/Tests/StagingPrefix/Consumer/CMakeLists.txt
index a230441e64..f7195b0ffe 100644
--- a/Tests/StagingPrefix/Consumer/CMakeLists.txt
+++ b/Tests/StagingPrefix/Consumer/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(Consumer)
diff --git a/Tests/StagingPrefix/Producer/CMakeLists.txt b/Tests/StagingPrefix/Producer/CMakeLists.txt
index eb3d98f2e6..c02d5de27b 100644
--- a/Tests/StagingPrefix/Producer/CMakeLists.txt
+++ b/Tests/StagingPrefix/Producer/CMakeLists.txt
@@ -1,5 +1,5 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(Producer)
add_library(foo SHARED foo.cpp)
diff --git a/Tests/StringFileTest/CMakeLists.txt b/Tests/StringFileTest/CMakeLists.txt
index 068fae9947..6c0de9d7dc 100644
--- a/Tests/StringFileTest/CMakeLists.txt
+++ b/Tests/StringFileTest/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(StringFileTest)
include_directories(${StringFileTest_BINARY_DIR})
diff --git a/Tests/SubDirSpaces/CMakeLists.txt b/Tests/SubDirSpaces/CMakeLists.txt
index ecd4353b76..26a7da98d3 100644
--- a/Tests/SubDirSpaces/CMakeLists.txt
+++ b/Tests/SubDirSpaces/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(SUBDIR)
# Some systems do not seem to support rpath with spaces.
diff --git a/Tests/TargetName/CMakeLists.txt b/Tests/TargetName/CMakeLists.txt
index 21752b7fd5..98640013b4 100644
--- a/Tests/TargetName/CMakeLists.txt
+++ b/Tests/TargetName/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(TargetName)
add_subdirectory(executables)
diff --git a/Tests/TestDriver/CMakeLists.txt b/Tests/TestDriver/CMakeLists.txt
index 3cc69c03e3..cd79372862 100644
--- a/Tests/TestDriver/CMakeLists.txt
+++ b/Tests/TestDriver/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(TestDriverTest)
set(Extra_SRCS testExtraStuff.cxx testExtraStuff2.cxx )
diff --git a/Tests/Testing/CMakeLists.txt b/Tests/Testing/CMakeLists.txt
index 8f69cbe442..44afd4e8c3 100644
--- a/Tests/Testing/CMakeLists.txt
+++ b/Tests/Testing/CMakeLists.txt
@@ -4,6 +4,8 @@
cmake_minimum_required (VERSION 2.7)
project (Testing)
+include (CTest)
+
#
# Lib and exe path
#
@@ -25,12 +27,6 @@ else ()
endif ()
#
-# Include Dart
-# (will also set NSLOOKUP, HOSTNAME, etc.)
-#
-include (${CMAKE_ROOT}/Modules/Dart.cmake)
-
-#
# Extra coverage
#
build_command(BUILD_COMMAND_VAR ${CMAKE_MAKE_PROGRAM})
diff --git a/Tests/TestsWorkingDirectory/CMakeLists.txt b/Tests/TestsWorkingDirectory/CMakeLists.txt
index 2a0b015659..f77370cbeb 100644
--- a/Tests/TestsWorkingDirectory/CMakeLists.txt
+++ b/Tests/TestsWorkingDirectory/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(TestsWorkingDirectoryProj)
add_executable(WorkingDirectory main.c)
diff --git a/Tests/TryCompile/CMakeLists.txt b/Tests/TryCompile/CMakeLists.txt
index 3e46ed5010..ab2e007422 100644
--- a/Tests/TryCompile/CMakeLists.txt
+++ b/Tests/TryCompile/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
if(POLICY CMP0129)
cmake_policy(SET CMP0129 NEW)
endif()
diff --git a/Tests/TryCompile/Inner/CMakeLists.txt b/Tests/TryCompile/Inner/CMakeLists.txt
index 9f89af2843..262662dbe0 100644
--- a/Tests/TryCompile/Inner/CMakeLists.txt
+++ b/Tests/TryCompile/Inner/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(TryCompileInner C)
try_compile(SHOULD_PASS
diff --git a/Tests/VSExternalInclude/CMakeLists.txt b/Tests/VSExternalInclude/CMakeLists.txt
index a3fd8d8ebe..a44988e24b 100644
--- a/Tests/VSExternalInclude/CMakeLists.txt
+++ b/Tests/VSExternalInclude/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 2.8.12)
+cmake_minimum_required (VERSION 3.5)
project(VSExternalInclude)
if(${CMAKE_GENERATOR} MATCHES "Visual Studio 1[0124567]")
diff --git a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt
index 950ec25d53..8918d98322 100644
--- a/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt
+++ b/Tests/VSGNUFortran/subdir/fortran/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(FortranHello Fortran C)
# add a function to test for -lsunquad on sunpro sun systems.
diff --git a/Tests/VSMidl/CMakeLists.txt b/Tests/VSMidl/CMakeLists.txt
index 342b8fb678..f24640f9e5 100644
--- a/Tests/VSMidl/CMakeLists.txt
+++ b/Tests/VSMidl/CMakeLists.txt
@@ -12,7 +12,7 @@ endif()
message(STATUS "CMAKE_BUILDNAME='${CMAKE_BUILDNAME}'")
message(STATUS "THIS_TESTNAME='${THIS_TESTNAME}'")
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(${THIS_TESTNAME})
include(ExternalProject)
diff --git a/Tests/VSMidl/src/CMakeLists.txt b/Tests/VSMidl/src/CMakeLists.txt
index 7e838b459f..d8fd93435a 100644
--- a/Tests/VSMidl/src/CMakeLists.txt
+++ b/Tests/VSMidl/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(VSMidl)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/\$(IntDir)")
diff --git a/Tests/VSNASM/CMakeLists.txt b/Tests/VSNASM/CMakeLists.txt
index a038ddd882..6f824258bd 100644
--- a/Tests/VSNASM/CMakeLists.txt
+++ b/Tests/VSNASM/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
project(VSNASM C ASM_NASM)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
diff --git a/Utilities/ClangTidyModule/.gitignore b/Utilities/ClangTidyModule/.gitignore
new file mode 100644
index 0000000000..e3f48ebafe
--- /dev/null
+++ b/Utilities/ClangTidyModule/.gitignore
@@ -0,0 +1,8 @@
+/CMakeUserPresets.json
+
+# Common build directories
+/build*/
+
+# Visual Studio Code
+/.vscode/
+/.cache/
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index b084dd5b98..fee21b65d6 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
if(NOT CMake_SOURCE_DIR)
set(CMakeDeveloperReference_STANDALONE 1)
- cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
+ cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 1afad43a64..6443632511 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -99,6 +99,7 @@
{ symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<60, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
{ symbol: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
+ { symbol: [ "std::remove_reference<Defer &>::type", private, "\"cmConfigure.h\"", public ] },
# Wrappers for 3rd-party libraries
{ include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index 856ae8156e..5270903549 100755
--- a/Utilities/Scripts/update-curl.bash
+++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@ readonly name="curl"
readonly ownership="Curl Upstream <curl-library@lists.haxx.se>"
readonly subtree="Utilities/cmcurl"
readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-7_87_0"
+readonly tag="curl-8_0_1"
readonly shortlog=false
readonly paths="
CMake/*
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index a9aa47d97a..bde6c6bf20 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
if(NOT CMake_SOURCE_DIR)
set(CMakeHelp_STANDALONE 1)
- cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
+ cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py
index 47e4909ba0..ffef5b36a2 100644
--- a/Utilities/Sphinx/cmake.py
+++ b/Utilities/Sphinx/cmake.py
@@ -1,103 +1,131 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
+# BEGIN imports
+
import os
import re
+from dataclasses import dataclass
+from typing import Any, List, Tuple, Type, cast
+
+import sphinx
+
+from docutils.utils.code_analyzer import Lexer, LexerError
+from docutils.parsers.rst import Directive, directives
+from docutils.transforms import Transform
+from docutils.nodes import Element, Node, TextElement, system_message
+from docutils import io, nodes
+
+from sphinx.directives import ObjectDescription, nl_escape_re
+from sphinx.domains import Domain, ObjType
+from sphinx.roles import XRefRole
+from sphinx.util.docutils import ReferenceRole
+from sphinx.util.nodes import make_refnode
+from sphinx.util import logging, ws_re
+from sphinx import addnodes
+
+# END imports
+
+# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+# BEGIN pygments tweaks
+
# Override much of pygments' CMakeLexer.
# We need to parse CMake syntax definitions, not CMake code.
# For hard test cases that use much of the syntax below, see
-# - module/FindPkgConfig.html (with "glib-2.0>=2.10 gtk+-2.0" and similar)
-# - module/ExternalProject.html (with http:// https:// git@; also has command options -E --build)
-# - manual/cmake-buildsystem.7.html (with nested $<..>; relative and absolute paths, "::")
+# - module/FindPkgConfig.html
+# (with "glib-2.0>=2.10 gtk+-2.0" and similar)
+# - module/ExternalProject.html
+# (with http:// https:// git@; also has command options -E --build)
+# - manual/cmake-buildsystem.7.html
+# (with nested $<..>; relative and absolute paths, "::")
from pygments.lexers import CMakeLexer
-from pygments.token import Name, Operator, Punctuation, String, Text, Comment, Generic, Whitespace, Number
+from pygments.token import (Comment, Name, Number, Operator, Punctuation,
+ String, Text, Whitespace)
from pygments.lexer import bygroups
# Notes on regular expressions below:
# - [\.\+-] are needed for string constants like gtk+-2.0
-# - Unix paths are recognized by '/'; support for Windows paths may be added if needed
+# - Unix paths are recognized by '/'; support for Windows paths may be added
+# if needed
# - (\\.) allows for \-escapes (used in manual/cmake-language.7)
# - $<..$<..$>..> nested occurrence in cmake-buildsystem
-# - Nested variable evaluations are only supported in a limited capacity. Only
-# one level of nesting is supported and at most one nested variable can be present.
+# - Nested variable evaluations are only supported in a limited capacity.
+# Only one level of nesting is supported and at most one nested variable can
+# be present.
CMakeLexer.tokens["root"] = [
- (r'\b(\w+)([ \t]*)(\()', bygroups(Name.Function, Text, Name.Function), '#push'), # fctn(
+ # fctn(
+ (r'\b(\w+)([ \t]*)(\()',
+ bygroups(Name.Function, Text, Name.Function), '#push'),
(r'\(', Name.Function, '#push'),
(r'\)', Name.Function, '#pop'),
(r'\[', Punctuation, '#push'),
(r'\]', Punctuation, '#pop'),
(r'[|;,.=*\-]', Punctuation),
- (r'\\\\', Punctuation), # used in commands/source_group
+ # used in commands/source_group
+ (r'\\\\', Punctuation),
(r'[:]', Operator),
- (r'[<>]=', Punctuation), # used in FindPkgConfig.cmake
- (r'\$<', Operator, '#push'), # $<...>
- (r'<[^<|]+?>(\w*\.\.\.)?', Name.Variable), # <expr>
- (r'(\$\w*\{)([^\}\$]*)?(?:(\$\w*\{)([^\}]+?)(\}))?([^\}]*?)(\})', # ${..} $ENV{..}, possibly nested
- bygroups(Operator, Name.Tag, Operator, Name.Tag, Operator, Name.Tag, Operator)),
- (r'([A-Z]+\{)(.+?)(\})', bygroups(Operator, Name.Tag, Operator)), # DATA{ ...}
- (r'[a-z]+(@|(://))((\\.)|[\w.+-:/\\])+', Name.Attribute), # URL, git@, ...
- (r'/\w[\w\.\+-/\\]*', Name.Attribute), # absolute path
+ # used in FindPkgConfig.cmake
+ (r'[<>]=', Punctuation),
+ # $<...>
+ (r'\$<', Operator, '#push'),
+ # <expr>
+ (r'<[^<|]+?>(\w*\.\.\.)?', Name.Variable),
+ # ${..} $ENV{..}, possibly nested
+ (r'(\$\w*\{)([^\}\$]*)?(?:(\$\w*\{)([^\}]+?)(\}))?([^\}]*?)(\})',
+ bygroups(Operator, Name.Tag, Operator, Name.Tag, Operator, Name.Tag,
+ Operator)),
+ # DATA{ ...}
+ (r'([A-Z]+\{)(.+?)(\})', bygroups(Operator, Name.Tag, Operator)),
+ # URL, git@, ...
+ (r'[a-z]+(@|(://))((\\.)|[\w.+-:/\\])+', Name.Attribute),
+ # absolute path
+ (r'/\w[\w\.\+-/\\]*', Name.Attribute),
(r'/', Name.Attribute),
- (r'\w[\w\.\+-]*/[\w.+-/\\]*', Name.Attribute), # relative path
- (r'[A-Z]((\\.)|[\w.+-])*[a-z]((\\.)|[\w.+-])*', Name.Builtin), # initial A-Z, contains a-z
+ # relative path
+ (r'\w[\w\.\+-]*/[\w.+-/\\]*', Name.Attribute),
+ # initial A-Z, contains a-z
+ (r'[A-Z]((\\.)|[\w.+-])*[a-z]((\\.)|[\w.+-])*', Name.Builtin),
(r'@?[A-Z][A-Z0-9_]*', Name.Constant),
(r'[a-z_]((\\;)|(\\ )|[\w.+-])*', Name.Builtin),
(r'[0-9][0-9\.]*', Number),
- (r'(?s)"(\\"|[^"])*"', String), # "string"
+ # "string"
+ (r'(?s)"(\\"|[^"])*"', String),
(r'\.\.\.', Name.Variable),
- (r'<', Operator, '#push'), # <..|..> is different from <expr>
+ # <..|..> is different from <expr>
+ (r'<', Operator, '#push'),
(r'>', Operator, '#pop'),
(r'\n', Whitespace),
(r'[ \t]+', Whitespace),
(r'#.*\n', Comment),
- # (r'[^<>\])\}\|$"# \t\n]+', Name.Exception), # fallback, for debugging only
+ # fallback, for debugging only
+ # (r'[^<>\])\}\|$"# \t\n]+', Name.Exception),
]
-from docutils.parsers.rst import Directive, directives
-from docutils.transforms import Transform
-from docutils import io, nodes
+# END pygments tweaks
-from sphinx.directives import ObjectDescription
-from sphinx.domains import Domain, ObjType
-from sphinx.roles import XRefRole
-from sphinx.util.nodes import make_refnode
-from sphinx import addnodes
+# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+# Require at least Sphinx 2.x.
+assert sphinx.version_info >= (2,)
+
+logger = logging.getLogger(__name__)
+
+# RE to split multiple command signatures.
+sig_end_re = re.compile(r'(?<=[)])\n')
+
+
+@dataclass
+class ObjectEntry:
+ docname: str
+ objtype: str
+ node_id: str
+ name: str
-sphinx_before_1_4 = False
-sphinx_before_1_7_2 = False
-try:
- from sphinx import version_info
- if version_info < (1, 4):
- sphinx_before_1_4 = True
- if version_info < (1, 7, 2):
- sphinx_before_1_7_2 = True
-except ImportError:
- # The `sphinx.version_info` tuple was added in Sphinx v1.2:
- sphinx_before_1_4 = True
- sphinx_before_1_7_2 = True
-
-if sphinx_before_1_7_2:
- # Monkey patch for sphinx generating invalid content for qcollectiongenerator
- # https://github.com/sphinx-doc/sphinx/issues/1435
- from sphinx.util.pycompat import htmlescape
- from sphinx.builders.qthelp import QtHelpBuilder
- old_build_keywords = QtHelpBuilder.build_keywords
- def new_build_keywords(self, title, refs, subitems):
- old_items = old_build_keywords(self, title, refs, subitems)
- new_items = []
- for item in old_items:
- before, rest = item.split("ref=\"", 1)
- ref, after = rest.split("\"")
- if ("<" in ref and ">" in ref):
- new_items.append(before + "ref=\"" + htmlescape(ref) + "\"" + after)
- else:
- new_items.append(item)
- return new_items
- QtHelpBuilder.build_keywords = new_build_keywords
class CMakeModule(Directive):
required_arguments = 1
@@ -112,7 +140,7 @@ class CMakeModule(Directive):
def run(self):
settings = self.state.document.settings
if not settings.file_insertion_enabled:
- raise self.warning('"%s" directive disabled.' % self.name)
+ raise self.warning(f'{self.name!r} directive disabled.')
env = self.state.document.settings.env
rel_path, path = env.relfn2path(self.arguments[0])
@@ -123,13 +151,12 @@ class CMakeModule(Directive):
settings.record_dependencies.add(path)
f = io.FileInput(source_path=path, encoding=encoding,
error_handler=e_handler)
- except UnicodeEncodeError as error:
- msg = ('Problems with "%s" directive path:\n'
- 'Cannot encode input file path "%s" '
- '(wrong locale?).' % (self.name, path))
+ except UnicodeEncodeError:
+ msg = (f'Problems with {self.name!r} directive path:\n'
+ f'Cannot encode input file path {path!r} (wrong locale?).')
raise self.severe(msg)
except IOError as error:
- msg = 'Problems with "%s" directive path:\n%s.' % (self.name, error)
+ msg = f'Problems with {self.name!r} directive path:\n{error}.'
raise self.severe(msg)
raw_lines = f.read().splitlines()
f.close()
@@ -149,7 +176,7 @@ class CMakeModule(Directive):
# Line mode: check for .rst start (bracket or line)
m = self.re_start.match(line)
if m:
- rst = ']%s]' % m.group('eq')
+ rst = f']{m.group("eq")}]'
line = ''
elif line == '#.rst:':
rst = '#'
@@ -164,21 +191,19 @@ class CMakeModule(Directive):
line = ''
lines.append(line)
if rst is not None and rst != '#':
- raise self.warning('"%s" found unclosed bracket "#[%s[.rst:" in %s' %
- (self.name, rst[1:-1], path))
+ raise self.warning(f'{self.name!r} found unclosed bracket '
+ f'"#[{rst[1:-1]}[.rst:" in {path!r}')
self.state_machine.insert_input(lines, path)
return []
+
class _cmake_index_entry:
def __init__(self, desc):
self.desc = desc
- def __call__(self, title, targetid, main = 'main'):
- # See https://github.com/sphinx-doc/sphinx/issues/2673
- if sphinx_before_1_4:
- return ('pair', u'%s ; %s' % (self.desc, title), targetid, main)
- else:
- return ('pair', u'%s ; %s' % (self.desc, title), targetid, main, None)
+ def __call__(self, title, targetid, main='main'):
+ return ('pair', f'{self.desc} ; {title}', targetid, main, None)
+
_cmake_index_objs = {
'command': _cmake_index_entry('command'),
@@ -200,13 +225,6 @@ _cmake_index_objs = {
'variable': _cmake_index_entry('variable'),
}
-def _cmake_object_inventory(env, document, line, objtype, targetid):
- inv = env.domaindata['cmake']['objects']
- if targetid in inv:
- document.reporter.warning(
- 'CMake object "%s" also described in "%s".' %
- (targetid, env.doc2path(inv[targetid][0])), line=line)
- inv[targetid] = (env.docname, objtype)
class CMakeTransform(Transform):
@@ -224,17 +242,19 @@ class CMakeTransform(Transform):
The cmake --help-*-list commands also depend on this convention.
Return the title or False if the document file does not exist.
"""
- env = self.document.settings.env
+ settings = self.document.settings
+ env = settings.env
title = self.titles.get(docname)
if title is None:
fname = os.path.join(env.srcdir, docname+'.rst')
try:
- f = open(fname, 'r')
+ f = open(fname, 'r', encoding=settings.input_encoding)
except IOError:
title = False
else:
for line in f:
- if len(line) > 0 and (line[0].isalnum() or line[0] == '<' or line[0] == '$'):
+ if len(line) > 0 and (line[0].isalnum() or
+ line[0] == '<' or line[0] == '$'):
title = line.rstrip()
break
f.close()
@@ -262,7 +282,7 @@ class CMakeTransform(Transform):
if m:
title = m.group(1)
targetname = title
- targetid = '%s:%s' % (objtype, targetname)
+ targetid = f'{objtype}:{targetname}'
targetnode = nodes.target('', '', ids=[targetid])
self.document.note_explicit_target(targetnode)
self.document.insert(0, targetnode)
@@ -270,72 +290,277 @@ class CMakeTransform(Transform):
indexnode = addnodes.index()
indexnode['entries'] = [make_index_entry(title, targetid)]
self.document.insert(0, indexnode)
+
# Add to cmake domain object inventory
- _cmake_object_inventory(env, self.document, 1, objtype, targetid)
+ domain = cast(CMakeDomain, env.get_domain('cmake'))
+ domain.note_object(objtype, targetname, targetid, targetid)
+
class CMakeObject(ObjectDescription):
+ def __init__(self, *args, **kwargs):
+ self.targetname = None
+ super().__init__(*args, **kwargs)
def handle_signature(self, sig, signode):
# called from sphinx.directives.ObjectDescription.run()
signode += addnodes.desc_name(sig, sig)
- if self.objtype == 'genex':
- m = CMakeXRefRole._re_genex.match(sig)
- if m:
- sig = m.group(1)
return sig
def add_target_and_index(self, name, sig, signode):
if self.objtype == 'command':
- targetname = name.lower()
+ targetname = name.lower()
+ elif self.targetname:
+ targetname = self.targetname
else:
- targetname = name
- targetid = '%s:%s' % (self.objtype, targetname)
+ targetname = name
+ targetid = f'{self.objtype}:{targetname}'
if targetid not in self.state.document.ids:
signode['names'].append(targetid)
signode['ids'].append(targetid)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
- _cmake_object_inventory(self.env, self.state.document,
- self.lineno, self.objtype, targetid)
+
+ domain = cast(CMakeDomain, self.env.get_domain('cmake'))
+ domain.note_object(self.objtype, targetname, targetid, targetid,
+ location=signode)
make_index_entry = _cmake_index_objs.get(self.objtype)
if make_index_entry:
self.indexnode['entries'].append(make_index_entry(name, targetid))
-class CMakeXRefRole(XRefRole):
+class CMakeGenexObject(CMakeObject):
+ option_spec = {
+ 'target': directives.unchanged,
+ }
+
+ def handle_signature(self, sig, signode):
+ name = super().handle_signature(sig, signode)
+
+ m = CMakeXRefRole._re_genex.match(sig)
+ if m:
+ name = m.group(1)
+
+ return name
+
+ def run(self):
+ target = self.options.get('target')
+ if target is not None:
+ self.targetname = target
+
+ return super().run()
+
+
+class CMakeSignatureObject(CMakeObject):
+ object_type = 'signature'
+
+ BREAK_ALL = 'all'
+ BREAK_SMART = 'smart'
+ BREAK_VERBATIM = 'verbatim'
+
+ BREAK_CHOICES = {BREAK_ALL, BREAK_SMART, BREAK_VERBATIM}
+
+ def break_option(argument):
+ return directives.choice(argument, CMakeSignatureObject.BREAK_CHOICES)
+
+ option_spec = {
+ 'target': directives.unchanged,
+ 'break': break_option,
+ }
+
+ def _break_signature_all(sig: str) -> str:
+ return ws_re.sub(' ', sig)
+
+ def _break_signature_verbatim(sig: str) -> str:
+ lines = [ws_re.sub('\xa0', line.strip()) for line in sig.split('\n')]
+ return ' '.join(lines)
+
+ def _break_signature_smart(sig: str) -> str:
+ tokens = []
+ for line in sig.split('\n'):
+ token = ''
+ delim = ''
+
+ for c in line.strip():
+ if len(delim) == 0 and ws_re.match(c):
+ if len(token):
+ tokens.append(ws_re.sub('\xa0', token))
+ token = ''
+ else:
+ if c == '[':
+ delim += ']'
+ elif c == '<':
+ delim += '>'
+ elif len(delim) and c == delim[-1]:
+ delim = delim[:-1]
+ token += c
+
+ if len(token):
+ tokens.append(ws_re.sub('\xa0', token))
+
+ return ' '.join(tokens)
+
+ def __init__(self, *args, **kwargs):
+ self.targetnames = {}
+ self.break_style = CMakeSignatureObject.BREAK_SMART
+ super().__init__(*args, **kwargs)
+
+ def get_signatures(self) -> List[str]:
+ content = nl_escape_re.sub('', self.arguments[0])
+ lines = sig_end_re.split(content)
+
+ if self.break_style == CMakeSignatureObject.BREAK_VERBATIM:
+ fixup = CMakeSignatureObject._break_signature_verbatim
+ elif self.break_style == CMakeSignatureObject.BREAK_SMART:
+ fixup = CMakeSignatureObject._break_signature_smart
+ else:
+ fixup = CMakeSignatureObject._break_signature_all
+
+ return [fixup(line.strip()) for line in lines]
+
+ def handle_signature(self, sig, signode):
+ language = 'cmake'
+ classes = ['code', 'cmake', 'highlight']
+
+ node = addnodes.desc_name(sig, '', classes=classes)
+
+ try:
+ tokens = Lexer(sig, language, 'short')
+ except LexerError as error:
+ if self.state.document.settings.report_level > 2:
+ # Silently insert without syntax highlighting.
+ tokens = Lexer(sig, language, 'none')
+ else:
+ raise self.warning(error)
+
+ for classes, value in tokens:
+ if value == '\xa0':
+ node += nodes.inline(value, value, classes=['nbsp'])
+ elif classes:
+ node += nodes.inline(value, value, classes=classes)
+ else:
+ node += nodes.Text(value)
+
+ signode.clear()
+ signode += node
+
+ return sig
+
+ def add_target_and_index(self, name, sig, signode):
+ sig = sig.replace('\xa0', ' ')
+ if sig in self.targetnames:
+ sigargs = self.targetnames[sig]
+ else:
+ def extract_keywords(params):
+ for p in params:
+ if p[0].isalpha():
+ yield p
+ else:
+ return
+
+ keywords = extract_keywords(sig.split('(')[1].split())
+ sigargs = ' '.join(keywords)
+ targetname = sigargs.lower()
+ targetid = nodes.make_id(targetname)
+
+ if targetid not in self.state.document.ids:
+ signode['names'].append(targetname)
+ signode['ids'].append(targetid)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ # Register the signature as a command object.
+ command = sig.split('(')[0].lower()
+ refname = f'{command}({sigargs})'
+ refid = f'command:{command}({targetname})'
+
+ domain = cast(CMakeDomain, self.env.get_domain('cmake'))
+ domain.note_object('command', name=refname, target_id=refid,
+ node_id=targetid, location=signode)
+
+ def run(self):
+ self.break_style = CMakeSignatureObject.BREAK_ALL
+
+ targets = self.options.get('target')
+ if targets is not None:
+ signatures = self.get_signatures()
+ targets = [t.strip() for t in targets.split('\n')]
+ for signature, target in zip(signatures, targets):
+ self.targetnames[signature] = target
+
+ self.break_style = (
+ self.options.get('break', CMakeSignatureObject.BREAK_SMART))
+
+ return super().run()
+
+
+class CMakeReferenceRole:
# See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'.
_re = re.compile(r'^(.+?)(\s*)(?<!\x00)<(.*?)>$', re.DOTALL)
+
+ @staticmethod
+ def _escape_angle_brackets(text: str) -> str:
+ # CMake cross-reference targets frequently contain '<' so escape
+ # any explicit `<target>` with '<' not preceded by whitespace.
+ while True:
+ m = CMakeReferenceRole._re.match(text)
+ if m and len(m.group(2)) == 0:
+ text = f'{m.group(1)}\x00<{m.group(3)}>'
+ else:
+ break
+ return text
+
+ def __class_getitem__(cls, parent: Any):
+ class Class(parent):
+ def __call__(self, name: str, rawtext: str, text: str,
+ *args, **kwargs
+ ) -> Tuple[List[Node], List[system_message]]:
+ text = CMakeReferenceRole._escape_angle_brackets(text)
+ return super().__call__(name, rawtext, text, *args, **kwargs)
+ return Class
+
+
+class CMakeCRefRole(CMakeReferenceRole[ReferenceRole]):
+ nodeclass: Type[Element] = nodes.reference
+ innernodeclass: Type[TextElement] = nodes.literal
+ classes: List[str] = ['cmake', 'literal']
+
+ def run(self) -> Tuple[List[Node], List[system_message]]:
+ refnode = self.nodeclass(self.rawtext)
+ self.set_source_info(refnode)
+
+ refnode['refid'] = nodes.make_id(self.target)
+ refnode += self.innernodeclass(self.rawtext, self.title,
+ classes=self.classes)
+
+ return [refnode], []
+
+
+class CMakeXRefRole(CMakeReferenceRole[XRefRole]):
+
_re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL)
_re_genex = re.compile(r'^\$<([^<>:]+)(:[^<>]+)?>$', re.DOTALL)
_re_guide = re.compile(r'^([^<>/]+)/([^<>]*)$', re.DOTALL)
- def __call__(self, typ, rawtext, text, *args, **keys):
- # Translate CMake command cross-references of the form:
- # `command_name(SUB_COMMAND)`
- # to have an explicit target:
- # `command_name(SUB_COMMAND) <command_name>`
+ def __call__(self, typ, rawtext, text, *args, **kwargs):
if typ == 'cmake:command':
+ # Translate a CMake command cross-reference of the form:
+ # `command_name(SUB_COMMAND)`
+ # to be its own explicit target:
+ # `command_name(SUB_COMMAND) <command_name(SUB_COMMAND)>`
+ # so the XRefRole `fix_parens` option does not add more `()`.
m = CMakeXRefRole._re_sub.match(text)
if m:
- text = '%s <%s>' % (text, m.group(1))
+ text = f'{text} <{text}>'
elif typ == 'cmake:genex':
m = CMakeXRefRole._re_genex.match(text)
if m:
- text = '%s <%s>' % (text, m.group(1))
+ text = f'{text} <{m.group(1)}>'
elif typ == 'cmake:guide':
m = CMakeXRefRole._re_guide.match(text)
if m:
- text = '%s <%s>' % (m.group(2), text)
- # CMake cross-reference targets frequently contain '<' so escape
- # any explicit `<target>` with '<' not preceded by whitespace.
- while True:
- m = CMakeXRefRole._re.match(text)
- if m and len(m.group(2)) == 0:
- text = '%s\x00<%s>' % (m.group(1), m.group(3))
- else:
- break
- return XRefRole.__call__(self, typ, rawtext, text, *args, **keys)
+ text = f'{m.group(2)} <{text}>'
+ return super().__call__(typ, rawtext, text, *args, **kwargs)
# We cannot insert index nodes using the result_nodes method
# because CMakeXRefRole is processed before substitution_reference
@@ -348,6 +573,7 @@ class CMakeXRefRole(XRefRole):
# def result_nodes(self, document, env, node, is_ref):
# pass
+
class CMakeXRefTransform(Transform):
# Run this transform early since we insert nodes we want
@@ -374,9 +600,13 @@ class CMakeXRefTransform(Transform):
# Do not index cross-references to guide sections.
continue
- targetnum = env.new_serialno('index-%s:%s' % (objtype, objname))
+ if objtype == 'command':
+ # Index signature references to their parent command.
+ objname = objname.split('(')[0].lower()
+
+ targetnum = env.new_serialno(f'index-{objtype}:{objname}')
- targetid = 'index-%s-%s:%s' % (targetnum, objtype, objname)
+ targetid = f'index-{targetnum}-{objtype}:{objname}'
targetnode = nodes.target('', '', ids=[targetid])
self.document.note_explicit_target(targetnode)
@@ -384,6 +614,7 @@ class CMakeXRefTransform(Transform):
indexnode['entries'] = [make_index_entry(objname, targetid, '')]
ref.replace_self([indexnode, targetnode, ref])
+
class CMakeDomain(Domain):
"""CMake domain."""
name = 'cmake'
@@ -410,23 +641,14 @@ class CMakeDomain(Domain):
directives = {
'command': CMakeObject,
'envvar': CMakeObject,
- 'genex': CMakeObject,
+ 'genex': CMakeGenexObject,
+ 'signature': CMakeSignatureObject,
'variable': CMakeObject,
- # Other object types cannot be created except by the CMakeTransform
- # 'generator': CMakeObject,
- # 'module': CMakeObject,
- # 'policy': CMakeObject,
- # 'prop_cache': CMakeObject,
- # 'prop_dir': CMakeObject,
- # 'prop_gbl': CMakeObject,
- # 'prop_inst': CMakeObject,
- # 'prop_sf': CMakeObject,
- # 'prop_test': CMakeObject,
- # 'prop_tgt': CMakeObject,
- # 'manual': CMakeObject,
+ # Other `object_types` cannot be created except by the `CMakeTransform`
}
roles = {
- 'command': CMakeXRefRole(fix_parens = True, lowercase = True),
+ 'cref': CMakeCRefRole(),
+ 'command': CMakeXRefRole(fix_parens=True, lowercase=True),
'cpack_gen': CMakeXRefRole(),
'envvar': CMakeXRefRole(),
'generator': CMakeXRefRole(),
@@ -450,25 +672,47 @@ class CMakeDomain(Domain):
def clear_doc(self, docname):
to_clear = set()
- for fullname, (fn, _) in self.data['objects'].items():
- if fn == docname:
+ for fullname, obj in self.data['objects'].items():
+ if obj.docname == docname:
to_clear.add(fullname)
for fullname in to_clear:
del self.data['objects'][fullname]
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
- targetid = '%s:%s' % (typ, target)
+ targetid = f'{typ}:{target}'
obj = self.data['objects'].get(targetid)
+
+ if obj is None and typ == 'command':
+ # If 'command(args)' wasn't found, try just 'command'.
+ # TODO: remove this fallback? warn?
+ # logger.warning(f'no match for {targetid}')
+ command = target.split('(')[0]
+ targetid = f'{typ}:{command}'
+ obj = self.data['objects'].get(targetid)
+
if obj is None:
# TODO: warn somehow?
return None
- return make_refnode(builder, fromdocname, obj[0], targetid,
+
+ return make_refnode(builder, fromdocname, obj.docname, obj.node_id,
contnode, target)
+ def note_object(self, objtype: str, name: str, target_id: str,
+ node_id: str, location: Any = None):
+ if target_id in self.data['objects']:
+ other = self.data['objects'][target_id].docname
+ logger.warning(
+ f'CMake object {target_id!r} also described in {other!r}',
+ location=location)
+
+ self.data['objects'][target_id] = ObjectEntry(
+ self.env.docname, objtype, node_id, name)
+
def get_objects(self):
- for refname, (docname, type) in self.data['objects'].items():
- yield (refname, refname, type, docname, refname, 1)
+ for refname, obj in self.data['objects'].items():
+ yield (refname, obj.name, obj.objtype, obj.docname, obj.node_id, 1)
+
def setup(app):
app.add_directive('cmake-module', CMakeModule)
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in
index fc3ecb5fca..d4e40599ce 100644
--- a/Utilities/Sphinx/conf.py.in
+++ b/Utilities/Sphinx/conf.py.in
@@ -89,3 +89,11 @@ html_favicon = '@conf_path@/static/cmake-favicon.ico'
# qthelp_qch_name = "CMake.qch"
linkcheck_ignore = [r'about:|https://gitlab.kitware.com/cmake/community/-/wikis/doc/cpack']
+
+linkcheck_allowed_redirects = {
+ r'https://cdash\.org': r'https://www\.cdash\.org/',
+ r'https://docs\.nvidia\.com/cuda/': r'https://docs\.nvidia\.com/cuda/index\.html',
+ r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments': r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments\?.*',
+ r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313',
+ r'https://www\.sphinx-doc\.org': r'https://www\.sphinx-doc\.org/en/master/',
+}
diff --git a/Utilities/Sphinx/create_identifiers.py b/Utilities/Sphinx/create_identifiers.py
index 0ff39a0c2a..61dd819b42 100755
--- a/Utilities/Sphinx/create_identifiers.py
+++ b/Utilities/Sphinx/create_identifiers.py
@@ -6,12 +6,12 @@ if len(sys.argv) != 2:
sys.exit(-1)
name = sys.argv[1] + "/CMake.qhp"
-f = open(name, "rb")
+f = open(name, "r", encoding="utf-8")
if not f:
sys.exit(-1)
-lines = f.read().decode("utf-8").splitlines()
+lines = f.read().splitlines()
if not lines:
sys.exit(-1)
@@ -47,5 +47,5 @@ for line in lines:
line = part1 + prefix + "id=\"" + domain_object_type + "/" + domain_object + "\" " + part2
newlines.append(line + "\n")
-f = open(name, "wb")
-f.writelines(map(lambda line: line.encode("utf-8"), newlines))
+f = open(name, "w", encoding="utf-8")
+f.writelines(newlines)
diff --git a/Utilities/Sphinx/static/cmake.css b/Utilities/Sphinx/static/cmake.css
index 324cd92eb1..6303cb1cde 100644
--- a/Utilities/Sphinx/static/cmake.css
+++ b/Utilities/Sphinx/static/cmake.css
@@ -17,6 +17,45 @@ div.sphinxsidebarwrapper {
background-color: #dfdfdf;
}
+/* Apply <pre> style (from classic.css) to signature directive argument. */
+.signature .sig {
+ padding: 5px;
+ background-color: #eeeeee;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+/* Add additional styling to signature directive argument. */
+.signature .sig {
+ margin-bottom: 5px;
+ padding-left: calc(5px + 3em);
+ text-indent: -3em;
+ font-family: monospace;
+}
+
+.signature .sig .code.sig-name {
+ font-weight: normal;
+}
+
+/* Implement non-breaking spaces in signatures. */
+.nbsp {
+ white-space: nowrap;
+}
+
+/* Add hanging indent to version-{added,changed} content. */
+div .versionadded > *,
+div .versionchanged > * {
+ padding-left: 2em;
+}
+
+div.versionadded > :first-child,
+div.versionchanged > :first-child {
+ text-indent: -2em;
+}
+
/* Remove unwanted margin in case list item contains a div-wrapping
directive like `.. versionadded` or `.. deprecated`. */
dd > :first-child > p {
diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake
index a38c10183c..0f2bdb4e7a 100644
--- a/Utilities/cmThirdPartyChecks.cmake
+++ b/Utilities/cmThirdPartyChecks.cmake
@@ -161,6 +161,7 @@ if(WIN32)
set(HAVE_REGEX_H 0)
set(HAVE_RSA_H 0)
set(HAVE_SELECT 0)
+ set(HAVE_SENDMSG 0)
set(HAVE_SETENV 0)
set(HAVE_SETMODE 1)
set(HAVE_SETRLIMIT 0)
diff --git a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
index b93e7539a1..a3d2bc4a28 100644
--- a/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
+++ b/Utilities/cmcurl/CMake/CMakeConfigurableFile.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
index 75215a122f..142e919449 100644
--- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
+++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 6a9fdea098..3dbba3cbb2 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindBearSSL.cmake b/Utilities/cmcurl/CMake/FindBearSSL.cmake
index 88d5e87ade..56a064eacd 100644
--- a/Utilities/cmcurl/CMake/FindBearSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindBearSSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake
index 833e1811ae..11ab7f8254 100644
--- a/Utilities/cmcurl/CMake/FindBrotli.cmake
+++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -28,7 +28,7 @@ find_path(BROTLI_INCLUDE_DIR "brotli/decode.h")
find_library(BROTLICOMMON_LIBRARY NAMES brotlicommon)
find_library(BROTLIDEC_LIBRARY NAMES brotlidec)
-find_package_handle_standard_args(BROTLI
+find_package_handle_standard_args(Brotli
FOUND_VAR
BROTLI_FOUND
REQUIRED_VARS
@@ -36,7 +36,7 @@ find_package_handle_standard_args(BROTLI
BROTLICOMMON_LIBRARY
BROTLI_INCLUDE_DIR
FAIL_MESSAGE
- "Could NOT find BROTLI"
+ "Could NOT find Brotli"
)
set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
diff --git a/Utilities/cmcurl/CMake/FindCARES.cmake b/Utilities/cmcurl/CMake/FindCARES.cmake
index 99cf31d913..fa75891189 100644
--- a/Utilities/cmcurl/CMake/FindCARES.cmake
+++ b/Utilities/cmcurl/CMake/FindCARES.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake
index ec2bd57bae..b244e610ec 100644
--- a/Utilities/cmcurl/CMake/FindGSS.cmake
+++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -181,14 +181,14 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
set(GSS_FLAVOUR "MIT")
else()
# prevent compiling the header - just check if we can include it
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D__ROKEN_H__")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__)
check_include_file( "roken.h" _GSS_HAVE_ROKEN_H)
check_include_file( "heimdal/roken.h" _GSS_HAVE_HEIMDAL_ROKEN_H)
if(_GSS_HAVE_ROKEN_H OR _GSS_HAVE_HEIMDAL_ROKEN_H)
set(GSS_FLAVOUR "Heimdal")
endif()
- set(CMAKE_REQUIRED_DEFINITIONS "")
+ list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__ROKEN_H__)
endif()
else()
# I'm not convinced if this is the right way but this is what autotools do at the moment
diff --git a/Utilities/cmcurl/CMake/FindLibPSL.cmake b/Utilities/cmcurl/CMake/FindLibPSL.cmake
index 66abdd79a3..e3bd68d1d7 100644
--- a/Utilities/cmcurl/CMake/FindLibPSL.cmake
+++ b/Utilities/cmcurl/CMake/FindLibPSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindLibSSH2.cmake b/Utilities/cmcurl/CMake/FindLibSSH2.cmake
index 0ec7f7e3b9..a0c251ae39 100644
--- a/Utilities/cmcurl/CMake/FindLibSSH2.cmake
+++ b/Utilities/cmcurl/CMake/FindLibSSH2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindMSH3.cmake b/Utilities/cmcurl/CMake/FindMSH3.cmake
index 96477e2828..7d9c6b6544 100644
--- a/Utilities/cmcurl/CMake/FindMSH3.cmake
+++ b/Utilities/cmcurl/CMake/FindMSH3.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
index fcd6717f6f..814bd97da1 100644
--- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake
+++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
index 6d70c4a049..3957646c43 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
index 8d8ebc1b1e..9b13e6c6ff 100644
--- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
+++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
index 61e54c2d67..ff0d49e338 100644
--- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -71,7 +71,7 @@ endif()
if(NGTCP2_FIND_COMPONENTS)
set(NGTCP2_CRYPTO_BACKEND "")
foreach(component IN LISTS NGTCP2_FIND_COMPONENTS)
- if(component MATCHES "^(BoringSSL|OpenSSL|GnuTLS)")
+ if(component MATCHES "^(BoringSSL|OpenSSL|wolfSSL|GnuTLS)")
if(NGTCP2_CRYPTO_BACKEND)
message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
endif()
diff --git a/Utilities/cmcurl/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake
index 6742dda83f..ccddf422a5 100644
--- a/Utilities/cmcurl/CMake/FindNSS.cmake
+++ b/Utilities/cmcurl/CMake/FindNSS.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindQUICHE.cmake b/Utilities/cmcurl/CMake/FindQUICHE.cmake
index fc47027d74..0488463d55 100644
--- a/Utilities/cmcurl/CMake/FindQUICHE.cmake
+++ b/Utilities/cmcurl/CMake/FindQUICHE.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
index 986f01e00e..d67c0eb24d 100644
--- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake
+++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake
index 2d6540443c..973e6ad4a9 100644
--- a/Utilities/cmcurl/CMake/FindZstd.cmake
+++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake
index 4d7380eb5f..e12bf303f4 100644
--- a/Utilities/cmcurl/CMake/Macros.cmake
+++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index ed8d28a216..fa1e458e61 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
index 3cb4ffec3d..37712377e4 100644
--- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
+++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -29,7 +29,6 @@ if(NOT UNIX)
set(HAVE_ARPA_INET_H 0)
set(HAVE_FCNTL_H 1)
- set(HAVE_INTTYPES_H 0)
set(HAVE_IO_H 1)
set(HAVE_NETDB_H 0)
set(HAVE_NETINET_IN_H 0)
@@ -37,7 +36,6 @@ if(NOT UNIX)
set(HAVE_PWD_H 0)
set(HAVE_SETJMP_H 1)
set(HAVE_SIGNAL_H 1)
- set(HAVE_STDINT_H 0)
set(HAVE_STDLIB_H 1)
set(HAVE_STRINGS_H 0)
set(HAVE_STRING_H 1)
diff --git a/Utilities/cmcurl/CMake/Utilities.cmake b/Utilities/cmcurl/CMake/Utilities.cmake
index 78bfd6ffe6..9ff38e3a0f 100644
--- a/Utilities/cmcurl/CMake/Utilities.cmake
+++ b/Utilities/cmcurl/CMake/Utilities.cmake
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
index 2549d41f42..3e0742d1e4 100644
--- a/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
+++ b/Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in
index 496a92d0e5..dbe4ed2102 100644
--- a/Utilities/cmcurl/CMake/curl-config.cmake.in
+++ b/Utilities/cmcurl/CMake/curl-config.cmake.in
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 49016c8863..dcade3d101 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -150,7 +150,7 @@ endif()
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -197,7 +197,7 @@ endif()
# HAVE_RAND_EGD: `RAND_egd` present in OpenSSL
# HAVE_BORINGSSL: OpenSSL is BoringSSL
# HAVE_PK11_CREATEMANAGEDGENERICOBJECTL: `PK11_CreateManagedGenericObject` present in NSS
-# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL
+# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
# HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
# HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd
#
@@ -253,7 +253,7 @@ if(WIN32)
set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string")
if(CURL_TARGET_WINDOWS_VERSION)
add_definitions(-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION})
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION})
set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
endif()
endif()
@@ -488,7 +488,7 @@ include(CheckCSourceCompiles)
# On windows preload settings
if(WIN32)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WINSOCKAPI_=")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake)
endif()
@@ -514,24 +514,6 @@ if(WIN32)
endif()
if(0) # This code not needed for building within CMake.
-# This check below for use of deprecated symbols is only temporary and is to
-# be removed again after a year's service. Remove after November 25, 2022.
-set(CURL_RECONFIG_REQUIRED 0)
-foreach(_LIB GSSAPI OPENLDAP LIBSSH LIBSSH2 BEARSSL MBEDTLS NSS OPENSSL
- SCHANNEL SECTRANSP WOLFSSL)
- if(CMAKE_USE_${_LIB})
- set(CURL_RECONFIG_REQUIRED 1)
- message(SEND_ERROR "The option CMAKE_USE_${_LIB} was renamed to CURL_USE_${_LIB}.")
- endif()
-endforeach()
-if(CMAKE_USE_WINSSL)
- set(CURL_RECONFIG_REQUIRED 1)
- message(SEND_ERROR "The option CMAKE_USE_WINSSL was renamed to CURL_USE_SCHANNEL.")
-endif()
-if(CURL_RECONFIG_REQUIRED)
- message(FATAL_ERROR "Reconfig required")
-endif()
-
# check SSL libraries
# TODO support GnuTLS
option(CURL_ENABLE_SSL "Enable SSL support" ON)
@@ -541,7 +523,7 @@ if(APPLE)
endif()
if(WIN32)
cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
- cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without openssl" ON
+ cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON
CURL_USE_SCHANNEL OFF)
endif()
cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
@@ -577,7 +559,6 @@ if(CURL_USE_SCHANNEL)
endif()
if(CURL_WINDOWS_SSPI)
set(USE_WINDOWS_SSPI ON)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DSECURITY_WIN32")
endif()
if(CURL_USE_SECTRANSP)
@@ -602,6 +583,62 @@ if(use_core_foundation)
list(APPEND CURL_LIBS "-framework CoreFoundation")
endif()
+# Keep compression lib detection before TLS detection, which
+# might depend on it.
+
+set(HAVE_LIBZ OFF)
+set(USE_ZLIB OFF)
+find_package(ZLIB)
+if(ZLIB_FOUND)
+ set(HAVE_LIBZ ON)
+ set(USE_ZLIB ON)
+
+ # Depend on ZLIB via imported targets if supported by the running
+ # version of CMake. This allows our dependents to get our dependencies
+ # transitively.
+ if(NOT CMAKE_VERSION VERSION_LESS 3.4)
+ if(CMAKE_USE_SYSTEM_ZLIB)
+ list(APPEND CURL_LIBS ZLIB::ZLIB)
+ else()
+ list(APPEND CURL_LIBS cmzlib)
+ endif()
+ else()
+ list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
+ include_directories(${ZLIB_INCLUDE_DIRS})
+ list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
+ endif()
+endif()
+
+option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF)
+set(HAVE_BROTLI OFF)
+if(CURL_BROTLI)
+ find_package(Brotli QUIET)
+ if(BROTLI_FOUND)
+ set(HAVE_BROTLI ON)
+ list(APPEND CURL_LIBS ${BROTLI_LIBRARIES})
+ include_directories(${BROTLI_INCLUDE_DIRS})
+ list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS})
+ endif()
+endif()
+
+option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF)
+set(HAVE_ZSTD OFF)
+if(CURL_ZSTD)
+ find_package(Zstd REQUIRED)
+ if (NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
+ cmake_push_check_state()
+ set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS})
+ set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES})
+ check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM)
+ cmake_pop_check_state()
+ endif()
+ if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM)
+ set(HAVE_ZSTD ON)
+ list(APPEND CURL_LIBS ${Zstd_LIBRARIES})
+ include_directories(${Zstd_INCLUDE_DIRS})
+ endif()
+endif()
+
if(CURL_USE_OPENSSL)
find_package(OpenSSL)
if(NOT OpenSSL_FOUND)
@@ -630,8 +667,6 @@ if(CURL_USE_OPENSSL)
if(CURL_CA_PATH)
add_definitions(-DCURL_CA_PATH="${CURL_CA_PATH}")
endif()
-
- add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED)
endif()
if(CURL_USE_MBEDTLS)
@@ -681,23 +716,39 @@ if(USE_NGHTTP2)
endif()
function(CheckQuicSupportInOpenSSL)
- # Be sure that the OpenSSL library actually supports QUIC.
+ # Be sure that the OpenSSL/wolfSSL library actually supports QUIC.
if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
cmake_push_check_state()
- set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
- set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
- check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+ if(USE_WOLFSSL)
+ set(CMAKE_REQUIRED_INCLUDES "${WolfSSL_INCLUDE_DIRS}")
+ set(CMAKE_REQUIRED_LIBRARIES "${WolfSSL_LIBRARIES}")
+ if(HAVE_LIBZ)
+ list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}")
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
+ endif()
+ if(WIN32)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES "crypt32")
+ endif()
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T) # to pull in stdint.h (as of wolfSSL v5.5.4)
+ check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+ else()
+ set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}")
+ check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+ endif()
cmake_pop_check_state()
endif()
if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
- message(FATAL_ERROR "QUIC support is missing in OpenSSL/BoringSSL. Try setting -DOPENSSL_ROOT_DIR")
+ message(FATAL_ERROR "QUIC support is missing in OpenSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
endif()
endfunction()
option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF)
if(USE_NGTCP2)
- if(USE_OPENSSL)
- if(HAVE_BORINGSSL)
+ if(USE_OPENSSL OR USE_WOLFSSL)
+ if(USE_WOLFSSL)
+ find_package(NGTCP2 REQUIRED wolfSSL)
+ elseif(HAVE_BORINGSSL)
find_package(NGTCP2 REQUIRED BoringSSL)
else()
find_package(NGTCP2 REQUIRED OpenSSL)
@@ -707,7 +758,7 @@ if(USE_NGTCP2)
# TODO add GnuTLS support as vtls library.
find_package(NGTCP2 REQUIRED GnuTLS)
else()
- message(FATAL_ERROR "ngtcp2 requires OpenSSL or GnuTLS")
+ message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS")
endif()
set(USE_NGTCP2 ON)
include_directories(${NGTCP2_INCLUDE_DIRS})
@@ -755,6 +806,8 @@ if(NOT CURL_DISABLE_LDAP)
check_library_exists_concat("wldap32" cldap_open HAVE_WLDAP32)
if(NOT HAVE_WLDAP32)
set(USE_WIN32_LDAP OFF)
+ elseif(NOT CURL_DISABLE_LDAPS)
+ set(HAVE_LDAP_SSL ON)
endif()
endif()
endif()
@@ -826,7 +879,7 @@ if(NOT CURL_DISABLE_LDAP)
return 0;
}"
)
- set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DLDAP_DEPRECATED=1")
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
if(HAVE_LIBLBER)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
@@ -870,56 +923,6 @@ if(WIN32)
endif()
endif()
-set(HAVE_LIBZ OFF)
-set(USE_ZLIB OFF)
-#optional_dependency(ZLIB)
-find_package(ZLIB)
-if(ZLIB_FOUND)
- set(HAVE_LIBZ ON)
- set(USE_ZLIB ON)
-
- # Depend on ZLIB via imported targets if supported by the running
- # version of CMake. This allows our dependents to get our dependencies
- # transitively.
- if(NOT CMAKE_VERSION VERSION_LESS 3.4)
- list(APPEND CURL_LIBS ZLIB::ZLIB)
- else()
- list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
- include_directories(${ZLIB_INCLUDE_DIRS})
- list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
- endif()
-endif()
-
-option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF)
-set(HAVE_BROTLI OFF)
-if(CURL_BROTLI)
- find_package(Brotli QUIET)
- if(BROTLI_FOUND)
- set(HAVE_BROTLI ON)
- list(APPEND CURL_LIBS ${BROTLI_LIBRARIES})
- include_directories(${BROTLI_INCLUDE_DIRS})
- list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS})
- endif()
-endif()
-
-option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF)
-set(HAVE_ZSTD OFF)
-if(CURL_ZSTD)
- find_package(Zstd REQUIRED)
- if (NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
- cmake_push_check_state()
- set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS})
- set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES})
- check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM)
- cmake_pop_check_state()
- endif()
- if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM)
- set(HAVE_ZSTD ON)
- list(APPEND CURL_LIBS ${Zstd_LIBRARIES})
- include_directories(${Zstd_INCLUDE_DIRS})
- endif()
-endif()
-
#libpsl
option(CURL_USE_LIBPSL "Use libPSL" ON)
mark_as_advanced(CURL_USE_LIBPSL)
@@ -1056,7 +1059,9 @@ elseif("${CURL_CA_BUNDLE}" STREQUAL "none")
unset(CURL_CA_BUNDLE CACHE)
elseif("${CURL_CA_BUNDLE}" STREQUAL "auto")
unset(CURL_CA_BUNDLE CACHE)
- set(CURL_CA_BUNDLE_AUTODETECT TRUE)
+ if(NOT CMAKE_CROSSCOMPILING)
+ set(CURL_CA_BUNDLE_AUTODETECT TRUE)
+ endif()
else()
set(CURL_CA_BUNDLE_SET TRUE)
endif()
@@ -1067,7 +1072,7 @@ elseif("${CURL_CA_PATH}" STREQUAL "none")
unset(CURL_CA_PATH CACHE)
elseif("${CURL_CA_PATH}" STREQUAL "auto")
unset(CURL_CA_PATH CACHE)
- if(NOT USE_NSS)
+ if(NOT CMAKE_CROSSCOMPILING AND NOT USE_NSS)
set(CURL_CA_PATH_AUTODETECT TRUE)
endif()
else()
@@ -1177,7 +1182,6 @@ check_include_file_concat("unistd.h" HAVE_UNISTD_H)
check_include_file_concat("utime.h" HAVE_UTIME_H)
check_include_file_concat("stddef.h" HAVE_STDDEF_H)
-check_include_file_concat("stdint.h" HAVE_STDINT_H)
check_include_file_concat("sys/utsname.h" HAVE_SYS_UTSNAME_H)
check_type_size(size_t SIZEOF_SIZE_T)
@@ -1204,6 +1208,7 @@ check_symbol_exists(socket "${CURL_INCLUDES}" HAVE_SOCKET)
check_symbol_exists(socketpair "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
check_symbol_exists(recv "${CURL_INCLUDES}" HAVE_RECV)
check_symbol_exists(send "${CURL_INCLUDES}" HAVE_SEND)
+check_symbol_exists(sendmsg "${CURL_INCLUDES}" HAVE_SENDMSG)
check_symbol_exists(select "${CURL_INCLUDES}" HAVE_SELECT)
check_symbol_exists(strdup "${CURL_INCLUDES}" HAVE_STRDUP)
check_symbol_exists(strtok_r "${CURL_INCLUDES}" HAVE_STRTOK_R)
@@ -1228,7 +1233,6 @@ check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
check_symbol_exists(signal "${CURL_INCLUDES}" HAVE_SIGNAL)
check_symbol_exists(strtoll "${CURL_INCLUDES}" HAVE_STRTOLL)
-check_symbol_exists(_strtoi64 "${CURL_INCLUDES}" HAVE__STRTOI64)
check_symbol_exists(strerror_r "${CURL_INCLUDES}" HAVE_STRERROR_R)
check_symbol_exists(siginterrupt "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
check_symbol_exists(getaddrinfo "${CURL_INCLUDES}" HAVE_GETADDRINFO)
@@ -1248,7 +1252,7 @@ check_symbol_exists(setrlimit "${CURL_INCLUDES}" HAVE_SETRLIMIT)
if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
# earlier MSVC compilers had faulty snprintf implementations
- check_symbol_exists(snprintf "${CURL_INCLUDES}" HAVE_SNPRINTF)
+ check_symbol_exists(snprintf "stdio.h" HAVE_SNPRINTF)
endif()
check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME)
check_symbol_exists(inet_ntop "${CURL_INCLUDES}" HAVE_INET_NTOP)
@@ -1435,8 +1439,6 @@ else()
set(CURL_PULL_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H})
set(CURL_PULL_SYS_POLL_H ${HAVE_SYS_POLL_H})
endif()
-set(CURL_PULL_STDINT_H ${HAVE_STDINT_H})
-set(CURL_PULL_INTTYPES_H ${HAVE_INTTYPES_H})
include(CMake/OtherTests.cmake)
@@ -1451,7 +1453,7 @@ if(WIN32)
# Check if crypto functions in wincrypt.h are actually available
if(HAVE_WINCRYPT_H)
- check_symbol_exists(CryptAcquireContext "${CURL_INCLUDES}" USE_WINCRYPT)
+ check_symbol_exists(CryptAcquireContext "windows.h;wincrypt.h" USE_WINCRYPT)
endif()
if(USE_WINCRYPT)
set(USE_WIN32_CRYPTO ON)
diff --git a/Utilities/cmcurl/COPYING b/Utilities/cmcurl/COPYING
index 90f05adf25..d1eab3eb93 100644
--- a/Utilities/cmcurl/COPYING
+++ b/Utilities/cmcurl/COPYING
@@ -1,6 +1,6 @@
COPYRIGHT AND PERMISSION NOTICE
-Copyright (c) 1996 - 2022, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2023, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index b354757afc..4c6288d99e 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -34,11 +34,12 @@
#endif
/* Compile-time deprecation macros. */
-#if defined(__GNUC__) && (__GNUC__ >= 6) && \
+#if defined(__GNUC__) && \
+ ((__GNUC__ > 12) || ((__GNUC__ == 12) && (__GNUC_MINOR__ >= 1 ))) && \
!defined(__INTEL_COMPILER) && \
!defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL)
-#define CURL_DEPRECATED(version, message) \
- __attribute__((deprecated("since " # version ". " message)))
+#define CURL_DEPRECATED(version, message) \
+ __attribute__((deprecated("since " # version ". " message)))
#define CURL_IGNORE_DEPRECATION(statements) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
@@ -167,7 +168,7 @@ typedef enum {
CURLSSLBACKEND_SECURETRANSPORT = 9,
CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10,
CURLSSLBACKEND_MBEDTLS = 11,
- CURLSSLBACKEND_MESALINK = 12,
+ CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12,
CURLSSLBACKEND_BEARSSL = 13,
CURLSSLBACKEND_RUSTLS = 14
} curl_sslbackend;
@@ -248,7 +249,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp,
#ifndef CURL_MAX_READ_SIZE
/* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */
-#define CURL_MAX_READ_SIZE 524288
+#define CURL_MAX_READ_SIZE (10*1024*1024)
#endif
#ifndef CURL_MAX_WRITE_SIZE
@@ -2259,8 +2260,13 @@ enum {
CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1
Upgrade */
- CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback.
- Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */
+ CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if
+ needed. For HTTPS only. For HTTP, this option
+ makes libcurl return error. */
+ CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS
+ only. For HTTP, this makes libcurl
+ return error. */
+
CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
};
@@ -2953,6 +2959,7 @@ typedef enum {
CURL_LOCK_DATA_SSL_SESSION,
CURL_LOCK_DATA_CONNECT,
CURL_LOCK_DATA_PSL,
+ CURL_LOCK_DATA_HSTS,
CURL_LOCK_DATA_LAST
} curl_lock_data;
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 94db4cde00..6f2affaeb8 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,17 +28,17 @@
a script at release-time. This was made its own header file in 7.11.2 */
/* This is the global package copyright */
-#define LIBCURL_COPYRIGHT "1996 - 2022 Daniel Stenberg, <daniel@haxx.se>."
+#define LIBCURL_COPYRIGHT "Daniel Stenberg, <daniel@haxx.se>."
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "7.87.0"
+#define LIBCURL_VERSION "8.0.1"
/* The numeric version number is also available "in parts" by using these
defines: */
-#define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 87
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_MAJOR 8
+#define LIBCURL_VERSION_MINOR 0
+#define LIBCURL_VERSION_PATCH 1
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
-#define LIBCURL_VERSION_NUM 0x075700
+#define LIBCURL_VERSION_NUM 0x080001
/*
* This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h
index 98ee8888ed..394668a8fa 100644
--- a/Utilities/cmcurl/include/curl/easy.h
+++ b/Utilities/cmcurl/include/curl/easy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h
index 1598c6f113..8df11e1e42 100644
--- a/Utilities/cmcurl/include/curl/header.h
+++ b/Utilities/cmcurl/include/curl/header.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h
index 06ef5c6d8c..e652a6520e 100644
--- a/Utilities/cmcurl/include/curl/mprintf.h
+++ b/Utilities/cmcurl/include/curl/mprintf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h
index c956d28e8f..30a3d93017 100644
--- a/Utilities/cmcurl/include/curl/multi.h
+++ b/Utilities/cmcurl/include/curl/multi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/options.h b/Utilities/cmcurl/include/curl/options.h
index a792687cf4..1ed76a95c6 100644
--- a/Utilities/cmcurl/include/curl/options.h
+++ b/Utilities/cmcurl/include/curl/options.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/stdcheaders.h b/Utilities/cmcurl/include/curl/stdcheaders.h
index 82e1b5fef6..7451aa3052 100644
--- a/Utilities/cmcurl/include/curl/stdcheaders.h
+++ b/Utilities/cmcurl/include/curl/stdcheaders.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h
index 11db51ecf3..def7739242 100644
--- a/Utilities/cmcurl/include/curl/system.h
+++ b/Utilities/cmcurl/include/curl/system.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -227,16 +227,14 @@
# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
#elif defined(__OS400__)
-# if defined(__ILEC400__)
-# define CURL_TYPEOF_CURL_OFF_T long long
-# define CURL_FORMAT_CURL_OFF_T "lld"
-# define CURL_FORMAT_CURL_OFF_TU "llu"
-# define CURL_SUFFIX_CURL_OFF_T LL
-# define CURL_SUFFIX_CURL_OFF_TU ULL
-# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
-# define CURL_PULL_SYS_TYPES_H 1
-# define CURL_PULL_SYS_SOCKET_H 1
-# endif
+# define CURL_TYPEOF_CURL_OFF_T long long
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+# define CURL_SUFFIX_CURL_OFF_T LL
+# define CURL_SUFFIX_CURL_OFF_TU ULL
+# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+# define CURL_PULL_SYS_TYPES_H 1
+# define CURL_PULL_SYS_SOCKET_H 1
#elif defined(__MVS__)
# if defined(__IBMC__) || defined(__IBMCPP__)
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h
index bf655bb632..bc8d7a78ce 100644
--- a/Utilities/cmcurl/include/curl/typecheck-gcc.h
+++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,9 +42,8 @@
*/
#define curl_easy_setopt(handle, option, value) \
__extension__({ \
- CURL_IGNORE_DEPRECATION(__typeof__(option) _curl_opt = option;) \
+ CURLoption _curl_opt = (option); \
if(__builtin_constant_p(_curl_opt)) { \
- (void) option; \
CURL_IGNORE_DEPRECATION( \
if(curlcheck_long_option(_curl_opt)) \
if(!curlcheck_long(value)) \
@@ -120,9 +119,8 @@
/* wraps curl_easy_getinfo() with typechecking */
#define curl_easy_getinfo(handle, info, arg) \
__extension__({ \
- CURL_IGNORE_DEPRECATION(__typeof__(info) _curl_info = info;) \
+ CURLINFO _curl_info = (info); \
if(__builtin_constant_p(_curl_info)) { \
- (void) info; \
CURL_IGNORE_DEPRECATION( \
if(curlcheck_string_info(_curl_info)) \
if(!curlcheck_arr((arg), char *)) \
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index e15c213ccd..b3504b683a 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,6 +62,7 @@ typedef enum {
CURLUE_BAD_SCHEME, /* 27 */
CURLUE_BAD_SLASHES, /* 28 */
CURLUE_BAD_USER, /* 29 */
+ CURLUE_LACKS_IDN, /* 30 */
CURLUE_LAST
} CURLUcode;
@@ -95,6 +96,7 @@ typedef enum {
#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the
scheme is unknown. */
#define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */
+#define CURLU_PUNYCODE (1<<12) /* get the host name in pynycode */
typedef struct Curl_URL CURLU;
@@ -115,14 +117,14 @@ CURL_EXTERN void curl_url_cleanup(CURLU *handle);
* curl_url_dup() duplicates a CURLU handle and returns a new copy. The new
* handle must also be freed with curl_url_cleanup().
*/
-CURL_EXTERN CURLU *curl_url_dup(CURLU *in);
+CURL_EXTERN CURLU *curl_url_dup(const CURLU *in);
/*
* curl_url_get() extracts a specific part of the URL from a CURLU
* handle. Returns error code. The returned pointer MUST be freed with
* curl_free() afterwards.
*/
-CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what,
+CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what,
char **part, unsigned int flags);
/*
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h
index 4d57f91e56..fd6a916547 100644
--- a/Utilities/cmcurl/include/curl/websockets.h
+++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,6 +33,7 @@ struct curl_ws_frame {
int flags; /* See the CURLWS_* defines */
curl_off_t offset; /* the offset of this data into the frame */
curl_off_t bytesleft; /* number of pending bytes left of the payload */
+ size_t len; /* size of the current data chunk */
};
/* flag bits */
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index 074ab9dac7..c3eb842386 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -47,29 +47,6 @@ if(WIN32 AND NOT CURL_STATICLIB)
list(APPEND CSOURCES libcurl.rc)
endif()
-# SET(CSOURCES
-# # memdebug.c -not used
-# # nwlib.c - Not used
-# # strtok.c - specify later
-# # strtoofft.c - specify later
-# )
-
-# #OPTION(CURL_MALLOC_DEBUG "Debug mallocs in Curl" OFF)
-# MARK_AS_ADVANCED(CURL_MALLOC_DEBUG)
-# IF(CURL_MALLOC_DEBUG)
-# SET(CSOURCES ${CSOURCES}
-# memdebug.c
-# )
-# ENDIF(CURL_MALLOC_DEBUG)
-
-# # only build compat strtoofft if we need to
-# IF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
-# SET(CSOURCES ${CSOURCES}
-# strtoofft.c
-# )
-# ENDIF(NOT HAVE_STRTOLL AND NOT HAVE__STRTOI64)
-
-
# The rest of the build
include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include)
@@ -136,11 +113,12 @@ set_target_properties(${LIB_NAME} PROPERTIES
if(0) # This code not needed for building within CMake.
if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+ CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
# FreeBSD comes with the a.out and elf flavours
# but a.out was supported up to version 3.x and
- # elf from 3.x. I cannot imagine someone runnig
+ # elf from 3.x. I cannot imagine someone running
# CMake on those ancient systems
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index 9eafa93b9a..663190a19f 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -79,16 +79,17 @@ LIB_VTLS_HFILES = \
vtls/x509asn1.h
LIB_VQUIC_CFILES = \
- vquic/msh3.c \
- vquic/ngtcp2.c \
- vquic/quiche.c \
+ vquic/curl_msh3.c \
+ vquic/curl_ngtcp2.c \
+ vquic/curl_quiche.c \
vquic/vquic.c
LIB_VQUIC_HFILES = \
- vquic/msh3.h \
- vquic/ngtcp2.h \
- vquic/quiche.h \
- vquic/vquic.h
+ vquic/curl_msh3.h \
+ vquic/curl_ngtcp2.h \
+ vquic/curl_quiche.h \
+ vquic/vquic.h \
+ vquic/vquic_int.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
@@ -106,6 +107,8 @@ LIB_CFILES = \
base64.c \
bufref.c \
c-hyper.c \
+ cf-https-connect.c \
+ cf-socket.c \
cfilters.c \
conncache.c \
connect.c \
@@ -118,6 +121,7 @@ LIB_CFILES = \
curl_get_line.c \
curl_gethostname.c \
curl_gssapi.c \
+ curl_log.c \
curl_memrchr.c \
curl_multibyte.c \
curl_ntlm_core.c \
@@ -219,7 +223,6 @@ LIB_CFILES = \
version.c \
version_win32.c \
warnless.c \
- wildcard.c \
ws.c
LIB_HFILES = \
@@ -229,6 +232,8 @@ LIB_HFILES = \
asyn.h \
bufref.h \
c-hyper.h \
+ cf-https-connect.h \
+ cf-socket.h \
cfilters.h \
conncache.h \
connect.h \
@@ -246,6 +251,7 @@ LIB_HFILES = \
curl_hmac.h \
curl_krb5.h \
curl_ldap.h \
+ curl_log.h \
curl_md4.h \
curl_md5.h \
curl_memory.h \
@@ -312,7 +318,6 @@ LIB_HFILES = \
pop3.h \
progress.h \
psl.h \
- quic.h \
rand.h \
rename.h \
rtsp.h \
@@ -346,7 +351,6 @@ LIB_HFILES = \
urldata.h \
version_win32.h \
warnless.h \
- wildcard.h \
ws.h
LIB_RCFILES = libcurl.rc
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index ec18e381c8..31a7abcc5d 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h
index 2751d272a1..7fea1434a5 100644
--- a/Utilities/cmcurl/lib/altsvc.h
+++ b/Utilities/cmcurl/lib/altsvc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c
index e8c2fc02e0..b0a9500267 100644
--- a/Utilities/cmcurl/lib/amigaos.c
+++ b/Utilities/cmcurl/lib/amigaos.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h
index 9abfb59eac..7997ede80e 100644
--- a/Utilities/cmcurl/lib/amigaos.h
+++ b/Utilities/cmcurl/lib/amigaos.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/arpa_telnet.h b/Utilities/cmcurl/lib/arpa_telnet.h
index 523f7f51e7..de1373800c 100644
--- a/Utilities/cmcurl/lib/arpa_telnet.h
+++ b/Utilities/cmcurl/lib/arpa_telnet.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 4436da3c4d..19fe8536b0 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index 705f0f612a..4d7f8605f7 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h
index 1aab21aaa7..7e207c4f56 100644
--- a/Utilities/cmcurl/lib/asyn.h
+++ b/Utilities/cmcurl/lib/asyn.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c
index bacd627cf4..e1b7b7287a 100644
--- a/Utilities/cmcurl/lib/base64.c
+++ b/Utilities/cmcurl/lib/base64.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/bufref.c b/Utilities/cmcurl/lib/bufref.c
index 91b037431d..ce686b6f37 100644
--- a/Utilities/cmcurl/lib/bufref.c
+++ b/Utilities/cmcurl/lib/bufref.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/bufref.h b/Utilities/cmcurl/lib/bufref.h
index 96b818b537..dd424f18f5 100644
--- a/Utilities/cmcurl/lib/bufref.h
+++ b/Utilities/cmcurl/lib/bufref.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2021 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c
index 65f5581aee..9c7632d354 100644
--- a/Utilities/cmcurl/lib/c-hyper.c
+++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -485,7 +485,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
if(k->upgr101 == UPGR101_WS) {
if(http_status == 101) {
/* verify the response */
- result = Curl_ws_accept(data);
+ result = Curl_ws_accept(data, NULL, 0);
if(result)
return result;
}
@@ -1128,6 +1128,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
goto error;
}
+#ifdef HAVE_LIBZ
+ /* we only consider transfer-encoding magic if libz support is built-in */
+ result = Curl_transferencode(data);
+ if(result)
+ goto error;
+ result = Curl_hyper_header(data, headers, data->state.aptr.te);
+ if(result)
+ goto error;
+#endif
+
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(data->state.aptr.accept_encoding);
@@ -1144,16 +1154,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
else
Curl_safefree(data->state.aptr.accept_encoding);
-#ifdef HAVE_LIBZ
- /* we only consider transfer-encoding magic if libz support is built-in */
- result = Curl_transferencode(data);
- if(result)
- goto error;
- result = Curl_hyper_header(data, headers, data->state.aptr.te);
- if(result)
- goto error;
-#endif
-
result = cookies(data, conn, headers);
if(result)
goto error;
diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h
index 70507ad2a4..4218cda75f 100644
--- a/Utilities/cmcurl/lib/c-hyper.h
+++ b/Utilities/cmcurl/lib/c-hyper.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c
new file mode 100644
index 0000000000..ed70ad05f9
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -0,0 +1,569 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "curl_log.h"
+#include "cfilters.h"
+#include "connect.h"
+#include "multiif.h"
+#include "cf-https-connect.h"
+#include "http2.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+typedef enum {
+ CF_HC_INIT,
+ CF_HC_CONNECT,
+ CF_HC_SUCCESS,
+ CF_HC_FAILURE
+} cf_hc_state;
+
+struct cf_hc_baller {
+ const char *name;
+ struct Curl_cfilter *cf;
+ CURLcode result;
+ struct curltime started;
+ int reply_ms;
+ bool enabled;
+};
+
+static void cf_hc_baller_reset(struct cf_hc_baller *b,
+ struct Curl_easy *data)
+{
+ if(b->cf) {
+ Curl_conn_cf_close(b->cf, data);
+ Curl_conn_cf_discard_chain(&b->cf, data);
+ b->cf = NULL;
+ }
+ b->result = CURLE_OK;
+ b->reply_ms = -1;
+}
+
+static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
+{
+ return b->enabled && b->cf && !b->result;
+}
+
+static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
+{
+ return !!b->cf;
+}
+
+static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
+ struct Curl_easy *data)
+{
+ if(b->reply_ms < 0)
+ b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
+ &b->reply_ms, NULL);
+ return b->reply_ms;
+}
+
+static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
+ const struct Curl_easy *data)
+{
+ return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
+}
+
+struct cf_hc_ctx {
+ cf_hc_state state;
+ const struct Curl_dns_entry *remotehost;
+ struct curltime started; /* when connect started */
+ CURLcode result; /* overall result */
+ struct cf_hc_baller h3_baller;
+ struct cf_hc_baller h21_baller;
+ int soft_eyeballs_timeout_ms;
+ int hard_eyeballs_timeout_ms;
+};
+
+static void cf_hc_baller_init(struct cf_hc_baller *b,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *name,
+ int transport)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ struct Curl_cfilter *save = cf->next;
+
+ b->name = name;
+ cf->next = NULL;
+ b->started = Curl_now();
+ b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
+ transport, CURL_CF_SSL_ENABLE);
+ b->cf = cf->next;
+ cf->next = save;
+}
+
+static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
+{
+ struct Curl_cfilter *save = cf->next;
+
+ cf->next = b->cf;
+ b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ b->cf = cf->next; /* it might mutate */
+ cf->next = save;
+ return b->result;
+}
+
+static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
+ if(ctx) {
+ cf_hc_baller_reset(&ctx->h3_baller, data);
+ cf_hc_baller_reset(&ctx->h21_baller, data);
+ ctx->state = CF_HC_INIT;
+ ctx->result = CURLE_OK;
+ ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
+ ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
+ }
+}
+
+static CURLcode baller_connected(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_hc_baller *winner)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(winner->cf);
+ if(winner != &ctx->h3_baller)
+ cf_hc_baller_reset(&ctx->h3_baller, data);
+ if(winner != &ctx->h21_baller)
+ cf_hc_baller_reset(&ctx->h21_baller, data);
+
+ DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
+ winner->name, (int)Curl_timediff(Curl_now(), winner->started),
+ cf_hc_baller_reply_ms(winner, data)));
+ cf->next = winner->cf;
+ winner->cf = NULL;
+
+ switch(cf->conn->alpn) {
+ case CURL_HTTP_VERSION_3:
+ infof(data, "using HTTP/3");
+ break;
+ case CURL_HTTP_VERSION_2:
+#ifdef USE_NGHTTP2
+ /* Using nghttp2, we add the filter "below" us, so when the conn
+ * closes, we tear it down for a fresh reconnect */
+ result = Curl_http2_switch_at(cf, data);
+ if(result) {
+ ctx->state = CF_HC_FAILURE;
+ ctx->result = result;
+ return result;
+ }
+#endif
+ infof(data, "using HTTP/2");
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ infof(data, "using HTTP/1.1");
+ break;
+ default:
+ infof(data, "using HTTP/1.x");
+ break;
+ }
+ ctx->state = CF_HC_SUCCESS;
+ cf->connected = TRUE;
+ Curl_conn_cf_cntrl(cf->next, data, TRUE,
+ CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+ return result;
+}
+
+
+static bool time_to_start_h21(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct curltime now)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ timediff_t elapsed_ms;
+
+ if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller))
+ return FALSE;
+
+ if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller))
+ return TRUE;
+
+ elapsed_ms = Curl_timediff(now, ctx->started);
+ if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
+ DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21",
+ ctx->hard_eyeballs_timeout_ms));
+ return TRUE;
+ }
+
+ if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
+ if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) {
+ DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not "
+ "seen any data, starting h21",
+ ctx->soft_eyeballs_timeout_ms));
+ return TRUE;
+ }
+ /* set the effective hard timeout again */
+ Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
+ EXPIRE_ALPN_EYEBALLS);
+ }
+ return FALSE;
+}
+
+static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ struct curltime now;
+ CURLcode result = CURLE_OK;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+ switch(ctx->state) {
+ case CF_HC_INIT:
+ DEBUGASSERT(!ctx->h3_baller.cf);
+ DEBUGASSERT(!ctx->h21_baller.cf);
+ DEBUGASSERT(!cf->next);
+ DEBUGF(LOG_CF(data, cf, "connect, init"));
+ ctx->started = now;
+ if(ctx->h3_baller.enabled) {
+ cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC);
+ if(ctx->h21_baller.enabled)
+ Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
+ }
+ else if(ctx->h21_baller.enabled)
+ cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
+ cf->conn->transport);
+ ctx->state = CF_HC_CONNECT;
+ /* FALLTHROUGH */
+
+ case CF_HC_CONNECT:
+ if(cf_hc_baller_is_active(&ctx->h3_baller)) {
+ result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done);
+ if(!result && *done) {
+ result = baller_connected(cf, data, &ctx->h3_baller);
+ goto out;
+ }
+ }
+
+ if(time_to_start_h21(cf, data, now)) {
+ cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
+ cf->conn->transport);
+ }
+
+ if(cf_hc_baller_is_active(&ctx->h21_baller)) {
+ DEBUGF(LOG_CF(data, cf, "connect, check h21"));
+ result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done);
+ if(!result && *done) {
+ result = baller_connected(cf, data, &ctx->h21_baller);
+ goto out;
+ }
+ }
+
+ if((!ctx->h3_baller.enabled || ctx->h3_baller.result) &&
+ (!ctx->h21_baller.enabled || ctx->h21_baller.result)) {
+ /* both failed or disabled. we give up */
+ DEBUGF(LOG_CF(data, cf, "connect, all failed"));
+ result = ctx->result = ctx->h3_baller.enabled?
+ ctx->h3_baller.result : ctx->h21_baller.result;
+ ctx->state = CF_HC_FAILURE;
+ goto out;
+ }
+ result = CURLE_OK;
+ *done = FALSE;
+ break;
+
+ case CF_HC_FAILURE:
+ result = ctx->result;
+ cf->connected = FALSE;
+ *done = FALSE;
+ break;
+
+ case CF_HC_SUCCESS:
+ result = CURLE_OK;
+ cf->connected = TRUE;
+ *done = TRUE;
+ break;
+ }
+
+out:
+ DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+ return result;
+}
+
+static int cf_hc_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ size_t i, j, s;
+ int brc, rc = GETSOCK_BLANK;
+ curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE];
+ struct cf_hc_baller *ballers[2];
+
+ if(cf->connected)
+ return cf->next->cft->get_select_socks(cf->next, data, socks);
+
+ ballers[0] = &ctx->h3_baller;
+ ballers[1] = &ctx->h21_baller;
+ for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
+ struct cf_hc_baller *b = ballers[i];
+ if(!cf_hc_baller_is_active(b))
+ continue;
+ brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
+ DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc));
+ if(!brc)
+ continue;
+ for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
+ if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) {
+ socks[s] = bsocks[j];
+ if(brc & GETSOCK_WRITESOCK(j))
+ rc |= GETSOCK_WRITESOCK(s);
+ if(brc & GETSOCK_READSOCK(j))
+ rc |= GETSOCK_READSOCK(s);
+ s++;
+ }
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc));
+ return rc;
+}
+
+static bool cf_hc_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
+ if(cf->connected)
+ return cf->next->cft->has_data_pending(cf->next, data);
+
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
+ return cf_hc_baller_data_pending(&ctx->h3_baller, data)
+ || cf_hc_baller_data_pending(&ctx->h21_baller, data);
+}
+
+static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+ struct Curl_cfilter *cfb;
+ struct curltime t, tmax;
+
+ memset(&tmax, 0, sizeof(tmax));
+ memset(&t, 0, sizeof(t));
+ cfb = ctx->h21_baller.enabled? ctx->h21_baller.cf : NULL;
+ if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
+ if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+ tmax = t;
+ }
+ memset(&t, 0, sizeof(t));
+ cfb = ctx->h3_baller.enabled? ctx->h3_baller.cf : NULL;
+ if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
+ if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+ tmax = t;
+ }
+ return tmax;
+}
+
+static CURLcode cf_hc_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ if(!cf->connected) {
+ switch(query) {
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf_hc_reset(cf, data);
+ cf->connected = FALSE;
+
+ if(cf->next) {
+ cf->next->cft->close(cf->next, data);
+ Curl_conn_cf_discard_chain(&cf->next, data);
+ }
+}
+
+static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_hc_ctx *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ cf_hc_reset(cf, data);
+ Curl_safefree(ctx);
+}
+
+struct Curl_cftype Curl_cft_http_connect = {
+ "HTTPS-CONNECT",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_hc_destroy,
+ cf_hc_connect,
+ cf_hc_close,
+ Curl_cf_def_get_host,
+ cf_hc_get_select_socks,
+ cf_hc_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_hc_query,
+};
+
+static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_hc_ctx *ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->remotehost = remotehost;
+ ctx->h3_baller.enabled = try_h3;
+ ctx->h21_baller.enabled = try_h21;
+
+ result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
+ if(result)
+ goto out;
+ ctx = NULL;
+ cf_hc_reset(cf, data);
+
+out:
+ *pcf = result? NULL : cf;
+ free(ctx);
+ return result;
+}
+
+CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+out:
+ return result;
+}
+
+CURLcode
+Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(data);
+ result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+out:
+ return result;
+}
+
+CURLcode Curl_cf_https_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost)
+{
+ bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */
+ CURLcode result = CURLE_OK;
+
+ (void)sockindex;
+ (void)remotehost;
+
+ if(!conn->bits.tls_enable_alpn)
+ goto out;
+
+ if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+ result = Curl_conn_may_http3(data, conn);
+ if(result) /* can't do it */
+ goto out;
+ try_h3 = TRUE;
+ try_h21 = FALSE;
+ }
+ else if(data->state.httpwant >= CURL_HTTP_VERSION_3) {
+ /* We assume that silently not even trying H3 is ok here */
+ /* TODO: should we fail instead? */
+ try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK);
+ try_h21 = TRUE;
+ }
+
+ result = Curl_cf_http_connect_add(data, conn, sockindex, remotehost,
+ try_h3, try_h21);
+out:
+ return result;
+}
+
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
diff --git a/Utilities/cmcurl/lib/cf-https-connect.h b/Utilities/cmcurl/lib/cf-https-connect.h
new file mode 100644
index 0000000000..6a39527317
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-https-connect.h
@@ -0,0 +1,58 @@
+#ifndef HEADER_CURL_CF_HTTP_H
+#define HEADER_CURL_CF_HTTP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_cftype;
+struct Curl_dns_entry;
+
+extern struct Curl_cftype Curl_cft_http_connect;
+
+CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21);
+
+CURLcode
+Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ bool try_h3, bool try_h21);
+
+
+CURLcode Curl_cf_https_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost);
+
+
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
+#endif /* HEADER_CURL_CF_HTTP_H */
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
new file mode 100644
index 0000000000..6d9ace4261
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -0,0 +1,1921 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* <netinet/tcp.h> may need it */
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h> /* for sockaddr_un */
+#endif
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "if2ip.h"
+#include "strerror.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "select.h"
+#include "url.h" /* for Curl_safefree() */
+#include "multiif.h"
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "inet_ntop.h"
+#include "inet_pton.h"
+#include "progress.h"
+#include "warnless.h"
+#include "conncache.h"
+#include "multihandle.h"
+#include "share.h"
+#include "version_win32.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
+{
+#if defined(TCP_NODELAY)
+ curl_socklen_t onoff = (curl_socklen_t) 1;
+ int level = IPPROTO_TCP;
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+#else
+ (void) data;
+#endif
+
+ if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
+ sizeof(onoff)) < 0)
+ infof(data, "Could not set TCP_NODELAY: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#else
+ (void)data;
+ (void)sockfd;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
+ sending data to a dead peer (instead of relying on the 4th argument to send
+ being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
+ systems? */
+static void nosigpipe(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int onoff = 1;
+ if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
+ sizeof(onoff)) < 0) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char buffer[STRERROR_LEN];
+ infof(data, "Could not set SO_NOSIGPIPE: %s",
+ Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#endif
+ }
+}
+#else
+#define nosigpipe(x,y) Curl_nop_stmt
+#endif
+
+#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
+/* DragonFlyBSD and Windows use millisecond units */
+#define KEEPALIVE_FACTOR(x) (x *= 1000)
+#else
+#define KEEPALIVE_FACTOR(x)
+#endif
+
+#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
+#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
+
+struct tcp_keepalive {
+ u_long onoff;
+ u_long keepalivetime;
+ u_long keepaliveinterval;
+};
+#endif
+
+static void
+tcpkeepalive(struct Curl_easy *data,
+ curl_socket_t sockfd)
+{
+ int optval = data->set.tcp_keepalive?1:0;
+
+ /* only set IDLE and INTVL if setting KEEPALIVE is successful */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
+ }
+ else {
+#if defined(SIO_KEEPALIVE_VALS)
+ struct tcp_keepalive vals;
+ DWORD dummy;
+ vals.onoff = 1;
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepalivetime = optval;
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ vals.keepaliveinterval = optval;
+ if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
+ NULL, 0, &dummy, NULL, NULL) != 0) {
+ infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
+ (int)sockfd, WSAGetLastError());
+ }
+#else
+#ifdef TCP_KEEPIDLE
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
+ }
+#elif defined(TCP_KEEPALIVE)
+ /* Mac OS X style */
+ optval = curlx_sltosi(data->set.tcp_keepidle);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
+ }
+#endif
+#ifdef TCP_KEEPINTVL
+ optval = curlx_sltosi(data->set.tcp_keepintvl);
+ KEEPALIVE_FACTOR(optval);
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void *)&optval, sizeof(optval)) < 0) {
+ infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
+ }
+#endif
+#endif
+ }
+}
+
+void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ /*
+ * The Curl_sockaddr_ex structure is basically libcurl's external API
+ * curl_sockaddr structure with enough space available to directly hold
+ * any protocol-specific address structures. The variable declared here
+ * will be used to pass / receive data to/from the fopensocket callback
+ * if this has been set, before that, it is initialized from parameters.
+ */
+ dest->family = ai->ai_family;
+ switch(transport) {
+ case TRNSPRT_TCP:
+ dest->socktype = SOCK_STREAM;
+ dest->protocol = IPPROTO_TCP;
+ break;
+ case TRNSPRT_UNIX:
+ dest->socktype = SOCK_STREAM;
+ dest->protocol = IPPROTO_IP;
+ break;
+ default: /* UDP and QUIC */
+ dest->socktype = SOCK_DGRAM;
+ dest->protocol = IPPROTO_UDP;
+ break;
+ }
+ dest->addrlen = ai->ai_addrlen;
+
+ if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
+ dest->addrlen = sizeof(struct Curl_sockaddr_storage);
+ memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen);
+}
+
+static CURLcode socket_open(struct Curl_easy *data,
+ struct Curl_sockaddr_ex *addr,
+ curl_socket_t *sockfd)
+{
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ if(data->set.fopensocket) {
+ /*
+ * If the opensocket callback is set, all the destination address
+ * information is passed to the callback. Depending on this information the
+ * callback may opt to abort the connection, this is indicated returning
+ * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
+ * the callback returns a valid socket the destination address information
+ * might have been changed and this 'new' address will actually be used
+ * here to connect.
+ */
+ Curl_set_in_callback(data, true);
+ *sockfd = data->set.fopensocket(data->set.opensocket_client,
+ CURLSOCKTYPE_IPCXN,
+ (struct curl_sockaddr *)addr);
+ Curl_set_in_callback(data, false);
+ }
+ else {
+ /* opensocket callback not set, so simply create the socket now */
+ *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+ }
+
+ if(*sockfd == CURL_SOCKET_BAD)
+ /* no socket, no connection */
+ return CURLE_COULDNT_CONNECT;
+
+#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+ if(data->conn->scope_id && (addr->family == AF_INET6)) {
+ struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
+ sa6->sin6_scope_id = data->conn->scope_id;
+ }
+#endif
+ return CURLE_OK;
+}
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * 'addr' should be a pointer to the correct struct to get data back, or NULL.
+ * 'sockfd' must be a pointer to a socket descriptor.
+ *
+ * If the open socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket_open(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ int transport,
+ curl_socket_t *sockfd)
+{
+ struct Curl_sockaddr_ex dummy;
+
+ if(!addr)
+ /* if the caller doesn't want info back, use a local temp copy */
+ addr = &dummy;
+
+ Curl_sock_assign_addr(addr, ai, transport);
+ return socket_open(data, addr, sockfd);
+}
+
+static int socket_close(struct Curl_easy *data, struct connectdata *conn,
+ int use_callback, curl_socket_t sock)
+{
+ if(use_callback && conn && conn->fclosesocket) {
+ int rc;
+ Curl_multi_closed(data, sock);
+ Curl_set_in_callback(data, true);
+ rc = conn->fclosesocket(conn->closesocket_client, sock);
+ Curl_set_in_callback(data, false);
+ return rc;
+ }
+
+ if(conn)
+ /* tell the multi-socket code about this */
+ Curl_multi_closed(data, sock);
+
+ sclose(sock);
+
+ return 0;
+}
+
+/*
+ * Close a socket.
+ *
+ * 'conn' can be NULL, beware!
+ */
+int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock)
+{
+ return socket_close(data, conn, FALSE, sock);
+}
+
+#ifdef USE_WINSOCK
+/* When you run a program that uses the Windows Sockets API, you may
+ experience slow performance when you copy data to a TCP server.
+
+ https://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+ The problem described in this knowledge-base is applied only to pre-Vista
+ Windows. Following function trying to detect OS version and skips
+ SO_SNDBUF adjustment for Windows Vista and above.
+*/
+#define DETECT_OS_NONE 0
+#define DETECT_OS_PREVISTA 1
+#define DETECT_OS_VISTA_OR_LATER 2
+
+void Curl_sndbufset(curl_socket_t sockfd)
+{
+ int val = CURL_MAX_WRITE_SIZE + 32;
+ int curval = 0;
+ int curlen = sizeof(curval);
+
+ static int detectOsState = DETECT_OS_NONE;
+
+ if(detectOsState == DETECT_OS_NONE) {
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ detectOsState = DETECT_OS_VISTA_OR_LATER;
+ else
+ detectOsState = DETECT_OS_PREVISTA;
+ }
+
+ if(detectOsState == DETECT_OS_VISTA_OR_LATER)
+ return;
+
+ if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
+ if(curval > val)
+ return;
+
+ setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+}
+#endif
+
+static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sockfd, int af, unsigned int scope)
+{
+ struct Curl_sockaddr_storage sa;
+ struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
+ curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
+#endif
+
+ struct Curl_dns_entry *h = NULL;
+ unsigned short port = data->set.localport; /* use this port number, 0 for
+ "random" */
+ /* how many port numbers to try to bind to, increasing one at a time */
+ int portnum = data->set.localportrange;
+ const char *dev = data->set.str[STRING_DEVICE];
+ int error;
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ int on = 1;
+#endif
+#ifndef ENABLE_IPV6
+ (void)scope;
+#endif
+
+ /*************************************************************
+ * Select device to bind socket to
+ *************************************************************/
+ if(!dev && !port)
+ /* no local kind of binding was requested */
+ return CURLE_OK;
+
+ memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
+
+ if(dev && (strlen(dev)<255) ) {
+ char myhost[256] = "";
+ int done = 0; /* -1 for error, 1 for address found */
+ bool is_interface = FALSE;
+ bool is_host = FALSE;
+ static const char *if_prefix = "if!";
+ static const char *host_prefix = "host!";
+
+ if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
+ dev += strlen(if_prefix);
+ is_interface = TRUE;
+ }
+ else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
+ dev += strlen(host_prefix);
+ is_host = TRUE;
+ }
+
+ /* interface */
+ if(!is_host) {
+#ifdef SO_BINDTODEVICE
+ /* I am not sure any other OSs than Linux that provide this feature,
+ * and at the least I cannot test. --Ben
+ *
+ * This feature allows one to tightly bind the local socket to a
+ * particular interface. This will force even requests to other
+ * local interfaces to go out the external interface.
+ *
+ *
+ * Only bind to the interface when specified as interface, not just
+ * as a hostname or ip address.
+ *
+ * interface might be a VRF, eg: vrf-blue, which means it cannot be
+ * converted to an IP address and would fail Curl_if2ip. Simply try
+ * to use it straight away.
+ */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
+ /* This is typically "errno 1, error: Operation not permitted" if
+ * you're not running as root or another suitable privileged
+ * user.
+ * If it succeeds it means the parameter was a valid interface and
+ * not an IP address. Return immediately.
+ */
+ return CURLE_OK;
+ }
+#endif
+
+ switch(Curl_if2ip(af,
+#ifdef ENABLE_IPV6
+ scope, conn->scope_id,
+#endif
+ dev, myhost, sizeof(myhost))) {
+ case IF2IP_NOT_FOUND:
+ if(is_interface) {
+ /* Do not fall back to treating it as a host name */
+ failf(data, "Couldn't bind to interface '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ break;
+ case IF2IP_AF_NOT_SUPPORTED:
+ /* Signal the caller to try another address family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ case IF2IP_FOUND:
+ is_interface = TRUE;
+ /*
+ * We now have the numerical IP address in the 'myhost' buffer
+ */
+ infof(data, "Local Interface %s is ip %s using address family %i",
+ dev, myhost, af);
+ done = 1;
+ break;
+ }
+ }
+ if(!is_interface) {
+ /*
+ * This was not an interface, resolve the name as a host name
+ * or IP number
+ *
+ * Temporarily force name resolution to use only the address type
+ * of the connection. The resolve functions should really be changed
+ * to take a type parameter instead.
+ */
+ unsigned char ipver = conn->ip_version;
+ int rc;
+
+ if(af == AF_INET)
+ conn->ip_version = CURL_IPRESOLVE_V4;
+#ifdef ENABLE_IPV6
+ else if(af == AF_INET6)
+ conn->ip_version = CURL_IPRESOLVE_V6;
+#endif
+
+ rc = Curl_resolv(data, dev, 80, FALSE, &h);
+ if(rc == CURLRESOLV_PENDING)
+ (void)Curl_resolver_wait_resolv(data, &h);
+ conn->ip_version = ipver;
+
+ if(h) {
+ /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
+ Curl_printable_address(h->addr, myhost, sizeof(myhost));
+ infof(data, "Name '%s' family %i resolved to '%s' family %i",
+ dev, af, myhost, h->addr->ai_family);
+ Curl_resolv_unlock(data, h);
+ if(af != h->addr->ai_family) {
+ /* bad IP version combo, signal the caller to try another address
+ family if available */
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ done = 1;
+ }
+ else {
+ /*
+ * provided dev was no interface (or interfaces are not supported
+ * e.g. solaris) no ip address and no domain we fail here
+ */
+ done = -1;
+ }
+ }
+
+ if(done > 0) {
+#ifdef ENABLE_IPV6
+ /* IPv6 address */
+ if(af == AF_INET6) {
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ char *scope_ptr = strchr(myhost, '%');
+ if(scope_ptr)
+ *(scope_ptr++) = '\0';
+#endif
+ if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
+ if(scope_ptr) {
+ /* The "myhost" string either comes from Curl_if2ip or from
+ Curl_printable_address. The latter returns only numeric scope
+ IDs and the former returns none at all. So the scope ID, if
+ present, is known to be numeric */
+ unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
+ if(scope_id > UINT_MAX)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ si6->sin6_scope_id = (unsigned int)scope_id;
+ }
+#endif
+ }
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ /* IPv4 address */
+ if((af == AF_INET) &&
+ (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+
+ if(done < 1) {
+ /* errorbuf is set false so failf will overwrite any message already in
+ the error buffer, so the user receives this error message instead of a
+ generic resolve error. */
+ data->state.errorbuf = FALSE;
+ failf(data, "Couldn't bind to '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }
+ }
+ else {
+ /* no device was given, prepare sa to match af's needs */
+#ifdef ENABLE_IPV6
+ if(af == AF_INET6) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ }
+ else
+#endif
+ if(af == AF_INET) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ }
+ }
+#ifdef IP_BIND_ADDRESS_NO_PORT
+ (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
+#endif
+ for(;;) {
+ if(bind(sockfd, sock, sizeof_sa) >= 0) {
+ /* we succeeded to bind */
+ struct Curl_sockaddr_storage add;
+ curl_socklen_t size = sizeof(add);
+ memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
+ if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return CURLE_INTERFACE_FAILED;
+ }
+ infof(data, "Local port: %hu", port);
+ conn->bits.bound = TRUE;
+ return CURLE_OK;
+ }
+
+ if(--portnum > 0) {
+ port++; /* try next port */
+ if(port == 0)
+ break;
+ infof(data, "Bind to local port %hu failed, trying next", port - 1);
+ /* We re-use/clobber the port variable here below */
+ if(sock->sa_family == AF_INET)
+ si4->sin_port = ntohs(port);
+#ifdef ENABLE_IPV6
+ else
+ si6->sin6_port = ntohs(port);
+#endif
+ }
+ else
+ break;
+ }
+ {
+ char buffer[STRERROR_LEN];
+ data->state.os_errno = error = SOCKERRNO;
+ failf(data, "bind failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ }
+
+ return CURLE_INTERFACE_FAILED;
+}
+
+/*
+ * verifyconnect() returns TRUE if the connect really has happened.
+ */
+static bool verifyconnect(curl_socket_t sockfd, int *error)
+{
+ bool rc = TRUE;
+#ifdef SO_ERROR
+ int err = 0;
+ curl_socklen_t errSize = sizeof(err);
+
+#ifdef WIN32
+ /*
+ * In October 2003 we effectively nullified this function on Windows due to
+ * problems with it using all CPU in multi-threaded cases.
+ *
+ * In May 2004, we bring it back to offer more info back on connect failures.
+ * Gisle Vanem could reproduce the former problems with this function, but
+ * could avoid them by adding this SleepEx() call below:
+ *
+ * "I don't have Rational Quantify, but the hint from his post was
+ * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
+ * just Sleep(0) would be enough?) would release whatever
+ * mutex/critical-section the ntdll call is waiting on.
+ *
+ * Someone got to verify this on Win-NT 4.0, 2000."
+ */
+
+#ifdef _WIN32_WCE
+ Sleep(0);
+#else
+ SleepEx(0, FALSE);
+#endif
+
+#endif
+
+ if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
+ err = SOCKERRNO;
+#ifdef _WIN32_WCE
+ /* Old WinCE versions don't support SO_ERROR */
+ if(WSAENOPROTOOPT == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+#if defined(EBADIOCTL) && defined(__minix)
+ /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
+ if(EBADIOCTL == err) {
+ SET_SOCKERRNO(0);
+ err = 0;
+ }
+#endif
+ if((0 == err) || (EISCONN == err))
+ /* we are connected, awesome! */
+ rc = TRUE;
+ else
+ /* This wasn't a successful connect */
+ rc = FALSE;
+ if(error)
+ *error = err;
+#else
+ (void)sockfd;
+ if(error)
+ *error = SOCKERRNO;
+#endif
+ return rc;
+}
+
+CURLcode Curl_socket_connect_result(struct Curl_easy *data,
+ const char *ipaddress, int error)
+{
+ char buffer[STRERROR_LEN];
+
+ switch(error) {
+ case EINPROGRESS:
+ case EWOULDBLOCK:
+#if defined(EAGAIN)
+#if (EAGAIN) != (EWOULDBLOCK)
+ /* On some platforms EAGAIN and EWOULDBLOCK are the
+ * same value, and on others they are different, hence
+ * the odd #if
+ */
+ case EAGAIN:
+#endif
+#endif
+ return CURLE_OK;
+
+ default:
+ /* unknown error, fallthrough and try another address! */
+ infof(data, "Immediate connect fail for %s: %s",
+ ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+ data->state.os_errno = error;
+ /* connect failed */
+ return CURLE_COULDNT_CONNECT;
+ }
+}
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+struct io_buffer {
+ char *bufr;
+ size_t allc; /* size of the current allocation */
+ size_t head; /* bufr index for next read */
+ size_t tail; /* bufr index for next write */
+};
+
+static void io_buffer_reset(struct io_buffer *iob)
+{
+ if(iob->bufr)
+ free(iob->bufr);
+ memset(iob, 0, sizeof(*iob));
+}
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
+struct cf_socket_ctx {
+ int transport;
+ struct Curl_sockaddr_ex addr; /* address to connect to */
+ curl_socket_t sock; /* current attempt socket */
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ struct io_buffer recv_buffer;
+#endif
+ char r_ip[MAX_IPADR_LEN]; /* remote IP as string */
+ int r_port; /* remote port number */
+ char l_ip[MAX_IPADR_LEN]; /* local IP as string */
+ int l_port; /* local port number */
+ struct curltime started_at; /* when socket was created */
+ struct curltime connected_at; /* when socket connected/got first byte */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ int error; /* errno of last failure or 0 */
+ BIT(got_first_byte); /* if first byte was received */
+ BIT(accepted); /* socket was accepted, not connected */
+ BIT(active);
+};
+
+static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->sock = CURL_SOCKET_BAD;
+ ctx->transport = transport;
+ Curl_sock_assign_addr(&ctx->addr, ai, transport);
+}
+
+static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ if(ctx && CURL_SOCKET_BAD != ctx->sock) {
+ if(ctx->active) {
+ /* We share our socket at cf->conn->sock[cf->sockindex] when active.
+ * If it is no longer there, someone has stolen (and hopefully
+ * closed it) and we just forget about it.
+ */
+ if(ctx->sock == cf->conn->sock[cf->sockindex]) {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, active)",
+ (int)ctx->sock));
+ socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
+ cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
+ "conn->sock[], discarding", (int)ctx->sock));
+ /* TODO: we do not want this to happen. Need to check which
+ * code is messing with conn->sock[cf->sockindex] */
+ }
+ ctx->sock = CURL_SOCKET_BAD;
+ if(cf->sockindex == FIRSTSOCKET)
+ cf->conn->remote_addr = NULL;
+ }
+ else {
+ /* this is our local socket, we did never publish it */
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d, not active)",
+ (int)ctx->sock));
+ sclose(ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ io_buffer_reset(&ctx->recv_buffer);
+#endif
+ ctx->active = FALSE;
+ memset(&ctx->started_at, 0, sizeof(ctx->started_at));
+ memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
+ }
+
+ cf->connected = FALSE;
+}
+
+static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ cf_socket_close(cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ free(ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode set_local_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+#ifdef HAVE_GETSOCKNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssloc;
+ curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+
+ memset(&ssloc, 0, sizeof(ssloc));
+ if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
+ int error = SOCKERRNO;
+ failf(data, "getsockname() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+ ctx->l_ip, &ctx->l_port)) {
+ failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+#else
+ (void)data;
+ ctx->l_ip[0] = 0;
+ ctx->l_port = -1;
+#endif
+ return CURLE_OK;
+}
+
+static CURLcode set_remote_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ /* store remote address and port used in this connection attempt */
+ if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
+ ctx->r_ip, &ctx->r_port)) {
+ char buffer[STRERROR_LEN];
+
+ ctx->error = errno;
+ /* malformed address or bug in inet_ntop, try next address */
+ failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_FAILED_INIT;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_socket_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int error = 0;
+ bool isconnected = FALSE;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+ bool is_tcp;
+ const char *ipmsg;
+
+ (void)data;
+ DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
+ ctx->started_at = Curl_now();
+ result = socket_open(data, &ctx->addr, &ctx->sock);
+ if(result)
+ goto out;
+
+ result = set_remote_ip(cf, data);
+ if(result)
+ goto out;
+
+#ifdef ENABLE_IPV6
+ if(ctx->addr.family == AF_INET6)
+ ipmsg = " Trying [%s]:%d...";
+ else
+#endif
+ ipmsg = " Trying %s:%d...";
+ infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+
+#ifdef ENABLE_IPV6
+ is_tcp = (ctx->addr.family == AF_INET
+ || ctx->addr.family == AF_INET6) &&
+ ctx->addr.socktype == SOCK_STREAM;
+#else
+ is_tcp = (ctx->addr.family == AF_INET) &&
+ ctx->addr.socktype == SOCK_STREAM;
+#endif
+ if(is_tcp && data->set.tcp_nodelay)
+ tcpnodelay(data, ctx->sock);
+
+ nosigpipe(data, ctx->sock);
+
+ Curl_sndbufset(ctx->sock);
+
+ if(is_tcp && data->set.tcp_keepalive)
+ tcpkeepalive(data, ctx->sock);
+
+ if(data->set.fsockopt) {
+ /* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
+ error = data->set.fsockopt(data->set.sockopt_client,
+ ctx->sock,
+ CURLSOCKTYPE_IPCXN);
+ Curl_set_in_callback(data, false);
+
+ if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
+ isconnected = TRUE;
+ else if(error) {
+ result = CURLE_ABORTED_BY_CALLBACK;
+ goto out;
+ }
+ }
+
+ /* possibly bind the local end to an IP, interface or port */
+ if(ctx->addr.family == AF_INET
+#ifdef ENABLE_IPV6
+ || ctx->addr.family == AF_INET6
+#endif
+ ) {
+ result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
+ Curl_ipv6_scope(&ctx->addr.sa_addr));
+ if(result) {
+ if(result == CURLE_UNSUPPORTED_PROTOCOL) {
+ /* The address family is not supported on this interface.
+ We can continue trying addresses */
+ result = CURLE_COULDNT_CONNECT;
+ }
+ goto out;
+ }
+ }
+
+ /* set socket non-blocking */
+ (void)curlx_nonblock(ctx->sock, TRUE);
+
+out:
+ if(result) {
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ }
+ else if(isconnected) {
+ set_local_ip(cf, data);
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ }
+ DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%d", result, ctx->sock));
+ return result;
+}
+
+static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool is_tcp_fastopen)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+#ifdef TCP_FASTOPEN_CONNECT
+ int optval = 1;
+#endif
+ int rc = -1;
+
+ (void)data;
+ if(is_tcp_fastopen) {
+#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
+# if defined(HAVE_BUILTIN_AVAILABLE)
+ /* while connectx function is available since macOS 10.11 / iOS 9,
+ it did not have the interface declared correctly until
+ Xcode 9 / macOS SDK 10.13 */
+ if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
+ sa_endpoints_t endpoints;
+ endpoints.sae_srcif = 0;
+ endpoints.sae_srcaddr = NULL;
+ endpoints.sae_srcaddrlen = 0;
+ endpoints.sae_dstaddr = &ctx->addr.sa_addr;
+ endpoints.sae_dstaddrlen = ctx->addr.addrlen;
+
+ rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
+ CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
+ NULL, 0, NULL, NULL);
+ }
+ else {
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ }
+# else
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+# endif /* HAVE_BUILTIN_AVAILABLE */
+#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
+ if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
+ (void *)&optval, sizeof(optval)) < 0)
+ infof(data, "Failed to enable TCP Fast Open on fd %d", ctx->sock);
+
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+#elif defined(MSG_FASTOPEN) /* old Linux */
+ if(cf->conn->given->flags & PROTOPT_SSL)
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ else
+ rc = 0; /* Do nothing */
+#endif
+ }
+ else {
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ }
+ return rc;
+}
+
+static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+ int rc = 0;
+
+ (void)data;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* TODO: need to support blocking connect? */
+ if(blocking)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ *done = FALSE; /* a very negative world view is best */
+ if(ctx->sock == CURL_SOCKET_BAD) {
+
+ result = cf_socket_open(cf, data);
+ if(result)
+ goto out;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect TCP socket */
+ rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
+ if(-1 == rc) {
+ result = Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+ goto out;
+ }
+ }
+
+#ifdef mpeix
+ /* Call this function once now, and ignore the results. We do this to
+ "clear" the error state on the socket so that we can later read it
+ reliably. This is reported necessary on the MPE/iX operating
+ system. */
+ (void)verifyconnect(ctx->sock, NULL);
+#endif
+ /* check socket for connect */
+ rc = SOCKET_WRITABLE(ctx->sock, 0);
+
+ if(rc == 0) { /* no connection yet */
+ DEBUGF(LOG_CF(data, cf, "not connected yet"));
+ return CURLE_OK;
+ }
+ else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
+ if(verifyconnect(ctx->sock, &ctx->error)) {
+ /* we are connected with TCP, awesome! */
+ ctx->connected_at = Curl_now();
+ set_local_ip(cf, data);
+ *done = TRUE;
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "connected"));
+ return CURLE_OK;
+ }
+ }
+ else if(rc & CURL_CSELECT_ERR) {
+ (void)verifyconnect(ctx->sock, &ctx->error);
+ result = CURLE_COULDNT_CONNECT;
+ }
+
+out:
+ if(result) {
+ if(ctx->error) {
+ data->state.os_errno = ctx->error;
+ SET_SOCKERRNO(ctx->error);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ {
+ char buffer[STRERROR_LEN];
+ infof(data, "connect to %s port %u failed: %s",
+ ctx->r_ip, ctx->r_port,
+ Curl_strerror(ctx->error, buffer, sizeof(buffer)));
+ }
+#endif
+ }
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ *done = FALSE;
+ }
+ return result;
+}
+
+static void cf_socket_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+}
+
+static int cf_socket_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int rc = GETSOCK_BLANK;
+
+ (void)data;
+ if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) {
+ socks[0] = ctx->sock;
+ rc |= GETSOCK_WRITESOCK(0);
+ }
+
+ return rc;
+}
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+
+static CURLcode pre_receive_plain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ struct io_buffer * const iob = &ctx->recv_buffer;
+
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. However, skip this, if buffer is already full. */
+ if((cf->conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
+ cf->conn->recv[cf->sockindex] == Curl_conn_recv &&
+ (!iob->bufr || (iob->allc > iob->tail))) {
+ const int readymask = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD,
+ CURL_SOCKET_BAD, 0);
+ if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
+ size_t bytestorecv = iob->allc - iob->tail;
+ ssize_t nread;
+ /* Have some incoming data */
+ if(!iob->bufr) {
+ /* Use buffer double default size for intermediate buffer */
+ iob->allc = 2 * data->set.buffer_size;
+ iob->bufr = malloc(iob->allc);
+ if(!iob->bufr)
+ return CURLE_OUT_OF_MEMORY;
+ iob->tail = 0;
+ iob->head = 0;
+ bytestorecv = iob->allc;
+ }
+
+ nread = sread(ctx->sock, iob->bufr + iob->tail, bytestorecv);
+ if(nread > 0)
+ iob->tail += (size_t)nread;
+ }
+ }
+ return CURLE_OK;
+}
+
+static ssize_t get_pre_recved(struct Curl_cfilter *cf, char *buf, size_t len)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ struct io_buffer * const iob = &ctx->recv_buffer;
+ size_t copysize;
+ if(!iob->bufr)
+ return 0;
+
+ DEBUGASSERT(iob->allc > 0);
+ DEBUGASSERT(iob->tail <= iob->allc);
+ DEBUGASSERT(iob->head <= iob->tail);
+ /* Check and process data that already received and storied in internal
+ intermediate buffer */
+ if(iob->tail > iob->head) {
+ copysize = CURLMIN(len, iob->tail - iob->head);
+ memcpy(buf, iob->bufr + iob->head, copysize);
+ iob->head += copysize;
+ }
+ else
+ copysize = 0; /* buffer was allocated, but nothing was received */
+
+ /* Free intermediate buffer if it has no unprocessed data */
+ if(iob->head == iob->tail)
+ io_buffer_reset(iob);
+
+ return (ssize_t)copysize;
+}
+#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
+
+static bool cf_socket_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int readable;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ if(ctx->recv_buffer.bufr && ctx->recv_buffer.allc &&
+ ctx->recv_buffer.tail > ctx->recv_buffer.head)
+ return TRUE;
+#endif
+
+ (void)data;
+ readable = SOCKET_READABLE(ctx->sock, 0);
+ return (readable > 0 && (readable & CURL_CSELECT_IN));
+}
+
+static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ curl_socket_t fdsave;
+ ssize_t nwritten;
+
+ *err = CURLE_OK;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ /* WinSock will destroy unread received data if send() is
+ failed.
+ To avoid lossage of received data, recv() must be
+ performed before every send() if any incoming data is
+ available. */
+ if(pre_receive_plain(cf, data)) {
+ *err = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+#endif
+
+ fdsave = cf->conn->sock[cf->sockindex];
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+
+#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
+ if(cf->conn->bits.tcp_fastopen) {
+ nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
+ &cf->conn->remote_addr->sa_addr,
+ cf->conn->remote_addr->addrlen);
+ cf->conn->bits.tcp_fastopen = FALSE;
+ }
+ else
+#endif
+ nwritten = swrite(ctx->sock, buf, len);
+
+ if(-1 == nwritten) {
+ int sockerr = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == sockerr)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+ due to its inability to send off data without blocking. We therefore
+ treat both error codes the same here */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
+ (EINPROGRESS == sockerr)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *err = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Send failure: %s",
+ Curl_strerror(sockerr, buffer, sizeof(buffer)));
+ data->state.os_errno = sockerr;
+ *err = CURLE_SEND_ERROR;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d",
+ len, (int)nwritten, *err));
+ cf->conn->sock[cf->sockindex] = fdsave;
+ return nwritten;
+}
+
+static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ curl_socket_t fdsave;
+ ssize_t nread;
+
+ *err = CURLE_OK;
+
+#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
+ /* Check and return data that already received and storied in internal
+ intermediate buffer */
+ nread = get_pre_recved(cf, buf, len);
+ if(nread > 0) {
+ *err = CURLE_OK;
+ return nread;
+ }
+#endif
+
+ fdsave = cf->conn->sock[cf->sockindex];
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+
+ nread = sread(ctx->sock, buf, len);
+
+ if(-1 == nread) {
+ int sockerr = SOCKERRNO;
+
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == sockerr)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
+ due to its inability to send off data without blocking. We therefore
+ treat both error codes the same here */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
+#endif
+ ) {
+ /* this is just a case of EWOULDBLOCK */
+ *err = CURLE_AGAIN;
+ }
+ else {
+ char buffer[STRERROR_LEN];
+ failf(data, "Recv failure: %s",
+ Curl_strerror(sockerr, buffer, sizeof(buffer)));
+ data->state.os_errno = sockerr;
+ *err = CURLE_RECV_ERROR;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
+ *err));
+ if(nread > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+ cf->conn->sock[cf->sockindex] = fdsave;
+ return nread;
+}
+
+static void conn_set_primary_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+#ifdef HAVE_GETPEERNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssrem;
+ curl_socklen_t plen;
+ int port;
+
+ plen = sizeof(ssrem);
+ memset(&ssrem, 0, plen);
+ if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
+ int error = SOCKERRNO;
+ failf(data, "getpeername() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+ cf->conn->primary_ip, &port)) {
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return;
+ }
+#else
+ cf->conn->primary_ip[0] = 0;
+ (void)data;
+#endif
+}
+
+static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ /* use this socket from now on */
+ cf->conn->sock[cf->sockindex] = ctx->sock;
+ /* the first socket info gets set at conn and data */
+ if(cf->sockindex == FIRSTSOCKET) {
+ cf->conn->remote_addr = &ctx->addr;
+ #ifdef ENABLE_IPV6
+ cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
+ #endif
+ conn_set_primary_ip(cf, data);
+ set_local_ip(cf, data);
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ }
+ ctx->active = TRUE;
+}
+
+static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_CONN_INFO_UPDATE:
+ cf_socket_active(cf, data);
+ break;
+ case CF_CTRL_DATA_SETUP:
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ break;
+ }
+ return CURLE_OK;
+}
+
+static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ struct pollfd pfd[1];
+ int r;
+
+ *input_pending = FALSE;
+ (void)data;
+ if(!ctx || ctx->sock == CURL_SOCKET_BAD)
+ return FALSE;
+
+ /* Check with 0 timeout if there are any events pending on the socket */
+ pfd[0].fd = ctx->sock;
+ pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
+ pfd[0].revents = 0;
+
+ r = Curl_poll(pfd, 1, 0);
+ if(r < 0) {
+ DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead"));
+ return FALSE;
+ }
+ else if(r == 0) {
+ DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive"));
+ return TRUE;
+ }
+ else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
+ DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead"));
+ return FALSE;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive"));
+ *input_pending = TRUE;
+ return TRUE;
+}
+
+static CURLcode cf_socket_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_SOCKET:
+ DEBUGASSERT(pres2);
+ *((curl_socket_t *)pres2) = ctx->sock;
+ return CURLE_OK;
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ switch(ctx->transport) {
+ case TRNSPRT_UDP:
+ case TRNSPRT_QUIC:
+ /* Since UDP connected sockets work different from TCP, we use the
+ * time of the first byte from the peer as the "connect" time. */
+ if(ctx->got_first_byte) {
+ *when = ctx->first_byte_at;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ *when = ctx->connected_at;
+ break;
+ }
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+struct Curl_cftype Curl_cft_tcp = {
+ "TCP",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_TCP);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ int rc;
+
+ /* QUIC needs a connected socket, nonblocking */
+ DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
+
+ rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ if(-1 == rc) {
+ return Curl_socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+ }
+ set_local_ip(cf, data);
+ DEBUGF(LOG_CF(data, cf, "%s socket %d connected: [%s:%d] -> [%s:%d]",
+ (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
+ ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port));
+
+ (void)curlx_nonblock(ctx->sock, TRUE);
+ switch(ctx->addr.family) {
+#if defined(__linux__) && defined(IP_MTU_DISCOVER)
+ case AF_INET: {
+ int val = IP_PMTUDISC_DO;
+ (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
+ case AF_INET6: {
+ int val = IPV6_PMTUDISC_DO;
+ (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
+ sizeof(val));
+ break;
+ }
+#endif
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_COULDNT_CONNECT;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ *done = FALSE;
+ if(ctx->sock == CURL_SOCKET_BAD) {
+ result = cf_socket_open(cf, data);
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result));
+ if(ctx->sock != CURL_SOCKET_BAD) {
+ socket_close(data, cf->conn, TRUE, ctx->sock);
+ ctx->sock = CURL_SOCKET_BAD;
+ }
+ goto out;
+ }
+
+ if(ctx->transport == TRNSPRT_QUIC) {
+ result = cf_udp_setup_quic(cf, data);
+ if(result)
+ goto out;
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d (%s:%d)",
+ ctx->sock, ctx->l_ip, ctx->l_port));
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%d "
+ "(unconnected)", ctx->sock));
+ }
+ *done = TRUE;
+ cf->connected = TRUE;
+ }
+out:
+ return result;
+}
+
+struct Curl_cftype Curl_cft_udp = {
+ "UDP",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_udp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+/* this is the TCP filter which can also handle this case */
+struct Curl_cftype Curl_cft_unix = {
+ "UNIX",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_connect,
+ cf_socket_close,
+ cf_socket_get_host,
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ struct cf_socket_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ DEBUGASSERT(transport == TRNSPRT_UNIX);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_socket_ctx_init(ctx, ai, transport);
+
+ result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+struct Curl_cftype Curl_cft_tcp_accept = {
+ "TCP-ACCEPT",
+ CF_TYPE_IP_CONNECT,
+ CURL_LOG_DEFAULT,
+ cf_socket_destroy,
+ cf_tcp_accept_connect,
+ cf_socket_close,
+ cf_socket_get_host, /* TODO: not accurate */
+ cf_socket_get_select_socks,
+ cf_socket_data_pending,
+ cf_socket_send,
+ cf_socket_recv,
+ cf_socket_cntrl,
+ cf_socket_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_socket_query,
+};
+
+CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct cf_socket_ctx *ctx = NULL;
+
+ /* replace any existing */
+ Curl_conn_cf_discard_all(data, conn, sockindex);
+ DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
+
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->transport = conn->transport;
+ ctx->sock = *s;
+ ctx->accepted = FALSE;
+ result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+ conn->sock[sockindex] = ctx->sock;
+ set_local_ip(cf, data);
+ ctx->active = TRUE;
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%d)", (int)ctx->sock));
+
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+ return result;
+}
+
+static void set_accepted_remote_ip(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_socket_ctx *ctx = cf->ctx;
+#ifdef HAVE_GETPEERNAME
+ char buffer[STRERROR_LEN];
+ struct Curl_sockaddr_storage ssrem;
+ curl_socklen_t plen;
+
+ ctx->r_ip[0] = 0;
+ ctx->r_port = 0;
+ plen = sizeof(ssrem);
+ memset(&ssrem, 0, plen);
+ if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
+ int error = SOCKERRNO;
+ failf(data, "getpeername() failed with errno %d: %s",
+ error, Curl_strerror(error, buffer, sizeof(buffer)));
+ return;
+ }
+ if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+ ctx->r_ip, &ctx->r_port)) {
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return;
+ }
+#else
+ ctx->r_ip[0] = 0;
+ ctx->r_port = 0;
+ (void)data;
+#endif
+}
+
+CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_socket_ctx *ctx = NULL;
+
+ cf = conn->cfilter[sockindex];
+ if(!cf || cf->cft != &Curl_cft_tcp_accept)
+ return CURLE_FAILED_INIT;
+
+ ctx = cf->ctx;
+ /* discard the listen socket */
+ socket_close(data, conn, TRUE, ctx->sock);
+ ctx->sock = *s;
+ conn->sock[sockindex] = ctx->sock;
+ set_accepted_remote_ip(cf, data);
+ set_local_ip(cf, data);
+ ctx->active = TRUE;
+ ctx->accepted = TRUE;
+ ctx->connected_at = Curl_now();
+ cf->connected = TRUE;
+ DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%d, remote=%s port=%d)",
+ (int)ctx->sock, ctx->r_ip, ctx->r_port));
+
+ return CURLE_OK;
+}
+
+bool Curl_cf_is_socket(struct Curl_cfilter *cf)
+{
+ return cf && (cf->cft == &Curl_cft_tcp ||
+ cf->cft == &Curl_cft_udp ||
+ cf->cft == &Curl_cft_unix ||
+ cf->cft == &Curl_cft_tcp_accept);
+}
+
+CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *psock,
+ const struct Curl_sockaddr_ex **paddr,
+ const char **pr_ip_str, int *pr_port,
+ const char **pl_ip_str, int *pl_port)
+{
+ if(Curl_cf_is_socket(cf) && cf->ctx) {
+ struct cf_socket_ctx *ctx = cf->ctx;
+
+ if(psock)
+ *psock = ctx->sock;
+ if(paddr)
+ *paddr = &ctx->addr;
+ if(pr_ip_str)
+ *pr_ip_str = ctx->r_ip;
+ if(pr_port)
+ *pr_port = ctx->r_port;
+ if(pl_port ||pl_ip_str) {
+ set_local_ip(cf, data);
+ if(pl_ip_str)
+ *pl_ip_str = ctx->l_ip;
+ if(pl_port)
+ *pl_port = ctx->l_port;
+ }
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h
new file mode 100644
index 0000000000..0eec61adb7
--- /dev/null
+++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -0,0 +1,185 @@
+#ifndef HEADER_CURL_CF_SOCKET_H
+#define HEADER_CURL_CF_SOCKET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "sockaddr.h"
+
+struct Curl_addrinfo;
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_sockaddr_ex;
+
+/*
+ * The Curl_sockaddr_ex structure is basically libcurl's external API
+ * curl_sockaddr structure with enough space available to directly hold any
+ * protocol-specific address structures. The variable declared here will be
+ * used to pass / receive data to/from the fopensocket callback if this has
+ * been set, before that, it is initialized from parameters.
+ */
+struct Curl_sockaddr_ex {
+ int family;
+ int socktype;
+ int protocol;
+ unsigned int addrlen;
+ union {
+ struct sockaddr addr;
+ struct Curl_sockaddr_storage buff;
+ } _sa_ex_u;
+};
+#define sa_addr _sa_ex_u.addr
+
+
+/*
+ * Create a socket based on info from 'conn' and 'ai'.
+ *
+ * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
+ * socket callback is set, used that!
+ *
+ */
+CURLcode Curl_socket_open(struct Curl_easy *data,
+ const struct Curl_addrinfo *ai,
+ struct Curl_sockaddr_ex *addr,
+ int transport,
+ curl_socket_t *sockfd);
+
+int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
+ curl_socket_t sock);
+
+/**
+ * Determine the curl code for a socket connect() == -1 with errno.
+ */
+CURLcode Curl_socket_connect_result(struct Curl_easy *data,
+ const char *ipaddress, int error);
+
+#ifdef USE_WINSOCK
+/* When you run a program that uses the Windows Sockets API, you may
+ experience slow performance when you copy data to a TCP server.
+
+ https://support.microsoft.com/kb/823764
+
+ Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
+ Buffer Size
+
+*/
+void Curl_sndbufset(curl_socket_t sockfd);
+#else
+#define Curl_sndbufset(y) Curl_nop_stmt
+#endif
+
+/**
+ * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
+ * set the transport used.
+ */
+void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a TCP socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a UDP socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that opens a UNIX socket to the given address
+ * when calling its `connect` implementation.
+ * The filter will not touch any connection/data flags and can be
+ * used in happy eyeballing. Once selected for use, its `_active()`
+ * method needs to be called.
+ */
+CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Creates a cfilter that keeps a listening socket.
+ */
+CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ curl_socket_t *s);
+
+/**
+ * Replace the listen socket with the accept()ed one.
+ */
+CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ curl_socket_t *s);
+
+/**
+ * Return TRUE iff `cf` is a socket filter.
+ */
+bool Curl_cf_is_socket(struct Curl_cfilter *cf);
+
+/**
+ * Peek at the socket and remote ip/port the socket filter is using.
+ * The filter owns all returned values.
+ * @param psock pointer to hold socket descriptor or NULL
+ * @param paddr pointer to hold addr reference or NULL
+ * @param pr_ip_str pointer to hold remote addr as string or NULL
+ * @param pr_port pointer to hold remote port number or NULL
+ * @param pl_ip_str pointer to hold local addr as string or NULL
+ * @param pl_port pointer to hold local port number or NULL
+ * Returns error if the filter is of invalid type.
+ */
+CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *psock,
+ const struct Curl_sockaddr_ex **paddr,
+ const char **pr_ip_str, int *pr_port,
+ const char **pl_ip_str, int *pl_port);
+
+extern struct Curl_cftype Curl_cft_tcp;
+extern struct Curl_cftype Curl_cft_udp;
+extern struct Curl_cftype Curl_cft_unix;
+extern struct Curl_cftype Curl_cft_tcp_accept;
+
+#endif /* HEADER_CURL_CF_SOCKET_H */
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index bcb33da771..e60d1386e6 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -34,9 +34,6 @@
#include "multiif.h"
#include "progress.h"
#include "warnless.h"
-#include "http_proxy.h"
-#include "socks.h"
-#include "vtls/vtls.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -54,89 +51,117 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data)
(void)data;
}
-CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
-{
- DEBUGASSERT(cf->next);
- return cf->next->cft->setup(cf->next, data, remotehost);
-}
-
-void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
-}
-
-void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
-}
-
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- DEBUGASSERT(cf->next);
cf->connected = FALSE;
- cf->next->cft->close(cf->next, data);
+ if(cf->next)
+ cf->next->cft->close(cf->next, data);
}
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->connect(cf->next, data, blocking, done);
+ CURLcode result;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ if(cf->next) {
+ result = cf->next->cft->connect(cf->next, data, blocking, done);
+ if(!result && *done) {
+ cf->connected = TRUE;
+ }
+ return result;
+ }
+ *done = FALSE;
+ return CURLE_FAILED_INIT;
}
void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
const char **phost, const char **pdisplay_host,
int *pport)
{
- DEBUGASSERT(cf->next);
- cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ if(cf->next)
+ cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+ else {
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+ }
}
int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->get_select_socks(cf->next, data, socks);
+ return cf->next?
+ cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
}
bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->has_data_pending(cf->next, data);
+ return cf->next?
+ cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->do_send(cf->next, data, buf, len, err);
+ return cf->next?
+ cf->next->cft->do_send(cf->next, data, buf, len, err) :
+ CURLE_RECV_ERROR;
}
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
- DEBUGASSERT(cf->next);
- return cf->next->cft->do_recv(cf->next, data, buf, len, err);
+ return cf->next?
+ cf->next->cft->do_recv(cf->next, data, buf, len, err) :
+ CURLE_SEND_ERROR;
}
-void Curl_conn_cf_discard_all(struct Curl_easy *data,
- struct connectdata *conn, int index)
+bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
{
- struct Curl_cfilter *cfn, *cf = conn->cfilter[index];
+ return cf->next?
+ cf->next->cft->is_alive(cf->next, data, input_pending) :
+ FALSE; /* pessimistic in absence of data */
+}
+
+CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ return cf->next?
+ cf->next->cft->keep_alive(cf->next, data) :
+ CURLE_OK;
+}
+
+CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cfn, *cf = *pcf;
if(cf) {
- conn->cfilter[index] = NULL;
+ *pcf = NULL;
while(cf) {
cfn = cf->next;
+ /* prevent destroying filter to mess with its sub-chain, since
+ * we have the reference now and will call destroy on it.
+ */
+ cf->next = NULL;
cf->cft->destroy(cf, data);
free(cf);
cf = cfn;
@@ -144,6 +169,12 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
}
}
+void Curl_conn_cf_discard_all(struct Curl_easy *data,
+ struct connectdata *conn, int index)
+{
+ Curl_conn_cf_discard_chain(&conn->cfilter[index], data);
+}
+
void Curl_conn_close(struct Curl_easy *data, int index)
{
struct Curl_cfilter *cf;
@@ -160,7 +191,6 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
size_t len, CURLcode *code)
{
struct Curl_cfilter *cf;
- ssize_t nread;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
@@ -169,13 +199,9 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
cf = cf->next;
}
if(cf) {
- nread = cf->cft->do_recv(cf, data, buf, len, code);
- /* DEBUGF(infof(data, "Curl_conn_recv(handle=%p, index=%d)"
- "-> %ld, err=%d", data, num, nread, *code));*/
- return nread;
+ return cf->cft->do_recv(cf, data, buf, len, code);
}
- failf(data, "no filter connected, conn=%ld, sockindex=%d",
- data->conn->connection_id, num);
+ failf(data, CMSGI(data->conn, num, "recv: no filter connected"));
*code = CURLE_FAILED_INIT;
return -1;
}
@@ -184,7 +210,6 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
const void *mem, size_t len, CURLcode *code)
{
struct Curl_cfilter *cf;
- ssize_t nwritten;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
@@ -193,13 +218,10 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
cf = cf->next;
}
if(cf) {
- nwritten = cf->cft->do_send(cf, data, mem, len, code);
- /* DEBUGF(infof(data, "Curl_conn_send(handle=%p, index=%d, len=%ld)"
- " -> %ld, err=%d", data, num, len, nwritten, *code));*/
- return nwritten;
+ return cf->cft->do_send(cf, data, mem, len, code);
}
- failf(data, "no filter connected, conn=%ld, sockindex=%d",
- data->conn->connection_id, num);
+ failf(data, CMSGI(data->conn, num, "send: no filter connected"));
+ DEBUGASSERT(0);
*code = CURLE_FAILED_INIT;
return -1;
}
@@ -234,12 +256,31 @@ void Curl_conn_cf_add(struct Curl_easy *data,
DEBUGASSERT(!cf->conn);
DEBUGASSERT(!cf->next);
- DEBUGF(infof(data, CMSGI(conn, index, "cf_add(filter=%s)"),
- cf->cft->name));
cf->next = conn->cfilter[index];
cf->conn = conn;
cf->sockindex = index;
conn->cfilter[index] = cf;
+ DEBUGF(LOG_CF(data, cf, "added"));
+}
+
+void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_cfilter *cf_new)
+{
+ struct Curl_cfilter *tail, **pnext;
+
+ DEBUGASSERT(cf_at);
+ DEBUGASSERT(cf_new);
+ DEBUGASSERT(!cf_new->conn);
+
+ tail = cf_at->next;
+ cf_at->next = cf_new;
+ do {
+ cf_new->conn = cf_at->conn;
+ cf_new->sockindex = cf_at->sockindex;
+ pnext = &cf_new->next;
+ cf_new = cf_new->next;
+ } while(cf_new);
+ *pnext = tail;
}
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -259,97 +300,54 @@ void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data)
free(cf);
}
-ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- return cf->cft->do_send(cf, data, buf, len, err);
+ if(cf)
+ return cf->cft->connect(cf, data, blocking, done);
+ return CURLE_FAILED_INIT;
}
-ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
- char *buf, size_t len, CURLcode *err)
+void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- return cf->cft->do_recv(cf, data, buf, len, err);
+ if(cf)
+ cf->cft->close(cf, data);
}
-CURLcode Curl_conn_setup(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost,
- int ssl_mode)
+int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
{
- struct Curl_cfilter *cf;
- CURLcode result;
-
- DEBUGASSERT(data);
- /* If no filter is set, we have the "default" setup of connection filters.
- * The filter chain from botton to top will be:
- * - SOCKET socket filter for outgoing connection to remotehost
- * if http_proxy tunneling is engaged:
- * - SSL if proxytype is CURLPROXY_HTTPS
- * - HTTP_PROXY_TUNNEL
- * otherwise, if socks_proxy is engaged:
- * - SOCKS_PROXY_TUNNEL
- * - SSL if conn->handler has PROTOPT_SSL
- */
- if(!conn->cfilter[sockindex]) {
- DEBUGF(infof(data, DMSGI(data, sockindex, "setup, init filter chain")));
- result = Curl_conn_socket_set(data, conn, sockindex);
- if(result)
- goto out;
-
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy) {
- result = Curl_conn_socks_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
+ if(cf)
+ return cf->cft->get_select_socks(cf, data, socks);
+ return 0;
+}
- if(conn->bits.httpproxy) {
-#ifdef USE_SSL
- if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
- result = Curl_ssl_cfilter_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* USE_SSL */
-
-#if !defined(CURL_DISABLE_HTTP)
- if(conn->bits.tunnel_proxy) {
- result = Curl_conn_http_proxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* !CURL_DISABLE_HTTP */
- }
-#endif /* !CURL_DISABLE_PROXY */
-
-#ifdef USE_SSL
- if(ssl_mode == CURL_CF_SSL_ENABLE
- || (ssl_mode != CURL_CF_SSL_DISABLE
- && conn->handler->flags & PROTOPT_SSL)) {
- result = Curl_ssl_cfilter_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#else
- (void)ssl_mode;
-#endif /* USE_SSL */
-
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
- if(data->set.haproxyprotocol) {
- result = Curl_conn_haproxy_add(data, conn, sockindex);
- if(result)
- goto out;
- }
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ if(cf)
+ return cf->cft->has_data_pending(cf, data);
+ return FALSE;
+}
- }
- DEBUGASSERT(conn->cfilter[sockindex]);
- cf = data->conn->cfilter[sockindex];
- result = cf->cft->setup(cf, data, remotehost);
+ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ if(cf)
+ return cf->cft->do_send(cf, data, buf, len, err);
+ *err = CURLE_SEND_ERROR;
+ return -1;
+}
-out:
- return result;
+ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ if(cf)
+ return cf->cft->do_recv(cf, data, buf, len, err);
+ *err = CURLE_RECV_ERROR;
+ return -1;
}
CURLcode Curl_conn_connect(struct Curl_easy *data,
@@ -358,16 +356,29 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
bool *done)
{
struct Curl_cfilter *cf;
- CURLcode result;
+ CURLcode result = CURLE_OK;
DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
DEBUGASSERT(cf);
- result = cf->cft->connect(cf, data, blocking, done);
+ if(!cf)
+ return CURLE_FAILED_INIT;
+
+ *done = cf->connected;
+ if(!*done) {
+ result = cf->cft->connect(cf, data, blocking, done);
+ if(!result && *done) {
+ Curl_conn_ev_update_info(data, data->conn);
+ Curl_conn_report_connect_stats(data, data->conn);
+ data->conn->keepalive = Curl_now();
+ }
+ else if(result) {
+ Curl_conn_report_connect_stats(data, data->conn);
+ }
+ }
- DEBUGF(infof(data, DMSGI(data, sockindex, "connect(block=%d)-> %d, done=%d"),
- blocking, result, *done));
return result;
}
@@ -394,11 +405,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
return FALSE;
}
-bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
+bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
{
- struct Curl_cfilter *cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
- (void)data;
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_SSL)
return TRUE;
@@ -408,6 +418,19 @@ bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex)
return FALSE;
}
+bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ for(; cf; cf = cf->next) {
+ if(cf->cft->flags & CF_TYPE_MULTIPLEX)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT
+ || cf->cft->flags & CF_TYPE_SSL)
+ return FALSE;
+ }
+ return FALSE;
+}
bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
{
@@ -416,8 +439,6 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
(void)data;
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
- if(Curl_recv_has_postponed_data(data->conn, sockindex))
- return TRUE;
cf = data->conn->cfilter[sockindex];
while(cf && !cf->connected) {
@@ -437,46 +458,16 @@ int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
+
+ /* if the next one is not yet connected, that's the one we want */
+ while(cf && cf->next && !cf->next->connected)
+ cf = cf->next;
if(cf) {
return cf->cft->get_select_socks(cf, data, socks);
}
return GETSOCK_BLANK;
}
-void Curl_conn_attach_data(struct connectdata *conn,
- struct Curl_easy *data)
-{
- size_t i;
- struct Curl_cfilter *cf;
-
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
- cf = conn->cfilter[i];
- if(cf) {
- while(cf) {
- cf->cft->attach_data(cf, data);
- cf = cf->next;
- }
- }
- }
-}
-
-void Curl_conn_detach_data(struct connectdata *conn,
- struct Curl_easy *data)
-{
- size_t i;
- struct Curl_cfilter *cf;
-
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
- cf = conn->cfilter[i];
- if(cf) {
- while(cf) {
- cf->cft->detach_data(cf, data);
- cf = cf->next;
- }
- }
- }
-}
-
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport)
@@ -491,7 +482,7 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
else {
/* Some filter ask during shutdown for this, mainly for debugging
* purposes. We hand out the defaults, however this is not always
- * accurate, as the connction might be tunneled, etc. But all that
+ * accurate, as the connection might be tunneled, etc. But all that
* state is already gone here. */
*phost = data->conn->host.name;
*pdisplay_host = data->conn->host.dispname;
@@ -499,4 +490,174 @@ void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
}
}
+CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ (void)cf;
+ (void)data;
+ (void)event;
+ (void)arg1;
+ (void)arg2;
+ return CURLE_OK;
+}
+
+CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+
+ for(; cf; cf = cf->next) {
+ if(Curl_cf_def_cntrl == cf->cft->cntrl)
+ continue;
+ result = cf->cft->cntrl(cf, data, event, arg1, arg2);
+ if(!ignore_result && result)
+ break;
+ }
+ return result;
+}
+
+curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ curl_socket_t sock;
+ if(cf && !cf->cft->query(cf, data, CF_QUERY_SOCKET, NULL, &sock))
+ return sock;
+ return CURL_SOCKET_BAD;
+}
+
+curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
+{
+ struct Curl_cfilter *cf;
+
+ cf = data->conn? data->conn->cfilter[sockindex] : NULL;
+ /* if the top filter has not connected, ask it (and its sub-filters)
+ * for the socket. Otherwise conn->sock[sockindex] should have it.
+ */
+ if(cf && !cf->connected)
+ return Curl_conn_cf_get_socket(cf, data);
+ return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
+}
+
+static CURLcode cf_cntrl_all(struct connectdata *conn,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2)
+{
+ CURLcode result = CURLE_OK;
+ size_t i;
+
+ for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
+ event, arg1, arg2);
+ if(!ignore_result && result)
+ break;
+ }
+ return result;
+}
+
+void Curl_conn_ev_data_attach(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL);
+}
+
+void Curl_conn_ev_data_detach(struct connectdata *conn,
+ struct Curl_easy *data)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL);
+}
+
+CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_SETUP, 0, NULL);
+}
+
+CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_IDLE, 0, NULL);
+}
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is donw with sending data (e.g. has uploaded everything).
+ */
+void Curl_conn_ev_data_done_send(struct Curl_easy *data)
+{
+ cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE_SEND, 0, NULL);
+}
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is finished - eventually premature, e.g. before being complete.
+ */
+void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature)
+{
+ cf_cntrl_all(data->conn, data, TRUE, CF_CTRL_DATA_DONE, premature, NULL);
+}
+
+CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause)
+{
+ return cf_cntrl_all(data->conn, data, FALSE,
+ CF_CTRL_DATA_PAUSE, do_pause, NULL);
+}
+
+void Curl_conn_ev_update_info(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+}
+
+void Curl_conn_report_connect_stats(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+ if(cf) {
+ struct curltime connected;
+ struct curltime appconnected;
+
+ memset(&connected, 0, sizeof(connected));
+ cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
+ if(connected.tv_sec || connected.tv_usec)
+ Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
+
+ memset(&appconnected, 0, sizeof(appconnected));
+ cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
+ if(appconnected.tv_sec || appconnected.tv_usec)
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
+ }
+}
+
+bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
+ bool *input_pending)
+{
+ struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+ return cf && !cf->conn->bits.close &&
+ cf->cft->is_alive(cf, data, input_pending);
+}
+
+CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ return cf? cf->cft->keep_alive(cf, data) : CURLE_OK;
+}
+
+size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode result;
+ int n = 0;
+
+ struct Curl_cfilter *cf = conn->cfilter[sockindex];
+ result = cf? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
+ &n, NULL) : CURLE_UNKNOWN_OPTION;
+ return (result || n <= 0)? 1 : (size_t)n;
+}
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h
index 4b81b42e6b..317f2bb191 100644
--- a/Utilities/cmcurl/lib/cfilters.h
+++ b/Utilities/cmcurl/lib/cfilters.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,11 +36,6 @@ struct connectdata;
typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf,
struct Curl_easy *data);
-/* Setup the connection for `data`, using destination `remotehost`.
- */
-typedef CURLcode Curl_cft_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost);
typedef void Curl_cft_close(struct Curl_cfilter *cf,
struct Curl_easy *data);
@@ -89,29 +84,90 @@ typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf,
size_t len, /* amount to read */
CURLcode *err); /* error to return */
-typedef void Curl_cft_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-typedef void Curl_cft_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
+typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending);
+
+typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
/**
- * The easy handle `data` is being detached (no longer served)
- * by connection `conn`. All filters are informed to release any resources
- * related to `data`.
- * Note: there may be several `data` attached to a connection at the same
- * time.
+ * Events/controls for connection filters, their arguments and
+ * return code handling. Filter callbacks are invoked "top down".
+ * Return code handling:
+ * "first fail" meaning that the first filter returning != CURLE_OK, will
+ * abort further event distribution and determine the result.
+ * "ignored" meaning return values are ignored and the event is distributed
+ * to all filters in the chain. Overall result is always CURLE_OK.
*/
-void Curl_conn_detach(struct connectdata *conn, struct Curl_easy *data);
+/* data event arg1 arg2 return */
+#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */
+#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */
+#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */
+#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */
+#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */
+#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */
+#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */
+/* update conn info at connection and data */
+#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */
+/**
+ * Handle event/control for the filter.
+ * Implementations MUST NOT chain calls to cf->next.
+ */
+typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2);
+
+
+/**
+ * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain.
+ * - MAX_CONCURRENT: the maximum number of parallel transfers the filter
+ * chain expects to handle at the same time.
+ * default: 1 if no filter overrides.
+ * - CONNECT_REPLY_MS: milliseconds until the first indication of a server
+ * response was received on a connect. For TCP, this
+ * reflects the time until the socket connected. On UDP
+ * this gives the time the first bytes from the server
+ * were received.
+ * -1 if not determined yet.
+ * - CF_QUERY_SOCKET: the socket used by the filter chain
+ */
+/* query res1 res2 */
+#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
+#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */
+#define CF_QUERY_SOCKET 3 /* - curl_socket_t */
+#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */
+#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */
+
+/**
+ * Query the cfilter for properties. Filters ignorant of a query will
+ * pass it "down" the filter chain.
+ */
+typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2);
+
+/**
+ * Type flags for connection filters. A filter can have none, one or
+ * many of those. Use to evaluate state/capabilities of a filter chain.
+ *
+ * CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like
+ * a CONNECT tunnel, a UNIX domain socket, a QUIC
+ * connection, etc.
+ * CF_TYPE_SSL: provide SSL/TLS
+ * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
+ */
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
+#define CF_TYPE_MULTIPLEX (1 << 2)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
const char *name; /* name of the filter type */
- long flags; /* flags of filter type */
+ int flags; /* flags of filter type */
+ int log_level; /* log level for such filters */
Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
- Curl_cft_setup *setup; /* setup for a connection */
Curl_cft_connect *connect; /* establish connection */
Curl_cft_close *close; /* close conn */
Curl_cft_get_host *get_host; /* host filter talks to */
@@ -119,8 +175,10 @@ struct Curl_cftype {
Curl_cft_data_pending *has_data_pending;/* conn has data pending */
Curl_cft_send *do_send; /* send data */
Curl_cft_recv *do_recv; /* receive data */
- Curl_cft_attach_data *attach_data; /* data is being handled here */
- Curl_cft_detach_data *detach_data; /* data is no longer handled here */
+ Curl_cft_cntrl *cntrl; /* events/control */
+ Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */
+ Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */
+ Curl_cft_query *query; /* query filter chain */
};
/* A connection filter instance, e.g. registered at a connection */
@@ -129,7 +187,7 @@ struct Curl_cfilter {
struct Curl_cfilter *next; /* next filter in chain */
void *ctx; /* filter type specific settings */
struct connectdata *conn; /* the connection this filter belongs to */
- int sockindex; /* TODO: like to get rid off this */
+ int sockindex; /* the index the filter is installed at */
BIT(connected); /* != 0 iff this filter is connected */
};
@@ -139,9 +197,6 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
/* Default implementations for the type functions, implementing pass-through
* the filter chain. */
-CURLcode Curl_cf_def_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost);
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -158,16 +213,23 @@ ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
-void Curl_cf_def_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-void Curl_cf_def_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
+CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2);
+bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending);
+CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2);
/**
* Create a new filter instance, unattached to the filter chain.
* Use Curl_conn_cf_add() to add it to the chain.
* @param pcf on success holds the created instance
- * @parm cft the filter type
+ * @param cft the filter type
* @param ctx the type specific context to use
*/
CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
@@ -176,7 +238,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
/**
* Add a filter instance to the `sockindex` filter chain at connection
- * `data->conn`. The filter must not already be attached. It is inserted at
+ * `conn`. The filter must not already be attached. It is inserted at
* the start of the chain (top).
*/
void Curl_conn_cf_add(struct Curl_easy *data,
@@ -185,11 +247,11 @@ void Curl_conn_cf_add(struct Curl_easy *data,
struct Curl_cfilter *cf);
/**
- * Remove and destroy all filters at chain `sockindex` on connection `conn`.
+ * Insert a filter (chain) after `cf_at`.
+ * `cf_new` must not already be attached.
*/
-void Curl_conn_cf_discard_all(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex);
+void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_cfilter *cf_new);
/**
* Discard, e.g. remove and destroy a specific filter instance.
@@ -198,29 +260,51 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
*/
void Curl_conn_cf_discard(struct Curl_cfilter *cf, struct Curl_easy *data);
+/**
+ * Discard all cfilters starting with `*pcf` and clearing it afterwards.
+ */
+void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
+ struct Curl_easy *data);
+
+/**
+ * Remove and destroy all filters at chain `sockindex` on connection `conn`.
+ */
+void Curl_conn_cf_discard_all(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done);
+void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
+int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks);
+bool Curl_conn_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data);
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, CURLcode *err);
ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
+CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool ignore_result,
+ int event, int arg1, void *arg2);
+
+/**
+ * Get the socket used by the filter chain starting at `cf`.
+ * Returns CURL_SOCKET_BAD if not available.
+ */
+curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+
#define CURL_CF_SSL_DEFAULT -1
#define CURL_CF_SSL_DISABLE 0
#define CURL_CF_SSL_ENABLE 1
/**
- * Setup the filter chain at `sockindex` in connection `conn`, invoking
- * the instance `setup(remotehost)` methods. If no filter chain is
- * installed yet, inspects the configuration in `data` to install a
- * suitable filter chain.
- */
-CURLcode Curl_conn_setup(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost,
- int ssl_mode);
-
-/**
* Bring the filter chain at `sockindex` for connection `data->conn` into
* connected state. Which will set `*done` to TRUE.
* This can be called on an already connected chain with no side effects.
@@ -248,7 +332,12 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
* (or will be once connected). This will return FALSE, if SSL
* is only used in proxying and not for the tunnel itself.
*/
-bool Curl_conn_is_ssl(struct Curl_easy *data, int sockindex);
+bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
+
+/**
+ * Connection provides multiplexing of easy handles at `socketindex`.
+ */
+bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
/**
* Close the filter chain at `sockindex` for connection `data->conn`.
@@ -264,6 +353,12 @@ bool Curl_conn_data_pending(struct Curl_easy *data,
int sockindex);
/**
+ * Return the socket used on data's connection for the index.
+ * Returns CURL_SOCKET_BAD if not available.
+ */
+curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
+
+/**
* Get any select fd flags and the socket filters at chain `sockindex`
* at connection `conn` might be waiting for.
*/
@@ -289,13 +384,12 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t len, CURLcode *code);
/**
- * The easy handle `data` is being attached (served) by connection `conn`.
- * All filters are informed to adapt to handling `data`.
- * Note: there may be several `data` attached to a connection at the same
- * time.
+ * The easy handle `data` is being attached to `conn`. This does
+ * not mean that data will actually do a transfer. Attachment is
+ * also used for temporary actions on the connection.
*/
-void Curl_conn_attach_data(struct connectdata *conn,
- struct Curl_easy *data);
+void Curl_conn_ev_data_attach(struct connectdata *conn,
+ struct Curl_easy *data);
/**
* The easy handle `data` is being detached (no longer served)
@@ -304,12 +398,142 @@ void Curl_conn_attach_data(struct connectdata *conn,
* Note: there may be several `data` attached to a connection at the same
* time.
*/
-void Curl_conn_detach_data(struct connectdata *conn,
- struct Curl_easy *data);
+void Curl_conn_ev_data_detach(struct connectdata *conn,
+ struct Curl_easy *data);
+
+/**
+ * Notify connection filters that they need to setup data for
+ * a transfer.
+ */
+CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that now would be a good time to
+ * perform any idle, e.g. time related, actions.
+ */
+CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is donw with sending data (e.g. has uploaded everything).
+ */
+void Curl_conn_ev_data_done_send(struct Curl_easy *data);
+
+/**
+ * Notify connection filters that the transfer represented by `data`
+ * is finished - eventually premature, e.g. before being complete.
+ */
+void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature);
+
+/**
+ * Notify connection filters that the transfer of data is paused/unpaused.
+ */
+CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause);
+
+/**
+ * Inform connection filters to update their info in `conn`.
+ */
+void Curl_conn_ev_update_info(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
+ * Update connection statistics
+ */
+void Curl_conn_report_connect_stats(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
+ * Check if FIRSTSOCKET's cfilter chain deems connection alive.
+ */
+bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
+ bool *input_pending);
+
+/**
+ * Try to upkeep the connection filters at sockindex.
+ */
+CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
const char **phost, const char **pdisplay_host,
int *pport);
+/**
+ * Get the maximum number of parallel transfers the connection
+ * expects to be able to handle at `sockindex`.
+ */
+size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+
+/**
+ * Types and macros used to keep the current easy handle in filter calls,
+ * allowing for nested invocations. See #10336.
+ *
+ * `cf_call_data` is intended to be a member of the cfilter's `ctx` type.
+ * A filter defines the macro `CF_CTX_CALL_DATA` to give access to that.
+ *
+ * With all values 0, the default, this indicates that there is no cfilter
+ * call with `data` ongoing.
+ * Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local
+ * variable and sets the `data` given, incrementing the `depth` counter.
+ *
+ * Macro `CF_DATA_RESTORE` restores the old values from the local variable,
+ * while checking that `depth` values are as expected (debug build), catching
+ * cases where a "lower" RESTORE was not called.
+ *
+ * Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current
+ * invocation.
+ */
+struct cf_call_data {
+ struct Curl_easy *data;
+#ifdef DEBUGBUILD
+ int depth;
+#endif
+};
+
+/**
+ * define to access the `struct cf_call_data for a cfilter. Normally
+ * a member in the cfilter's `ctx`.
+ *
+ * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance
+*/
+
+#ifdef DEBUGBUILD
+
+#define CF_DATA_SAVE(save, cf, data) \
+ do { \
+ (save) = CF_CTX_CALL_DATA(cf); \
+ DEBUGASSERT((save).data == NULL || (save).depth > 0); \
+ CF_CTX_CALL_DATA(cf).depth++; \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ } while(0)
+
+#define CF_DATA_RESTORE(cf, save) \
+ do { \
+ DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \
+ DEBUGASSERT((save).data == NULL || (save).depth > 0); \
+ CF_CTX_CALL_DATA(cf) = (save); \
+ } while(0)
+
+#else /* DEBUGBUILD */
+
+#define CF_DATA_SAVE(save, cf, data) \
+ do { \
+ (save) = CF_CTX_CALL_DATA(cf); \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ } while(0)
+
+#define CF_DATA_RESTORE(cf, save) \
+ do { \
+ CF_CTX_CALL_DATA(cf) = (save); \
+ } while(0)
+
+#endif /* !DEBUGBUILD */
+
+#define CF_DATA_CURRENT(cf) \
+ ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL)
#endif /* HEADER_CURL_CFILTERS_H */
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index a557ac6dc9..1c736da605 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2016, Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -45,13 +45,6 @@
#define HASHKEY_SIZE 128
-static void conn_llist_dtor(void *user, void *element)
-{
- struct connectdata *conn = element;
- (void)user;
- conn->bundle = NULL;
-}
-
static CURLcode bundle_create(struct connectbundle **bundlep)
{
DEBUGASSERT(*bundlep == NULL);
@@ -62,17 +55,12 @@ static CURLcode bundle_create(struct connectbundle **bundlep)
(*bundlep)->num_connections = 0;
(*bundlep)->multiuse = BUNDLE_UNKNOWN;
- Curl_llist_init(&(*bundlep)->conn_list, (Curl_llist_dtor) conn_llist_dtor);
+ Curl_llist_init(&(*bundlep)->conn_list, NULL);
return CURLE_OK;
}
static void bundle_destroy(struct connectbundle *bundle)
{
- if(!bundle)
- return;
-
- Curl_llist_destroy(&bundle->conn_list, NULL);
-
free(bundle);
}
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h
index 94664bc357..959767d596 100644
--- a/Utilities/cmcurl/lib/conncache.h
+++ b/Utilities/cmcurl/lib/conncache.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2014, Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index af0413836b..10d0c11ae9 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,106 +59,30 @@
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
+#include "cf-https-connect.h"
+#include "cf-socket.h"
#include "select.h"
#include "url.h" /* for Curl_safefree() */
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "inet_ntop.h"
#include "inet_pton.h"
-#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
+#include "vtls/vtls.h" /* for vtsl cfilters */
#include "progress.h"
#include "warnless.h"
#include "conncache.h"
#include "multihandle.h"
#include "share.h"
#include "version_win32.h"
-#include "quic.h"
+#include "vquic/vquic.h" /* for quic cfilters */
+#include "http_proxy.h"
+#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-static bool verifyconnect(curl_socket_t sockfd, int *error);
-
-#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
-/* DragonFlyBSD and Windows use millisecond units */
-#define KEEPALIVE_FACTOR(x) (x *= 1000)
-#else
-#define KEEPALIVE_FACTOR(x)
-#endif
-
-#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
-#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
-
-struct tcp_keepalive {
- u_long onoff;
- u_long keepalivetime;
- u_long keepaliveinterval;
-};
-#endif
-
-static void
-tcpkeepalive(struct Curl_easy *data,
- curl_socket_t sockfd)
-{
- int optval = data->set.tcp_keepalive?1:0;
-
- /* only set IDLE and INTVL if setting KEEPALIVE is successful */
- if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
- }
- else {
-#if defined(SIO_KEEPALIVE_VALS)
- struct tcp_keepalive vals;
- DWORD dummy;
- vals.onoff = 1;
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- vals.keepalivetime = optval;
- optval = curlx_sltosi(data->set.tcp_keepintvl);
- KEEPALIVE_FACTOR(optval);
- vals.keepaliveinterval = optval;
- if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
- NULL, 0, &dummy, NULL, NULL) != 0) {
- infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
- (int)sockfd, WSAGetLastError());
- }
-#else
-#ifdef TCP_KEEPIDLE
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
- }
-#elif defined(TCP_KEEPALIVE)
- /* Mac OS X style */
- optval = curlx_sltosi(data->set.tcp_keepidle);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
- }
-#endif
-#ifdef TCP_KEEPINTVL
- optval = curlx_sltosi(data->set.tcp_keepintvl);
- KEEPALIVE_FACTOR(optval);
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
- (void *)&optval, sizeof(optval)) < 0) {
- infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
- }
-#endif
-#endif
- }
-}
-
-static CURLcode
-singleipconnect(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai, /* start connecting to this */
- int tempindex); /* 0 or 1 among the temp ones */
/*
* Curl_timeleft() returns the amount of milliseconds left allowed for the
@@ -230,387 +154,6 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
return timeout_ms;
}
-static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd, int af, unsigned int scope)
-{
- struct Curl_sockaddr_storage sa;
- struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
- curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
- struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
-#ifdef ENABLE_IPV6
- struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
-#endif
-
- struct Curl_dns_entry *h = NULL;
- unsigned short port = data->set.localport; /* use this port number, 0 for
- "random" */
- /* how many port numbers to try to bind to, increasing one at a time */
- int portnum = data->set.localportrange;
- const char *dev = data->set.str[STRING_DEVICE];
- int error;
-#ifdef IP_BIND_ADDRESS_NO_PORT
- int on = 1;
-#endif
-#ifndef ENABLE_IPV6
- (void)scope;
-#endif
-
- /*************************************************************
- * Select device to bind socket to
- *************************************************************/
- if(!dev && !port)
- /* no local kind of binding was requested */
- return CURLE_OK;
-
- memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
-
- if(dev && (strlen(dev)<255) ) {
- char myhost[256] = "";
- int done = 0; /* -1 for error, 1 for address found */
- bool is_interface = FALSE;
- bool is_host = FALSE;
- static const char *if_prefix = "if!";
- static const char *host_prefix = "host!";
-
- if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
- dev += strlen(if_prefix);
- is_interface = TRUE;
- }
- else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
- dev += strlen(host_prefix);
- is_host = TRUE;
- }
-
- /* interface */
- if(!is_host) {
-#ifdef SO_BINDTODEVICE
- /* I am not sure any other OSs than Linux that provide this feature,
- * and at the least I cannot test. --Ben
- *
- * This feature allows one to tightly bind the local socket to a
- * particular interface. This will force even requests to other
- * local interfaces to go out the external interface.
- *
- *
- * Only bind to the interface when specified as interface, not just
- * as a hostname or ip address.
- *
- * interface might be a VRF, eg: vrf-blue, which means it cannot be
- * converted to an IP address and would fail Curl_if2ip. Simply try
- * to use it straight away.
- */
- if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
- dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
- /* This is typically "errno 1, error: Operation not permitted" if
- * you're not running as root or another suitable privileged
- * user.
- * If it succeeds it means the parameter was a valid interface and
- * not an IP address. Return immediately.
- */
- return CURLE_OK;
- }
-#endif
-
- switch(Curl_if2ip(af,
-#ifdef ENABLE_IPV6
- scope, conn->scope_id,
-#endif
- dev, myhost, sizeof(myhost))) {
- case IF2IP_NOT_FOUND:
- if(is_interface) {
- /* Do not fall back to treating it as a host name */
- failf(data, "Couldn't bind to interface '%s'", dev);
- return CURLE_INTERFACE_FAILED;
- }
- break;
- case IF2IP_AF_NOT_SUPPORTED:
- /* Signal the caller to try another address family if available */
- return CURLE_UNSUPPORTED_PROTOCOL;
- case IF2IP_FOUND:
- is_interface = TRUE;
- /*
- * We now have the numerical IP address in the 'myhost' buffer
- */
- infof(data, "Local Interface %s is ip %s using address family %i",
- dev, myhost, af);
- done = 1;
- break;
- }
- }
- if(!is_interface) {
- /*
- * This was not an interface, resolve the name as a host name
- * or IP number
- *
- * Temporarily force name resolution to use only the address type
- * of the connection. The resolve functions should really be changed
- * to take a type parameter instead.
- */
- unsigned char ipver = conn->ip_version;
- int rc;
-
- if(af == AF_INET)
- conn->ip_version = CURL_IPRESOLVE_V4;
-#ifdef ENABLE_IPV6
- else if(af == AF_INET6)
- conn->ip_version = CURL_IPRESOLVE_V6;
-#endif
-
- rc = Curl_resolv(data, dev, 0, FALSE, &h);
- if(rc == CURLRESOLV_PENDING)
- (void)Curl_resolver_wait_resolv(data, &h);
- conn->ip_version = ipver;
-
- if(h) {
- /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
- Curl_printable_address(h->addr, myhost, sizeof(myhost));
- infof(data, "Name '%s' family %i resolved to '%s' family %i",
- dev, af, myhost, h->addr->ai_family);
- Curl_resolv_unlock(data, h);
- if(af != h->addr->ai_family) {
- /* bad IP version combo, signal the caller to try another address
- family if available */
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
- done = 1;
- }
- else {
- /*
- * provided dev was no interface (or interfaces are not supported
- * e.g. solaris) no ip address and no domain we fail here
- */
- done = -1;
- }
- }
-
- if(done > 0) {
-#ifdef ENABLE_IPV6
- /* IPv6 address */
- if(af == AF_INET6) {
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
- char *scope_ptr = strchr(myhost, '%');
- if(scope_ptr)
- *(scope_ptr++) = '\0';
-#endif
- if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
- si6->sin6_family = AF_INET6;
- si6->sin6_port = htons(port);
-#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
- if(scope_ptr) {
- /* The "myhost" string either comes from Curl_if2ip or from
- Curl_printable_address. The latter returns only numeric scope
- IDs and the former returns none at all. So the scope ID, if
- present, is known to be numeric */
- unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
- if(scope_id > UINT_MAX)
- return CURLE_UNSUPPORTED_PROTOCOL;
-
- si6->sin6_scope_id = (unsigned int)scope_id;
- }
-#endif
- }
- sizeof_sa = sizeof(struct sockaddr_in6);
- }
- else
-#endif
- /* IPv4 address */
- if((af == AF_INET) &&
- (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
- si4->sin_family = AF_INET;
- si4->sin_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in);
- }
- }
-
- if(done < 1) {
- /* errorbuf is set false so failf will overwrite any message already in
- the error buffer, so the user receives this error message instead of a
- generic resolve error. */
- data->state.errorbuf = FALSE;
- failf(data, "Couldn't bind to '%s'", dev);
- return CURLE_INTERFACE_FAILED;
- }
- }
- else {
- /* no device was given, prepare sa to match af's needs */
-#ifdef ENABLE_IPV6
- if(af == AF_INET6) {
- si6->sin6_family = AF_INET6;
- si6->sin6_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in6);
- }
- else
-#endif
- if(af == AF_INET) {
- si4->sin_family = AF_INET;
- si4->sin_port = htons(port);
- sizeof_sa = sizeof(struct sockaddr_in);
- }
- }
-#ifdef IP_BIND_ADDRESS_NO_PORT
- (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
-#endif
- for(;;) {
- if(bind(sockfd, sock, sizeof_sa) >= 0) {
- /* we succeeded to bind */
- struct Curl_sockaddr_storage add;
- curl_socklen_t size = sizeof(add);
- memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
- if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
- char buffer[STRERROR_LEN];
- data->state.os_errno = error = SOCKERRNO;
- failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return CURLE_INTERFACE_FAILED;
- }
- infof(data, "Local port: %hu", port);
- conn->bits.bound = TRUE;
- return CURLE_OK;
- }
-
- if(--portnum > 0) {
- port++; /* try next port */
- if(port == 0)
- break;
- infof(data, "Bind to local port %hu failed, trying next", port - 1);
- /* We re-use/clobber the port variable here below */
- if(sock->sa_family == AF_INET)
- si4->sin_port = ntohs(port);
-#ifdef ENABLE_IPV6
- else
- si6->sin6_port = ntohs(port);
-#endif
- }
- else
- break;
- }
- {
- char buffer[STRERROR_LEN];
- data->state.os_errno = error = SOCKERRNO;
- failf(data, "bind failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- }
-
- return CURLE_INTERFACE_FAILED;
-}
-
-/*
- * verifyconnect() returns TRUE if the connect really has happened.
- */
-static bool verifyconnect(curl_socket_t sockfd, int *error)
-{
- bool rc = TRUE;
-#ifdef SO_ERROR
- int err = 0;
- curl_socklen_t errSize = sizeof(err);
-
-#ifdef WIN32
- /*
- * In October 2003 we effectively nullified this function on Windows due to
- * problems with it using all CPU in multi-threaded cases.
- *
- * In May 2004, we bring it back to offer more info back on connect failures.
- * Gisle Vanem could reproduce the former problems with this function, but
- * could avoid them by adding this SleepEx() call below:
- *
- * "I don't have Rational Quantify, but the hint from his post was
- * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
- * just Sleep(0) would be enough?) would release whatever
- * mutex/critical-section the ntdll call is waiting on.
- *
- * Someone got to verify this on Win-NT 4.0, 2000."
- */
-
-#ifdef _WIN32_WCE
- Sleep(0);
-#else
- SleepEx(0, FALSE);
-#endif
-
-#endif
-
- if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
- err = SOCKERRNO;
-#ifdef _WIN32_WCE
- /* Old WinCE versions don't support SO_ERROR */
- if(WSAENOPROTOOPT == err) {
- SET_SOCKERRNO(0);
- err = 0;
- }
-#endif
-#if defined(EBADIOCTL) && defined(__minix)
- /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
- if(EBADIOCTL == err) {
- SET_SOCKERRNO(0);
- err = 0;
- }
-#endif
- if((0 == err) || (EISCONN == err))
- /* we are connected, awesome! */
- rc = TRUE;
- else
- /* This wasn't a successful connect */
- rc = FALSE;
- if(error)
- *error = err;
-#else
- (void)sockfd;
- if(error)
- *error = SOCKERRNO;
-#endif
- return rc;
-}
-
-/* update tempaddr[tempindex] (to the next entry), makes sure to stick
- to the correct family */
-static struct Curl_addrinfo *ainext(struct connectdata *conn,
- int tempindex,
- bool next) /* use next entry? */
-{
- struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
- if(ai && next)
- ai = ai->ai_next;
- while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
- ai = ai->ai_next;
- conn->tempaddr[tempindex] = ai;
- return ai;
-}
-
-/* Used within the multi interface. Try next IP address, returns error if no
- more address exists or error */
-static CURLcode trynextip(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- int tempindex)
-{
- CURLcode result = CURLE_COULDNT_CONNECT;
-
- /* First clean up after the failed socket.
- Don't close it yet to ensure that the next IP's socket gets a different
- file descriptor, which can prevent bugs when the curl_multi_socket_action
- interface is used with certain select() replacements such as kqueue. */
- curl_socket_t fd_to_close = conn->tempsock[tempindex];
- conn->tempsock[tempindex] = CURL_SOCKET_BAD;
-
- if(sockindex == FIRSTSOCKET) {
- struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
-
- while(ai) {
- result = singleipconnect(data, conn, ai, tempindex);
- if(result == CURLE_COULDNT_CONNECT) {
- ai = ainext(conn, tempindex, TRUE);
- continue;
- }
- break;
- }
- }
-
- if(fd_to_close != CURL_SOCKET_BAD)
- Curl_closesocket(data, conn, fd_to_close);
-
- return result;
-}
-
/* Copies connection info into the transfer handle to make it available when
the transfer handle is no longer associated with the connection. */
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
@@ -629,6 +172,28 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
data->info.conn_local_port = local_port;
}
+static const struct Curl_addrinfo *
+addr_first_match(const struct Curl_addrinfo *addr, int family)
+{
+ while(addr) {
+ if(addr->ai_family == family)
+ return addr;
+ addr = addr->ai_next;
+ }
+ return NULL;
+}
+
+static const struct Curl_addrinfo *
+addr_next_match(const struct Curl_addrinfo *addr, int family)
+{
+ while(addr && addr->ai_next) {
+ addr = addr->ai_next;
+ if(addr->ai_family == family)
+ return addr;
+ }
+ return NULL;
+}
+
/* retrieves ip address and port from a sockaddr structure.
note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
@@ -686,623 +251,493 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
return FALSE;
}
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_conninfo_remote(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t sockfd)
-{
-#ifdef HAVE_GETPEERNAME
- char buffer[STRERROR_LEN];
- struct Curl_sockaddr_storage ssrem;
- curl_socklen_t plen;
- int port;
- plen = sizeof(struct Curl_sockaddr_storage);
- memset(&ssrem, 0, sizeof(ssrem));
- if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
- int error = SOCKERRNO;
- failf(data, "getpeername() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return;
- }
- if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
- conn->primary_ip, &port)) {
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- return;
- }
-#else
- (void)data;
- (void)conn;
- (void)sockfd;
-#endif
-}
+struct connfind {
+ long id_tofind;
+ struct connectdata *found;
+};
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
- char *local_ip, int *local_port)
+static int conn_is_conn(struct Curl_easy *data,
+ struct connectdata *conn, void *param)
{
-#ifdef HAVE_GETSOCKNAME
- char buffer[STRERROR_LEN];
- struct Curl_sockaddr_storage ssloc;
- curl_socklen_t slen;
- slen = sizeof(struct Curl_sockaddr_storage);
- memset(&ssloc, 0, sizeof(ssloc));
- if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
- int error = SOCKERRNO;
- failf(data, "getsockname() failed with errno %d: %s",
- error, Curl_strerror(error, buffer, sizeof(buffer)));
- return;
- }
- if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
- local_ip, local_port)) {
- failf(data, "ssloc inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- return;
- }
-#else
+ struct connfind *f = (struct connfind *)param;
(void)data;
- (void)sockfd;
- (void)local_ip;
- (void)local_port;
-#endif
-}
-
-/* retrieves the start/end point information of a socket of an established
- connection */
-void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd)
-{
- /* 'local_ip' and 'local_port' get filled with local's numerical
- ip address and port number whenever an outgoing connection is
- **established** from the primary socket to a remote address. */
- char local_ip[MAX_IPADR_LEN] = "";
- int local_port = -1;
-
- if(!conn->bits.reuse &&
- (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen))
- Curl_conninfo_remote(data, conn, sockfd);
- Curl_conninfo_local(data, sockfd, local_ip, &local_port);
-
- /* persist connection info in session handle */
- Curl_persistconninfo(data, conn, local_ip, local_port);
-}
-
-/*
- * post_connect() is called after a successful connect to the peer
- */
-static void post_connect(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
-{
- Curl_updateconninfo(data, conn, conn->sock[sockindex]);
- Curl_verboseconnect(data, conn);
- data->info.numconnects++; /* to track the number of connections made */
+ if(conn->connection_id == f->id_tofind) {
+ f->found = conn;
+ return 1;
+ }
+ return 0;
}
/*
- * is_connected() checks if the socket has connected.
+ * Used to extract socket and connectdata struct for the most recent
+ * transfer on the given Curl_easy.
+ *
+ * The returned socket will be CURL_SOCKET_BAD in case of failure!
*/
-static CURLcode is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected)
+curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
+ struct connectdata **connp)
{
- CURLcode result = CURLE_OK;
- timediff_t allow;
- int error = 0;
- struct curltime now;
- int rc = 0;
- int i;
-
- DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
-
- *connected = FALSE; /* a very negative world view is best */
-
- now = Curl_now();
-
- /* Check if any of the conn->tempsock we use for establishing connections
- * succeeded and, if so, close any ongoing other ones.
- * Transfer the successful conn->tempsock to conn->sock[sockindex]
- * and set conn->tempsock to CURL_SOCKET_BAD.
- * If transport is QUIC, we need to shutdown the ongoing 'other'
- * connect attempts in a QUIC appropriate way. */
- for(i = 0; i<2; i++) {
- const int other = i ^ 1;
- if(conn->tempsock[i] == CURL_SOCKET_BAD)
- continue;
- error = 0;
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- result = Curl_quic_is_connected(data, conn, i, connected);
- if(!result && *connected) {
- /* use this socket from now on */
- conn->sock[sockindex] = conn->tempsock[i];
- conn->ip_addr = conn->tempaddr[i];
- conn->tempsock[i] = CURL_SOCKET_BAD;
- post_connect(data, conn, sockindex);
- connkeep(conn, "HTTP/3 default");
- if(conn->tempsock[other] != CURL_SOCKET_BAD)
- Curl_quic_disconnect(data, conn, other);
- return CURLE_OK;
- }
- /* When a QUIC connect attempt fails, the better error explanation is in
- 'result' and not in errno */
- if(result) {
- conn->tempsock[i] = CURL_SOCKET_BAD;
- error = SOCKERRNO;
- }
- }
- else
-#endif
- {
-#ifdef mpeix
- /* Call this function once now, and ignore the results. We do this to
- "clear" the error state on the socket so that we can later read it
- reliably. This is reported necessary on the MPE/iX operating
- system. */
- (void)verifyconnect(conn->tempsock[i], NULL);
-#endif
+ DEBUGASSERT(data);
- /* check socket for connect */
- rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
- }
+ /* this works for an easy handle:
+ * - that has been used for curl_easy_perform()
+ * - that is associated with a multi handle, and whose connection
+ * was detached with CURLOPT_CONNECT_ONLY
+ */
+ if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
+ struct connectdata *c;
+ struct connfind find;
+ find.id_tofind = data->state.lastconnect_id;
+ find.found = NULL;
- if(rc == 0) { /* no connection yet */
- if(Curl_timediff(now, conn->connecttime) >=
- conn->timeoutms_per_addr[i]) {
- infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
- "ms connect time, move on!", conn->timeoutms_per_addr[i]);
- error = ETIMEDOUT;
- }
+ Curl_conncache_foreach(data,
+ data->share && (data->share->specifier
+ & (1<< CURL_LOCK_DATA_CONNECT))?
+ &data->share->conn_cache:
+ data->multi_easy?
+ &data->multi_easy->conn_cache:
+ &data->multi->conn_cache, &find, conn_is_conn);
- /* should we try another protocol family? */
- if(i == 0 && !conn->bits.parallel_connect &&
- (Curl_timediff(now, conn->connecttime) >=
- data->set.happy_eyeballs_timeout)) {
- conn->bits.parallel_connect = TRUE; /* starting now */
- trynextip(data, conn, sockindex, 1);
- }
+ if(!find.found) {
+ data->state.lastconnect_id = -1;
+ return CURL_SOCKET_BAD;
}
- else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
- if(verifyconnect(conn->tempsock[i], &error)) {
- /* we are connected with TCP, awesome! */
-
- /* use this socket from now on */
- conn->sock[sockindex] = conn->tempsock[i];
- conn->ip_addr = conn->tempaddr[i];
- conn->tempsock[i] = CURL_SOCKET_BAD;
-#ifdef ENABLE_IPV6
- conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
-#endif
-
- /* close the other socket, if open */
- if(conn->tempsock[other] != CURL_SOCKET_BAD) {
- Curl_closesocket(data, conn, conn->tempsock[other]);
- conn->tempsock[other] = CURL_SOCKET_BAD;
- }
- *connected = TRUE;
- return CURLE_OK;
- }
- }
- else if(rc & CURL_CSELECT_ERR) {
- (void)verifyconnect(conn->tempsock[i], &error);
- }
+ c = find.found;
+ if(connp)
+ /* only store this if the caller cares for it */
+ *connp = c;
+ return c->sock[FIRSTSOCKET];
+ }
+ return CURL_SOCKET_BAD;
+}
- /*
- * The connection failed here, we should attempt to connect to the "next
- * address" for the given host. But first remember the latest error.
- */
- if(error) {
- data->state.os_errno = error;
- SET_SOCKERRNO(error);
- if(conn->tempaddr[i]) {
- CURLcode status;
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
- char ipaddress[MAX_IPADR_LEN];
- char buffer[STRERROR_LEN];
- Curl_printable_address(conn->tempaddr[i], ipaddress,
- sizeof(ipaddress));
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- infof(data, "connect to %s port %u failed: %s",
- ipaddress, conn->port, curl_easy_strerror(result));
- }
- else
+/*
+ * Curl_conncontrol() marks streams or connection for closure.
+ */
+void Curl_conncontrol(struct connectdata *conn,
+ int ctrl /* see defines in header */
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ , const char *reason
#endif
- infof(data, "connect to %s port %u failed: %s",
- ipaddress, conn->port,
- Curl_strerror(error, buffer, sizeof(buffer)));
+ )
+{
+ /* close if a connection, or a stream that isn't multiplexed. */
+ /* This function will be called both before and after this connection is
+ associated with a transfer. */
+ bool closeit, is_multiplex;
+ DEBUGASSERT(conn);
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ (void)reason; /* useful for debugging */
#endif
-
- allow = Curl_timeleft(data, &now, TRUE);
- conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
- allow : allow / 2;
- ainext(conn, i, TRUE);
- status = trynextip(data, conn, sockindex, i);
- if((status != CURLE_COULDNT_CONNECT) ||
- conn->tempsock[other] == CURL_SOCKET_BAD) {
- /* the last attempt failed and no other sockets remain open */
- if(!result)
- result = status;
- }
- }
- }
- }
-
- /*
- * Now that we've checked whether we are connected, check whether we've
- * already timed out.
- *
- * First figure out how long time we have left to connect */
-
- allow = Curl_timeleft(data, &now, TRUE);
-
- if(allow < 0) {
- /* time-out, bail out, go home */
- failf(data, "Connection timeout after %ld ms",
- Curl_timediff(now, data->progress.t_startsingle));
- return CURLE_OPERATION_TIMEDOUT;
+ is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
+ closeit = (ctrl == CONNCTRL_CONNECTION) ||
+ ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
+ if((ctrl == CONNCTRL_STREAM) && is_multiplex)
+ ; /* stream signal on multiplex conn never affects close state */
+ else if((bit)closeit != conn->bits.close) {
+ conn->bits.close = closeit; /* the only place in the source code that
+ should assign this bit */
}
+}
- if(result &&
- (conn->tempsock[0] == CURL_SOCKET_BAD) &&
- (conn->tempsock[1] == CURL_SOCKET_BAD)) {
- /* no more addresses to try */
- const char *hostname;
- CURLcode failreason = result;
-
- /* if the first address family runs out of addresses to try before the
- happy eyeball timeout, go ahead and try the next family now */
- result = trynextip(data, conn, sockindex, 1);
- if(!result)
- return result;
+/**
+ * job walking the matching addr infos, creating a sub-cfilter with the
+ * provided method `cf_create` and running setup/connect on it.
+ */
+struct eyeballer {
+ const char *name;
+ const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
+ int ai_family; /* matching address family only */
+ cf_ip_connect_create *cf_create; /* for creating cf */
+ struct Curl_cfilter *cf; /* current sub-cfilter connecting */
+ struct eyeballer *primary; /* eyeballer this one is backup for */
+ timediff_t delay_ms; /* delay until start */
+ struct curltime started; /* start of current attempt */
+ timediff_t timeoutms; /* timeout for current attempt */
+ expire_id timeout_id; /* ID for Curl_expire() */
+ CURLcode result;
+ int error;
+ BIT(has_started); /* attempts have started */
+ BIT(is_done); /* out of addresses/time */
+ BIT(connected); /* cf has connected */
+};
- result = failreason;
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy)
- hostname = conn->socks_proxy.host.name;
- else if(conn->bits.httpproxy)
- hostname = conn->http_proxy.host.name;
- else
-#endif
- if(conn->bits.conn_to_host)
- hostname = conn->conn_to_host.name;
- else
- hostname = conn->host.name;
+typedef enum {
+ SCFST_INIT,
+ SCFST_WAITING,
+ SCFST_DONE
+} cf_connect_state;
- failf(data, "Failed to connect to %s port %u after "
- "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
- hostname, conn->port,
- Curl_timediff(now, data->progress.t_startsingle),
- curl_easy_strerror(result));
+struct cf_he_ctx {
+ int transport;
+ cf_ip_connect_create *cf_create;
+ const struct Curl_dns_entry *remotehost;
+ cf_connect_state state;
+ struct eyeballer *baller[2];
+ struct eyeballer *winner;
+ struct curltime started;
+};
- Curl_quic_disconnect(data, conn, 0);
- Curl_quic_disconnect(data, conn, 1);
+static CURLcode eyeballer_new(struct eyeballer **pballer,
+ cf_ip_connect_create *cf_create,
+ const struct Curl_addrinfo *addr,
+ int ai_family,
+ struct eyeballer *primary,
+ timediff_t delay_ms,
+ timediff_t timeout_ms,
+ expire_id timeout_id)
+{
+ struct eyeballer *baller;
-#ifdef WSAETIMEDOUT
- if(WSAETIMEDOUT == data->state.os_errno)
- result = CURLE_OPERATION_TIMEDOUT;
-#elif defined(ETIMEDOUT)
- if(ETIMEDOUT == data->state.os_errno)
- result = CURLE_OPERATION_TIMEDOUT;
-#endif
- }
- else
- result = CURLE_OK; /* still trying */
+ *pballer = NULL;
+ baller = calloc(1, sizeof(*baller) + 1000);
+ if(!baller)
+ return CURLE_OUT_OF_MEMORY;
- return result;
+ baller->name = ((ai_family == AF_INET)? "ipv4" : (
+#ifdef ENABLE_IPV6
+ (ai_family == AF_INET6)? "ipv6" :
+#endif
+ "ip"));
+ baller->cf_create = cf_create;
+ baller->addr = addr;
+ baller->ai_family = ai_family;
+ baller->primary = primary;
+ baller->delay_ms = delay_ms;
+ baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)?
+ timeout_ms / 2 : timeout_ms;
+ baller->timeout_id = timeout_id;
+ baller->result = CURLE_COULDNT_CONNECT;
+
+ *pballer = baller;
+ return CURLE_OK;
}
-static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
+static void baller_close(struct eyeballer *baller,
+ struct Curl_easy *data)
{
-#if defined(TCP_NODELAY)
- curl_socklen_t onoff = (curl_socklen_t) 1;
- int level = IPPROTO_TCP;
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char buffer[STRERROR_LEN];
-#else
- (void) data;
-#endif
-
- if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
- sizeof(onoff)) < 0)
- infof(data, "Could not set TCP_NODELAY: %s",
- Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-#else
- (void)data;
- (void)sockfd;
-#endif
+ if(baller && baller->cf) {
+ Curl_conn_cf_discard_chain(&baller->cf, data);
+ }
}
-#ifdef SO_NOSIGPIPE
-/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
- sending data to a dead peer (instead of relying on the 4th argument to send
- being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
- systems? */
-static void nosigpipe(struct Curl_easy *data,
- curl_socket_t sockfd)
+static void baller_free(struct eyeballer *baller,
+ struct Curl_easy *data)
{
- int onoff = 1;
- if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
- sizeof(onoff)) < 0) {
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char buffer[STRERROR_LEN];
- infof(data, "Could not set SO_NOSIGPIPE: %s",
- Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
-#endif
+ if(baller) {
+ baller_close(baller, data);
+ free(baller);
}
}
-#else
-#define nosigpipe(x,y) Curl_nop_stmt
-#endif
-
-#ifdef USE_WINSOCK
-/* When you run a program that uses the Windows Sockets API, you may
- experience slow performance when you copy data to a TCP server.
-
- https://support.microsoft.com/kb/823764
-
- Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
- Buffer Size
-
- The problem described in this knowledge-base is applied only to pre-Vista
- Windows. Following function trying to detect OS version and skips
- SO_SNDBUF adjustment for Windows Vista and above.
-*/
-#define DETECT_OS_NONE 0
-#define DETECT_OS_PREVISTA 1
-#define DETECT_OS_VISTA_OR_LATER 2
-void Curl_sndbufset(curl_socket_t sockfd)
+static void baller_next_addr(struct eyeballer *baller)
{
- int val = CURL_MAX_WRITE_SIZE + 32;
- int curval = 0;
- int curlen = sizeof(curval);
-
- static int detectOsState = DETECT_OS_NONE;
-
- if(detectOsState == DETECT_OS_NONE) {
- if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL))
- detectOsState = DETECT_OS_VISTA_OR_LATER;
- else
- detectOsState = DETECT_OS_PREVISTA;
- }
-
- if(detectOsState == DETECT_OS_VISTA_OR_LATER)
- return;
-
- if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
- if(curval > val)
- return;
-
- setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
+ baller->addr = addr_next_match(baller->addr, baller->ai_family);
}
-#endif
/*
- * singleipconnect()
+ * Initiate a connect attempt walk.
*
* Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
* CURL_SOCKET_BAD. Other errors will however return proper errors.
- *
- * singleipconnect() connects to the given IP only, and it may return without
- * having connected.
*/
-static CURLcode singleipconnect(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_addrinfo *ai,
- int tempindex)
+static void baller_initiate(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller)
{
- struct Curl_sockaddr_ex addr;
- int rc = -1;
- int error = 0;
- bool isconnected = FALSE;
- curl_socket_t sockfd;
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct Curl_cfilter *cf_prev = baller->cf;
+ struct Curl_cfilter *wcf;
CURLcode result;
- char ipaddress[MAX_IPADR_LEN];
- int port;
- bool is_tcp;
-#ifdef TCP_FASTOPEN_CONNECT
- int optval = 1;
-#endif
- const char *ipmsg;
- char buffer[STRERROR_LEN];
- curl_socket_t *sockp = &conn->tempsock[tempindex];
- *sockp = CURL_SOCKET_BAD;
- result = Curl_socket(data, ai, &addr, &sockfd);
+
+ /* Don't close a previous cfilter yet to ensure that the next IP's
+ socket gets a different file descriptor, which can prevent bugs when
+ the curl_multi_socket_action interface is used with certain select()
+ replacements such as kqueue. */
+ result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
+ ctx->transport);
if(result)
- return result;
+ goto out;
- /* store remote address and port used in this connection attempt */
- if(!Curl_addr2string(&addr.sa_addr, addr.addrlen,
- ipaddress, &port)) {
- /* malformed address or bug in inet_ntop, try next address */
- failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
- errno, Curl_strerror(errno, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, sockfd);
- return CURLE_OK;
+ /* the new filter might have sub-filters */
+ for(wcf = baller->cf; wcf; wcf = wcf->next) {
+ wcf->conn = cf->conn;
+ wcf->sockindex = cf->sockindex;
}
-#ifdef ENABLE_IPV6
- if(addr.family == AF_INET6)
- ipmsg = " Trying [%s]:%d...";
- else
-#endif
- ipmsg = " Trying %s:%d...";
- infof(data, ipmsg, ipaddress, port);
-
-#ifdef ENABLE_IPV6
- is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
- addr.socktype == SOCK_STREAM;
-#else
- is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
-#endif
- if(is_tcp && data->set.tcp_nodelay)
- tcpnodelay(data, sockfd);
- nosigpipe(data, sockfd);
+ if(addr_next_match(baller->addr, baller->ai_family)) {
+ Curl_expire(data, baller->timeoutms, baller->timeout_id);
+ }
- Curl_sndbufset(sockfd);
+out:
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "%s failed", baller->name));
+ baller_close(baller, data);
+ }
+ if(cf_prev)
+ Curl_conn_cf_discard_chain(&cf_prev, data);
+ baller->result = result;
+}
- if(is_tcp && data->set.tcp_keepalive)
- tcpkeepalive(data, sockfd);
+/**
+ * Start a connection attempt on the current baller address.
+ * Will return CURLE_OK on the first address where a socket
+ * could be created and the non-blocking connect started.
+ * Returns error when all remaining addresses have been tried.
+ */
+static CURLcode baller_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ timediff_t timeoutms)
+{
+ baller->error = 0;
+ baller->connected = FALSE;
+ baller->has_started = TRUE;
+
+ while(baller->addr) {
+ baller->started = Curl_now();
+ baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
+ timeoutms / 2 : timeoutms;
+ baller_initiate(cf, data, baller);
+ if(!baller->result)
+ break;
+ baller_next_addr(baller);
+ }
+ if(!baller->addr) {
+ baller->is_done = TRUE;
+ }
+ return baller->result;
+}
- if(data->set.fsockopt) {
- /* activate callback for setting socket options */
- Curl_set_in_callback(data, true);
- error = data->set.fsockopt(data->set.sockopt_client,
- sockfd,
- CURLSOCKTYPE_IPCXN);
- Curl_set_in_callback(data, false);
- if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
- isconnected = TRUE;
- else if(error) {
- Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
- return CURLE_ABORTED_BY_CALLBACK;
- }
+/* Used within the multi interface. Try next IP address, returns error if no
+ more address exists or error */
+static CURLcode baller_start_next(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ timediff_t timeoutms)
+{
+ if(cf->sockindex == FIRSTSOCKET) {
+ baller_next_addr(baller);
+ baller_start(cf, data, baller, timeoutms);
+ }
+ else {
+ baller->error = 0;
+ baller->connected = FALSE;
+ baller->has_started = TRUE;
+ baller->is_done = TRUE;
+ baller->result = CURLE_COULDNT_CONNECT;
}
+ return baller->result;
+}
- /* possibly bind the local end to an IP, interface or port */
- if(addr.family == AF_INET
-#ifdef ENABLE_IPV6
- || addr.family == AF_INET6
+static CURLcode baller_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct eyeballer *baller,
+ struct curltime *now,
+ bool *connected)
+{
+ (void)cf;
+ *connected = baller->connected;
+ if(!baller->result && !*connected) {
+ /* evaluate again */
+ baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
+
+ if(!baller->result) {
+ if (*connected) {
+ baller->connected = TRUE;
+ baller->is_done = TRUE;
+ }
+ else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
+ infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T
+ "ms, move on!", baller->name, baller->timeoutms);
+#if defined(ETIMEDOUT)
+ baller->error = ETIMEDOUT;
#endif
- ) {
- result = bindlocal(data, conn, sockfd, addr.family,
- Curl_ipv6_scope(&addr.sa_addr));
- if(result) {
- Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
- if(result == CURLE_UNSUPPORTED_PROTOCOL) {
- /* The address family is not supported on this interface.
- We can continue trying addresses */
- return CURLE_COULDNT_CONNECT;
+ baller->result = CURLE_OPERATION_TIMEDOUT;
}
- return result;
}
}
+ return baller->result;
+}
- /* set socket non-blocking */
- (void)curlx_nonblock(sockfd, TRUE);
+/*
+ * is_connected() checks if the socket has connected.
+ */
+static CURLcode is_connected(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *connected)
+{
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct connectdata *conn = cf->conn;
+ CURLcode result;
+ struct curltime now;
+ size_t i;
+ int ongoing, not_started;
+ const char *hostname;
- conn->connecttime = Curl_now();
- if(conn->num_addr > 1) {
- Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
- Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
- }
+ /* Check if any of the conn->tempsock we use for establishing connections
+ * succeeded and, if so, close any ongoing other ones.
+ * Transfer the successful conn->tempsock to conn->sock[sockindex]
+ * and set conn->tempsock to CURL_SOCKET_BAD.
+ * If transport is QUIC, we need to shutdown the ongoing 'other'
+ * cot ballers in a QUIC appropriate way. */
+evaluate:
+ *connected = FALSE; /* a very negative world view is best */
+ now = Curl_now();
+ ongoing = not_started = 0;
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
- /* Connect TCP and QUIC sockets */
- if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
- if(conn->bits.tcp_fastopen) {
-#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
-# if defined(HAVE_BUILTIN_AVAILABLE)
- /* while connectx function is available since macOS 10.11 / iOS 9,
- it did not have the interface declared correctly until
- Xcode 9 / macOS SDK 10.13 */
- if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
- sa_endpoints_t endpoints;
- endpoints.sae_srcif = 0;
- endpoints.sae_srcaddr = NULL;
- endpoints.sae_srcaddrlen = 0;
- endpoints.sae_dstaddr = &addr.sa_addr;
- endpoints.sae_dstaddrlen = addr.addrlen;
-
- rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
- CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
- NULL, 0, NULL, NULL);
+ if(!baller || baller->is_done)
+ continue;
+
+ if(!baller->has_started) {
+ ++not_started;
+ continue;
+ }
+ baller->result = baller_connect(cf, data, baller, &now, connected);
+ DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d",
+ baller->name, baller->result, *connected));
+
+ if(!baller->result) {
+ if(*connected) {
+ /* connected, declare the winner */
+ ctx->winner = baller;
+ ctx->baller[i] = NULL;
+ break;
}
- else {
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ else { /* still waiting */
+ ++ongoing;
}
-# else
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
-# endif /* HAVE_BUILTIN_AVAILABLE */
-#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
- if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
- (void *)&optval, sizeof(optval)) < 0)
- infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
-
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
-#elif defined(MSG_FASTOPEN) /* old Linux */
- if(conn->given->flags & PROTOPT_SSL)
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
- else
- rc = 0; /* Do nothing */
-#endif
}
- else {
- rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ else if(!baller->is_done) {
+ /* The baller failed to connect, start its next attempt */
+ if(baller->error) {
+ data->state.os_errno = baller->error;
+ SET_SOCKERRNO(baller->error);
+ }
+ baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
+ if(baller->is_done) {
+ DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+ }
+ else {
+ /* next attempt was started */
+ DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name));
+ ++ongoing;
+ }
}
+ }
- if(-1 == rc)
- error = SOCKERRNO;
-#ifdef ENABLE_QUIC
- else if(conn->transport == TRNSPRT_QUIC) {
- /* pass in 'sockfd' separately since it hasn't been put into the
- tempsock array at this point */
- result = Curl_quic_connect(data, conn, sockfd, tempindex,
- &addr.sa_addr, addr.addrlen);
- if(result)
- error = SOCKERRNO;
+ if(ctx->winner) {
+ *connected = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Nothing connected, check the time before we might
+ * start new ballers or return ok. */
+ if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
+ failf(data, "Connection timeout after %ld ms",
+ Curl_timediff(now, data->progress.t_startsingle));
+ return CURLE_OPERATION_TIMEDOUT;
+ }
+
+ /* Check if we have any waiting ballers to start now. */
+ if(not_started > 0) {
+ int added = 0;
+
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+
+ if(!baller || baller->has_started)
+ continue;
+ /* We start its primary baller has failed to connect or if
+ * its start delay_ms have expired */
+ if((baller->primary && baller->primary->is_done) ||
+ Curl_timediff(now, ctx->started) >= baller->delay_ms) {
+ baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
+ if(baller->is_done) {
+ DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%ldms)",
+ baller->name, baller->timeoutms));
+ ++ongoing;
+ ++added;
+ }
+ }
}
-#endif
+ if(added > 0)
+ goto evaluate;
}
- else {
- *sockp = sockfd;
+
+ if(ongoing > 0) {
+ /* We are still trying, return for more waiting */
+ *connected = FALSE;
return CURLE_OK;
}
- if(-1 == rc) {
- switch(error) {
- case EINPROGRESS:
- case EWOULDBLOCK:
-#if defined(EAGAIN)
-#if (EAGAIN) != (EWOULDBLOCK)
- /* On some platforms EAGAIN and EWOULDBLOCK are the
- * same value, and on others they are different, hence
- * the odd #if
- */
- case EAGAIN:
-#endif
-#endif
- result = CURLE_OK;
+ /* all ballers have failed to connect. */
+ DEBUGF(LOG_CF(data, cf, "all eyeballers failed"));
+ result = CURLE_COULDNT_CONNECT;
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d",
+ baller?baller->name:NULL,
+ baller?baller->has_started:0,
+ baller?baller->result:0));
+ if(baller && baller->has_started && baller->result) {
+ result = baller->result;
break;
-
- default:
- /* unknown error, fallthrough and try another address! */
- infof(data, "Immediate connect fail for %s: %s",
- ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
- data->state.os_errno = error;
-
- /* connect failed */
- Curl_closesocket(data, conn, sockfd);
- result = CURLE_COULDNT_CONNECT;
}
}
- if(!result)
- *sockp = sockfd;
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy)
+ hostname = conn->socks_proxy.host.name;
+ else if(conn->bits.httpproxy)
+ hostname = conn->http_proxy.host.name;
+ else
+#endif
+ if(conn->bits.conn_to_host)
+ hostname = conn->conn_to_host.name;
+ else
+ hostname = conn->host.name;
+
+ failf(data, "Failed to connect to %s port %u after "
+ "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+ hostname, conn->port,
+ Curl_timediff(now, data->progress.t_startsingle),
+ curl_easy_strerror(result));
+
+#ifdef WSAETIMEDOUT
+ if(WSAETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#elif defined(ETIMEDOUT)
+ if(ETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#endif
return result;
}
/*
- * TCP connect to the given host with timeout, proxy or remote doesn't matter.
- * There might be more than one IP address to try out. Fill in the passed
- * pointer with the connected socket.
+ * Connect to the given host with timeout, proxy or remote doesn't matter.
+ * There might be more than one IP address to try out.
*/
-
-CURLcode Curl_connecthost(struct Curl_easy *data,
- struct connectdata *conn, /* context */
- const struct Curl_dns_entry *remotehost)
+static CURLcode start_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
{
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct connectdata *conn = cf->conn;
CURLcode result = CURLE_COULDNT_CONNECT;
- int i;
+ int ai_family0, ai_family1;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ const struct Curl_addrinfo *addr0, *addr1;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
@@ -1310,58 +745,76 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
- conn->num_addr = Curl_num_addresses(remotehost->addr);
- conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
- conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
-
- /* Max time for the next connection attempt */
- conn->timeoutms_per_addr[0] =
- conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
- conn->timeoutms_per_addr[1] =
- conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
+ ctx->started = Curl_now();
+ /* remotehost->addr is the list of addresses from the resolver, each
+ * with an address family. The list has at least one entry, possibly
+ * many more.
+ * We try at most 2 at a time, until we either get a connection or
+ * run out of addresses to try. Since likelihood of success is tied
+ * to the address family (e.g. IPV6 might not work at all ), we want
+ * the 2 connect attempt ballers to try different families, if possible.
+ *
+ */
if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
/* any IP version is allowed */
- conn->tempfamily[0] = conn->tempaddr[0]?
- conn->tempaddr[0]->ai_family:0;
+ ai_family0 = remotehost->addr?
+ remotehost->addr->ai_family : 0;
#ifdef ENABLE_IPV6
- conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
+ ai_family1 = ai_family0 == AF_INET6 ?
AF_INET : AF_INET6;
#else
- conn->tempfamily[1] = AF_UNSPEC;
+ ai_family1 = AF_UNSPEC;
#endif
}
else {
/* only one IP version is allowed */
- conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
+ ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
AF_INET :
#ifdef ENABLE_IPV6
AF_INET6;
#else
AF_UNSPEC;
#endif
- conn->tempfamily[1] = AF_UNSPEC;
-
- ainext(conn, 0, FALSE); /* find first address of the right type */
+ ai_family1 = AF_UNSPEC;
}
- ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
-
- DEBUGF(infof(data, "family0 == %s, family1 == %s",
- conn->tempfamily[0] == AF_INET ? "v4" : "v6",
- conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
+ /* Get the first address in the list that matches the family,
+ * this might give NULL, if we do not have any matches. */
+ addr0 = addr_first_match(remotehost->addr, ai_family0);
+ addr1 = addr_first_match(remotehost->addr, ai_family1);
+ if(!addr0 && addr1) {
+ /* switch around, so a single baller always uses addr0 */
+ addr0 = addr1;
+ ai_family0 = ai_family1;
+ addr1 = NULL;
+ }
- /* get through the list in family order in case of quick failures */
- for(i = 0; (i < 2) && result; i++) {
- while(conn->tempaddr[i]) {
- result = singleipconnect(data, conn, conn->tempaddr[i], i);
- if(!result)
- break;
- ainext(conn, i, TRUE);
- }
+ /* We found no address that matches our criteria, we cannot connect */
+ if(!addr0) {
+ return CURLE_COULDNT_CONNECT;
}
+
+ memset(ctx->baller, 0, sizeof(ctx->baller));
+ result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
+ NULL, 0, /* no primary/delay, start now */
+ timeout_ms, EXPIRE_DNS_PER_NAME);
if(result)
return result;
+ DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)",
+ ctx->baller[0]->name, ctx->baller[0]->timeoutms));
+ if(addr1) {
+ /* second one gets a delayed start */
+ result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
+ ctx->baller[0], /* wait on that to fail */
+ /* or start this delayed */
+ data->set.happy_eyeballs_timeout,
+ timeout_ms, EXPIRE_DNS_PER_NAME2);
+ if(result)
+ return result;
+ DEBUGF(LOG_CF(data, cf, "created %s (timeout %ldms)",
+ ctx->baller[1]->name, ctx->baller[1]->timeoutms));
+ }
Curl_expire(data, data->set.happy_eyeballs_timeout,
EXPIRE_HAPPY_EYEBALLS);
@@ -1369,310 +822,366 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
return CURLE_OK;
}
-struct connfind {
- long id_tofind;
- struct connectdata *found;
-};
-
-static int conn_is_conn(struct Curl_easy *data,
- struct connectdata *conn, void *param)
+static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct connfind *f = (struct connfind *)param;
- (void)data;
- if(conn->connection_id == f->id_tofind) {
- f->found = conn;
- return 1;
+ struct cf_he_ctx *ctx = cf->ctx;
+ size_t i;
+
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(data);
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ baller_free(ctx->baller[i], data);
+ ctx->baller[i] = NULL;
}
- return 0;
+ baller_free(ctx->winner, data);
+ ctx->winner = NULL;
}
-/*
- * Used to extract socket and connectdata struct for the most recent
- * transfer on the given Curl_easy.
- *
- * The returned socket will be CURL_SOCKET_BAD in case of failure!
- */
-curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
- struct connectdata **connp)
+static int cf_he_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
{
- DEBUGASSERT(data);
+ struct cf_he_ctx *ctx = cf->ctx;
+ size_t i, s;
+ int wrc, rc = GETSOCK_BLANK;
+ curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE];
- /* this works for an easy handle:
- * - that has been used for curl_easy_perform()
- * - that is associated with a multi handle, and whose connection
- * was detached with CURLOPT_CONNECT_ONLY
- */
- if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
- struct connectdata *c;
- struct connfind find;
- find.id_tofind = data->state.lastconnect_id;
- find.found = NULL;
+ if(cf->connected)
+ return cf->next->cft->get_select_socks(cf->next, data, socks);
- Curl_conncache_foreach(data,
- data->share && (data->share->specifier
- & (1<< CURL_LOCK_DATA_CONNECT))?
- &data->share->conn_cache:
- data->multi_easy?
- &data->multi_easy->conn_cache:
- &data->multi->conn_cache, &find, conn_is_conn);
+ for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ if(!baller || !baller->cf)
+ continue;
- if(!find.found) {
- data->state.lastconnect_id = -1;
- return CURL_SOCKET_BAD;
+ wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks);
+ if(wrc) {
+ /* TODO: we assume we get at most one socket back */
+ socks[s] = wsocks[0];
+ if(wrc & GETSOCK_WRITESOCK(0))
+ rc |= GETSOCK_WRITESOCK(s);
+ if(wrc & GETSOCK_READSOCK(0))
+ rc |= GETSOCK_READSOCK(s);
+ s++;
}
-
- c = find.found;
- if(connp)
- /* only store this if the caller cares for it */
- *connp = c;
- return c->sock[FIRSTSOCKET];
}
- return CURL_SOCKET_BAD;
+ return rc;
}
-/*
- * Check if a connection seems to be alive.
- */
-bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode cf_he_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- (void)data;
- /* First determine if ssl */
- if(Curl_conn_is_ssl(data, FIRSTSOCKET)) {
- /* use the SSL context */
- if(!Curl_ssl_check_cxn(data, conn))
- return false; /* FIN received */
+ struct cf_he_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
}
-/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
-#ifdef MSG_PEEK
- else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
- return false;
- else {
- /* use the socket */
- char buf;
- if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
- (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
- return false; /* FIN received */
- }
+
+ (void)blocking; /* TODO: do we want to support this? */
+ DEBUGASSERT(ctx);
+ *done = FALSE;
+
+ switch(ctx->state) {
+ case SCFST_INIT:
+ DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
+ DEBUGASSERT(!cf->connected);
+ result = start_connect(cf, data, ctx->remotehost);
+ if(result)
+ return result;
+ ctx->state = SCFST_WAITING;
+ /* FALLTHROUGH */
+ case SCFST_WAITING:
+ result = is_connected(cf, data, done);
+ if(!result && *done) {
+ DEBUGASSERT(ctx->winner);
+ DEBUGASSERT(ctx->winner->cf);
+ DEBUGASSERT(ctx->winner->cf->connected);
+ /* we have a winner. Install and activate it.
+ * close/free all others. */
+ ctx->state = SCFST_DONE;
+ cf->connected = TRUE;
+ cf->next = ctx->winner->cf;
+ ctx->winner->cf = NULL;
+ cf_he_ctx_clear(cf, data);
+ Curl_conn_cf_cntrl(cf->next, data, TRUE,
+ CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
+
+ if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+ Curl_verboseconnect(data, cf->conn);
+ data->info.numconnects++; /* to track the # of connections made */
+ }
+ break;
+ case SCFST_DONE:
+ *done = TRUE;
+ break;
}
-#endif
- return true;
+ return result;
}
-/*
- * Close a socket.
- *
- * 'conn' can be NULL, beware!
- */
-int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sock)
+static void cf_he_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- if(conn && conn->fclosesocket) {
- if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
- /* if this socket matches the second socket, and that was created with
- accept, then we MUST NOT call the callback but clear the accepted
- status */
- conn->bits.sock_accepted = FALSE;
- else {
- int rc;
- Curl_multi_closed(data, sock);
- Curl_set_in_callback(data, true);
- rc = conn->fclosesocket(conn->closesocket_client, sock);
- Curl_set_in_callback(data, false);
- return rc;
- }
- }
-
- if(conn)
- /* tell the multi-socket code about this */
- Curl_multi_closed(data, sock);
+ struct cf_he_ctx *ctx = cf->ctx;
- sclose(sock);
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf_he_ctx_clear(cf, data);
+ cf->connected = FALSE;
+ ctx->state = SCFST_INIT;
- return 0;
+ if(cf->next) {
+ cf->next->cft->close(cf->next, data);
+ Curl_conn_cf_discard_chain(&cf->next, data);
+ }
}
-/*
- * Create a socket based on info from 'conn' and 'ai'.
- *
- * 'addr' should be a pointer to the correct struct to get data back, or NULL.
- * 'sockfd' must be a pointer to a socket descriptor.
- *
- * If the open socket callback is set, used that!
- *
- */
-CURLcode Curl_socket(struct Curl_easy *data,
- const struct Curl_addrinfo *ai,
- struct Curl_sockaddr_ex *addr,
- curl_socket_t *sockfd)
+static bool cf_he_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- struct Curl_sockaddr_ex dummy;
-
- if(!addr)
- /* if the caller doesn't want info back, use a local temp copy */
- addr = &dummy;
-
- /*
- * The Curl_sockaddr_ex structure is basically libcurl's external API
- * curl_sockaddr structure with enough space available to directly hold
- * any protocol-specific address structures. The variable declared here
- * will be used to pass / receive data to/from the fopensocket callback
- * if this has been set, before that, it is initialized from parameters.
- */
+ struct cf_he_ctx *ctx = cf->ctx;
+ size_t i;
- addr->family = ai->ai_family;
- switch(conn->transport) {
- case TRNSPRT_TCP:
- addr->socktype = SOCK_STREAM;
- addr->protocol = IPPROTO_TCP;
- break;
- case TRNSPRT_UNIX:
- addr->socktype = SOCK_STREAM;
- addr->protocol = IPPROTO_IP;
- break;
- default: /* UDP and QUIC */
- addr->socktype = SOCK_DGRAM;
- addr->protocol = IPPROTO_UDP;
- break;
- }
- addr->addrlen = ai->ai_addrlen;
-
- if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
- addr->addrlen = sizeof(struct Curl_sockaddr_storage);
- memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
-
- if(data->set.fopensocket) {
- /*
- * If the opensocket callback is set, all the destination address
- * information is passed to the callback. Depending on this information the
- * callback may opt to abort the connection, this is indicated returning
- * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
- * the callback returns a valid socket the destination address information
- * might have been changed and this 'new' address will actually be used
- * here to connect.
- */
- Curl_set_in_callback(data, true);
- *sockfd = data->set.fopensocket(data->set.opensocket_client,
- CURLSOCKTYPE_IPCXN,
- (struct curl_sockaddr *)addr);
- Curl_set_in_callback(data, false);
+ if(cf->connected)
+ return cf->next->cft->has_data_pending(cf->next, data);
+
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ if(!baller || !baller->cf)
+ continue;
+ if(baller->cf->cft->has_data_pending(baller->cf, data))
+ return TRUE;
}
- else
- /* opensocket callback not set, so simply create the socket now */
- *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+ return FALSE;
+}
- if(*sockfd == CURL_SOCKET_BAD)
- /* no socket, no connection */
- return CURLE_COULDNT_CONNECT;
+static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query)
+{
+ struct cf_he_ctx *ctx = cf->ctx;
+ struct curltime t, tmax;
+ size_t i;
+
+ memset(&tmax, 0, sizeof(tmax));
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+
+ memset(&t, 0, sizeof(t));
+ if(baller && baller->cf &&
+ !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
+ if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+ tmax = t;
+ }
+ }
+ return tmax;
+}
- if(conn->transport == TRNSPRT_QUIC) {
- /* QUIC sockets need to be nonblocking */
- (void)curlx_nonblock(*sockfd, TRUE);
- switch(addr->family) {
-#if defined(__linux__) && defined(IP_MTU_DISCOVER)
- case AF_INET: {
- int val = IP_PMTUDISC_DO;
- (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
- sizeof(val));
- break;
+static CURLcode cf_he_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_he_ctx *ctx = cf->ctx;
+
+ if(!cf->connected) {
+ switch(query) {
+ case CF_QUERY_CONNECT_REPLY_MS: {
+ int reply_ms = -1;
+ size_t i;
+
+ for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+ struct eyeballer *baller = ctx->baller[i];
+ int breply_ms;
+
+ if(baller && baller->cf &&
+ !baller->cf->cft->query(baller->cf, data, query,
+ &breply_ms, NULL)) {
+ if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
+ reply_ms = breply_ms;
+ }
+ }
+ *pres1 = reply_ms;
+ DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1));
+ return CURLE_OK;
}
-#endif
-#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
- case AF_INET6: {
- int val = IPV6_PMTUDISC_DO;
- (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
- sizeof(val));
- break;
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
+ return CURLE_OK;
}
-#endif
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
+ return CURLE_OK;
+ }
+ default:
+ break;
}
}
-#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
- if(conn->scope_id && (addr->family == AF_INET6)) {
- struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
- sa6->sin6_scope_id = conn->scope_id;
- }
-#endif
-
- return CURLE_OK;
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
}
-/*
- * Curl_conncontrol() marks streams or connection for closure.
- */
-void Curl_conncontrol(struct connectdata *conn,
- int ctrl /* see defines in header */
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- , const char *reason
-#endif
- )
+static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- /* close if a connection, or a stream that isn't multiplexed. */
- /* This function will be called both before and after this connection is
- associated with a transfer. */
- bool closeit;
- DEBUGASSERT(conn);
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- (void)reason; /* useful for debugging */
-#endif
- closeit = (ctrl == CONNCTRL_CONNECTION) ||
- ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
- if((ctrl == CONNCTRL_STREAM) &&
- (conn->handler->flags & PROTOPT_STREAM))
- ;
- else if((bit)closeit != conn->bits.close) {
- conn->bits.close = closeit; /* the only place in the source code that
- should assign this bit */
+ struct cf_he_ctx *ctx = cf->ctx;
+
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ if(ctx) {
+ cf_he_ctx_clear(cf, data);
}
+ /* release any resources held in state */
+ Curl_safefree(ctx);
}
-typedef enum {
- SCFST_INIT,
- SCFST_WAITING,
- SCFST_DONE
-} cf_connect_state;
-
-struct socket_cf_ctx {
- const struct Curl_dns_entry *remotehost;
- cf_connect_state state;
+struct Curl_cftype Curl_cft_happy_eyeballs = {
+ "HAPPY-EYEBALLS",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_he_destroy,
+ cf_he_connect,
+ cf_he_close,
+ Curl_cf_def_get_host,
+ cf_he_get_select_socks,
+ cf_he_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_he_query,
};
-static int socket_cf_get_select_socks(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- curl_socket_t *socks)
+CURLcode Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ cf_ip_connect_create *cf_create,
+ const struct Curl_dns_entry *remotehost,
+ int transport)
{
- struct connectdata *conn = cf->conn;
- int i, s, rc = GETSOCK_BLANK;
+ struct cf_he_ctx *ctx = NULL;
+ CURLcode result;
(void)data;
- if(cf->connected) {
- return rc;
+ (void)conn;
+ *pcf = NULL;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->transport = transport;
+ ctx->cf_create = cf_create;
+ ctx->remotehost = remotehost;
+
+ result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
+
+out:
+ if(result) {
+ Curl_safefree(*pcf);
+ Curl_safefree(ctx);
}
+ return result;
+}
- for(i = s = 0; i<2; i++) {
- if(conn->tempsock[i] != CURL_SOCKET_BAD) {
- socks[s] = conn->tempsock[i];
- rc |= GETSOCK_WRITESOCK(s);
+struct transport_provider {
+ int transport;
+ cf_ip_connect_create *cf_create;
+};
+
+static
+#ifndef DEBUGBUILD
+const
+#endif
+struct transport_provider transport_providers[] = {
+ { TRNSPRT_TCP, Curl_cf_tcp_create },
#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC)
- /* when connecting QUIC, we want to read the socket too */
- rc |= GETSOCK_READSOCK(s);
+ { TRNSPRT_QUIC, Curl_cf_quic_create },
#endif
- s++;
+ { TRNSPRT_UDP, Curl_cf_udp_create },
+ { TRNSPRT_UNIX, Curl_cf_unix_create },
+};
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+static cf_ip_connect_create *get_cf_create(int transport)
+{
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ if(transport == transport_providers[i].transport)
+ return transport_providers[i].cf_create;
+ }
+ return NULL;
+}
+
+#ifdef DEBUGBUILD
+void Curl_debug_set_transport_provider(int transport,
+ cf_ip_connect_create *cf_create)
+{
+ size_t i;
+ for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ if(transport == transport_providers[i].transport) {
+ transport_providers[i].cf_create = cf_create;
+ return;
}
}
+}
+#endif /* DEBUGBUILD */
- return rc;
+static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport)
+{
+ cf_ip_connect_create *cf_create;
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ /* Need to be first */
+ DEBUGASSERT(cf_at);
+ cf_create = get_cf_create(transport);
+ if(!cf_create) {
+ DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport));
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ result = Curl_cf_happy_eyeballs_create(&cf, data, cf_at->conn,
+ cf_create, remotehost,
+ transport);
+ if(result)
+ return result;
+
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return CURLE_OK;
}
-static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking, bool *done)
+typedef enum {
+ CF_SETUP_INIT,
+ CF_SETUP_CNNCT_EYEBALLS,
+ CF_SETUP_CNNCT_SOCKS,
+ CF_SETUP_CNNCT_HTTP_PROXY,
+ CF_SETUP_CNNCT_HAPROXY,
+ CF_SETUP_CNNCT_SSL,
+ CF_SETUP_DONE
+} cf_setup_state;
+
+struct cf_setup_ctx {
+ cf_setup_state state;
+ const struct Curl_dns_entry *remotehost;
+ int ssl_mode;
+ int transport;
+};
+
+static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
{
- struct connectdata *conn = cf->conn;
- int sockindex = cf->sockindex;
- struct socket_cf_ctx *ctx = cf->ctx;
+ struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(cf->connected) {
@@ -1680,253 +1189,241 @@ static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- (void)blocking;
- DEBUGASSERT(ctx);
- *done = FALSE;
- switch(ctx->state) {
- case SCFST_INIT:
- DEBUGASSERT(CURL_SOCKET_BAD == conn->sock[sockindex]);
- DEBUGASSERT(!cf->connected);
- result = Curl_connecthost(data, conn, ctx->remotehost);
- if(!result)
- ctx->state = SCFST_WAITING;
- break;
- case SCFST_WAITING:
- result = is_connected(data, conn, sockindex, done);
- if(!result && *done) {
- Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
- if(Curl_conn_is_ssl(data, FIRSTSOCKET) ||
- (conn->handler->protocol & PROTO_FAMILY_SSH))
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
- post_connect(data, conn, sockindex);
- ctx->state = SCFST_DONE;
- cf->connected = TRUE;
- }
- break;
- case SCFST_DONE:
- *done = TRUE;
- break;
+ /* connect current sub-chain */
+connect_sub_chain:
+ if(cf->next && !cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
}
- return result;
-}
-static CURLcode socket_cf_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
-{
- struct socket_cf_ctx *ctx = cf->ctx;
+ if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
+ result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
+ if(result)
+ return result;
+ ctx->state = CF_SETUP_CNNCT_EYEBALLS;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
+ }
- (void)data;
- DEBUGASSERT(ctx);
- if(ctx->remotehost != remotehost) {
- if(ctx->remotehost) {
- /* switching dns entry? TODO: reset? */
- }
- ctx->remotehost = remotehost;
+ /* sub-chain connected, do we need to add more? */
+#ifndef CURL_DISABLE_PROXY
+ if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
+ result = Curl_cf_socks_proxy_insert_after(cf, data);
+ if(result)
+ return result;
+ ctx->state = CF_SETUP_CNNCT_SOCKS;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
}
- DEBUGF(infof(data, CFMSG(cf, "setup(remotehost=%s)"),
- cf->conn->hostname_resolve));
- return CURLE_OK;
-}
-static void socket_cf_close(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- int sockindex = cf->sockindex;
- struct socket_cf_ctx *ctx = cf->ctx;
-
- DEBUGASSERT(ctx);
- /* close possibly still open sockets */
- if(CURL_SOCKET_BAD != cf->conn->sock[sockindex]) {
- Curl_closesocket(data, cf->conn, cf->conn->sock[sockindex]);
- cf->conn->sock[sockindex] = CURL_SOCKET_BAD;
+ if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
+#ifdef USE_SSL
+ if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS
+ && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
+ result = Curl_cf_ssl_proxy_insert_after(cf, data);
+ if(result)
+ return result;
+ }
+#endif /* USE_SSL */
+
+#if !defined(CURL_DISABLE_HTTP)
+ if(cf->conn->bits.tunnel_proxy) {
+ result = Curl_cf_http_proxy_insert_after(cf, data);
+ if(result)
+ return result;
+ }
+#endif /* !CURL_DISABLE_HTTP */
+ ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
}
- if(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) {
- Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]);
- cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD;
+#endif /* !CURL_DISABLE_PROXY */
+
+ if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
+#if !defined(CURL_DISABLE_PROXY)
+ if(data->set.haproxyprotocol) {
+ if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
+ failf(data, "haproxy protocol not support with SSL "
+ "encryption in place (QUIC?)");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ result = Curl_cf_haproxy_insert_after(cf, data);
+ if(result)
+ return result;
+ }
+#endif /* !CURL_DISABLE_PROXY */
+ ctx->state = CF_SETUP_CNNCT_HAPROXY;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
}
- cf->connected = FALSE;
- ctx->state = SCFST_INIT;
-}
-
-static void socket_cf_get_host(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char **phost,
- const char **pdisplay_host,
- int *pport)
-{
- (void)data;
- *phost = cf->conn->host.name;
- *pdisplay_host = cf->conn->host.dispname;
- *pport = cf->conn->port;
-}
-static bool socket_cf_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
-{
- int readable;
- (void)data;
- DEBUGASSERT(cf);
+ if(ctx->state < CF_SETUP_CNNCT_SSL) {
+#ifdef USE_SSL
+ if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
+ || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
+ && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
+ && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
+ result = Curl_cf_ssl_insert_after(cf, data);
+ if(result)
+ return result;
+ }
+#endif /* USE_SSL */
+ ctx->state = CF_SETUP_CNNCT_SSL;
+ if(!cf->next || !cf->next->connected)
+ goto connect_sub_chain;
+ }
- readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
- return (readable > 0 && (readable & CURL_CSELECT_IN));
+ ctx->state = CF_SETUP_DONE;
+ cf->connected = TRUE;
+ *done = TRUE;
+ return CURLE_OK;
}
-static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
- const void *buf, size_t len, CURLcode *err)
+static void cf_setup_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- ssize_t nwritten;
- nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
- return nwritten;
-}
+ struct cf_setup_ctx *ctx = cf->ctx;
-static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
- char *buf, size_t len, CURLcode *err)
-{
- ssize_t nread;
- nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
- return nread;
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf->connected = FALSE;
+ ctx->state = CF_SETUP_INIT;
+
+ if(cf->next) {
+ cf->next->cft->close(cf->next, data);
+ Curl_conn_cf_discard_chain(&cf->next, data);
+ }
}
-static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct socket_cf_ctx *state = cf->ctx;
+ struct cf_setup_ctx *ctx = cf->ctx;
(void)data;
- if(cf->connected) {
- socket_cf_close(cf, data);
- }
- /* release any resources held in state */
- Curl_safefree(state);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ Curl_safefree(ctx);
}
-static const struct Curl_cftype cft_socket = {
- "SOCKET",
- CF_TYPE_IP_CONNECT,
- socket_cf_destroy,
- socket_cf_setup,
- socket_cf_connect,
- socket_cf_close,
- socket_cf_get_host,
- socket_cf_get_select_socks,
- socket_cf_data_pending,
- socket_cf_send,
- socket_cf_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
+
+struct Curl_cftype Curl_cft_setup = {
+ "SETUP",
+ 0,
+ CURL_LOG_DEFAULT,
+ cf_setup_destroy,
+ cf_setup_connect,
+ cf_setup_close,
+ Curl_cf_def_get_host,
+ Curl_cf_def_get_select_socks,
+ Curl_cf_def_data_pending,
+ Curl_cf_def_send,
+ Curl_cf_def_recv,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
-CURLcode Curl_conn_socket_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- CURLcode result;
struct Curl_cfilter *cf = NULL;
- struct socket_cf_ctx *scf_ctx = NULL;
+ struct cf_setup_ctx *ctx;
+ CURLcode result = CURLE_OK;
- /* Need to be first */
- DEBUGASSERT(conn);
- DEBUGASSERT(!conn->cfilter[sockindex]);
- scf_ctx = calloc(sizeof(*scf_ctx), 1);
- if(!scf_ctx) {
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- result = Curl_cf_create(&cf, &cft_socket, scf_ctx);
+ ctx->state = CF_SETUP_INIT;
+ ctx->remotehost = remotehost;
+ ctx->ssl_mode = ssl_mode;
+ ctx->transport = transport;
+
+ result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
if(result)
goto out;
- Curl_conn_cf_add(data, conn, sockindex, cf);
+ ctx = NULL;
out:
- if(result) {
- Curl_safefree(cf);
- Curl_safefree(scf_ctx);
- }
+ *pcf = result? NULL : cf;
+ free(ctx);
return result;
}
-static CURLcode socket_accept_cf_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking, bool *done)
+CURLcode Curl_cf_setup_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- /* we start accepted, if we ever close, we cannot go on */
- (void)data;
- (void)blocking;
- if(cf->connected) {
- *done = TRUE;
- return CURLE_OK;
- }
- return CURLE_FAILED_INIT;
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(data);
+ result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+out:
+ return result;
}
-static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
+CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode)
{
- /* we start accepted, if we ever close, we cannot go on */
- (void)data;
- (void)remotehost;
- if(cf->connected) {
- return CURLE_OK;
- }
- return CURLE_FAILED_INIT;
-}
+ struct Curl_cfilter *cf;
+ CURLcode result;
-static const struct Curl_cftype cft_socket_accept = {
- "SOCKET-ACCEPT",
- CF_TYPE_IP_CONNECT,
- socket_cf_destroy,
- socket_accept_cf_setup,
- socket_accept_cf_connect,
- socket_cf_close,
- socket_cf_get_host, /* TODO: not accurate */
- Curl_cf_def_get_select_socks,
- socket_cf_data_pending,
- socket_cf_send,
- socket_cf_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
-};
+ DEBUGASSERT(data);
+ result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+out:
+ return result;
+}
-CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex, curl_socket_t *s)
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode)
{
- CURLcode result;
- struct Curl_cfilter *cf = NULL;
- struct socket_cf_ctx *scf_ctx = NULL;
+ CURLcode result = CURLE_OK;
- cf = conn->cfilter[sockindex];
- if(cf && cf->cft == &cft_socket_accept) {
- /* already an accept filter installed, just replace the socket */
- scf_ctx = cf->ctx;
- result = CURLE_OK;
- }
- else {
- /* replace any existing */
- Curl_conn_cf_discard_all(data, conn, sockindex);
- scf_ctx = calloc(sizeof(*scf_ctx), 1);
- if(!scf_ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- result = Curl_cf_create(&cf, &cft_socket_accept, scf_ctx);
+ DEBUGASSERT(data);
+ DEBUGASSERT(conn->handler);
+
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+ if(!conn->cfilter[sockindex] &&
+ conn->handler->protocol == CURLPROTO_HTTPS &&
+ (ssl_mode == CURL_CF_SSL_ENABLE || ssl_mode != CURL_CF_SSL_DISABLE)) {
+
+ result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
if(result)
goto out;
- Curl_conn_cf_add(data, conn, sockindex, cf);
}
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
- /* close any existing socket and replace */
- Curl_closesocket(data, conn, conn->sock[sockindex]);
- conn->sock[sockindex] = *s;
- conn->bits.sock_accepted = TRUE;
- cf->connected = TRUE;
- scf_ctx->state = SCFST_DONE;
+ /* Still no cfilter set, apply default. */
+ if(!conn->cfilter[sockindex]) {
+ result = Curl_cf_setup_add(data, conn, sockindex, remotehost,
+ conn->transport, ssl_mode);
+ if(result)
+ goto out;
+ }
+ DEBUGASSERT(conn->cfilter[sockindex]);
out:
- if(result) {
- Curl_safefree(cf);
- Curl_safefree(scf_ctx);
- }
return result;
}
+
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h
index 1e90a8561e..e4fa10c4cb 100644
--- a/Utilities/cmcurl/lib/connect.h
+++ b/Utilities/cmcurl/lib/connect.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,9 +29,7 @@
#include "sockaddr.h"
#include "timeval.h"
-CURLcode Curl_connecthost(struct Curl_easy *data,
- struct connectdata *conn,
- const struct Curl_dns_entry *host);
+struct Curl_dns_entry;
/* generic function that returns how much time there's left to run, according
to the timeouts set */
@@ -53,67 +51,8 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
-/*
- * Check if a connection seems to be alive.
- */
-bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn);
-
-#ifdef USE_WINSOCK
-/* When you run a program that uses the Windows Sockets API, you may
- experience slow performance when you copy data to a TCP server.
-
- https://support.microsoft.com/kb/823764
-
- Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
- Buffer Size
-
-*/
-void Curl_sndbufset(curl_socket_t sockfd);
-#else
-#define Curl_sndbufset(y) Curl_nop_stmt
-#endif
-
-void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd);
-void Curl_conninfo_remote(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sockfd);
-void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
- char *local_ip, int *local_port);
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
char *local_ip, int local_port);
-int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t sock);
-
-/*
- * The Curl_sockaddr_ex structure is basically libcurl's external API
- * curl_sockaddr structure with enough space available to directly hold any
- * protocol-specific address structures. The variable declared here will be
- * used to pass / receive data to/from the fopensocket callback if this has
- * been set, before that, it is initialized from parameters.
- */
-struct Curl_sockaddr_ex {
- int family;
- int socktype;
- int protocol;
- unsigned int addrlen;
- union {
- struct sockaddr addr;
- struct Curl_sockaddr_storage buff;
- } _sa_ex_u;
-};
-#define sa_addr _sa_ex_u.addr
-
-/*
- * Create a socket based on info from 'conn' and 'ai'.
- *
- * Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
- * socket callback is set, used that!
- *
- */
-CURLcode Curl_socket(struct Curl_easy *data,
- const struct Curl_addrinfo *ai,
- struct Curl_sockaddr_ex *addr,
- curl_socket_t *sockfd);
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
@@ -148,13 +87,71 @@ void Curl_conncontrol(struct connectdata *conn,
#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP)
#endif
-CURLcode Curl_conn_socket_set(struct Curl_easy *data,
+/**
+ * Create a cfilter for making an "ip" connection to the
+ * given address, using parameters from `conn`. The "ip" connection
+ * can be a TCP socket, a UDP socket or even a QUIC connection.
+ *
+ * It MUST use only the supplied `ai` for its connection attempt.
+ *
+ * Such a filter may be used in "happy eyeball" scenarios, and its
+ * `connect` implementation needs to support non-blocking. Once connected,
+ * it MAY be installed in the connection filter chain to serve transfers.
+ */
+typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+/**
+ * Create a happy eyeball connection filter that uses the, once resolved,
+ * address information to connect on ip families based on connection
+ * configuration.
+ * @param pcf output, the created cfilter
+ * @param data easy handle used in creation
+ * @param conn connection the filter is created for
+ * @param cf_create method to create the sub-filters performing the
+ * actual connects.
+ */
+CURLcode
+Curl_cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
struct connectdata *conn,
- int sockindex);
-
-CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- curl_socket_t *s);
+ cf_ip_connect_create *cf_create,
+ const struct Curl_dns_entry *remotehost,
+ int transport);
+
+CURLcode Curl_cf_setup_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode);
+
+CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost,
+ int transport,
+ int ssl_mode);
+
+/**
+ * Setup the cfilters at `sockindex` in connection `conn`.
+ * If no filter chain is installed yet, inspects the configuration
+ * in `data` and `conn? to install a suitable filter chain.
+ */
+CURLcode Curl_conn_setup(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ const struct Curl_dns_entry *remotehost,
+ int ssl_mode);
+
+extern struct Curl_cftype Curl_cft_happy_eyeballs;
+extern struct Curl_cftype Curl_cft_setup;
+
+#ifdef DEBUGBUILD
+void Curl_debug_set_transport_provider(int transport,
+ cf_ip_connect_create *cf_create);
+#endif
#endif /* HEADER_CURL_CONNECT_H */
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index 9e345b197e..aa1e0cbee8 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,7 +33,15 @@
#endif
#ifdef HAVE_BROTLI
+#if defined(__GNUC__)
+/* Ignore -Wvla warnings in brotli headers */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvla"
+#endif
#include <brotli/decode.h>
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
#endif
#ifdef HAVE_ZSTD
@@ -977,7 +985,8 @@ static const struct content_encoding error_encoding = {
static struct contenc_writer *
new_unencoding_writer(struct Curl_easy *data,
const struct content_encoding *handler,
- struct contenc_writer *downstream)
+ struct contenc_writer *downstream,
+ int order)
{
struct contenc_writer *writer;
@@ -987,6 +996,7 @@ new_unencoding_writer(struct Curl_easy *data,
if(writer) {
writer->handler = handler;
writer->downstream = downstream;
+ writer->order = order;
if(handler->init_writer(data, writer)) {
free(writer);
writer = NULL;
@@ -1042,10 +1052,10 @@ static const struct content_encoding *find_encoding(const char *name,
/* Set-up the unencoding stack from the Content-Encoding header value.
* See RFC 7231 section 3.1.2.2. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked)
+ const char *enclist, int is_transfer)
{
struct SingleRequest *k = &data->req;
- int counter = 0;
+ unsigned int order = is_transfer? 2: 1;
do {
const char *name;
@@ -1062,7 +1072,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
namelen = enclist - name + 1;
/* Special case: chunked encoding is handled at the reader level. */
- if(maybechunked && namelen == 7 && strncasecompare(name, "chunked", 7)) {
+ if(is_transfer && namelen == 7 && strncasecompare(name, "chunked", 7)) {
k->chunk = TRUE; /* chunks coming our way. */
Curl_httpchunk_init(data); /* init our chunky engine. */
}
@@ -1071,7 +1081,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
struct contenc_writer *writer;
if(!k->writer_stack) {
- k->writer_stack = new_unencoding_writer(data, &client_encoding, NULL);
+ k->writer_stack = new_unencoding_writer(data, &client_encoding,
+ NULL, 0);
if(!k->writer_stack)
return CURLE_OUT_OF_MEMORY;
@@ -1080,16 +1091,29 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
if(!encoding)
encoding = &error_encoding; /* Defer error at stack use. */
- if(++counter >= MAX_ENCODE_STACK) {
- failf(data, "Reject response due to %u content encodings",
- counter);
+ if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) {
+ failf(data, "Reject response due to more than %u content encodings",
+ MAX_ENCODE_STACK);
return CURLE_BAD_CONTENT_ENCODING;
}
/* Stack the unencoding stage. */
- writer = new_unencoding_writer(data, encoding, k->writer_stack);
- if(!writer)
- return CURLE_OUT_OF_MEMORY;
- k->writer_stack = writer;
+ if(order >= k->writer_stack->order) {
+ writer = new_unencoding_writer(data, encoding,
+ k->writer_stack, order);
+ if(!writer)
+ return CURLE_OUT_OF_MEMORY;
+ k->writer_stack = writer;
+ }
+ else {
+ struct contenc_writer *w = k->writer_stack;
+ while(w->downstream && order < w->downstream->order)
+ w = w->downstream;
+ writer = new_unencoding_writer(data, encoding,
+ w->downstream, order);
+ if(!writer)
+ return CURLE_OUT_OF_MEMORY;
+ w->downstream = writer;
+ }
}
} while(*enclist);
@@ -1099,11 +1123,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
#else
/* Stubs for builds without HTTP. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked)
+ const char *enclist, int is_transfer)
{
(void) data;
(void) enclist;
- (void) maybechunked;
+ (void) is_transfer;
return CURLE_NOT_BUILT_IN;
}
diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h
index 3c278cf727..56e7f97f70 100644
--- a/Utilities/cmcurl/lib/content_encoding.h
+++ b/Utilities/cmcurl/lib/content_encoding.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
struct contenc_writer {
const struct content_encoding *handler; /* Encoding handler. */
struct contenc_writer *downstream; /* Downstream writer. */
+ unsigned int order; /* Ordering within writer stack. */
};
/* Content encoding writer. */
@@ -46,7 +47,7 @@ struct content_encoding {
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
- const char *enclist, int maybechunked);
+ const char *enclist, int is_transfer);
CURLcode Curl_unencode_write(struct Curl_easy *data,
struct contenc_writer *writer,
const char *buf, size_t nbytes);
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index bccf2e8882..0c6e0f7cda 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -101,13 +101,14 @@ Example set of cookies:
#include "parsedate.h"
#include "rename.h"
#include "fopen.h"
+#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-static void strstore(char **str, const char *newstr);
+static void strstore(char **str, const char *newstr, size_t len);
static void freecookie(struct Cookie *co)
{
@@ -122,15 +123,17 @@ static void freecookie(struct Cookie *co)
free(co);
}
-static bool tailmatch(const char *cooke_domain, const char *hostname)
+static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len,
+ const char *hostname)
{
- size_t cookie_domain_len = strlen(cooke_domain);
size_t hostname_len = strlen(hostname);
if(hostname_len < cookie_domain_len)
return FALSE;
- if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len))
+ if(!strncasecompare(cookie_domain,
+ hostname + hostname_len-cookie_domain_len,
+ cookie_domain_len))
return FALSE;
/*
@@ -176,7 +179,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
/* #-fragments are already cut off! */
if(0 == strlen(uri_path) || uri_path[0] != '/') {
- strstore(&uri_path, "/");
+ strstore(&uri_path, "/", 1);
if(!uri_path)
return FALSE;
}
@@ -310,7 +313,7 @@ static char *sanitize_cookie_path(const char *cookie_path)
/* RFC6265 5.2.4 The Path Attribute */
if(new_path[0] != '/') {
/* Let cookie-path be the default-path. */
- strstore(&new_path, "/");
+ strstore(&new_path, "/", 1);
return new_path;
}
@@ -329,14 +332,13 @@ static char *sanitize_cookie_path(const char *cookie_path)
*/
void Curl_cookie_loadfiles(struct Curl_easy *data)
{
- struct curl_slist *list = data->state.cookielist;
+ struct curl_slist *list = data->set.cookielist;
if(list) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
while(list) {
- struct CookieInfo *newcookies = Curl_cookie_init(data,
- list->data,
- data->cookies,
- data->set.cookiesession);
+ struct CookieInfo *newcookies =
+ Curl_cookie_init(data, list->data, data->cookies,
+ data->set.cookiesession);
if(!newcookies)
/*
* Failure may be due to OOM or a bad cookie; both are ignored
@@ -347,8 +349,6 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
data->cookies = newcookies;
list = list->next;
}
- curl_slist_free_all(data->state.cookielist); /* clean up list */
- data->state.cookielist = NULL; /* don't do this again! */
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
}
@@ -362,10 +362,14 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
* parsing in a last-wins scenario. The caller is responsible for checking
* for OOM errors.
*/
-static void strstore(char **str, const char *newstr)
+static void strstore(char **str, const char *newstr, size_t len)
{
+ DEBUGASSERT(newstr);
+ DEBUGASSERT(str);
free(*str);
- *str = strdup(newstr);
+ *str = Curl_memdup(newstr, len + 1);
+ if(*str)
+ (*str)[len] = 0;
}
/*
@@ -427,15 +431,19 @@ static void remove_expired(struct CookieInfo *cookies)
}
/* Make sure domain contains a dot or is localhost. */
-static bool bad_domain(const char *domain)
+static bool bad_domain(const char *domain, size_t len)
{
- if(strcasecompare(domain, "localhost"))
+ if((len == 9) && strncasecompare(domain, "localhost", 9))
return FALSE;
else {
/* there must be a dot present, but that dot must not be a trailing dot */
- char *dot = strchr(domain, '.');
- if(dot)
- return dot[1] ? FALSE : TRUE;
+ char *dot = memchr(domain, '.', len);
+ if(dot) {
+ size_t i = dot - domain;
+ if((len - i) > 1)
+ /* the dot is not the last byte */
+ return FALSE;
+ }
}
return TRUE;
}
@@ -515,10 +523,9 @@ Curl_cookie_add(struct Curl_easy *data,
if(httpheader) {
/* This line was read off an HTTP-header */
- char name[MAX_NAME];
- char what[MAX_NAME];
+ const char *namep;
+ const char *valuep;
const char *ptr;
- const char *semiptr;
size_t linelength = strlen(lineptr);
if(linelength > MAX_COOKIE_LINE) {
@@ -527,73 +534,65 @@ Curl_cookie_add(struct Curl_easy *data,
return NULL;
}
- semiptr = strchr(lineptr, ';'); /* first, find a semicolon */
-
- while(*lineptr && ISBLANK(*lineptr))
- lineptr++;
-
ptr = lineptr;
do {
- /* we have a <what>=<this> pair or a stand-alone word here */
- name[0] = what[0] = 0; /* init the buffers */
- if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%"
- MAX_NAME_TXT "[^;\r\n]",
- name, what)) {
- /*
- * Use strstore() below to properly deal with received cookie
- * headers that have the same string property set more than once,
- * and then we use the last one.
- */
- const char *whatptr;
+ size_t vlen;
+ size_t nlen;
+
+ while(*ptr && ISBLANK(*ptr))
+ ptr++;
+
+ /* we have a <name>=<value> pair or a stand-alone word here */
+ nlen = strcspn(ptr, ";\t\r\n=");
+ if(nlen) {
bool done = FALSE;
- bool sep;
- size_t len = strlen(what);
- size_t nlen = strlen(name);
- const char *endofn = &ptr[ nlen ];
+ bool sep = FALSE;
- /*
- * Check for too long individual name or contents, or too long
- * combination of name + contents. Chrome and Firefox support 4095 or
- * 4096 bytes combo
- */
- if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
- ((nlen + len) > MAX_NAME)) {
- freecookie(co);
- infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
- nlen, len);
- return NULL;
- }
+ namep = ptr;
+ ptr += nlen;
- /* name ends with a '=' ? */
- sep = (*endofn == '=')?TRUE:FALSE;
+ /* trim trailing spaces and tabs after name */
+ while(nlen && ISBLANK(namep[nlen - 1]))
+ nlen--;
- if(nlen) {
- endofn--; /* move to the last character */
- if(ISBLANK(*endofn)) {
- /* skip trailing spaces in name */
- while(*endofn && ISBLANK(*endofn) && nlen) {
- endofn--;
- nlen--;
- }
- name[nlen] = 0; /* new end of name */
+ if(*ptr == '=') {
+ vlen = strcspn(++ptr, ";\r\n");
+ valuep = ptr;
+ sep = TRUE;
+ ptr = &valuep[vlen];
+
+ /* Strip off trailing whitespace from the value */
+ while(vlen && ISBLANK(valuep[vlen-1]))
+ vlen--;
+
+ /* Skip leading whitespace from the value */
+ while(vlen && ISBLANK(*valuep)) {
+ valuep++;
+ vlen--;
}
- }
- /* Strip off trailing whitespace from the 'what' */
- while(len && ISBLANK(what[len-1])) {
- what[len-1] = 0;
- len--;
+ /* Reject cookies with a TAB inside the value */
+ if(memchr(valuep, '\t', vlen)) {
+ freecookie(co);
+ infof(data, "cookie contains TAB, dropping");
+ return NULL;
+ }
+ }
+ else {
+ valuep = NULL;
+ vlen = 0;
}
- /* Skip leading whitespace from the 'what' */
- whatptr = what;
- while(*whatptr && ISBLANK(*whatptr))
- whatptr++;
-
- /* Reject cookies with a TAB inside the content */
- if(strchr(whatptr, '\t')) {
+ /*
+ * Check for too long individual name or contents, or too long
+ * combination of name + contents. Chrome and Firefox support 4095 or
+ * 4096 bytes combo
+ */
+ if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
+ ((nlen + vlen) > MAX_NAME)) {
freecookie(co);
- infof(data, "cookie contains TAB, dropping");
+ infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
+ nlen, vlen);
return NULL;
}
@@ -603,13 +602,19 @@ Curl_cookie_add(struct Curl_easy *data,
* "the rest". Prefixes must start with '__' and end with a '-', so
* only test for names where that can possibly be true.
*/
- if(nlen > 3 && name[0] == '_' && name[1] == '_') {
- if(strncasecompare("__Secure-", name, 9))
+ if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
+ if(strncasecompare("__Secure-", namep, 9))
co->prefix |= COOKIE_PREFIX__SECURE;
- else if(strncasecompare("__Host-", name, 7))
+ else if(strncasecompare("__Host-", namep, 7))
co->prefix |= COOKIE_PREFIX__HOST;
}
+ /*
+ * Use strstore() below to properly deal with received cookie
+ * headers that have the same string property set more than once,
+ * and then we use the last one.
+ */
+
if(!co->name) {
/* The very first name/value pair is the actual cookie name */
if(!sep) {
@@ -617,20 +622,20 @@ Curl_cookie_add(struct Curl_easy *data,
badcookie = TRUE;
break;
}
- co->name = strdup(name);
- co->value = strdup(whatptr);
+ strstore(&co->name, namep, nlen);
+ strstore(&co->value, valuep, vlen);
done = TRUE;
if(!co->name || !co->value) {
badcookie = TRUE;
break;
}
- if(invalid_octets(whatptr) || invalid_octets(name)) {
+ if(invalid_octets(co->value) || invalid_octets(co->name)) {
infof(data, "invalid octets in name/value, cookie dropped");
badcookie = TRUE;
break;
}
}
- else if(!len) {
+ else if(!vlen) {
/*
* this was a "<name>=" with no content, and we must allow
* 'secure' and 'httponly' specified this weirdly
@@ -641,7 +646,7 @@ Curl_cookie_add(struct Curl_easy *data,
* using a secure protocol, or when the cookie is being set by
* reading from file
*/
- if(strcasecompare("secure", name)) {
+ if((nlen == 6) && strncasecompare("secure", namep, 6)) {
if(secure || !c->running) {
co->secure = TRUE;
}
@@ -650,7 +655,7 @@ Curl_cookie_add(struct Curl_easy *data,
break;
}
}
- else if(strcasecompare("httponly", name))
+ else if((nlen == 8) && strncasecompare("httponly", namep, 8))
co->httponly = TRUE;
else if(sep)
/* there was a '=' so we're not done parsing this field */
@@ -658,8 +663,8 @@ Curl_cookie_add(struct Curl_easy *data,
}
if(done)
;
- else if(strcasecompare("path", name)) {
- strstore(&co->path, whatptr);
+ else if((nlen == 4) && strncasecompare("path", namep, 4)) {
+ strstore(&co->path, valuep, vlen);
if(!co->path) {
badcookie = TRUE; /* out of memory bad */
break;
@@ -671,7 +676,8 @@ Curl_cookie_add(struct Curl_easy *data,
break;
}
}
- else if(strcasecompare("domain", name) && whatptr[0]) {
+ else if((nlen == 6) &&
+ strncasecompare("domain", namep, 6) && vlen) {
bool is_ip;
/*
@@ -679,8 +685,10 @@ Curl_cookie_add(struct Curl_easy *data,
* the given domain is not valid and thus cannot be set.
*/
- if('.' == whatptr[0])
- whatptr++; /* ignore preceding dot */
+ if('.' == valuep[0]) {
+ valuep++; /* ignore preceding dot */
+ vlen--;
+ }
#ifndef USE_LIBPSL
/*
@@ -688,16 +696,17 @@ Curl_cookie_add(struct Curl_easy *data,
* TLD or otherwise "protected" suffix. To reduce risk, we require a
* dot OR the exact host name being "localhost".
*/
- if(bad_domain(whatptr))
+ if(bad_domain(valuep, vlen))
domain = ":";
#endif
- is_ip = Curl_host_is_ipnum(domain ? domain : whatptr);
+ is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
if(!domain
- || (is_ip && !strcmp(whatptr, domain))
- || (!is_ip && tailmatch(whatptr, domain))) {
- strstore(&co->domain, whatptr);
+ || (is_ip && !strncmp(valuep, domain, vlen) &&
+ (vlen == strlen(domain)))
+ || (!is_ip && tailmatch(valuep, vlen, domain))) {
+ strstore(&co->domain, valuep, vlen);
if(!co->domain) {
badcookie = TRUE;
break;
@@ -713,17 +722,17 @@ Curl_cookie_add(struct Curl_easy *data,
*/
badcookie = TRUE;
infof(data, "skipped cookie with bad tailmatch domain: %s",
- whatptr);
+ valuep);
}
}
- else if(strcasecompare("version", name)) {
- strstore(&co->version, whatptr);
+ else if((nlen == 7) && strncasecompare("version", namep, 7)) {
+ strstore(&co->version, valuep, vlen);
if(!co->version) {
badcookie = TRUE;
break;
}
}
- else if(strcasecompare("max-age", name)) {
+ else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
/*
* Defined in RFC2109:
*
@@ -733,14 +742,14 @@ Curl_cookie_add(struct Curl_easy *data,
* client should discard the cookie. A value of zero means the
* cookie should be discarded immediately.
*/
- strstore(&co->maxage, whatptr);
+ strstore(&co->maxage, valuep, vlen);
if(!co->maxage) {
badcookie = TRUE;
break;
}
}
- else if(strcasecompare("expires", name)) {
- strstore(&co->expirestr, whatptr);
+ else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
+ strstore(&co->expirestr, valuep, vlen);
if(!co->expirestr) {
badcookie = TRUE;
break;
@@ -755,24 +764,13 @@ Curl_cookie_add(struct Curl_easy *data,
/* this is an "illegal" <what>=<this> pair */
}
- if(!semiptr || !*semiptr) {
- /* we already know there are no more cookies */
- semiptr = NULL;
- continue;
- }
-
- ptr = semiptr + 1;
while(*ptr && ISBLANK(*ptr))
ptr++;
- semiptr = strchr(ptr, ';'); /* now, find the next semicolon */
-
- if(!semiptr && *ptr)
- /*
- * There are no more semicolons, but there's a final name=value pair
- * coming up
- */
- semiptr = strchr(ptr, '\0');
- } while(semiptr);
+ if(*ptr == ';')
+ ptr++;
+ else
+ break;
+ } while(1);
if(co->maxage) {
CURLofft offt;
@@ -1059,7 +1057,7 @@ Curl_cookie_add(struct Curl_easy *data,
Curl_psl_release(data);
}
else
- acceptable = !bad_domain(domain);
+ acceptable = !bad_domain(domain, strlen(domain));
if(!acceptable) {
infof(data, "cookie '%s' dropped, domain '%s' must not "
@@ -1301,7 +1299,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
*/
remove_expired(c);
- if(fromfile && fp)
+ if(fromfile)
fclose(fp);
}
@@ -1449,7 +1447,8 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
/* now check if the domain is correct */
if(!co->domain ||
- (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+ (co->tailmatch && !is_ip &&
+ tailmatch(co->domain, co->domain? strlen(co->domain):0, host)) ||
((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
/*
* the right part of the host matches the domain stuff in the
@@ -1800,13 +1799,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
CURLcode res;
if(data->set.str[STRING_COOKIEJAR]) {
- if(data->state.cookielist) {
- /* If there is a list of cookie files to read, do it first so that
- we have all the told files read before we write the new jar.
- Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
- Curl_cookie_loadfiles(data);
- }
-
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
/* if we have a destination file for all the cookies to get dumped to */
@@ -1816,12 +1808,6 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
}
else {
- if(cleanup && data->state.cookielist) {
- /* since nothing is written, we can just free the list of cookie file
- names */
- curl_slist_free_all(data->state.cookielist); /* clean up list */
- data->state.cookielist = NULL;
- }
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
}
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index abc0a2e8a0..39bb08bc49 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c
index bcea883564..35a06350d8 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.c
+++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h
index b778121a7c..c757c49c5c 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.h
+++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h
index 85368a163c..806d4431cf 100644
--- a/Utilities/cmcurl/lib/curl_base64.h
+++ b/Utilities/cmcurl/lib/curl_base64.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index 9cde948f1c..6e2458dc8b 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h
index dc6b8cab86..1d1d60c28d 100644
--- a/Utilities/cmcurl/lib/curl_ctype.h
+++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,7 @@
#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f'))
#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F'))
-#define ISLOWCNTRL(x) ((x) >= 0 && ((x) <= 0x1f))
+#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f)
#define IS7F(x) ((x) == 0x7f)
#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d))
diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c
index a2bf648c29..5c623b35bc 100644
--- a/Utilities/cmcurl/lib/curl_des.c
+++ b/Utilities/cmcurl/lib/curl_des.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h
index c1c167471a..6ec450accc 100644
--- a/Utilities/cmcurl/lib/curl_des.h
+++ b/Utilities/cmcurl/lib/curl_des.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2015 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c
index 3cc7734f31..11c662a4c7 100644
--- a/Utilities/cmcurl/lib/curl_endian.c
+++ b/Utilities/cmcurl/lib/curl_endian.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_endian.h b/Utilities/cmcurl/lib/curl_endian.h
index 08d52e7c52..fa283214b1 100644
--- a/Utilities/cmcurl/lib/curl_endian.h
+++ b/Utilities/cmcurl/lib/curl_endian.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.c b/Utilities/cmcurl/lib/curl_fnmatch.c
index b8a85a96ec..5f9ca4f1be 100644
--- a/Utilities/cmcurl/lib/curl_fnmatch.c
+++ b/Utilities/cmcurl/lib/curl_fnmatch.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_fnmatch.h b/Utilities/cmcurl/lib/curl_fnmatch.h
index 8324be533d..595646ff0d 100644
--- a/Utilities/cmcurl/lib/curl_fnmatch.h
+++ b/Utilities/cmcurl/lib/curl_fnmatch.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c
index 0d8c285a1d..686abe7511 100644
--- a/Utilities/cmcurl/lib/curl_get_line.c
+++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_get_line.h b/Utilities/cmcurl/lib/curl_get_line.h
index b2a534d001..0ff32c5c2c 100644
--- a/Utilities/cmcurl/lib/curl_get_line.h
+++ b/Utilities/cmcurl/lib/curl_get_line.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gethostname.c b/Utilities/cmcurl/lib/curl_gethostname.c
index 4747e938db..706b2e6892 100644
--- a/Utilities/cmcurl/lib/curl_gethostname.c
+++ b/Utilities/cmcurl/lib/curl_gethostname.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gethostname.h b/Utilities/cmcurl/lib/curl_gethostname.h
index b7360969ac..9281d9c242 100644
--- a/Utilities/cmcurl/lib/curl_gethostname.h
+++ b/Utilities/cmcurl/lib/curl_gethostname.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c
index 01ab48ec3e..c6fe1256b2 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.c
+++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -34,10 +34,16 @@
#include "curl_memory.h"
#include "memdebug.h"
-gss_OID_desc Curl_spnego_mech_oid = {
+#if defined(__GNUC__)
+#define CURL_ALIGN8 __attribute__ ((aligned(8)))
+#else
+#define CURL_ALIGN8
+#endif
+
+gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
6, (char *)"\x2b\x06\x01\x05\x05\x02"
};
-gss_OID_desc Curl_krb5_mech_oid = {
+gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
};
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h
index b4ed482aa2..7b9a534ea2 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.h
+++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h
index 36c0bd62e5..11625c0cb4 100644
--- a/Utilities/cmcurl/lib/curl_hmac.h
+++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_krb5.h b/Utilities/cmcurl/lib/curl_krb5.h
index ccd6f10e3d..ccf6b96a87 100644
--- a/Utilities/cmcurl/lib/curl_krb5.h
+++ b/Utilities/cmcurl/lib/curl_krb5.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h
index ba3ede4260..8a1d807ed9 100644
--- a/Utilities/cmcurl/lib/curl_ldap.h
+++ b/Utilities/cmcurl/lib/curl_ldap.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_log.c b/Utilities/cmcurl/lib/curl_log.c
new file mode 100644
index 0000000000..2301cff122
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_log.c
@@ -0,0 +1,223 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_log.h"
+#include "urldata.h"
+#include "easyif.h"
+#include "cfilters.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "strcase.h"
+
+#include "cf-socket.h"
+#include "connect.h"
+#include "http2.h"
+#include "http_proxy.h"
+#include "cf-https-connect.h"
+#include "socks.h"
+#include "strtok.h"
+#include "vtls/vtls.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size)
+{
+ if(data->set.verbose) {
+ static const char s_infotype[CURLINFO_END][3] = {
+ "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+ if(data->set.fdebug) {
+ bool inCallback = Curl_is_in_callback(data);
+ Curl_set_in_callback(data, true);
+ (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
+ Curl_set_in_callback(data, inCallback);
+ }
+ else {
+ switch(type) {
+ case CURLINFO_TEXT:
+ case CURLINFO_HEADER_OUT:
+ case CURLINFO_HEADER_IN:
+ fwrite(s_infotype[type], 2, 1, data->set.err);
+ fwrite(ptr, size, 1, data->set.err);
+ break;
+ default: /* nada */
+ break;
+ }
+ }
+ }
+}
+
+
+/* Curl_failf() is for messages stating why we failed.
+ * The message SHALL NOT include any LF or CR.
+ */
+void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(data->set.verbose || data->set.errorbuffer) {
+ va_list ap;
+ int len;
+ char error[CURL_ERROR_SIZE + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+
+ if(data->set.errorbuffer && !data->state.errorbuf) {
+ strcpy(data->set.errorbuffer, error);
+ data->state.errorbuf = TRUE; /* wrote error string */
+ }
+ error[len++] = '\n';
+ error[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, error, len);
+ va_end(ap);
+ }
+}
+
+/* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
+
+void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(data && data->set.verbose) {
+ va_list ap;
+ int len;
+ char buffer[MAXINFO + 2];
+ va_start(ap, fmt);
+ len = mvsnprintf(buffer, MAXINFO, fmt, ap);
+ va_end(ap);
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ }
+}
+
+#ifdef DEBUGBUILD
+
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...)
+{
+ DEBUGASSERT(cf);
+ if(data && Curl_log_cf_is_debug(cf)) {
+ va_list ap;
+ int len;
+ char buffer[MAXINFO + 2];
+ len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ",
+ cf->conn->connection_id, cf->sockindex? "/2" : "",
+ cf->cft->name);
+ va_start(ap, fmt);
+ len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+ va_end(ap);
+ buffer[len++] = '\n';
+ buffer[len] = '\0';
+ Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ }
+}
+
+
+static struct Curl_cftype *cf_types[] = {
+ &Curl_cft_tcp,
+ &Curl_cft_udp,
+ &Curl_cft_unix,
+ &Curl_cft_tcp_accept,
+ &Curl_cft_happy_eyeballs,
+ &Curl_cft_setup,
+#ifdef USE_NGHTTP2
+ &Curl_cft_nghttp2,
+#endif
+#ifdef USE_SSL
+ &Curl_cft_ssl,
+ &Curl_cft_ssl_proxy,
+#endif
+#if !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP)
+ &Curl_cft_http_proxy,
+#endif /* !CURL_DISABLE_HTTP */
+ &Curl_cft_haproxy,
+ &Curl_cft_socks_proxy,
+#endif /* !CURL_DISABLE_PROXY */
+#ifdef ENABLE_QUIC
+ &Curl_cft_http3,
+#endif
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+ &Curl_cft_http_connect,
+#endif
+ NULL,
+};
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+CURLcode Curl_log_init(void)
+{
+ const char *setting = getenv("CURL_DEBUG");
+ if(setting) {
+ char *token, *tok_buf, *tmp;
+ size_t i;
+
+ tmp = strdup(setting);
+ if(!tmp)
+ return CURLE_OUT_OF_MEMORY;
+
+ token = strtok_r(tmp, ", ", &tok_buf);
+ while(token) {
+ for(i = 0; cf_types[i]; ++i) {
+ if(strcasecompare(token, cf_types[i]->name)) {
+ cf_types[i]->log_level = CURL_LOG_DEBUG;
+ break;
+ }
+ }
+ token = strtok_r(NULL, ", ", &tok_buf);
+ }
+ free(tmp);
+ }
+ return CURLE_OK;
+}
+#else /* DEBUGBUILD */
+
+CURLcode Curl_log_init(void)
+{
+ return CURLE_OK;
+}
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...)
+{
+ (void)data;
+ (void)cf;
+ (void)fmt;
+}
+#endif
+
+#endif /* !DEBUGBUILD */
diff --git a/Utilities/cmcurl/lib/curl_log.h b/Utilities/cmcurl/lib/curl_log.h
new file mode 100644
index 0000000000..ad6143fa99
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_log.h
@@ -0,0 +1,138 @@
+#ifndef HEADER_CURL_LOG_H
+#define HEADER_CURL_LOG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+struct Curl_easy;
+struct Curl_cfilter;
+
+/**
+ * Init logging, return != 0 on failure.
+ */
+CURLcode Curl_log_init(void);
+
+
+void Curl_infof(struct Curl_easy *, const char *fmt, ...);
+void Curl_failf(struct Curl_easy *, const char *fmt, ...);
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+
+#if defined(HAVE_VARIADIC_MACROS_C99)
+#define infof(...) Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC)
+#define infof(x...) Curl_nop_stmt
+#else
+#error "missing VARIADIC macro define, fix and rebuild!"
+#endif
+
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define infof Curl_infof
+
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#define failf Curl_failf
+
+
+#define CURL_LOG_DEFAULT 0
+#define CURL_LOG_DEBUG 1
+#define CURL_LOG_TRACE 2
+
+
+/* the function used to output verbose information */
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+ char *ptr, size_t size);
+
+#ifdef DEBUGBUILD
+
+/* explainer: we have some mix configuration and werror settings
+ * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
+ * on gnuc and some other compiler. Need to treat carefully.
+ */
+#if defined(HAVE_VARIADIC_MACROS_C99) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+
+#define LOG_CF(data, cf, ...) \
+ do { if(Curl_log_cf_is_debug(cf)) \
+ Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0)
+#else
+#define LOG_CF Curl_log_cf_debug
+#endif
+
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+#if defined(__GNUC__) && !defined(printf) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+ !defined(__MINGW32__)
+ const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+#else
+ const char *fmt, ...);
+#endif
+
+#define Curl_log_cf_is_debug(cf) \
+ ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG)
+
+#else /* !DEBUGBUILD */
+
+#if defined(HAVE_VARIADIC_MACROS_C99) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define LOG_CF(...) Curl_nop_stmt
+#define Curl_log_cf_debug(...) Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC) && \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define LOG_CF(x...) Curl_nop_stmt
+#define Curl_log_cf_debug(x...) Curl_nop_stmt
+#else
+#define LOG_CF Curl_log_cf_debug
+/* without c99, we seem unable to completely define away this function. */
+void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
+ const char *fmt, ...);
+#endif
+
+#define Curl_log_cf_is_debug(x) ((void)(x), FALSE)
+
+#endif /* !DEBUGBUILD */
+
+#define LOG_CF_IS_DEBUG(x) Curl_log_cf_is_debug(x)
+
+/* Macros intended for DEBUGF logging, use like:
+ * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
+ * and it will output:
+ * [CONN-1-0][CF-SSL] this filter very much rocks
+ * on connection #1 with sockindex 0 for filter of type "SSL". */
+#define DMSG(d,msg) \
+ "[CONN-%ld] "msg, (d)->conn->connection_id
+#define DMSGI(d,i,msg) \
+ "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
+#define CMSG(c,msg) \
+ "[CONN-%ld] "msg, (c)->connection_id
+#define CMSGI(c,i,msg) \
+ "[CONN-%ld-%d] "msg, (c)->connection_id, (i)
+#define CFMSG(cf,msg) \
+ "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
+ (cf)->sockindex, (cf)->cft->name
+
+
+
+#endif /* HEADER_CURL_LOG_H */
diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h
index 8049355cff..03567b9916 100644
--- a/Utilities/cmcurl/lib/curl_md4.h
+++ b/Utilities/cmcurl/lib/curl_md4.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h
index 789329654b..ec2512f002 100644
--- a/Utilities/cmcurl/lib/curl_md5.h
+++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h
index 092fc9f758..7af139196e 100644
--- a/Utilities/cmcurl/lib/curl_memory.h
+++ b/Utilities/cmcurl/lib/curl_memory.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memrchr.c b/Utilities/cmcurl/lib/curl_memrchr.c
index c329a6176a..3f3dc6de16 100644
--- a/Utilities/cmcurl/lib/curl_memrchr.c
+++ b/Utilities/cmcurl/lib/curl_memrchr.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h
index e7654e1d9a..a1a4ba0927 100644
--- a/Utilities/cmcurl/lib/curl_memrchr.h
+++ b/Utilities/cmcurl/lib/curl_memrchr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c
index 309dccbfef..522ea34e82 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.c
+++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_multibyte.h b/Utilities/cmcurl/lib/curl_multibyte.h
index 929714873a..ddac1f6382 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.h
+++ b/Utilities/cmcurl/lib/curl_multibyte.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index 690f8f7674..25d2526025 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,12 +36,13 @@
/* Please keep the SSL backend-specific #if branches in this order:
1. USE_OPENSSL
- 2. USE_GNUTLS
- 3. USE_NSS
- 4. USE_MBEDTLS
- 5. USE_SECTRANSP
- 6. USE_OS400CRYPTO
- 7. USE_WIN32_CRYPTO
+ 2. USE_WOLFSSL
+ 3. USE_GNUTLS
+ 4. USE_NSS
+ 5. USE_MBEDTLS
+ 6. USE_SECTRANSP
+ 7. USE_OS400CRYPTO
+ 8. USE_WIN32_CRYPTO
This ensures that:
- the same SSL branch gets activated throughout this source
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h
index 60444c9dc6..33b651f5ea 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,11 +37,11 @@
#define NTLM_NEEDS_NSS_INIT
#endif
-#ifdef USE_WOLFSSL
+#if defined(USE_OPENSSL)
+# include <openssl/ssl.h>
+#elif defined(USE_WOLFSSL)
# include <wolfssl/options.h>
# include <wolfssl/openssl/ssl.h>
-#elif defined(USE_OPENSSL)
-# include <openssl/ssl.h>
#endif
/* Helpers to generate function byte arguments in little endian order */
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index fcf5075227..a10e2a1b09 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.h b/Utilities/cmcurl/lib/curl_ntlm_wb.h
index 1f04db8c3d..37704c0fe0 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_path.c b/Utilities/cmcurl/lib/curl_path.c
index f00e3ee74d..977e5336f5 100644
--- a/Utilities/cmcurl/lib/curl_path.c
+++ b/Utilities/cmcurl/lib/curl_path.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -32,70 +32,65 @@
#include "escape.h"
#include "memdebug.h"
+#define MAX_SSHPATH_LEN 100000 /* arbitrary */
+
/* figure out the path to work with in this particular request */
CURLcode Curl_getworkingpath(struct Curl_easy *data,
char *homedir, /* when SFTP is used */
char **path) /* returns the allocated
real path to work with */
{
- char *real_path = NULL;
char *working_path;
size_t working_path_len;
+ struct dynbuf npath;
CURLcode result =
Curl_urldecode(data->state.up.path, 0, &working_path,
&working_path_len, REJECT_ZERO);
if(result)
return result;
+ /* new path to switch to in case we need to */
+ Curl_dyn_init(&npath, MAX_SSHPATH_LEN);
+
/* Check for /~/, indicating relative to the user's home directory */
- if(data->conn->handler->protocol & CURLPROTO_SCP) {
- real_path = malloc(working_path_len + 1);
- if(!real_path) {
+ if((data->conn->handler->protocol & CURLPROTO_SCP) &&
+ (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) {
+ /* It is referenced to the home directory, so strip the leading '/~/' */
+ if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
- if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3)))
- /* It is referenced to the home directory, so strip the leading '/~/' */
- memcpy(real_path, working_path + 3, working_path_len - 2);
- else
- memcpy(real_path, working_path, 1 + working_path_len);
}
- else if(data->conn->handler->protocol & CURLPROTO_SFTP) {
- if((working_path_len > 1) && (working_path[1] == '~')) {
- size_t homelen = strlen(homedir);
- real_path = malloc(homelen + working_path_len + 1);
- if(!real_path) {
- free(working_path);
- return CURLE_OUT_OF_MEMORY;
- }
- /* It is referenced to the home directory, so strip the
- leading '/' */
- memcpy(real_path, homedir, homelen);
- /* Only add a trailing '/' if homedir does not end with one */
- if(homelen == 0 || real_path[homelen - 1] != '/') {
- real_path[homelen] = '/';
- homelen++;
- real_path[homelen] = '\0';
- }
- if(working_path_len > 3) {
- memcpy(real_path + homelen, working_path + 3,
- 1 + working_path_len -3);
- }
+ else if((data->conn->handler->protocol & CURLPROTO_SFTP) &&
+ (working_path_len > 2) && !memcmp(working_path, "/~/", 3)) {
+ size_t len;
+ const char *p;
+ int copyfrom = 3;
+ if(Curl_dyn_add(&npath, homedir)) {
+ free(working_path);
+ return CURLE_OUT_OF_MEMORY;
}
- else {
- real_path = malloc(working_path_len + 1);
- if(!real_path) {
- free(working_path);
- return CURLE_OUT_OF_MEMORY;
- }
- memcpy(real_path, working_path, 1 + working_path_len);
+ /* Copy a separating '/' if homedir does not end with one */
+ len = Curl_dyn_len(&npath);
+ p = Curl_dyn_ptr(&npath);
+ if(len && (p[len-1] != '/'))
+ copyfrom = 2;
+
+ if(Curl_dyn_addn(&npath,
+ &working_path[copyfrom], working_path_len - copyfrom)) {
+ free(working_path);
+ return CURLE_OUT_OF_MEMORY;
}
}
- free(working_path);
+ if(Curl_dyn_len(&npath)) {
+ free(working_path);
- /* store the pointer for the caller to receive */
- *path = real_path;
+ /* store the pointer for the caller to receive */
+ *path = Curl_dyn_ptr(&npath);
+ }
+ else
+ *path = working_path;
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/curl_path.h b/Utilities/cmcurl/lib/curl_path.h
index 98e56eaad3..9ed09dea83 100644
--- a/Utilities/cmcurl/lib/curl_path.h
+++ b/Utilities/cmcurl/lib/curl_path.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h
index 3823828cd9..6d3d492cc4 100644
--- a/Utilities/cmcurl/lib/curl_printf.h
+++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_range.c b/Utilities/cmcurl/lib/curl_range.c
index 49999367d7..d499953c9e 100644
--- a/Utilities/cmcurl/lib/curl_range.c
+++ b/Utilities/cmcurl/lib/curl_range.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_range.h b/Utilities/cmcurl/lib/curl_range.h
index 33570abc25..77679e2b94 100644
--- a/Utilities/cmcurl/lib/curl_range.h
+++ b/Utilities/cmcurl/lib/curl_range.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c
index 1932cb4ecb..2679a2cdc1 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.c
+++ b/Utilities/cmcurl/lib/curl_rtmp.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h
index f856085dc3..9b93ee060b 100644
--- a/Utilities/cmcurl/lib/curl_rtmp.h
+++ b/Utilities/cmcurl/lib/curl_rtmp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index 46ee800a43..119fb9b25a 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -36,7 +36,8 @@
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
- !defined(CURL_DISABLE_POP3)
+ !defined(CURL_DISABLE_POP3) || \
+ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/curl_sasl.h b/Utilities/cmcurl/lib/curl_sasl.h
index c709d56a67..e94e6431a2 100644
--- a/Utilities/cmcurl/lib/curl_sasl.h
+++ b/Utilities/cmcurl/lib/curl_sasl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -125,9 +125,9 @@ struct SASL {
unsigned short authmechs; /* Accepted authentication mechanisms */
unsigned short prefmech; /* Preferred authentication mechanism */
unsigned short authused; /* Auth mechanism used for the connection */
- bool resetprefs; /* For URL auth option parsing. */
- bool mutual_auth; /* Mutual authentication enabled (GSSAPI only) */
- bool force_ir; /* Protocol always supports initial response */
+ BIT(resetprefs); /* For URL auth option parsing. */
+ BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */
+ BIT(force_ir); /* Protocol always supports initial response */
};
/* This is used to test whether the line starts with the given mechanism */
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 084d19a4e8..06fb613996 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -456,8 +456,8 @@
# endif
#endif
-#if (SIZEOF_CURL_OFF_T == 4)
-# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFF)
+#if (SIZEOF_CURL_OFF_T < 8)
+#error "too small curl_off_t"
#else
/* assume SIZEOF_CURL_OFF_T == 8 */
# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
@@ -792,14 +792,16 @@ endings either CRLF or LF so 't' is appropriate.
#define FOPEN_APPENDTEXT "a"
#endif
-/* WinSock destroys recv() buffer when send() failed.
- * Enabled automatically for Windows and for Cygwin as Cygwin sockets are
- * wrappers for WinSock sockets. https://github.com/curl/curl/issues/657
- * Define DONT_USE_RECV_BEFORE_SEND_WORKAROUND to force disable workaround.
+/* Windows workaround to recv before every send, because apparently Winsock
+ * destroys destroys recv() buffer when send() failed.
+ * This workaround is now disabled by default since it caused hard to fix bugs.
+ * Define USE_RECV_BEFORE_SEND_WORKAROUND to enable it.
+ * https://github.com/curl/curl/issues/657
+ * https://github.com/curl/curl/pull/10409
*/
#if !defined(DONT_USE_RECV_BEFORE_SEND_WORKAROUND)
# if defined(WIN32) || defined(__CYGWIN__)
-# define USE_RECV_BEFORE_SEND_WORKAROUND
+/* # define USE_RECV_BEFORE_SEND_WORKAROUND */
# endif
#else /* DONT_USE_RECV_BEFORE_SEND_WORKAROUND */
# ifdef USE_RECV_BEFORE_SEND_WORKAROUND
@@ -851,6 +853,13 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
#define USE_HTTP3
#endif
+/* Certain Windows implementations are not aligned with what curl expects,
+ so always use the local one on this platform. E.g. the mingw-w64
+ implementation can return wrong results for non-ASCII inputs. */
+#if defined(HAVE_BASENAME) && defined(WIN32)
+#undef HAVE_BASENAME
+#endif
+
#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
# if defined(__MINGW32__) && !defined(LUP_SECURE)
typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
@@ -868,4 +877,10 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
# endif
#endif
+/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no
+ replacements (yet) so tell the compiler to not warn for them. */
+#ifdef USE_OPENSSL
+#define OPENSSL_SUPPRESS_DEPRECATED
+#endif
+
#endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h
index ac4a7f1b35..dde7229d23 100644
--- a/Utilities/cmcurl/lib/curl_setup_once.h
+++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -69,6 +69,14 @@
#include <unistd.h>
#endif
+#ifdef USE_WOLFSSL
+# if defined(HAVE_STDINT_H)
+# include <stdint.h>
+# elif defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+# endif
+#endif
+
#ifdef __hpux
# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
# ifdef _APP32_64BIT_OFF_T
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h
index 39523af0c8..c5e157bee1 100644
--- a/Utilities/cmcurl/lib/curl_sha256.h
+++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c
index 33108c48e9..eb21e7e2b0 100644
--- a/Utilities/cmcurl/lib/curl_sspi.c
+++ b/Utilities/cmcurl/lib/curl_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h
index ad111309c5..9816d59c84 100644
--- a/Utilities/cmcurl/lib/curl_sspi.h
+++ b/Utilities/cmcurl/lib/curl_sspi.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c
index dff61676c9..e13e2947c2 100644
--- a/Utilities/cmcurl/lib/curl_threads.c
+++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h
index 63392f671f..facbc73705 100644
--- a/Utilities/cmcurl/lib/curl_threads.h
+++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/curlx.h b/Utilities/cmcurl/lib/curlx.h
index 1796afa00a..7a753d6824 100644
--- a/Utilities/cmcurl/lib/curlx.h
+++ b/Utilities/cmcurl/lib/curlx.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index 993373ecae..0ce62a00e9 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -98,37 +98,27 @@ const struct Curl_handler Curl_handler_dict = {
PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
};
-static char *unescape_word(const char *inputbuff)
+#define DYN_DICT_WORD 10000
+static char *unescape_word(const char *input)
{
- char *newp = NULL;
- char *dictp;
- size_t len;
-
- CURLcode result = Curl_urldecode(inputbuff, 0, &newp, &len,
- REJECT_NADA);
- if(!newp || result)
- return NULL;
-
- dictp = malloc(len*2 + 1); /* add one for terminating zero */
- if(dictp) {
- char *ptr;
- char ch;
- int olen = 0;
- /* According to RFC2229 section 2.2, these letters need to be escaped with
- \[letter] */
- for(ptr = newp;
- (ch = *ptr) != 0;
- ptr++) {
- if((ch <= 32) || (ch == 127) ||
- (ch == '\'') || (ch == '\"') || (ch == '\\')) {
- dictp[olen++] = '\\';
- }
- dictp[olen++] = ch;
- }
- dictp[olen] = 0;
+ struct dynbuf out;
+ const char *ptr;
+ CURLcode result = CURLE_OK;
+ Curl_dyn_init(&out, DYN_DICT_WORD);
+
+ /* According to RFC2229 section 2.2, these letters need to be escaped with
+ \[letter] */
+ for(ptr = input; *ptr; ptr++) {
+ char ch = *ptr;
+ if((ch <= 32) || (ch == 127) ||
+ (ch == '\'') || (ch == '\"') || (ch == '\\'))
+ result = Curl_dyn_addn(&out, "\\", 1);
+ if(!result)
+ result = Curl_dyn_addn(&out, ptr, 1);
+ if(result)
+ return NULL;
}
- free(newp);
- return dictp;
+ return Curl_dyn_ptr(&out);
}
/* sendf() sends formatted data to the server */
@@ -178,20 +168,25 @@ static CURLcode sendf(curl_socket_t sockfd, struct Curl_easy *data,
static CURLcode dict_do(struct Curl_easy *data, bool *done)
{
char *word;
- char *eword;
+ char *eword = NULL;
char *ppath;
char *database = NULL;
char *strategy = NULL;
char *nthdef = NULL; /* This is not part of the protocol, but required
by RFC 2229 */
- CURLcode result = CURLE_OK;
+ CURLcode result;
struct connectdata *conn = data->conn;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
- char *path = data->state.up.path;
+ char *path;
*done = TRUE; /* unconditionally */
+ /* url-decode path before further evaluation */
+ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
+ if(result)
+ return result;
+
if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
@@ -225,8 +220,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
}
eword = unescape_word(word);
- if(!eword)
- return CURLE_OUT_OF_MEMORY;
+ if(!eword) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
result = sendf(sockfd, data,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
@@ -239,11 +236,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
strategy,
eword);
- free(eword);
-
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); /* no upload */
}
@@ -273,8 +268,10 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
}
eword = unescape_word(word);
- if(!eword)
- return CURLE_OUT_OF_MEMORY;
+ if(!eword) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
result = sendf(sockfd, data,
"CLIENT " LIBCURL_NAME " " LIBCURL_VERSION "\r\n"
@@ -285,11 +282,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
database,
eword);
- free(eword);
-
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
@@ -310,13 +305,16 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
"QUIT\r\n", ppath);
if(result) {
failf(data, "Failed sending DICT request");
- return result;
+ goto error;
}
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
}
}
- return CURLE_OK;
+ error:
+ free(eword);
+ free(path);
+ return result;
}
#endif /* CURL_DISABLE_DICT */
diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h
index b283a0dfc1..ba9a92719b 100644
--- a/Utilities/cmcurl/lib/dict.h
+++ b/Utilities/cmcurl/lib/dict.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index 3b1d5d60ef..922d757845 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -396,6 +396,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
goto error;
dohp->pending++;
+#ifdef ENABLE_IPV6
if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* create IPv6 DoH request */
result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
@@ -405,6 +406,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
goto error;
dohp->pending++;
}
+#endif
return NULL;
error:
@@ -950,7 +952,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, ai, dohp->host, dohp->port);
+ dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h
index 678e807fe4..7d7b694f33 100644
--- a/Utilities/cmcurl/lib/doh.h
+++ b/Utilities/cmcurl/lib/doh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/dynbuf.c b/Utilities/cmcurl/lib/dynbuf.c
index 0b1cf9afd8..bd3b9356c7 100644
--- a/Utilities/cmcurl/lib/dynbuf.c
+++ b/Utilities/cmcurl/lib/dynbuf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -99,8 +99,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
include that as well when it uses this code */
void *p = realloc(s->bufr, a);
if(!p) {
- Curl_safefree(s->bufr);
- s->leng = s->allc = 0;
+ Curl_dyn_free(s);
return CURLE_OUT_OF_MEMORY;
}
s->bufr = p;
diff --git a/Utilities/cmcurl/lib/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h
index 04a728c779..57ad62b22b 100644
--- a/Utilities/cmcurl/lib/dynbuf.h
+++ b/Utilities/cmcurl/lib/dynbuf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index d7f93be1e0..27124a72f5 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -65,6 +65,7 @@
#include "easyif.h"
#include "multiif.h"
#include "select.h"
+#include "cfilters.h"
#include "sendf.h" /* for failf function prototype */
#include "connect.h" /* for Curl_getconnectinfo */
#include "slist.h"
@@ -113,7 +114,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
#if defined(_WIN32_WCE)
#define system_strdup _strdup
#elif !defined(HAVE_STRDUP)
-#define system_strdup curlx_strdup
+#define system_strdup Curl_strdup
#else
#define system_strdup strdup
#endif
@@ -164,6 +165,11 @@ static CURLcode global_init(long flags, bool memoryfuncs)
#endif
}
+ if(Curl_log_init()) {
+ DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n"));
+ goto fail;
+ }
+
if(!Curl_ssl_init()) {
DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
goto fail;
@@ -913,11 +919,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
goto fail;
}
- /* duplicate all values in 'change' */
- if(data->state.cookielist) {
- outcurl->state.cookielist =
- Curl_slist_duplicate(data->state.cookielist);
- if(!outcurl->state.cookielist)
+ if(data->set.cookielist) {
+ outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist);
+ if(!outcurl->set.cookielist)
goto fail;
}
#endif
@@ -1003,8 +1007,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
if(outcurl) {
#ifndef CURL_DISABLE_COOKIES
- curl_slist_free_all(outcurl->state.cookielist);
- outcurl->state.cookielist = NULL;
+ curl_slist_free_all(outcurl->set.cookielist);
+ outcurl->set.cookielist = NULL;
#endif
Curl_safefree(outcurl->state.buffer);
Curl_dyn_free(&outcurl->state.headerb);
@@ -1101,7 +1105,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
k->keepon = newstate;
if(!(newstate & KEEP_RECV_PAUSE)) {
- Curl_http2_stream_pause(data, FALSE);
+ Curl_conn_ev_data_pause(data, FALSE);
if(data->state.tempcount) {
/* there are buffers for sending that can be delivered as the receive
@@ -1224,7 +1228,6 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
return result;
*n = (size_t)n1;
-
return CURLE_OK;
}
@@ -1294,29 +1297,34 @@ static int conn_upkeep(struct Curl_easy *data,
struct connectdata *conn,
void *param)
{
- /* Param is unused. */
- (void)param;
+ struct curltime *now = param;
- if(conn->handler->connection_check) {
- /* briefly attach the connection to this transfer for the purpose of
- checking it */
- Curl_attach_connection(data, conn);
+ if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+ return 0;
+ /* briefly attach for action */
+ Curl_attach_connection(data, conn);
+ if(conn->handler->connection_check) {
/* Do a protocol-specific keepalive check on the connection. */
conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
- /* detach the connection again */
- Curl_detach_connection(data);
}
+ else {
+ /* Do the generic action on the FIRSTSOCKE filter chain */
+ Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
+ }
+ Curl_detach_connection(data);
+ conn->keepalive = *now;
return 0; /* continue iteration */
}
static CURLcode upkeep(struct conncache *conn_cache, void *data)
{
+ struct curltime now = Curl_now();
/* Loop over every connection and make connection alive. */
Curl_conncache_foreach(data,
conn_cache,
- data,
+ &now,
conn_upkeep);
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
index d96e56b8d8..5fa9477d06 100644
--- a/Utilities/cmcurl/lib/easy_lock.h
+++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c
index a639bb3758..2b8a521cd2 100644
--- a/Utilities/cmcurl/lib/easygetopt.c
+++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* ___|___/|_| ______|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyif.h b/Utilities/cmcurl/lib/easyif.h
index 205382cd07..570ebef326 100644
--- a/Utilities/cmcurl/lib/easyif.h
+++ b/Utilities/cmcurl/lib/easyif.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c
index 03b814e5aa..a9c1efd006 100644
--- a/Utilities/cmcurl/lib/easyoptions.c
+++ b/Utilities/cmcurl/lib/easyoptions.c
@@ -1,11 +1,11 @@
/***************************************************************************
* _ _ ____ _
- * Project ___| | | | _ | |
+ * Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
- * ___|___/|_| ______|
+ * \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/easyoptions.h b/Utilities/cmcurl/lib/easyoptions.h
index 33f816d6b6..24b4cd93ed 100644
--- a/Utilities/cmcurl/lib/easyoptions.h
+++ b/Utilities/cmcurl/lib/easyoptions.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c
index ed59838bd1..56aa2b3988 100644
--- a/Utilities/cmcurl/lib/escape.c
+++ b/Utilities/cmcurl/lib/escape.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -97,7 +97,7 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
return strdup("");
while(length--) {
- unsigned char in = *string; /* we need to treat the characters unsigned */
+ unsigned char in = *string++; /* treat the characters unsigned */
if(Curl_isunreserved(in)) {
/* append this */
@@ -106,15 +106,28 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
}
else {
/* encode it */
- if(Curl_dyn_addf(&d, "%%%02X", in))
+ const char hex[] = "0123456789ABCDEF";
+ char out[3]={'%'};
+ out[1] = hex[in>>4];
+ out[2] = hex[in & 0xf];
+ if(Curl_dyn_addn(&d, out, 3))
return NULL;
}
- string++;
}
return Curl_dyn_ptr(&d);
}
+static const unsigned char hextable[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
+ 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */
+};
+
+/* the input is a single hex digit */
+#define onehex2dec(x) hextable[x - '0']
+
/*
* Curl_urldecode() URL decodes the given string.
*
@@ -137,54 +150,47 @@ CURLcode Curl_urldecode(const char *string, size_t length,
{
size_t alloc;
char *ns;
- size_t strindex = 0;
- unsigned long hex;
DEBUGASSERT(string);
DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */
- alloc = (length?length:strlen(string)) + 1;
- ns = malloc(alloc);
+ alloc = (length?length:strlen(string));
+ ns = malloc(alloc + 1);
if(!ns)
return CURLE_OUT_OF_MEMORY;
- while(--alloc > 0) {
+ /* store output string */
+ *ostring = ns;
+
+ while(alloc) {
unsigned char in = *string;
if(('%' == in) && (alloc > 2) &&
ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
/* this is two hexadecimal digits following a '%' */
- char hexstr[3];
- char *ptr;
- hexstr[0] = string[1];
- hexstr[1] = string[2];
- hexstr[2] = 0;
-
- hex = strtoul(hexstr, &ptr, 16);
+ in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]);
- in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
-
- string += 2;
- alloc -= 2;
+ string += 3;
+ alloc -= 3;
+ }
+ else {
+ string++;
+ alloc--;
}
if(((ctrl == REJECT_CTRL) && (in < 0x20)) ||
((ctrl == REJECT_ZERO) && (in == 0))) {
- free(ns);
+ Curl_safefree(*ostring);
return CURLE_URL_MALFORMAT;
}
- ns[strindex++] = in;
- string++;
+ *ns++ = in;
}
- ns[strindex] = 0; /* terminate it */
+ *ns = 0; /* terminate it */
if(olen)
/* store output size */
- *olen = strindex;
-
- /* store output string */
- *ostring = ns;
+ *olen = ns - *ostring;
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h
index 61d4611bf7..cdbb712acc 100644
--- a/Utilities/cmcurl/lib/escape.h
+++ b/Utilities/cmcurl/lib/escape.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index 3f642be461..51c5d07ce4 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h
index 826d45380d..4565525592 100644
--- a/Utilities/cmcurl/lib/file.h
+++ b/Utilities/cmcurl/lib/file.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fileinfo.c b/Utilities/cmcurl/lib/fileinfo.c
index 7bbf24bdeb..409b38fcbc 100644
--- a/Utilities/cmcurl/lib/fileinfo.c
+++ b/Utilities/cmcurl/lib/fileinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fileinfo.h b/Utilities/cmcurl/lib/fileinfo.h
index 5bad718cc8..af44212501 100644
--- a/Utilities/cmcurl/lib/fileinfo.h
+++ b/Utilities/cmcurl/lib/fileinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c
index ad3691ba9d..f710dbf05a 100644
--- a/Utilities/cmcurl/lib/fopen.c
+++ b/Utilities/cmcurl/lib/fopen.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -106,7 +106,6 @@ fail:
free(tempstore);
- *tempname = NULL;
return result;
}
diff --git a/Utilities/cmcurl/lib/fopen.h b/Utilities/cmcurl/lib/fopen.h
index 289e55f2af..e3a919d073 100644
--- a/Utilities/cmcurl/lib/fopen.h
+++ b/Utilities/cmcurl/lib/fopen.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index b30e8deee4..2bdb9f26ec 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h
index c6c6397cd2..caabb6324c 100644
--- a/Utilities/cmcurl/lib/formdata.h
+++ b/Utilities/cmcurl/lib/formdata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 874725bae3..ef9ec1e41f 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -61,6 +61,7 @@
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
+#include "cf-socket.h"
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
@@ -285,8 +286,8 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
conn->bits.do_more = FALSE;
(void)curlx_nonblock(s, TRUE); /* enable non-blocking */
- /* Replace any filter on SECONDARY with one listeing on this socket */
- result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET, &s);
+ /* Replace any filter on SECONDARY with one listening on this socket */
+ result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
if(result)
return result;
@@ -435,6 +436,12 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
bool connected;
DEBUGF(infof(data, "ftp InitiateTransfer()"));
+ if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
+ !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
+ result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
+ if(result)
+ return result;
+ }
result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
if(result || !connected)
return result;
@@ -819,26 +826,11 @@ static int ftp_domore_getsock(struct Curl_easy *data,
if(FTP_STOP == ftpc->state) {
int bits = GETSOCK_READSOCK(0);
- bool any = FALSE;
/* if stopped and still in this state, then we're also waiting for a
connect on the secondary connection */
socks[0] = conn->sock[FIRSTSOCKET];
-
- if(!data->set.ftp_use_port) {
- int s;
- int i;
- /* PORT is used to tell the server to connect to us, and during that we
- don't do happy eyeballs, but we do if we connect to the server */
- for(s = 1, i = 0; i<2; i++) {
- if(conn->tempsock[i] != CURL_SOCKET_BAD) {
- socks[s] = conn->tempsock[i];
- bits |= GETSOCK_WRITESOCK(s++);
- any = TRUE;
- }
- }
- }
- if(!any) {
+ if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
socks[1] = conn->sock[SECONDARYSOCKET];
bits |= GETSOCK_WRITESOCK(1) | GETSOCK_READSOCK(1);
}
@@ -1024,9 +1016,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(*addr != '\0') {
/* attempt to get the address of the given interface name */
- switch(Curl_if2ip(conn->ip_addr->ai_family,
+ switch(Curl_if2ip(conn->remote_addr->family,
#ifdef ENABLE_IPV6
- Curl_ipv6_scope(conn->ip_addr->ai_addr),
+ Curl_ipv6_scope(&conn->remote_addr->sa_addr),
conn->scope_id,
#endif
addr, hbuf, sizeof(hbuf))) {
@@ -1097,7 +1089,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
portsock = CURL_SOCKET_BAD;
error = 0;
for(ai = res; ai; ai = ai->ai_next) {
- if(Curl_socket(data, ai, NULL, &portsock)) {
+ if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
error = SOCKERRNO;
continue;
}
@@ -1266,9 +1258,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */
ftpc->count1 = fcmd;
- /* Replace any filter on SECONDARY with one listeing on this socket */
- result = Curl_conn_socket_accepted_set(data, conn, SECONDARYSOCKET,
- &portsock);
+ /* Replace any filter on SECONDARY with one listening on this socket */
+ result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
if(result)
goto out;
portsock = CURL_SOCKET_BAD; /* now held in filter */
@@ -1279,7 +1270,7 @@ out:
state(data, FTP_STOP);
}
if(portsock != CURL_SOCKET_BAD)
- Curl_closesocket(data, conn, portsock);
+ Curl_socket_close(data, conn, portsock);
free(addr);
return result;
}
@@ -1810,6 +1801,29 @@ static char *control_address(struct connectdata *conn)
return conn->primary_ip;
}
+static bool match_pasv_6nums(const char *p,
+ unsigned int *array) /* 6 numbers */
+{
+ int i;
+ for(i = 0; i < 6; i++) {
+ unsigned long num;
+ char *endp;
+ if(i) {
+ if(*p != ',')
+ return FALSE;
+ p++;
+ }
+ if(!ISDIGIT(*p))
+ return FALSE;
+ num = strtoul(p, &endp, 10);
+ if(num > 255)
+ return FALSE;
+ array[i] = (unsigned int)num;
+ p = endp;
+ }
+ return TRUE;
+}
+
static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
int ftpcode)
{
@@ -1829,27 +1843,18 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* positive EPSV response */
char *ptr = strchr(str, '(');
if(ptr) {
- unsigned int num;
- char separator[4];
+ char sep;
ptr++;
- if(5 == sscanf(ptr, "%c%c%c%u%c",
- &separator[0],
- &separator[1],
- &separator[2],
- &num,
- &separator[3])) {
- const char sep1 = separator[0];
- int i;
-
- /* The four separators should be identical, or else this is an oddly
- formatted reply and we bail out immediately. */
- for(i = 1; i<4; i++) {
- if(separator[i] != sep1) {
- ptr = NULL; /* set to NULL to signal error */
- break;
- }
- }
- if(num > 0xffff) {
+ /* |||12345| */
+ sep = ptr[0];
+ /* the ISDIGIT() check here is because strtoul() accepts leading minus
+ etc */
+ if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
+ char *endp;
+ unsigned long num = strtoul(&ptr[3], &endp, 10);
+ if(*endp != sep)
+ ptr = NULL;
+ else if(num > 0xffff) {
failf(data, "Illegal port number in EPSV reply");
return CURLE_FTP_WEIRD_PASV_REPLY;
}
@@ -1871,8 +1876,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
else if((ftpc->count1 == 1) &&
(ftpcode == 227)) {
/* positive PASV response */
- unsigned int ip[4] = {0, 0, 0, 0};
- unsigned int port[2] = {0, 0};
+ unsigned int ip[6];
/*
* Scan for a sequence of six comma-separated numbers and use them as
@@ -1884,15 +1888,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
* "227 Entering passive mode. 127,0,0,1,4,51"
*/
while(*str) {
- if(6 == sscanf(str, "%u,%u,%u,%u,%u,%u",
- &ip[0], &ip[1], &ip[2], &ip[3],
- &port[0], &port[1]))
+ if(match_pasv_6nums(str, ip))
break;
str++;
}
- if(!*str || (ip[0] > 255) || (ip[1] > 255) || (ip[2] > 255) ||
- (ip[3] > 255) || (port[0] > 255) || (port[1] > 255) ) {
+ if(!*str) {
failf(data, "Couldn't interpret the 227-response");
return CURLE_FTP_WEIRD_227_FORMAT;
}
@@ -1912,7 +1913,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
if(!ftpc->newhost)
return CURLE_OUT_OF_MEMORY;
- ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
+ ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff);
}
else if(ftpc->count1 == 0) {
/* EPSV failed, move on to PASV */
@@ -1954,7 +1955,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
- Curl_conninfo_remote(data, conn, conn->sock[FIRSTSOCKET]);
+ Curl_conn_ev_update_info(data, conn);
Curl_safefree(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
if(!ftpc->newhost)
@@ -2047,6 +2048,30 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
return result;
}
+static int twodigit(const char *p)
+{
+ return (p[0]-'0') * 10 + (p[1]-'0');
+}
+
+static bool ftp_213_date(const char *p, int *year, int *month, int *day,
+ int *hour, int *minute, int *second)
+{
+ size_t len = strlen(p);
+ if(len < 14)
+ return FALSE;
+ *year = twodigit(&p[0]) * 100 + twodigit(&p[2]);
+ *month = twodigit(&p[4]);
+ *day = twodigit(&p[6]);
+ *hour = twodigit(&p[8]);
+ *minute = twodigit(&p[10]);
+ *second = twodigit(&p[12]);
+
+ if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
+ (*second > 60))
+ return FALSE;
+ return TRUE;
+}
+
static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
int ftpcode)
{
@@ -2061,8 +2086,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
/* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
last .sss part is optional and means fractions of a second */
int year, month, day, hour, minute, second;
- if(6 == sscanf(&data->state.buffer[4], "%04d%02d%02d%02d%02d%02d",
- &year, &month, &day, &hour, &minute, &second)) {
+ if(ftp_213_date(&data->state.buffer[4],
+ &year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
char timebuf[24];
msnprintf(timebuf, sizeof(timebuf),
@@ -2569,13 +2594,11 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data)
/* for USER and PASS responses */
static CURLcode ftp_state_user_resp(struct Curl_easy *data,
- int ftpcode,
- ftpstate instate)
+ int ftpcode)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- (void)instate; /* no use for this yet */
/* some need password anyway, and others just return 2xx ignored */
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
@@ -2652,7 +2675,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
int ftpcode;
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
- static const char ftpauth[][4] = { "SSL", "TLS" };
+ static const char * const ftpauth[] = { "SSL", "TLS" };
size_t nread = 0;
if(pp->sendleft)
@@ -2670,7 +2693,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
/* 230 User logged in - already! Take as 220 if TLS required. */
if(data->set.use_ssl <= CURLUSESSL_TRY ||
conn->bits.ftp_use_control_ssl)
- return ftp_state_user_resp(data, ftpcode, ftpc->state);
+ return ftp_state_user_resp(data, ftpcode);
}
else if(ftpcode != 220) {
failf(data, "Got a %03d ftp-server response when 220 was expected",
@@ -2742,7 +2765,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
if((ftpcode == 234) || (ftpcode == 334)) {
/* this was BLOCKING, keep it so for now */
bool done;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result) {
/* we failed and bail out */
@@ -2775,7 +2798,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
case FTP_USER:
case FTP_PASS:
- result = ftp_state_user_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_user_resp(data, ftpcode);
break;
case FTP_ACCT:
@@ -3238,7 +3261,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
if(data->state.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
Curl_set_in_callback(data, true);
- data->set.chunk_end(data->wildcard.customptr);
+ data->set.chunk_end(data->set.wildcardptr);
Curl_set_in_callback(data, false);
}
ftpc->known_filesize = -1;
@@ -3745,7 +3768,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
char *last_slash;
struct FTP *ftp = data->req.p.ftp;
char *path = ftp->path;
- struct WildcardData *wildcard = &(data->wildcard);
+ struct WildcardData *wildcard = data->wildcard;
CURLcode result = CURLE_OK;
struct ftp_wc *ftpwc = NULL;
@@ -3793,7 +3816,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
goto fail;
}
- wildcard->protdata = ftpwc; /* put it to the WildcardData tmp pointer */
+ wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
wildcard->dtor = wc_data_dtor;
/* wildcard does not support NOCWD option (assert it?) */
@@ -3831,13 +3854,13 @@ static CURLcode init_wc_data(struct Curl_easy *data)
}
Curl_safefree(wildcard->pattern);
wildcard->dtor = ZERO_NULL;
- wildcard->protdata = NULL;
+ wildcard->ftpwc = NULL;
return result;
}
static CURLcode wc_statemach(struct Curl_easy *data)
{
- struct WildcardData * const wildcard = &(data->wildcard);
+ struct WildcardData * const wildcard = data->wildcard;
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
@@ -3854,7 +3877,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
case CURLWC_MATCHING: {
/* In this state is LIST response successfully parsed, so lets restore
previous WRITEFUNCTION callback and WRITEDATA pointer */
- struct ftp_wc *ftpwc = wildcard->protdata;
+ struct ftp_wc *ftpwc = wildcard->ftpwc;
data->set.fwrite_func = ftpwc->backup.write_function;
data->set.out = ftpwc->backup.file_descriptor;
ftpwc->backup.write_function = ZERO_NULL;
@@ -3893,7 +3916,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
long userresponse;
Curl_set_in_callback(data, true);
userresponse = data->set.chunk_bgn(
- finfo, wildcard->customptr, (int)wildcard->filelist.size);
+ finfo, data->set.wildcardptr, (int)wildcard->filelist.size);
Curl_set_in_callback(data, false);
switch(userresponse) {
case CURL_CHUNK_BGN_FUNC_SKIP:
@@ -3933,7 +3956,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
case CURLWC_SKIP: {
if(data->set.chunk_end) {
Curl_set_in_callback(data, true);
- data->set.chunk_end(data->wildcard.customptr);
+ data->set.chunk_end(data->set.wildcardptr);
Curl_set_in_callback(data, false);
}
Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
@@ -3943,7 +3966,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
}
case CURLWC_CLEAN: {
- struct ftp_wc *ftpwc = wildcard->protdata;
+ struct ftp_wc *ftpwc = wildcard->ftpwc;
result = CURLE_OK;
if(ftpwc)
result = Curl_ftp_parselist_geterror(ftpwc->parser);
@@ -3956,7 +3979,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
case CURLWC_ERROR:
case CURLWC_CLEAR:
if(wildcard->dtor)
- wildcard->dtor(wildcard->protdata);
+ wildcard->dtor(wildcard->ftpwc);
return result;
}
}
@@ -3983,8 +4006,8 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
if(data->state.wildcardmatch) {
result = wc_statemach(data);
- if(data->wildcard.state == CURLWC_SKIP ||
- data->wildcard.state == CURLWC_DONE) {
+ if(data->wildcard->state == CURLWC_SKIP ||
+ data->wildcard->state == CURLWC_DONE) {
/* do not call ftp_regular_transfer */
return CURLE_OK;
}
@@ -4070,6 +4093,8 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
}
freedirs(ftpc);
+ Curl_safefree(ftpc->account);
+ Curl_safefree(ftpc->alternative_to_user);
Curl_safefree(ftpc->prevpath);
Curl_safefree(ftpc->server_os);
Curl_pp_disconnect(pp);
@@ -4339,11 +4364,31 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
char *type;
struct FTP *ftp;
CURLcode result = CURLE_OK;
+ struct ftp_conn *ftpc = &conn->proto.ftpc;
- data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
+ ftp = calloc(sizeof(struct FTP), 1);
if(!ftp)
return CURLE_OUT_OF_MEMORY;
+ /* clone connection related data that is FTP specific */
+ if(data->set.str[STRING_FTP_ACCOUNT]) {
+ ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
+ if(!ftpc->account) {
+ free(ftp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) {
+ ftpc->alternative_to_user =
+ strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
+ if(!ftpc->alternative_to_user) {
+ Curl_safefree(ftpc->account);
+ free(ftp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ data->req.p.ftp = ftp;
+
ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
/* FTP URLs support an extension like ";type=<typecode>" that
@@ -4378,7 +4423,9 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
/* get some initial data into the ftp struct */
ftp->transfer = PPTRANSFER_BODY;
ftp->downloadsize = 0;
- conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
+ ftpc->known_filesize = -1; /* unknown size for now */
+ ftpc->use_ssl = data->set.use_ssl;
+ ftpc->ccc = data->set.ftp_ccc;
return result;
}
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h
index 7f6f4328d1..977fc883b1 100644
--- a/Utilities/cmcurl/lib/ftp.h
+++ b/Utilities/cmcurl/lib/ftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -42,7 +42,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread,
/****************************************************************************
* FTP unique setup
***************************************************************************/
-typedef enum {
+enum {
FTP_STOP, /* do nothing state, stops the state machine */
FTP_WAIT220, /* waiting for the initial 220 response immediately after
a connect */
@@ -80,7 +80,8 @@ typedef enum {
FTP_STOR, /* generic state for STOR and APPE */
FTP_QUIT,
FTP_LAST /* never used */
-} ftpstate;
+};
+typedef unsigned char ftpstate; /* use the enum values */
struct ftp_parselist_data; /* defined later in ftplistparser.c */
@@ -119,41 +120,46 @@ struct FTP {
struct */
struct ftp_conn {
struct pingpong pp;
+ char *account;
+ char *alternative_to_user;
char *entrypath; /* the PWD reply when we logged on */
char *file; /* url-decoded file name (or path) */
char **dirs; /* realloc()ed array for path components */
- int dirdepth; /* number of entries used in the 'dirs' array */
- bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
- file size and 226/250 status check. It should still
- read the line, just ignore the result. */
- bool ctl_valid; /* Tells Curl_ftp_quit() whether or not to do anything. If
- the connection has timed out or been closed, this
- should be FALSE when it gets to Curl_ftp_quit() */
- bool cwddone; /* if it has been determined that the proper CWD combo
- already has been done */
- int cwdcount; /* number of CWD commands issued */
- bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
- caching the current directory */
- bool wait_data_conn; /* this is set TRUE if data connection is waited */
- /* newhost is the (allocated) IP addr or host name to connect the data
- connection to */
- unsigned short newport;
char *newhost;
char *prevpath; /* url-decoded conn->path from the previous transfer */
char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
and others (A/I or zero) */
- int count1; /* general purpose counter for the state machine */
- int count2; /* general purpose counter for the state machine */
- int count3; /* general purpose counter for the state machine */
- ftpstate state; /* always use ftp.c:state() to change state! */
- ftpstate state_saved; /* transfer type saved to be reloaded after
- data connection is established */
curl_off_t retr_size_saved; /* Size of retrieved file saved */
char *server_os; /* The target server operating system. */
curl_off_t known_filesize; /* file size is different from -1, if wildcard
LIST parsing was done and wc_statemach set
it */
+ int dirdepth; /* number of entries used in the 'dirs' array */
+ int cwdcount; /* number of CWD commands issued */
+ int count1; /* general purpose counter for the state machine */
+ int count2; /* general purpose counter for the state machine */
+ int count3; /* general purpose counter for the state machine */
+ /* newhost is the (allocated) IP addr or host name to connect the data
+ connection to */
+ unsigned short newport;
+ ftpstate state; /* always use ftp.c:state() to change state! */
+ ftpstate state_saved; /* transfer type saved to be reloaded after data
+ connection is established */
+ unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
+ IMAP or POP3 or others! (type: curl_usessl)*/
+ unsigned char ccc; /* ccc level for this connection */
BIT(ftp_trying_alternative);
+ BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer)
+ file size and 226/250 status check. It should still
+ read the line, just ignore the result. */
+ BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If
+ the connection has timed out or been closed, this
+ should be FALSE when it gets to Curl_ftp_quit() */
+ BIT(cwddone); /* if it has been determined that the proper CWD combo
+ already has been done */
+ BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
+ caching the current directory */
+ BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
};
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index 3d529ef236..39001e3f65 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -181,6 +181,43 @@ struct ftp_parselist_data {
} offsets;
};
+static void fileinfo_dtor(void *user, void *element)
+{
+ (void)user;
+ Curl_fileinfo_cleanup(element);
+}
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc)
+{
+ Curl_llist_init(&wc->filelist, fileinfo_dtor);
+ wc->state = CURLWC_INIT;
+
+ return CURLE_OK;
+}
+
+void Curl_wildcard_dtor(struct WildcardData **wcp)
+{
+ struct WildcardData *wc = *wcp;
+ if(!wc)
+ return;
+
+ if(wc->dtor) {
+ wc->dtor(wc->ftpwc);
+ wc->dtor = ZERO_NULL;
+ wc->ftpwc = NULL;
+ }
+ DEBUGASSERT(wc->ftpwc == NULL);
+
+ Curl_llist_destroy(&wc->filelist, NULL);
+ free(wc->path);
+ wc->path = NULL;
+ free(wc->pattern);
+ wc->pattern = NULL;
+ wc->state = CURLWC_INIT;
+ free(wc);
+ *wcp = NULL;
+}
+
struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
{
return calloc(1, sizeof(struct ftp_parselist_data));
@@ -274,8 +311,8 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
struct fileinfo *infop)
{
curl_fnmatch_callback compare;
- struct WildcardData *wc = &data->wildcard;
- struct ftp_wc *ftpwc = wc->protdata;
+ struct WildcardData *wc = data->wildcard;
+ struct ftp_wc *ftpwc = wc->ftpwc;
struct Curl_llist *llist = &wc->filelist;
struct ftp_parselist_data *parser = ftpwc->parser;
bool add = TRUE;
@@ -330,7 +367,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
{
size_t bufflen = size*nmemb;
struct Curl_easy *data = (struct Curl_easy *)connptr;
- struct ftp_wc *ftpwc = data->wildcard.protdata;
+ struct ftp_wc *ftpwc = data->wildcard->ftpwc;
struct ftp_parselist_data *parser = ftpwc->parser;
struct fileinfo *infop;
struct curl_fileinfo *finfo;
diff --git a/Utilities/cmcurl/lib/ftplistparser.h b/Utilities/cmcurl/lib/ftplistparser.h
index 0a80543417..5ba1f6a97d 100644
--- a/Utilities/cmcurl/lib/ftplistparser.h
+++ b/Utilities/cmcurl/lib/ftplistparser.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,5 +39,39 @@ struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void);
void Curl_ftp_parselist_data_free(struct ftp_parselist_data **pl_data);
+/* list of wildcard process states */
+typedef enum {
+ CURLWC_CLEAR = 0,
+ CURLWC_INIT = 1,
+ CURLWC_MATCHING, /* library is trying to get list of addresses for
+ downloading */
+ CURLWC_DOWNLOADING,
+ CURLWC_CLEAN, /* deallocate resources and reset settings */
+ CURLWC_SKIP, /* skip over concrete file */
+ CURLWC_ERROR, /* error cases */
+ CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop
+ will end */
+} wildcard_states;
+
+typedef void (*wildcard_dtor)(void *ptr);
+
+/* struct keeping information about wildcard download process */
+struct WildcardData {
+ char *path; /* path to the directory, where we trying wildcard-match */
+ char *pattern; /* wildcard pattern */
+ struct Curl_llist filelist; /* llist with struct Curl_fileinfo */
+ struct ftp_wc *ftpwc; /* pointer to FTP wildcard data */
+ wildcard_dtor dtor;
+ unsigned char state; /* wildcard_states */
+};
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc);
+void Curl_wildcard_dtor(struct WildcardData **wcp);
+
+struct Curl_easy;
+
+#else
+/* FTP is disabled */
+#define Curl_wildcard_dtor(x)
#endif /* CURL_DISABLE_FTP */
#endif /* HEADER_CURL_FTPLISTPARSER_H */
diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h
index 8891b1d5d6..075c02e54f 100644
--- a/Utilities/cmcurl/lib/functypes.h
+++ b/Utilities/cmcurl/lib/functypes.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getenv.c b/Utilities/cmcurl/lib/getenv.c
index 5f00fd13a4..8069784728 100644
--- a/Utilities/cmcurl/lib/getenv.c
+++ b/Utilities/cmcurl/lib/getenv.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index 3a24c65294..826ffd0b0c 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/getinfo.h b/Utilities/cmcurl/lib/getinfo.h
index 1b5e8c20f8..56bb440b43 100644
--- a/Utilities/cmcurl/lib/getinfo.h
+++ b/Utilities/cmcurl/lib/getinfo.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index 6fbb7de327..4a11d9364e 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h
index 4ea269d2b4..9e3365b71c 100644
--- a/Utilities/cmcurl/lib/gopher.h
+++ b/Utilities/cmcurl/lib/gopher.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/h2h3.c b/Utilities/cmcurl/lib/h2h3.c
index 3a9288df5c..3b21699c3c 100644
--- a/Utilities/cmcurl/lib/h2h3.c
+++ b/Utilities/cmcurl/lib/h2h3.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -118,6 +118,7 @@ static header_instruction inspect_header(const char *name, size_t namelen,
CURLcode Curl_pseudo_headers(struct Curl_easy *data,
const char *mem, /* the request */
const size_t len /* size of request */,
+ size_t* hdrlen /* opt size of headers read */,
struct h2h3req **hp)
{
struct connectdata *conn = data->conn;
@@ -291,6 +292,12 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data,
}
}
+ if(hdrlen) {
+ /* Skip trailing CRLF */
+ end += 4;
+ *hdrlen = end - mem;
+ }
+
hreq->entries = nheader;
*hp = hreq;
diff --git a/Utilities/cmcurl/lib/h2h3.h b/Utilities/cmcurl/lib/h2h3.h
index c35b7061fa..396c12c625 100644
--- a/Utilities/cmcurl/lib/h2h3.h
+++ b/Utilities/cmcurl/lib/h2h3.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -51,6 +51,7 @@ struct h2h3req {
CURLcode Curl_pseudo_headers(struct Curl_easy *data,
const char *request,
const size_t len,
+ size_t* hdrlen /* optional */,
struct h2h3req **hp);
/*
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c
index b6a2a33c72..06ce92cbd5 100644
--- a/Utilities/cmcurl/lib/hash.c
+++ b/Utilities/cmcurl/lib/hash.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h
index 5b59bf1118..9cfffc25b0 100644
--- a/Utilities/cmcurl/lib/hash.h
+++ b/Utilities/cmcurl/lib/hash.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c
index 978c918f4f..6cd7e31703 100644
--- a/Utilities/cmcurl/lib/headers.c
+++ b/Utilities/cmcurl/lib/headers.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -38,14 +38,13 @@
/* Generate the curl_header struct for the user. This function MUST assign all
struct fields in the output struct. */
-static void copy_header_external(struct Curl_easy *data,
- struct Curl_header_store *hs,
+static void copy_header_external(struct Curl_header_store *hs,
size_t index,
size_t amount,
struct Curl_llist_element *e,
- struct curl_header **hout)
+ struct curl_header *hout)
{
- struct curl_header *h = *hout = &data->state.headerout;
+ struct curl_header *h = hout;
h->name = hs->name;
h->value = hs->value;
h->amount = amount;
@@ -118,7 +117,9 @@ CURLHcode curl_easy_header(CURL *easy,
return CURLHE_MISSING;
}
/* this is the name we want */
- copy_header_external(data, hs, nameindex, amount, e_pick, hout);
+ copy_header_external(hs, nameindex, amount, e_pick,
+ &data->state.headerout[0]);
+ *hout = &data->state.headerout[0];
return CURLHE_OK;
}
@@ -132,7 +133,6 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
struct Curl_llist_element *pick;
struct Curl_llist_element *e;
struct Curl_header_store *hs;
- struct curl_header *hout;
size_t amount = 0;
size_t index = 0;
@@ -179,8 +179,9 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
index = amount - 1;
}
- copy_header_external(data, hs, index, amount, pick, &hout);
- return hout;
+ copy_header_external(hs, index, amount, pick,
+ &data->state.headerout[1]);
+ return &data->state.headerout[1];
}
static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h
index 96332dbd0b..a5229ea22f 100644
--- a/Utilities/cmcurl/lib/headers.h
+++ b/Utilities/cmcurl/lib/headers.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c
index dfb0db5757..8d8de1757d 100644
--- a/Utilities/cmcurl/lib/hmac.c
+++ b/Utilities/cmcurl/lib/hmac.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c
index df50d13e19..2f6762ca4e 100644
--- a/Utilities/cmcurl/lib/hostasyn.c
+++ b/Utilities/cmcurl/lib/hostasyn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -78,7 +78,7 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
dns = Curl_cache_addr(data, ai,
- data->state.async.hostname,
+ data->state.async.hostname, 0,
data->state.async.port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index dd427a2c66..d0dc2e8d5c 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -167,18 +167,25 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
/*
* Create a hostcache id string for the provided host + port, to be used by
- * the DNS caching. Without alloc.
+ * the DNS caching. Without alloc. Return length of the id string.
*/
-static void
-create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
+static size_t
+create_hostcache_id(const char *name,
+ size_t nlen, /* 0 or actual name length */
+ int port, char *ptr, size_t buflen)
{
- size_t len = strlen(name);
+ size_t len = nlen ? nlen : strlen(name);
+ size_t olen = 0;
+ DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
if(len > (buflen - 7))
len = buflen - 7;
/* store and lower case the name */
- while(len--)
+ while(len--) {
*ptr++ = Curl_raw_tolower(*name++);
- msnprintf(ptr, 7, ":%u", port);
+ olen++;
+ }
+ olen += msnprintf(ptr, 7, ":%u", port);
+ return olen;
}
struct hostcache_prune_data {
@@ -260,20 +267,18 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
int port)
{
struct Curl_dns_entry *dns = NULL;
- size_t entry_len;
char entry_id[MAX_HOSTCACHE_LEN];
/* Create an entry id, based upon the hostname and port */
- create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
- entry_len = strlen(entry_id);
+ size_t entry_len = create_hostcache_id(hostname, 0, port,
+ entry_id, sizeof(entry_id));
/* See if its already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
/* No entry found in cache, check if we might have a wildcard entry */
if(!dns && data->state.wildcard_resolve) {
- create_hostcache_id("*", port, entry_id, sizeof(entry_id));
- entry_len = strlen(entry_id);
+ entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
/* See if it's already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
@@ -438,6 +443,7 @@ struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy *data,
struct Curl_addrinfo *addr,
const char *hostname,
+ size_t hostlen, /* length or zero */
int port)
{
char entry_id[MAX_HOSTCACHE_LEN];
@@ -461,8 +467,8 @@ Curl_cache_addr(struct Curl_easy *data,
}
/* Create an entry id, based upon the hostname and port */
- create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
- entry_len = strlen(entry_id);
+ entry_len = create_hostcache_id(hostname, hostlen, port,
+ entry_id, sizeof(entry_id));
dns->inuse = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
@@ -791,7 +797,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, addr, hostname, port);
+ dns = Curl_cache_addr(data, addr, hostname, 0, port);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
@@ -1059,8 +1065,7 @@ void Curl_hostcache_clean(struct Curl_easy *data,
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
{
struct curl_slist *hostp;
- char hostname[256];
- int port = 0;
+ char *host_end;
/* Default is no wildcard found */
data->state.wildcard_resolve = false;
@@ -1070,18 +1075,25 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
if(!hostp->data)
continue;
if(hostp->data[0] == '-') {
+ unsigned long num = 0;
size_t entry_len;
-
- if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
- infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'",
+ size_t hlen = 0;
+ host_end = strchr(&hostp->data[1], ':');
+
+ if(host_end) {
+ hlen = host_end - &hostp->data[1];
+ num = strtoul(++host_end, NULL, 10);
+ if(!hlen || (num > 0xffff))
+ host_end = NULL;
+ }
+ if(!host_end) {
+ infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
hostp->data);
continue;
}
-
/* Create an entry id, based upon the hostname and port */
- create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
- entry_len = strlen(entry_id);
-
+ entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
+ entry_id, sizeof(entry_id));
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -1102,25 +1114,22 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
char *addr_begin;
char *addr_end;
char *port_ptr;
+ int port = 0;
char *end_ptr;
bool permanent = TRUE;
- char *host_begin;
- char *host_end;
unsigned long tmp_port;
bool error = true;
+ char *host_begin = hostp->data;
+ size_t hlen = 0;
- host_begin = hostp->data;
if(host_begin[0] == '+') {
host_begin++;
permanent = FALSE;
}
host_end = strchr(host_begin, ':');
- if(!host_end ||
- ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname)))
+ if(!host_end)
goto err;
-
- memcpy(hostname, host_begin, host_end - host_begin);
- hostname[host_end - host_begin] = '\0';
+ hlen = host_end - host_begin;
port_ptr = host_end + 1;
tmp_port = strtoul(port_ptr, &end_ptr, 10);
@@ -1196,8 +1205,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
}
/* Create an entry id, based upon the hostname and port */
- create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
- entry_len = strlen(entry_id);
+ entry_len = create_hostcache_id(host_begin, hlen, port,
+ entry_id, sizeof(entry_id));
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -1206,8 +1215,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
if(dns) {
- infof(data, "RESOLVE %s:%d is - old addresses discarded",
- hostname, port);
+ infof(data, "RESOLVE %.*s:%d is - old addresses discarded",
+ (int)hlen, host_begin, port);
/* delete old entry, there are two reasons for this
1. old entry may have different addresses.
2. even if entry with correct addresses is already in the cache,
@@ -1223,7 +1232,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
}
/* put this new host in the cache */
- dns = Curl_cache_addr(data, head, hostname, port);
+ dns = Curl_cache_addr(data, head, host_begin, hlen, port);
if(dns) {
if(permanent)
dns->timestamp = 0; /* mark as permanent */
@@ -1239,13 +1248,13 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
Curl_freeaddrinfo(head);
return CURLE_OUT_OF_MEMORY;
}
- infof(data, "Added %s:%d:%s to DNS cache%s",
- hostname, port, addresses, permanent ? "" : " (non-permanent)");
+ infof(data, "Added %.*s:%d:%s to DNS cache%s",
+ (int)hlen, host_begin, port, addresses,
+ permanent ? "" : " (non-permanent)");
/* Wildcard hostname */
- if(hostname[0] == '*' && hostname[1] == '\0') {
- infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks",
- hostname, port);
+ if((hlen == 1) && (host_begin[0] == '*')) {
+ infof(data, "RESOLVE *:%d using wildcard", port);
data->state.wildcard_resolve = true;
}
}
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index 3b1d814dab..4b5481f659 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -178,7 +178,7 @@ Curl_fetch_addr(struct Curl_easy *data,
*/
struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
- const char *hostname, int port);
+ const char *hostname, size_t hostlen, int port);
#ifndef INADDR_NONE
#define CURL_INADDR_NONE (in_addr_t) ~0
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c
index 109bd1e5ea..9140180ffd 100644
--- a/Utilities/cmcurl/lib/hostip4.c
+++ b/Utilities/cmcurl/lib/hostip4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index af8bc23021..6b0ba55e9f 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c
index 73d1e50569..ca8b0758c4 100644
--- a/Utilities/cmcurl/lib/hostsyn.c
+++ b/Utilities/cmcurl/lib/hostsyn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index c449120f31..64cbae10c9 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -39,6 +39,7 @@
#include "parsedate.h"
#include "fopen.h"
#include "rename.h"
+#include "share.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -425,14 +426,23 @@ static CURLcode hsts_add(struct hsts *h, char *line)
if(2 == rc) {
time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
TIME_T_MAX;
- CURLcode result;
+ CURLcode result = CURLE_OK;
char *p = host;
bool subdomain = FALSE;
+ struct stsentry *e;
if(p[0] == '.') {
p++;
subdomain = TRUE;
}
- result = hsts_create(h, p, subdomain, expires);
+ /* only add it if not already present */
+ e = Curl_hsts(h, p, subdomain);
+ if(!e)
+ result = hsts_create(h, p, subdomain, expires);
+ else {
+ /* the same host name, use the largest expire time */
+ if(expires > e->expires)
+ e->expires = expires;
+ }
if(result)
return result;
}
@@ -551,4 +561,18 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
return CURLE_OK;
}
+void Curl_hsts_loadfiles(struct Curl_easy *data)
+{
+ struct curl_slist *l = data->set.hstslist;
+ if(l) {
+ Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
+
+ while(l) {
+ (void)Curl_hsts_loadfile(data, data->hsts, l->data);
+ l = l->next;
+ }
+ Curl_share_unlock(data, CURL_LOCK_DATA_HSTS);
+ }
+}
+
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h
index 0e36a7756b..d3431a5d7a 100644
--- a/Utilities/cmcurl/lib/hsts.h
+++ b/Utilities/cmcurl/lib/hsts.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,9 +59,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
struct hsts *h, const char *file);
CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
struct hsts *h);
+void Curl_hsts_loadfiles(struct Curl_easy *data);
#else
#define Curl_hsts_cleanup(x)
#define Curl_hsts_loadcb(x,y) CURLE_OK
#define Curl_hsts_save(x,y,z)
+#define Curl_hsts_loadfiles(x)
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
#endif /* HEADER_CURL_HSTS_H */
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 1b7502280e..faa486cc6f 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,6 +62,7 @@
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
+#include "vquic/vquic.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "curl_ntlm_wb.h"
@@ -87,6 +88,7 @@
#include "hsts.h"
#include "ws.h"
#include "c-hyper.h"
+#include "curl_ctype.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -150,7 +152,7 @@ const struct Curl_handler Curl_handler_ws = {
http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ZERO_NULL, /* disconnect */
+ Curl_ws_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -204,7 +206,7 @@ const struct Curl_handler Curl_handler_wss = {
http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ZERO_NULL, /* disconnect */
+ Curl_ws_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
@@ -218,41 +220,6 @@ const struct Curl_handler Curl_handler_wss = {
#endif
-static CURLcode h3_setup_conn(struct Curl_easy *data,
- struct connectdata *conn)
-{
-#ifdef ENABLE_QUIC
- /* We want HTTP/3 directly, setup the filter chain ourself,
- * overriding the default behaviour. */
- DEBUGASSERT(conn->transport == TRNSPRT_QUIC);
-
- if(!(conn->handler->flags & PROTOPT_SSL)) {
- failf(data, "HTTP/3 requested for non-HTTPS URL");
- return CURLE_URL_MALFORMAT;
- }
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy) {
- failf(data, "HTTP/3 is not supported over a SOCKS proxy");
- return CURLE_URL_MALFORMAT;
- }
- if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
- failf(data, "HTTP/3 is not supported over a HTTP proxy");
- return CURLE_URL_MALFORMAT;
- }
-#endif
-
- DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)",
- conn->connection_id, FIRSTSOCKET));
- return Curl_conn_socket_set(data, conn, FIRSTSOCKET);
-
-#else /* ENABLE_QUIC */
- (void)conn;
- (void)data;
- DEBUGF(infof(data, "QUIC is not supported in this build"));
- return CURLE_NOT_BUILT_IN;
-#endif /* !ENABLE_QUIC */
-}
-
static CURLcode http_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
@@ -267,20 +234,14 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
Curl_mime_initpart(&http->form);
data->req.p.http = http;
+ connkeep(conn, "HTTP default");
- if(data->state.httpwant == CURL_HTTP_VERSION_3) {
- conn->transport = TRNSPRT_QUIC;
+ if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+ CURLcode result = Curl_conn_may_http3(data, conn);
+ if(result)
+ return result;
}
- if(conn->transport == TRNSPRT_QUIC) {
- return h3_setup_conn(data, conn);
- }
- else {
- if(!CONN_INUSE(conn))
- /* if not already multi-using, setup connection details */
- Curl_http2_setup_conn(conn);
- Curl_http2_setup_req(data);
- }
return CURLE_OK;
}
@@ -1256,8 +1217,8 @@ static size_t readmoredata(char *buffer,
size_t nitems,
void *userp)
{
- struct Curl_easy *data = (struct Curl_easy *)userp;
- struct HTTP *http = data->req.p.http;
+ struct HTTP *http = (struct HTTP *)userp;
+ struct Curl_easy *data = http->backup.data;
size_t fullsize = size * nitems;
if(!http->postsize)
@@ -1309,6 +1270,7 @@ static size_t readmoredata(char *buffer,
*/
CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data,
+ struct HTTP *http,
/* add the number of sent bytes to this
counter */
curl_off_t *bytes_written,
@@ -1321,14 +1283,13 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
char *ptr;
size_t size;
struct connectdata *conn = data->conn;
- struct HTTP *http = data->req.p.http;
size_t sendsize;
curl_socket_t sockfd;
size_t headersize;
DEBUGASSERT(socketindex <= SECONDARYSOCKET);
- sockfd = conn->sock[socketindex];
+ sockfd = Curl_conn_get_socket(data, socketindex);
/* The looping below is required since we use non-blocking sockets, but due
to the circumstances we will just loop and try again and again etc */
@@ -1456,10 +1417,11 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
http->backup.fread_in = data->state.in;
http->backup.postdata = http->postdata;
http->backup.postsize = http->postsize;
+ http->backup.data = data;
/* set the new pointers for the request-sending */
data->state.fread_func = (curl_read_callback)readmoredata;
- data->state.in = (void *)data;
+ data->state.in = (void *)http;
http->postdata = ptr;
http->postsize = (curl_off_t)size;
@@ -1468,7 +1430,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
http->send_buffer = *in; /* copy the whole struct */
http->sending = HTTPSEND_REQUEST;
-
return CURLE_OK;
}
http->sending = HTTPSEND_BODY;
@@ -1579,8 +1540,8 @@ static int http_getsock_do(struct Curl_easy *data,
curl_socket_t *socks)
{
/* write mode */
- (void)data;
- socks[0] = conn->sock[FIRSTSOCKET];
+ (void)conn;
+ socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET);
return GETSOCK_WRITESOCK(0);
}
@@ -1610,8 +1571,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;
Curl_dyn_free(&http->send_buffer);
- Curl_http2_done(data, premature);
- Curl_quic_done(data, premature);
Curl_mime_cleanpart(&http->form);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
@@ -1664,17 +1623,10 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data,
static const char *get_http_string(const struct Curl_easy *data,
const struct connectdata *conn)
{
-#ifdef ENABLE_QUIC
- if((data->state.httpwant == CURL_HTTP_VERSION_3) ||
- (conn->httpversion == 30))
+ if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
return "3";
-#endif
-
-#ifdef USE_NGHTTP2
- if(conn->proto.httpc.h2)
+ if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
return "2";
-#endif
-
if(Curl_use_http_1_1plus(data, conn))
return "1.1";
@@ -2359,7 +2311,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
curl_off_t included_body = 0;
#else
/* from this point down, this function should not be used */
-#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
+#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK
#endif
CURLcode result = CURLE_OK;
struct HTTP *http = data->req.p.http;
@@ -2388,7 +2340,16 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
}
- if(http->postsize) {
+ /* For really small puts we don't use Expect: headers at all, and for
+ the somewhat bigger ones we allow the app to disable it. Just make
+ sure that the expect100header is always set to the preferred value
+ here. */
+ ptr = Curl_checkheaders(data, STRCONST("Expect"));
+ if(ptr) {
+ data->state.expect100header =
+ Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
+ }
+ else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
result = expect100(data, conn, r);
if(result)
return result;
@@ -2403,7 +2364,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
Curl_pgrsSetUploadSize(data, http->postsize);
/* this sends the buffer and frees all the buffer resources */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending PUT request");
@@ -2424,7 +2386,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
if(result)
return result;
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
@@ -2440,8 +2403,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
we don't upload data chunked, as RFC2616 forbids us to set both
kinds of headers (Transfer-Encoding: chunked and Content-Length) */
if(http->postsize != -1 && !data->req.upload_chunky &&
- (conn->bits.authneg ||
- !Curl_checkheaders(data, STRCONST("Content-Length")))) {
+ (!Curl_checkheaders(data, STRCONST("Content-Length")))) {
/* we allow replacing this header if not during auth negotiation,
although it isn't very wise to actually set your own */
result = Curl_dyn_addf(r,
@@ -2495,7 +2457,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
http->sending = HTTPSEND_BODY;
/* this sends the buffer and frees all the buffer resources */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending POST request");
@@ -2561,7 +2524,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
/* In HTTP2, we send request body in DATA frame regardless of
its size. */
- if(conn->httpversion != 20 &&
+ if(conn->httpversion < 20 &&
!data->state.expect100header &&
(http->postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
@@ -2612,11 +2575,10 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
else {
/* A huge POST coming up, do data separate from the request */
http->postdata = data->set.postfields;
-
http->sending = HTTPSEND_BODY;
-
+ http->backup.data = data;
data->state.fread_func = (curl_read_callback)readmoredata;
- data->state.in = (void *)data;
+ data->state.in = (void *)http;
/* set the upload size to the progress meter */
Curl_pgrsSetUploadSize(data, http->postsize);
@@ -2655,7 +2617,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
}
}
/* issue the request */
- result = Curl_buffer_send(r, data, &data->info.request_size, included_body,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, included_body,
FIRSTSOCKET);
if(result)
@@ -2671,7 +2634,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
return result;
/* issue the request */
- result = Curl_buffer_send(r, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(r, data, data->req.p.http,
+ &data->info.request_size, 0,
FIRSTSOCKET);
if(result)
failf(data, "Failed sending HTTP request");
@@ -3021,50 +2985,27 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
the rest of the request in the PERFORM phase. */
*done = TRUE;
- if(conn->transport != TRNSPRT_QUIC) {
- if(conn->httpversion < 20) { /* unless the connection is re-used and
- already http2 */
- switch(conn->alpn) {
- case CURL_HTTP_VERSION_2:
- conn->httpversion = 20; /* we know we're on HTTP/2 now */
-
- result = Curl_http2_switched(data, NULL, 0);
- if(result)
- return result;
- break;
- case CURL_HTTP_VERSION_1_1:
- /* continue with HTTP/1.1 when explicitly requested */
- break;
- default:
- /* Check if user wants to use HTTP/2 with clear TCP */
-#ifdef USE_NGHTTP2
- if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
- /* We don't support HTTP/2 proxies yet. Also it's debatable
- whether or not this setting should apply to HTTP/2 proxies. */
- infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
- break;
- }
-#endif
- DEBUGF(infof(data, "HTTP/2 over clean TCP"));
- conn->httpversion = 20;
-
- result = Curl_http2_switched(data, NULL, 0);
- if(result)
- return result;
- }
-#endif
- break;
- }
- }
- else {
- /* prepare for an http2 request */
- result = Curl_http2_setup(data, conn);
+ switch(conn->alpn) {
+ case CURL_HTTP_VERSION_3:
+ DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
+ break;
+ case CURL_HTTP_VERSION_2:
+ DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ /* continue with HTTP/1.1 when explicitly requested */
+ break;
+ default:
+ /* Check if user wants to use HTTP/2 with clear TCP */
+ if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
+ DEBUGF(infof(data, "HTTP/2 over clean TCP"));
+ result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
return result;
}
+ break;
}
+
http = data->req.p.http;
DEBUGASSERT(http);
@@ -3224,7 +3165,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
- conn->httpversion != 20 &&
+ conn->httpversion < 20 &&
(data->state.httpwant == CURL_HTTP_VERSION_2)) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
over SSL */
@@ -3236,8 +3177,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
result = Curl_http_cookies(data, conn, &req);
+#ifdef USE_WEBSOCKETS
if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
result = Curl_ws_request(data, &req);
+#endif
if(!result)
result = Curl_add_timecondition(data, &req);
if(!result)
@@ -3282,7 +3225,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
}
- if((conn->httpversion == 20) && data->req.upload_chunky)
+ if((conn->httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@@ -3669,7 +3612,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
#endif
)) {
/* the ALPN of the current request */
- enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ enum alpnid id = (conn->httpversion == 30)? ALPN_h3 :
+ (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
result = Curl_altsvc_parse(data, data->asi,
headp + strlen("Alt-Svc:"),
id, conn->host.name,
@@ -3963,7 +3907,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* switch to http2 now. The bytes after response headers
are also processed here, otherwise they are lost. */
- result = Curl_http2_switched(data, k->str, *nread);
+ result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
+ k->str, *nread);
if(result)
return result;
*nread = 0;
@@ -3971,7 +3916,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
#ifdef USE_WEBSOCKETS
else if(k->upgr101 == UPGR101_WS) {
/* verify the response */
- result = Curl_ws_accept(data);
+ result = Curl_ws_accept(data, k->str, *nread);
if(result)
return result;
k->header = FALSE; /* no more header to parse! */
@@ -4191,11 +4136,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
stream. In order to do this, we keep reading until we
close the stream. */
if(0 == k->maxdownload
-#if defined(USE_NGHTTP2)
- && !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- conn->httpversion == 20)
-#endif
- )
+ && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
+ && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
*stop_reading = TRUE;
if(*stop_reading) {
@@ -4220,11 +4162,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(!k->headerline++) {
/* This is the first header, it MUST be the error code line
or else we consider this to be the body right away! */
- int httpversion_major;
- int rtspversion_major;
- int nc = 0;
-#define HEADER1 headp /* no conversion needed, just use headp */
-
+ bool fine_statusline = FALSE;
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
/*
* https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
@@ -4233,39 +4171,60 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
* says. We allow any three-digit number here, but we cannot make
* guarantees on future behaviors since it isn't within the protocol.
*/
- char separator;
- char twoorthree[2];
int httpversion = 0;
- char digit4 = 0;
- nc = sscanf(HEADER1,
- " HTTP/%1d.%1d%c%3d%c",
- &httpversion_major,
- &httpversion,
- &separator,
- &k->httpcode,
- &digit4);
-
- if(nc == 1 && httpversion_major >= 2 &&
- 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
- conn->httpversion = 0;
- nc = 4;
- separator = ' ';
- }
-
- /* There can only be a 4th response code digit stored in 'digit4' if
- all the other fields were parsed and stored first, so nc is 5 when
- digit4 a digit.
-
- The sscanf() line above will also allow zero-prefixed and negative
- numbers, so we check for that too here.
- */
- else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) {
- failf(data, "Unsupported response code in HTTP response");
- return CURLE_UNSUPPORTED_PROTOCOL;
+ char *p = headp;
+
+ while(*p && ISBLANK(*p))
+ p++;
+ if(!strncmp(p, "HTTP/", 5)) {
+ p += 5;
+ switch(*p) {
+ case '1':
+ p++;
+ if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
+ if(ISBLANK(p[2])) {
+ httpversion = 10 + (p[1] - '0');
+ p += 3;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(ISSPACE(*p))
+ fine_statusline = TRUE;
+ }
+ }
+ }
+ if(!fine_statusline) {
+ failf(data, "Unsupported HTTP/1 subversion in response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ break;
+ case '2':
+ case '3':
+ if(!ISBLANK(p[1]))
+ break;
+ httpversion = (*p - '0') * 10;
+ p += 2;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(!ISSPACE(*p))
+ break;
+ fine_statusline = TRUE;
+ }
+ break;
+ default: /* unsupported */
+ failf(data, "Unsupported HTTP version in response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
}
- if((nc >= 4) && (' ' == separator)) {
- httpversion += 10 * httpversion_major;
+ if(fine_statusline) {
+ if(k->httpcode < 100) {
+ failf(data, "Unsupported response code in HTTP response");
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
switch(httpversion) {
case 10:
case 11:
@@ -4290,54 +4249,52 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
if(conn->httpversion < 20) {
conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
- infof(data, "Mark bundle as not supporting multiuse");
}
}
- else if(!nc) {
- /* this is the real world, not a Nirvana
- NCSA 1.5.x returns this crap when asked for HTTP/1.1
- */
- nc = sscanf(HEADER1, " HTTP %3d", &k->httpcode);
- conn->httpversion = 10;
-
+ else {
/* If user has set option HTTP200ALIASES,
compare header line against list of aliases
*/
- if(!nc) {
- statusline check =
- checkhttpprefix(data,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
- if(check == STATUS_DONE) {
- nc = 1;
- k->httpcode = 200;
- conn->httpversion = 10;
- }
+ statusline check =
+ checkhttpprefix(data,
+ Curl_dyn_ptr(&data->state.headerb),
+ Curl_dyn_len(&data->state.headerb));
+ if(check == STATUS_DONE) {
+ fine_statusline = TRUE;
+ k->httpcode = 200;
+ conn->httpversion = 10;
}
}
- else {
- failf(data, "Unsupported HTTP version in response");
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
}
else if(conn->handler->protocol & CURLPROTO_RTSP) {
- char separator;
- int rtspversion;
- nc = sscanf(HEADER1,
- " RTSP/%1d.%1d%c%3d",
- &rtspversion_major,
- &rtspversion,
- &separator,
- &k->httpcode);
- if((nc == 4) && (' ' == separator)) {
- conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */
- }
- else {
- nc = 0;
+ char *p = headp;
+ while(*p && ISBLANK(*p))
+ p++;
+ if(!strncmp(p, "RTSP/", 5)) {
+ p += 5;
+ if(ISDIGIT(*p)) {
+ p++;
+ if((p[0] == '.') && ISDIGIT(p[1])) {
+ if(ISBLANK(p[2])) {
+ p += 3;
+ if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
+ k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
+ (p[2] - '0');
+ p += 3;
+ if(ISSPACE(*p)) {
+ fine_statusline = TRUE;
+ conn->httpversion = 11; /* RTSP acts like HTTP 1.1 */
+ }
+ }
+ }
+ }
+ }
+ if(!fine_statusline)
+ return CURLE_WEIRD_SERVER_REPLY;
}
}
- if(nc) {
+ if(fine_statusline) {
result = Curl_http_statusline(data, conn);
if(result)
return result;
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index ecfe4eed0a..444abc0be6 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,6 +24,11 @@
*
***************************************************************************/
#include "curl_setup.h"
+
+#if defined(USE_MSH3) && !defined(_WIN32)
+#include <pthread.h>
+#endif
+
#include "ws.h"
typedef enum {
@@ -37,11 +42,7 @@ typedef enum {
#ifndef CURL_DISABLE_HTTP
-#ifdef USE_NGHTTP2
-#include <nghttp2/nghttp2.h>
-#endif
-
-#if defined(_WIN32) && defined(ENABLE_QUIC)
+#if defined(ENABLE_QUIC) || defined(USE_NGHTTP2)
#include <stdint.h>
#endif
@@ -73,8 +74,10 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
const struct connectdata *conn,
const char *thisheader,
const size_t thislen);
+struct HTTP; /* see below */
CURLcode Curl_buffer_send(struct dynbuf *in,
struct Curl_easy *data,
+ struct HTTP *http,
curl_off_t *bytes_written,
curl_off_t included_body_bytes,
int socketindex);
@@ -179,29 +182,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
struct h3out; /* see ngtcp2 */
#endif
-#ifdef USE_MSH3
-#ifdef _WIN32
-#define msh3_lock CRITICAL_SECTION
-#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
-#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
-#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
-#define msh3_lock_release(lock) LeaveCriticalSection(lock)
-#else /* !_WIN32 */
-#include <pthread.h>
-#define msh3_lock pthread_mutex_t
-#define msh3_lock_initialize(lock) { \
- pthread_mutexattr_t attr; \
- pthread_mutexattr_init(&attr); \
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
- pthread_mutex_init(lock, &attr); \
- pthread_mutexattr_destroy(&attr); \
-}
-#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
-#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
-#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
-#endif /* _WIN32 */
-#endif /* USE_MSH3 */
-
/****************************************************************************
* HTTP unique setup
***************************************************************************/
@@ -220,6 +200,7 @@ struct HTTP {
void *fread_in; /* backup storage for fread_in pointer */
const char *postdata;
curl_off_t postsize;
+ struct Curl_easy *data;
} backup;
enum {
@@ -258,7 +239,6 @@ struct HTTP {
#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3)
bool bodystarted;
int status_code; /* HTTP status code */
- bool closed; /* TRUE on HTTP2 stream close */
char *mem; /* points to a buffer in memory to store received data */
size_t len; /* size of the buffer 'mem' points to */
size_t memlen; /* size of data copied to mem */
@@ -268,6 +248,8 @@ struct HTTP {
const uint8_t *upload_mem; /* points to a buffer to read from */
size_t upload_len; /* size of the buffer 'upload_mem' points to */
curl_off_t upload_left; /* number of bytes left to upload */
+ bool closed; /* TRUE on stream close */
+ bool reset; /* TRUE on stream reset */
#endif
#ifdef ENABLE_QUIC
@@ -278,20 +260,25 @@ struct HTTP {
bool firstheader; /* FALSE until headers arrive */
bool firstbody; /* FALSE until body arrives */
bool h3req; /* FALSE until request is issued */
-#endif
+#endif /* !USE_MSH3 */
bool upload_done;
-#endif
+#endif /* ENABLE_QUIC */
#ifdef USE_NGHTTP3
- size_t unacked_window;
+ size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
struct h3out *h3out; /* per-stream buffers for upload */
struct dynbuf overflow; /* excess data received during a single Curl_read */
-#endif
+#endif /* USE_NGHTTP3 */
#ifdef USE_MSH3
struct MSH3_REQUEST *req;
- msh3_lock recv_lock;
+#ifdef _WIN32
+ CRITICAL_SECTION recv_lock;
+#else /* !_WIN32 */
+ pthread_mutex_t recv_lock;
+#endif /* _WIN32 */
/* Receive Buffer (Headers and Data) */
uint8_t* recv_buf;
size_t recv_buf_alloc;
+ size_t recv_buf_max;
/* Receive Headers */
size_t recv_header_len;
bool recv_header_complete;
@@ -300,53 +287,13 @@ struct HTTP {
bool recv_data_complete;
/* General Receive Error */
CURLcode recv_error;
-#endif
-};
-
-#ifdef USE_NGHTTP2
-/* h2 settings for this connection */
-struct h2settings {
- uint32_t max_concurrent_streams;
- bool enable_push;
-};
-#endif
-
-struct http_conn {
-#ifdef USE_NGHTTP2
-#define H2_BINSETTINGS_LEN 80
- uint8_t binsettings[H2_BINSETTINGS_LEN];
- size_t binlen; /* length of the binsettings data */
-
- /* We associate the connectdata struct with the connection, but we need to
- make sure we can identify the current "driving" transfer. This is a
- work-around for the lack of nghttp2_session_set_user_data() in older
- nghttp2 versions that we want to support. (Added in 1.31.0) */
- struct Curl_easy *trnsfr;
-
- nghttp2_session *h2;
- Curl_send *send_underlying; /* underlying send Curl_send callback */
- Curl_recv *recv_underlying; /* underlying recv Curl_recv callback */
- char *inbuf; /* buffer to receive data from underlying socket */
- size_t inbuflen; /* number of bytes filled in inbuf */
- size_t nread_inbuf; /* number of bytes read from in inbuf */
- /* We need separate buffer for transmission and reception because we
- may call nghttp2_session_send() after the
- nghttp2_session_mem_recv() but mem buffer is still not full. In
- this case, we wrongly sends the content of mem buffer if we share
- them for both cases. */
- int32_t pause_stream_id; /* stream ID which paused
- nghttp2_session_mem_recv */
- size_t drain_total; /* sum of all stream's UrlState.drain */
-
- /* this is a hash of all individual streams (Curl_easy structs) */
- struct h2settings settings;
-
- /* list of settings that will be sent */
- nghttp2_settings_entry local_settings[3];
- size_t local_settings_num;
-#else
- int unused; /* prevent a compiler warning */
-#endif
+#endif /* USE_MSH3 */
+#ifdef USE_QUICHE
+ bool h3_got_header; /* TRUE when h3 stream has recvd some HEADER */
+ bool h3_recving_data; /* TRUE when h3 stream is reading DATA */
+ bool h3_body_pending; /* TRUE when h3 stream may have more body DATA */
+ struct h3_event_node *pending;
+#endif /* USE_QUICHE */
};
CURLcode Curl_http_size(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index b9d3245cf8..b0ce87d987 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -35,6 +35,7 @@
#include "strcase.h"
#include "multiif.h"
#include "url.h"
+#include "cfilters.h"
#include "connect.h"
#include "strtoofft.h"
#include "strdup.h"
@@ -63,302 +64,388 @@
#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
-#ifdef DEBUG_HTTP2
-#define H2BUGF(x) x
-#else
-#define H2BUGF(x) do { } while(0)
-#endif
-static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
- char *mem, size_t len, CURLcode *err);
-static bool http2_connisdead(struct Curl_easy *data,
- struct connectdata *conn);
-static int h2_session_send(struct Curl_easy *data,
- nghttp2_session *h2);
-static int h2_process_pending_input(struct Curl_easy *data,
- struct http_conn *httpc,
- CURLcode *err);
+#define H2_SETTINGS_IV_LEN 3
+#define H2_BINSETTINGS_LEN 80
-/*
- * Curl_http2_init_state() is called when the easy handle is created and
- * allows for HTTP/2 specific init of state.
- */
-void Curl_http2_init_state(struct UrlState *state)
+static int populate_settings(nghttp2_settings_entry *iv,
+ struct Curl_easy *data)
{
- state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+ iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
+ iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
+
+ iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
+ iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
+
+ iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
+ iv[2].value = data->multi->push_cb != NULL;
+
+ return 3;
}
-/*
- * Curl_http2_init_userset() is called when the easy handle is created and
- * allows for HTTP/2 specific user-set fields.
- */
-void Curl_http2_init_userset(struct UserDefined *set)
+static size_t populate_binsettings(uint8_t *binsettings,
+ struct Curl_easy *data)
{
- set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
+ nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
+ int ivlen;
+
+ ivlen = populate_settings(iv, data);
+ /* this returns number of bytes it wrote */
+ return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
+ iv, ivlen);
}
-static int http2_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *sock)
-{
- const struct http_conn *c = &conn->proto.httpc;
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
- struct HTTP *stream = data->req.p.http;
+struct cf_h2_ctx {
+ nghttp2_session *h2;
+ uint32_t max_concurrent_streams;
+ /* The easy handle used in the current filter call, cleared at return */
+ struct cf_call_data call_data;
+
+ char *inbuf; /* buffer to receive data from underlying socket */
+ size_t inbuflen; /* number of bytes filled in inbuf */
+ size_t nread_inbuf; /* number of bytes read from in inbuf */
+
+ struct dynbuf outbuf;
+
+ /* We need separate buffer for transmission and reception because we
+ may call nghttp2_session_send() after the
+ nghttp2_session_mem_recv() but mem buffer is still not full. In
+ this case, we wrongly sends the content of mem buffer if we share
+ them for both cases. */
+ int32_t pause_stream_id; /* stream ID which paused
+ nghttp2_session_mem_recv */
+ size_t drain_total; /* sum of all stream's UrlState.drain */
+ int32_t goaway_error;
+ int32_t last_stream_id;
+ BIT(goaway);
+ BIT(enable_push);
+};
- sock[0] = conn->sock[FIRSTSOCKET];
+/* How to access `call_data` from a cf_h2 filter */
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct cf_h2_ctx *)(cf)->ctx)->call_data
- if(!(k->keepon & KEEP_RECV_PAUSE))
- /* Unless paused - in an HTTP/2 connection we can basically always get a
- frame so we should always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
- /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
- there's a window to send data in */
- if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
- nghttp2_session_want_write(c->h2)) &&
- (nghttp2_session_get_remote_window_size(c->h2) &&
- nghttp2_session_get_stream_remote_window_size(c->h2,
- stream->stream_id)))
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
+{
+ struct cf_call_data save = ctx->call_data;
- return bitmap;
+ if(ctx->h2) {
+ nghttp2_session_del(ctx->h2);
+ }
+ free(ctx->inbuf);
+ Curl_dyn_free(&ctx->outbuf);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->call_data = save;
}
-/*
- * http2_stream_free() free HTTP2 stream related data
- */
-static void http2_stream_free(struct HTTP *http)
+static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
{
- if(http) {
- Curl_dyn_free(&http->header_recvbuf);
- for(; http->push_headers_used > 0; --http->push_headers_used) {
- free(http->push_headers[http->push_headers_used - 1]);
- }
- free(http->push_headers);
- http->push_headers = NULL;
+ if(ctx) {
+ cf_h2_ctx_clear(ctx);
+ free(ctx);
}
}
+static int h2_client_new(struct Curl_cfilter *cf,
+ nghttp2_session_callbacks *cbs)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+
+#if NGHTTP2_VERSION_NUM < 0x013200
+ /* before 1.50.0 */
+ return nghttp2_session_client_new(&ctx->h2, cbs, cf);
+#else
+ nghttp2_option *o;
+ int rc = nghttp2_option_new(&o);
+ if(rc)
+ return rc;
+ /* turn off RFC 9113 leading and trailing white spaces validation against
+ HTTP field value. */
+ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
+ rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
+ nghttp2_option_del(o);
+ return rc;
+#endif
+}
+
+static ssize_t send_callback(nghttp2_session *h2,
+ const uint8_t *mem, size_t length, int flags,
+ void *userp);
+static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
+ void *userp);
+static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
+ int32_t stream_id,
+ const uint8_t *mem, size_t len, void *userp);
+static int on_stream_close(nghttp2_session *session, int32_t stream_id,
+ uint32_t error_code, void *userp);
+static int on_begin_headers(nghttp2_session *session,
+ const nghttp2_frame *frame, void *userp);
+static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen,
+ uint8_t flags,
+ void *userp);
+static int error_callback(nghttp2_session *session, const char *msg,
+ size_t len, void *userp);
+
/*
- * Disconnects *a* connection used for HTTP/2. It might be an old one from the
- * connection cache and not the "main" one. Don't touch the easy handle!
+ * multi_connchanged() is called to tell that there is a connection in
+ * this multi handle that has changed state (multiplexing become possible, the
+ * number of allowed streams changed or similar), and a subsequent use of this
+ * multi handle should move CONNECT_PEND handles back to CONNECT to have them
+ * retry.
*/
+static void multi_connchanged(struct Curl_multi *multi)
+{
+ multi->recheckstate = TRUE;
+}
-static CURLcode http2_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
+static CURLcode http2_data_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct http_conn *c = &conn->proto.httpc;
- (void)dead_connection;
-#ifndef DEBUG_HTTP2
- (void)data;
-#endif
+ struct HTTP *stream = data->req.p.http;
- H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
+ (void)cf;
+ DEBUGASSERT(stream);
+ DEBUGASSERT(data->state.buffer);
- nghttp2_session_del(c->h2);
- Curl_safefree(c->inbuf);
+ stream->stream_id = -1;
- H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
+ Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
+ Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
+
+ stream->bodystarted = FALSE;
+ stream->status_code = -1;
+ stream->pausedata = NULL;
+ stream->pauselen = 0;
+ stream->closed = FALSE;
+ stream->close_handled = FALSE;
+ stream->memlen = 0;
+ stream->error = NGHTTP2_NO_ERROR;
+ stream->upload_left = 0;
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+ stream->mem = data->state.buffer;
+ stream->len = data->set.buffer_size;
return CURLE_OK;
}
/*
- * The server may send us data at any point (e.g. PING frames). Therefore,
- * we cannot assume that an HTTP/2 socket is dead just because it is readable.
- *
- * Instead, if it is readable, run Curl_connalive() to peek at the socket
- * and distinguish between closed and data.
+ * Initialize the cfilter context
*/
-static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool via_h1_upgrade)
{
- int sval;
- bool dead = TRUE;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ int rc;
+ nghttp2_session_callbacks *cbs = NULL;
+
+ DEBUGASSERT(!ctx->h2);
+ ctx->inbuf = malloc(H2_BUFSIZE);
+ if(!ctx->inbuf)
+ goto out;
+ /* we want to aggregate small frames, SETTINGS, PRIO, UPDATES */
+ Curl_dyn_init(&ctx->outbuf, 4*1024);
+
+ rc = nghttp2_session_callbacks_new(&cbs);
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2 callbacks");
+ goto out;
+ }
+
+ nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
+ nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
+ cbs, on_data_chunk_recv);
+ nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
+ nghttp2_session_callbacks_set_on_begin_headers_callback(
+ cbs, on_begin_headers);
+ nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
+ nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
+
+ /* The nghttp2 session is not yet setup, do it */
+ rc = h2_client_new(cf, cbs);
+ if(rc) {
+ failf(data, "Couldn't initialize nghttp2");
+ goto out;
+ }
+ ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
+
+ result = http2_data_setup(cf, data);
+ if(result)
+ goto out;
- if(conn->bits.close)
- return TRUE;
+ if(via_h1_upgrade) {
+ /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
+ * in the H1 request and we upgrade from there. This stream
+ * is opened implicitly as #1. */
+ uint8_t binsettings[H2_BINSETTINGS_LEN];
+ size_t binlen; /* length of the binsettings data */
- sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
- if(sval == 0) {
- /* timeout */
- dead = FALSE;
- }
- else if(sval & CURL_CSELECT_ERR) {
- /* socket is in an error state */
- dead = TRUE;
- }
- else if(sval & CURL_CSELECT_IN) {
- /* readable with no error. could still be closed */
- dead = !Curl_connalive(data, conn);
- if(!dead) {
- /* This happens before we've sent off a request and the connection is
- not in use by any other transfer, there shouldn't be any data here,
- only "protocol frames" */
- CURLcode result;
- struct http_conn *httpc = &conn->proto.httpc;
- ssize_t nread = -1;
- if(httpc->recv_underlying)
- /* if called "too early", this pointer isn't setup yet! */
- nread = ((Curl_recv *)httpc->recv_underlying)(
- data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
- if(nread != -1) {
- H2BUGF(infof(data,
- "%d bytes stray data read before trying h2 connection",
- (int)nread));
- httpc->nread_inbuf = 0;
- httpc->inbuflen = nread;
- if(h2_process_pending_input(data, httpc, &result) < 0)
- /* immediate error, considered dead */
- dead = TRUE;
- }
- else
- /* the read failed so let's say this is dead anyway */
- dead = TRUE;
+ binlen = populate_binsettings(binsettings, data);
+
+ stream->stream_id = 1;
+ /* queue SETTINGS frame (again) */
+ rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
+ data->state.httpreq == HTTPREQ_HEAD,
+ NULL);
+ if(rc) {
+ failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
+ }
+
+ rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->stream_id,
+ data);
+ if(rc) {
+ infof(data, "http/2: failed to set user_data for stream %u",
+ stream->stream_id);
+ DEBUGASSERT(0);
+ }
+ }
+ else {
+ nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
+ int ivlen;
+
+ /* H2 Settings need to be submitted. Stream is not open yet. */
+ DEBUGASSERT(stream->stream_id == -1);
+
+ ivlen = populate_settings(iv, data);
+ rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
+ iv, ivlen);
+ if(rc) {
+ failf(data, "nghttp2_submit_settings() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
}
}
- return dead;
+ rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
+ HTTP2_HUGE_WINDOW_SIZE);
+ if(rc) {
+ failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ result = CURLE_HTTP2;
+ goto out;
+ }
+
+ /* all set, traffic will be send on connect */
+ result = CURLE_OK;
+
+out:
+ if(cbs)
+ nghttp2_session_callbacks_del(cbs);
+ return result;
}
+static CURLcode h2_session_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static int h2_process_pending_input(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err);
+
/*
- * Set the transfer that is currently using this HTTP/2 connection.
+ * http2_stream_free() free HTTP2 stream related data
*/
-static void set_transfer(struct http_conn *c,
- struct Curl_easy *data)
+static void http2_stream_free(struct HTTP *stream)
{
- c->trnsfr = data;
+ if(stream) {
+ Curl_dyn_free(&stream->header_recvbuf);
+ for(; stream->push_headers_used > 0; --stream->push_headers_used) {
+ free(stream->push_headers[stream->push_headers_used - 1]);
+ }
+ free(stream->push_headers);
+ stream->push_headers = NULL;
+ }
}
/*
- * Get the transfer that is currently using this HTTP/2 connection.
+ * Returns nonzero if current HTTP/2 session should be closed.
*/
-static struct Curl_easy *get_transfer(struct http_conn *c)
+static int should_close_session(struct cf_h2_ctx *ctx)
{
- DEBUGASSERT(c && c->trnsfr);
- return c->trnsfr;
+ return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) &&
+ !nghttp2_session_want_write(ctx->h2);
}
-static unsigned int http2_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
+/*
+ * The server may send us data at any point (e.g. PING frames). Therefore,
+ * we cannot assume that an HTTP/2 socket is dead just because it is readable.
+ *
+ * Check the lower filters first and, if successful, peek at the socket
+ * and distinguish between closed and data.
+ */
+static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool *input_pending)
{
- unsigned int ret_val = CONNRESULT_NONE;
- struct http_conn *c = &conn->proto.httpc;
- int rc;
- bool send_frames = false;
-
- if(checks_to_perform & CONNCHECK_ISDEAD) {
- if(http2_connisdead(data, conn))
- ret_val |= CONNRESULT_DEAD;
- }
-
- if(checks_to_perform & CONNCHECK_KEEPALIVE) {
- struct curltime now = Curl_now();
- timediff_t elapsed = Curl_timediff(now, conn->keepalive);
-
- if(elapsed > data->set.upkeep_interval_ms) {
- /* Perform an HTTP/2 PING */
- rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
- if(!rc) {
- /* Successfully added a PING frame to the session. Need to flag this
- so the frame is sent. */
- send_frames = true;
- }
+ struct cf_h2_ctx *ctx = cf->ctx;
+ bool alive = TRUE;
+
+ *input_pending = FALSE;
+ if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+ return FALSE;
+
+ if(*input_pending) {
+ /* This happens before we've sent off a request and the connection is
+ not in use by any other transfer, there shouldn't be any data here,
+ only "protocol frames" */
+ CURLcode result;
+ ssize_t nread = -1;
+
+ *input_pending = FALSE;
+ Curl_attach_connection(data, cf->conn);
+ nread = Curl_conn_cf_recv(cf->next, data,
+ ctx->inbuf, H2_BUFSIZE, &result);
+ if(nread != -1) {
+ DEBUGF(LOG_CF(data, cf, "%d bytes stray data read before trying "
+ "h2 connection", (int)nread));
+ ctx->nread_inbuf = 0;
+ ctx->inbuflen = nread;
+ if(h2_process_pending_input(cf, data, &result) < 0)
+ /* immediate error, considered dead */
+ alive = FALSE;
else {
- failf(data, "nghttp2_submit_ping() failed: %s(%d)",
- nghttp2_strerror(rc), rc);
+ alive = !should_close_session(ctx);
}
-
- conn->keepalive = now;
}
+ else {
+ /* the read failed so let's say this is dead anyway */
+ alive = FALSE;
+ }
+ Curl_detach_connection(data);
}
- if(send_frames) {
- set_transfer(c, data); /* set the transfer */
- rc = nghttp2_session_send(c->h2);
- if(rc)
- failf(data, "nghttp2_session_send() failed: %s(%d)",
- nghttp2_strerror(rc), rc);
- }
-
- return ret_val;
+ return alive;
}
-/* called from http_setup_conn */
-void Curl_http2_setup_req(struct Curl_easy *data)
+static CURLcode http2_send_ping(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct HTTP *http = data->req.p.http;
- http->bodystarted = FALSE;
- http->status_code = -1;
- http->pausedata = NULL;
- http->pauselen = 0;
- http->closed = FALSE;
- http->close_handled = FALSE;
- http->mem = NULL;
- http->len = 0;
- http->memlen = 0;
- http->error = NGHTTP2_NO_ERROR;
-}
-
-/* called from http_setup_conn */
-void Curl_http2_setup_conn(struct connectdata *conn)
-{
- conn->proto.httpc.settings.max_concurrent_streams =
- DEFAULT_MAX_CONCURRENT_STREAMS;
-}
+ struct cf_h2_ctx *ctx = cf->ctx;
+ int rc;
-/*
- * HTTP2 handler interface. This isn't added to the general list of protocols
- * but will be used at run-time when the protocol is dynamically switched from
- * HTTP to HTTP2.
- */
-static const struct Curl_handler Curl_handler_http2 = {
- "HTTP", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- http2_getsock, /* proto_getsock */
- http2_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- http2_getsock, /* perform_getsock */
- http2_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- http2_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTP, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_STREAM /* flags */
-};
+ rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL);
+ if(rc) {
+ failf(data, "nghttp2_submit_ping() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ return CURLE_HTTP2;
+ }
-static const struct Curl_handler Curl_handler_http2_ssl = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- http2_getsock, /* proto_getsock */
- http2_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- http2_getsock, /* perform_getsock */
- http2_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- http2_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
+ rc = nghttp2_session_send(ctx->h2);
+ if(rc) {
+ failf(data, "nghttp2_session_send() failed: %s(%d)",
+ nghttp2_strerror(rc), rc);
+ return CURLE_SEND_ERROR;
+ }
+ return CURLE_OK;
+}
/*
* Store nghttp2 version info in this buffer.
@@ -369,31 +456,75 @@ void Curl_http2_ver(char *p, size_t len)
(void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
}
+static CURLcode flush_output(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ size_t buflen = Curl_dyn_len(&ctx->outbuf);
+ ssize_t written;
+ CURLcode result;
+
+ if(!buflen)
+ return CURLE_OK;
+
+ DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen));
+ written = Curl_conn_cf_send(cf->next, data, Curl_dyn_ptr(&ctx->outbuf),
+ buflen, &result);
+ if(written < 0) {
+ return result;
+ }
+ if((size_t)written < buflen) {
+ Curl_dyn_tail(&ctx->outbuf, buflen - (size_t)written);
+ return CURLE_AGAIN;
+ }
+ else {
+ Curl_dyn_reset(&ctx->outbuf);
+ }
+ return CURLE_OK;
+}
+
/*
* The implementation of nghttp2_send_callback type. Here we write |data| with
* size |length| to the network and return the number of bytes actually
* written. See the documentation of nghttp2_send_callback for the details.
*/
static ssize_t send_callback(nghttp2_session *h2,
- const uint8_t *mem, size_t length, int flags,
+ const uint8_t *buf, size_t blen, int flags,
void *userp)
{
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *c = &conn->proto.httpc;
- struct Curl_easy *data = get_transfer(c);
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t written;
CURLcode result = CURLE_OK;
+ size_t buflen = Curl_dyn_len(&ctx->outbuf);
(void)h2;
(void)flags;
+ DEBUGASSERT(data);
- if(!c->send_underlying)
- /* called before setup properly! */
- return NGHTTP2_ERR_CALLBACK_FAILURE;
-
- written = ((Curl_send*)c->send_underlying)(data, FIRSTSOCKET,
- mem, length, &result);
+ if(blen < 1024 && (buflen + blen + 1 < ctx->outbuf.toobig)) {
+ result = Curl_dyn_addn(&ctx->outbuf, buf, blen);
+ if(result) {
+ failf(data, "Failed to add data to output buffer");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ return blen;
+ }
+ if(buflen) {
+ /* not adding, flush buffer */
+ result = flush_output(cf, data);
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+ failf(data, "Failed sending HTTP2 data");
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "h2 conn send %zu bytes", blen));
+ written = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
if(result == CURLE_AGAIN) {
return NGHTTP2_ERR_WOULDBLOCK;
}
@@ -467,26 +598,33 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
/*
* This specific transfer on this connection has been "drained".
*/
-static void drained_transfer(struct Curl_easy *data,
- struct http_conn *httpc)
+static void drained_transfer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- DEBUGASSERT(httpc->drain_total >= data->state.drain);
- httpc->drain_total -= data->state.drain;
- data->state.drain = 0;
+ if(data->state.drain) {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ DEBUGASSERT(ctx->drain_total > 0);
+ ctx->drain_total--;
+ data->state.drain = 0;
+ }
}
/*
* Mark this transfer to get "drained".
*/
-static void drain_this(struct Curl_easy *data,
- struct http_conn *httpc)
+static void drain_this(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- data->state.drain++;
- httpc->drain_total++;
- DEBUGASSERT(httpc->drain_total >= data->state.drain);
+ if(!data->state.drain) {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ data->state.drain = 1;
+ ctx->drain_total++;
+ DEBUGASSERT(ctx->drain_total > 0);
+ }
}
-static struct Curl_easy *duphandle(struct Curl_easy *data)
+static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct Curl_easy *second = curl_easy_duphandle(data);
if(second) {
@@ -497,9 +635,8 @@ static struct Curl_easy *duphandle(struct Curl_easy *data)
}
else {
second->req.p.http = http;
- Curl_dyn_init(&http->header_recvbuf, DYN_H2_HEADERS);
- Curl_http2_setup_req(second);
- second->state.stream_weight = data->state.stream_weight;
+ http2_data_setup(cf, second);
+ second->state.priority.weight = data->state.priority.weight;
}
}
return second;
@@ -559,22 +696,23 @@ static int set_transfer_url(struct Curl_easy *data,
return 0;
}
-static int push_promise(struct Curl_easy *data,
- struct connectdata *conn,
+static int push_promise(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
const nghttp2_push_promise *frame)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
int rv; /* one of the CURL_PUSH_* defines */
- H2BUGF(infof(data, "PUSH_PROMISE received, stream %u",
- frame->promised_stream_id));
+
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] PUSH_PROMISE received",
+ frame->promised_stream_id));
if(data->multi->push_cb) {
struct HTTP *stream;
struct HTTP *newstream;
struct curl_pushheaders heads;
CURLMcode rc;
- struct http_conn *httpc;
size_t i;
/* clone the parent */
- struct Curl_easy *newhandle = duphandle(data);
+ struct Curl_easy *newhandle = h2_duphandle(cf, data);
if(!newhandle) {
infof(data, "failed to duplicate handle");
rv = CURL_PUSH_DENY; /* FAIL HARD */
@@ -584,7 +722,7 @@ static int push_promise(struct Curl_easy *data,
heads.data = data;
heads.frame = frame;
/* ask the application */
- H2BUGF(infof(data, "Got PUSH_PROMISE, ask application"));
+ DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application"));
stream = data->req.p.http;
if(!stream) {
@@ -630,7 +768,7 @@ static int push_promise(struct Curl_easy *data,
/* approved, add to the multi handle and immediately switch to PERFORM
state with the given connection !*/
- rc = Curl_multi_add_perform(data->multi, newhandle, conn);
+ rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
if(rc) {
infof(data, "failed to add handle to multi");
http2_stream_free(newhandle->req.p.http);
@@ -640,8 +778,7 @@ static int push_promise(struct Curl_easy *data,
goto fail;
}
- httpc = &conn->proto.httpc;
- rv = nghttp2_session_set_stream_user_data(httpc->h2,
+ rv = nghttp2_session_set_stream_user_data(ctx->h2,
frame->promised_stream_id,
newhandle);
if(rv) {
@@ -655,84 +792,85 @@ static int push_promise(struct Curl_easy *data,
Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
}
else {
- H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it"));
+ DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it"));
rv = CURL_PUSH_DENY;
}
fail:
return rv;
}
-/*
- * multi_connchanged() is called to tell that there is a connection in
- * this multi handle that has changed state (multiplexing become possible, the
- * number of allowed streams changed or similar), and a subsequent use of this
- * multi handle should move CONNECT_PEND handles back to CONNECT to have them
- * retry.
- */
-static void multi_connchanged(struct Curl_multi *multi)
-{
- multi->recheckstate = TRUE;
-}
-
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
void *userp)
{
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s = NULL;
struct HTTP *stream = NULL;
- struct Curl_easy *data = get_transfer(httpc);
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
int rv;
size_t left, ncopy;
int32_t stream_id = frame->hd.stream_id;
CURLcode result;
+ DEBUGASSERT(data);
if(!stream_id) {
/* stream ID zero is for connection-oriented stuff */
- if(frame->hd.type == NGHTTP2_SETTINGS) {
- uint32_t max_conn = httpc->settings.max_concurrent_streams;
- H2BUGF(infof(data, "Got SETTINGS"));
- httpc->settings.max_concurrent_streams =
- nghttp2_session_get_remote_settings(
+ DEBUGASSERT(data);
+ switch(frame->hd.type) {
+ case NGHTTP2_SETTINGS: {
+ uint32_t max_conn = ctx->max_concurrent_streams;
+ DEBUGF(LOG_CF(data, cf, "recv frame SETTINGS"));
+ ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
- httpc->settings.enable_push =
- nghttp2_session_get_remote_settings(
- session, NGHTTP2_SETTINGS_ENABLE_PUSH);
- H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
- httpc->settings.max_concurrent_streams));
- H2BUGF(infof(data, "ENABLE_PUSH == %s",
- httpc->settings.enable_push?"TRUE":"false"));
- if(max_conn != httpc->settings.max_concurrent_streams) {
+ ctx->enable_push = nghttp2_session_get_remote_settings(
+ session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
+ DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d",
+ ctx->max_concurrent_streams));
+ DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s",
+ ctx->enable_push ? "TRUE" : "false"));
+ if(data && max_conn != ctx->max_concurrent_streams) {
/* only signal change if the value actually changed */
- infof(data,
- "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
- httpc->settings.max_concurrent_streams);
+ DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u",
+ ctx->max_concurrent_streams));
+ multi_connchanged(data->multi);
+ }
+ break;
+ }
+ case NGHTTP2_GOAWAY:
+ ctx->goaway = TRUE;
+ ctx->goaway_error = frame->goaway.error_code;
+ ctx->last_stream_id = frame->goaway.last_stream_id;
+ if(data) {
+ infof(data, "recveived GOAWAY, error=%d, last_stream=%u",
+ ctx->goaway_error, ctx->last_stream_id);
multi_connchanged(data->multi);
}
+ break;
+ case NGHTTP2_WINDOW_UPDATE:
+ DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE"));
+ break;
+ default:
+ DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type));
}
return 0;
}
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
if(!data_s) {
- H2BUGF(infof(data,
- "No Curl_easy associated with stream: %u",
- stream_id));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] No Curl_easy associated",
+ stream_id));
return 0;
}
stream = data_s->req.p.http;
if(!stream) {
- H2BUGF(infof(data_s, "No proto pointer for stream: %u",
- stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] No proto pointer", stream_id));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
- H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
- frame->hd.type, stream_id));
-
switch(frame->hd.type) {
case NGHTTP2_DATA:
- /* If body started on this stream, then receiving DATA is illegal. */
+ /* If !body started on this stream, then receiving DATA is illegal. */
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame DATA", stream_id));
if(!stream->bodystarted) {
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
@@ -741,8 +879,17 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
+ if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ /* Stream has ended. If there is pending data, ensure that read
+ will occur to consume it. */
+ if(!data->state.drain && stream->memlen) {
+ drain_this(cf, data_s);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ }
break;
case NGHTTP2_HEADERS:
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame HEADERS", stream_id));
if(stream->bodystarted) {
/* Only valid HEADERS after body started is trailer HEADERS. We
buffer them in on_header callback. */
@@ -776,19 +923,18 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
stream->nread_header_recvbuf += ncopy;
DEBUGASSERT(stream->mem);
- H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
- ncopy, stream_id, stream->mem));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu header bytes, at %p",
+ stream_id, ncopy, (void *)stream->mem));
stream->len -= ncopy;
stream->memlen += ncopy;
- drain_this(data_s, httpc);
- /* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
- Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ drain_this(cf, data_s);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
break;
case NGHTTP2_PUSH_PROMISE:
- rv = push_promise(data_s, conn, &frame->push_promise);
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id));
+ rv = push_promise(cf, data_s, &frame->push_promise);
if(rv) { /* deny! */
int h2;
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
@@ -798,14 +944,32 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
if(nghttp2_is_fatal(h2))
return NGHTTP2_ERR_CALLBACK_FAILURE;
else if(rv == CURL_PUSH_ERROROUT) {
- DEBUGF(infof(data_s, "Fail the parent stream (too)"));
+ DEBUGF(LOG_CF(data_s, cf, "Fail the parent stream (too)"));
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
break;
+ case NGHTTP2_RST_STREAM:
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv RST", stream_id));
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ drain_this(cf, data);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ break;
+ case NGHTTP2_WINDOW_UPDATE:
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id));
+ if((data_s->req.keepon & KEEP_SEND_HOLD) &&
+ (data_s->req.keepon & KEEP_SEND)) {
+ data_s->req.keepon &= ~KEEP_SEND_HOLD;
+ drain_this(cf, data_s);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] un-holding after win update",
+ stream_id));
+ }
+ break;
default:
- H2BUGF(infof(data_s, "Got frame type %x for stream %u",
- frame->hd.type, stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] recv frame %x",
+ stream_id, frame->hd.type));
break;
}
return 0;
@@ -815,15 +979,15 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *mem, size_t len, void *userp)
{
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream;
struct Curl_easy *data_s;
size_t nread;
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
- (void)session;
(void)flags;
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
+ DEBUGASSERT(CF_DATA_CURRENT(cf));
/* get the stream from the hash based on Stream ID */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
@@ -831,8 +995,8 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
/* Receiving a Stream ID not in the hash should not happen - unless
we have aborted a transfer artificially and there were more data
in the pipeline. Silently ignore. */
- H2BUGF(fprintf(stderr, "Data for stream %u but it doesn't exist\n",
- stream_id));
+ DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%u] Data for unknown",
+ stream_id));
return 0;
}
@@ -846,34 +1010,24 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
stream->len -= nread;
stream->memlen += nread;
- drain_this(data_s, &conn->proto.httpc);
-
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s) {
+ drain_this(cf, data_s);
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+ }
- H2BUGF(infof(data_s, "%zu data received for stream %u "
- "(%zu left in buffer %p, total %zu)",
- nread, stream_id,
- stream->len, stream->mem,
- stream->memlen));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu DATA recvd, "
+ "(buffer now holds %zu, %zu still free in %p)",
+ stream_id, nread,
+ stream->memlen, stream->len, (void *)stream->mem));
if(nread < len) {
stream->pausedata = mem + nread;
stream->pauselen = len - nread;
- H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
- ", stream %u",
- len - nread, stream_id));
- data_s->conn->proto.httpc.pause_stream_id = stream_id;
-
- return NGHTTP2_ERR_PAUSE;
- }
-
- /* pause execution of nghttp2 if we received data for another handle
- in order to process them first. */
- if(get_transfer(httpc) != data_s) {
- data_s->conn->proto.httpc.pause_stream_id = stream_id;
-
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] %zu not recvd -> NGHTTP2_ERR_PAUSE",
+ stream_id, len - nread));
+ ctx->pause_stream_id = stream_id;
+ drain_this(cf, data_s);
return NGHTTP2_ERR_PAUSE;
}
@@ -883,65 +1037,66 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
static int on_stream_close(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *userp)
{
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data_s;
struct HTTP *stream;
- struct connectdata *conn = (struct connectdata *)userp;
int rv;
(void)session;
- (void)stream_id;
- if(stream_id) {
- struct http_conn *httpc;
- /* get the stream from the hash based on Stream ID, stream ID zero is for
- connection-oriented stuff */
- data_s = nghttp2_session_get_stream_user_data(session, stream_id);
- if(!data_s) {
- /* We could get stream ID not in the hash. For example, if we
- decided to reject stream (e.g., PUSH_PROMISE). */
- return 0;
- }
- H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
- nghttp2_http2_strerror(error_code), error_code, stream_id));
- stream = data_s->req.p.http;
- if(!stream)
- return NGHTTP2_ERR_CALLBACK_FAILURE;
+ /* get the stream from the hash based on Stream ID, stream ID zero is for
+ connection-oriented stuff */
+ data_s = stream_id?
+ nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
+ if(!data_s) {
+ return 0;
+ }
+ stream = data_s->req.p.http;
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] on_stream_close(), %s (err %d)",
+ stream_id, nghttp2_http2_strerror(error_code), error_code));
+ if(!stream)
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
- stream->closed = TRUE;
- httpc = &conn->proto.httpc;
- drain_this(data_s, httpc);
+ stream->closed = TRUE;
+ stream->error = error_code;
+ if(stream->error)
+ stream->reset = TRUE;
+
+ if(CF_DATA_CURRENT(cf) != data_s) {
+ drain_this(cf, data_s);
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
- stream->error = error_code;
+ }
- /* remove the entry from the hash as the stream is now gone */
- rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
- if(rv) {
- infof(data_s, "http/2: failed to clear user_data for stream %u",
- stream_id);
- DEBUGASSERT(0);
- }
- if(stream_id == httpc->pause_stream_id) {
- H2BUGF(infof(data_s, "Stopped the pause stream"));
- httpc->pause_stream_id = 0;
- }
- H2BUGF(infof(data_s, "Removed stream %u hash", stream_id));
- stream->stream_id = 0; /* cleared */
+ /* remove `data_s` from the nghttp2 stream */
+ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
+ if(rv) {
+ infof(data_s, "http/2: failed to clear user_data for stream %u",
+ stream_id);
+ DEBUGASSERT(0);
+ }
+ if(stream_id == ctx->pause_stream_id) {
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed the pause stream",
+ stream_id));
+ ctx->pause_stream_id = 0;
}
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] closed now", stream_id));
return 0;
}
static int on_begin_headers(nghttp2_session *session,
const nghttp2_frame *frame, void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct HTTP *stream;
struct Curl_easy *data_s = NULL;
- (void)userp;
+ (void)cf;
data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
if(!data_s) {
return 0;
}
- H2BUGF(infof(data_s, "on_begin_headers() was called"));
+ DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called"));
if(frame->hd.type != NGHTTP2_HEADERS) {
return 0;
@@ -989,11 +1144,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
uint8_t flags,
void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct HTTP *stream;
struct Curl_easy *data_s;
int32_t stream_id = frame->hd.stream_id;
- struct connectdata *conn = (struct connectdata *)userp;
- struct http_conn *httpc = &conn->proto.httpc;
CURLcode result;
(void)flags;
@@ -1020,13 +1174,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!strcmp(H2H3_PSEUDO_AUTHORITY, (const char *)name)) {
/* pseudo headers are lower case */
int rc = 0;
- char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
+ char *check = aprintf("%s:%d", cf->conn->host.name,
+ cf->conn->remote_port);
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(!strcasecompare(check, (const char *)value) &&
- ((conn->remote_port != conn->given->defport) ||
- !strcasecompare(conn->host.name, (const char *)value))) {
+ ((cf->conn->remote_port != cf->conn->given->defport) ||
+ !strcasecompare(cf->conn->host.name, (const char *)value))) {
/* This is push is not for the same authority that was asked for in
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
* PUSH_PROMISE for which the server is not authoritative as a stream
@@ -1075,11 +1230,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(stream->bodystarted) {
/* This is a trailer */
- H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
- value));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] trailer: %.*s: %.*s",
+ stream->stream_id,
+ (int)namelen, name,
+ (int)valuelen, value));
result = Curl_dyn_addf(&stream->trailer_recvbuf,
- "%.*s: %.*s\r\n", namelen, name,
- valuelen, value);
+ "%.*s: %.*s\r\n", (int)namelen, name,
+ (int)valuelen, value);
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1110,11 +1267,11 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
- H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
- stream->status_code, data_s));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] status: HTTP/2 %03d",
+ stream->stream_id, stream->status_code));
return 0;
}
@@ -1134,11 +1291,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(result)
return NGHTTP2_ERR_CALLBACK_FAILURE;
/* if we receive data for another handle, wake that up */
- if(get_transfer(httpc) != data_s)
+ if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
- H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
- value));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] header: %.*s: %.*s",
+ stream->stream_id,
+ (int)namelen, name,
+ (int)valuelen, value));
return 0; /* 0 is successful */
}
@@ -1150,12 +1309,13 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
nghttp2_data_source *source,
void *userp)
{
+ struct Curl_cfilter *cf = userp;
struct Curl_easy *data_s;
struct HTTP *stream = NULL;
size_t nread;
(void)source;
- (void)userp;
+ (void)cf;
if(stream_id) {
/* get the stream from the hash based on Stream ID, stream ID zero is for
connection-oriented stuff */
@@ -1186,9 +1346,8 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
else if(nread == 0)
return NGHTTP2_ERR_DEFERRED;
- H2BUGF(infof(data_s, "data_source_read_callback: "
- "returns %zu bytes stream %u",
- nread, stream_id));
+ DEBUGF(LOG_CF(data_s, cf, "[h2sid=%u] data_source_read_callback: "
+ "returns %zu bytes", stream_id, nread));
return nread;
}
@@ -1207,147 +1366,57 @@ static int error_callback(nghttp2_session *session,
}
#endif
-static void populate_settings(struct Curl_easy *data,
- struct http_conn *httpc)
+static void http2_data_done(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool premature)
{
- nghttp2_settings_entry *iv = httpc->local_settings;
-
- iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
- iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
-
- iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
- iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
-
- iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
- iv[2].value = data->multi->push_cb != NULL;
-
- httpc->local_settings_num = 3;
-}
-
-void Curl_http2_done(struct Curl_easy *data, bool premature)
-{
- struct HTTP *http = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
/* there might be allocated resources done before this got the 'h2' pointer
setup */
- Curl_dyn_free(&http->header_recvbuf);
- Curl_dyn_free(&http->trailer_recvbuf);
- if(http->push_headers) {
+ Curl_dyn_free(&stream->header_recvbuf);
+ Curl_dyn_free(&stream->trailer_recvbuf);
+ if(stream->push_headers) {
/* if they weren't used and then freed before */
- for(; http->push_headers_used > 0; --http->push_headers_used) {
- free(http->push_headers[http->push_headers_used - 1]);
+ for(; stream->push_headers_used > 0; --stream->push_headers_used) {
+ free(stream->push_headers[stream->push_headers_used - 1]);
}
- free(http->push_headers);
- http->push_headers = NULL;
+ free(stream->push_headers);
+ stream->push_headers = NULL;
}
- if(!(data->conn->handler->protocol&PROTO_FAMILY_HTTP) ||
- !httpc->h2) /* not HTTP/2 ? */
+ if(!ctx || !ctx->h2)
return;
/* do this before the reset handling, as that might clear ->stream_id */
- if(http->stream_id == httpc->pause_stream_id) {
- H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id));
- httpc->pause_stream_id = 0;
+ if(stream->stream_id && stream->stream_id == ctx->pause_stream_id) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] DONE, the pause stream",
+ stream->stream_id));
+ ctx->pause_stream_id = 0;
}
- if(premature || (!http->closed && http->stream_id)) {
+
+ (void)premature;
+ if(!stream->closed && stream->stream_id) {
/* RST_STREAM */
- set_transfer(httpc, data); /* set the transfer */
- H2BUGF(infof(data, "RST stream %u", http->stream_id));
- if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
- http->stream_id, NGHTTP2_STREAM_CLOSED))
- (void)nghttp2_session_send(httpc->h2);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] RST", stream->stream_id));
+ if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->stream_id, NGHTTP2_STREAM_CLOSED))
+ (void)nghttp2_session_send(ctx->h2);
}
if(data->state.drain)
- drained_transfer(data, httpc);
+ drained_transfer(cf, data);
/* -1 means unassigned and 0 means cleared */
- if(http->stream_id > 0) {
- int rv = nghttp2_session_set_stream_user_data(httpc->h2,
- http->stream_id, 0);
+ if(nghttp2_session_get_stream_user_data(ctx->h2, stream->stream_id)) {
+ int rv = nghttp2_session_set_stream_user_data(ctx->h2,
+ stream->stream_id, 0);
if(rv) {
infof(data, "http/2: failed to clear user_data for stream %u",
- http->stream_id);
+ stream->stream_id);
DEBUGASSERT(0);
}
- set_transfer(httpc, NULL);
- http->stream_id = 0;
- }
-}
-
-static int client_new(struct connectdata *conn,
- nghttp2_session_callbacks *callbacks)
-{
-#if NGHTTP2_VERSION_NUM < 0x013200
- /* before 1.50.0 */
- return nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
-#else
- nghttp2_option *o;
- int rc = nghttp2_option_new(&o);
- if(rc)
- return rc;
- /* turn off RFC 9113 leading and trailing white spaces validation against
- HTTP field value. */
- nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
- rc = nghttp2_session_client_new2(&conn->proto.httpc.h2, callbacks, conn,
- o);
- nghttp2_option_del(o);
- return rc;
-#endif
-}
-
-/*
- * Initialize nghttp2 for a Curl connection
- */
-static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
-{
- if(!conn->proto.httpc.h2) {
- int rc;
- nghttp2_session_callbacks *callbacks;
-
- conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
- if(!conn->proto.httpc.inbuf)
- return CURLE_OUT_OF_MEMORY;
-
- rc = nghttp2_session_callbacks_new(&callbacks);
-
- if(rc) {
- failf(data, "Couldn't initialize nghttp2 callbacks");
- return CURLE_OUT_OF_MEMORY; /* most likely at least */
- }
-
- /* nghttp2_send_callback */
- nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
- /* nghttp2_on_frame_recv_callback */
- nghttp2_session_callbacks_set_on_frame_recv_callback
- (callbacks, on_frame_recv);
- /* nghttp2_on_data_chunk_recv_callback */
- nghttp2_session_callbacks_set_on_data_chunk_recv_callback
- (callbacks, on_data_chunk_recv);
- /* nghttp2_on_stream_close_callback */
- nghttp2_session_callbacks_set_on_stream_close_callback
- (callbacks, on_stream_close);
- /* nghttp2_on_begin_headers_callback */
- nghttp2_session_callbacks_set_on_begin_headers_callback
- (callbacks, on_begin_headers);
- /* nghttp2_on_header_callback */
- nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
-
- nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
-
- /* The nghttp2 session is not yet setup, do it */
- rc = client_new(conn, callbacks);
-
- nghttp2_session_callbacks_del(callbacks);
-
- if(rc) {
- failf(data, "Couldn't initialize nghttp2");
- return CURLE_OUT_OF_MEMORY; /* most likely at least */
- }
}
- return CURLE_OK;
}
/*
@@ -1357,26 +1426,18 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
struct Curl_easy *data)
{
CURLcode result;
- ssize_t binlen;
char *base64;
size_t blen;
- struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
- uint8_t *binsettings = conn->proto.httpc.binsettings;
- struct http_conn *httpc = &conn->proto.httpc;
-
- populate_settings(data, httpc);
+ uint8_t binsettings[H2_BINSETTINGS_LEN];
+ size_t binlen; /* length of the binsettings data */
- /* this returns number of bytes it wrote */
- binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
- httpc->local_settings,
- httpc->local_settings_num);
+ binlen = populate_binsettings(binsettings, data);
if(binlen <= 0) {
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
Curl_dyn_free(req);
return CURLE_FAILED_INIT;
}
- conn->proto.httpc.binlen = binlen;
result = Curl_base64url_encode((const char *)binsettings, binlen,
&base64, &blen);
@@ -1398,79 +1459,70 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
}
/*
- * Returns nonzero if current HTTP/2 session should be closed.
- */
-static int should_close_session(struct http_conn *httpc)
-{
- return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
- !nghttp2_session_want_write(httpc->h2);
-}
-
-/*
* h2_process_pending_input() processes pending input left in
* httpc->inbuf. Then, call h2_session_send() to send pending data.
* This function returns 0 if it succeeds, or -1 and error code will
* be assigned to *err.
*/
-static int h2_process_pending_input(struct Curl_easy *data,
- struct http_conn *httpc,
+static int h2_process_pending_input(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
CURLcode *err)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
ssize_t nread;
- char *inbuf;
ssize_t rv;
- nread = httpc->inbuflen - httpc->nread_inbuf;
- inbuf = httpc->inbuf + httpc->nread_inbuf;
+ nread = ctx->inbuflen - ctx->nread_inbuf;
+ if(nread) {
+ char *inbuf = ctx->inbuf + ctx->nread_inbuf;
- set_transfer(httpc, data); /* set the transfer */
- rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
- if(rv < 0) {
- failf(data,
- "h2_process_pending_input: nghttp2_session_mem_recv() returned "
- "%zd:%s", rv, nghttp2_strerror((int)rv));
- *err = CURLE_RECV_ERROR;
- return -1;
- }
+ rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)inbuf, nread);
+ if(rv < 0) {
+ failf(data,
+ "h2_process_pending_input: nghttp2_session_mem_recv() returned "
+ "%zd:%s", rv, nghttp2_strerror((int)rv));
+ *err = CURLE_RECV_ERROR;
+ return -1;
+ }
- if(nread == rv) {
- H2BUGF(infof(data,
- "h2_process_pending_input: All data in connection buffer "
- "processed"));
- httpc->inbuflen = 0;
- httpc->nread_inbuf = 0;
- }
- else {
- httpc->nread_inbuf += rv;
- H2BUGF(infof(data,
- "h2_process_pending_input: %zu bytes left in connection "
- "buffer",
- httpc->inbuflen - httpc->nread_inbuf));
+ if(nread == rv) {
+ DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed"));
+ ctx->inbuflen = 0;
+ ctx->nread_inbuf = 0;
+ }
+ else {
+ ctx->nread_inbuf += rv;
+ DEBUGF(LOG_CF(data, cf, "h2_process_pending_input: %zu bytes left "
+ "in connection buffer",
+ ctx->inbuflen - ctx->nread_inbuf));
+ }
}
- rv = h2_session_send(data, httpc->h2);
+ rv = h2_session_send(cf, data);
if(rv) {
*err = CURLE_SEND_ERROR;
return -1;
}
- if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
+ if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
/* No more requests are allowed in the current session, so
the connection may not be reused. This is set when a
GOAWAY frame has been received or when the limit of stream
identifiers has been reached. */
- connclose(data->conn, "http/2: No new requests allowed");
+ connclose(cf->conn, "http/2: No new requests allowed");
}
- if(should_close_session(httpc)) {
+ if(should_close_session(ctx)) {
struct HTTP *stream = data->req.p.http;
- H2BUGF(infof(data,
+ DEBUGF(LOG_CF(data, cf,
"h2_process_pending_input: nothing to do in this session"));
- if(stream->error)
+ if(stream->reset)
+ *err = CURLE_PARTIAL_FILE;
+ else if(stream->error)
*err = CURLE_HTTP2;
else {
/* not an error per se, but should still close the connection */
- connclose(data->conn, "GOAWAY received");
+ connclose(cf->conn, "GOAWAY received");
*err = CURLE_OK;
}
return -1;
@@ -1478,79 +1530,70 @@ static int h2_process_pending_input(struct Curl_easy *data,
return 0;
}
-/*
- * Called from transfer.c:done_sending when we stop uploading.
- */
-CURLcode Curl_http2_done_sending(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
+ struct HTTP *stream = data->req.p.http;
- if((conn->handler == &Curl_handler_http2_ssl) ||
- (conn->handler == &Curl_handler_http2)) {
- /* make sure this is only attempted for HTTP/2 transfers */
- struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &conn->proto.httpc;
- nghttp2_session *h2 = httpc->h2;
-
- if(stream->upload_left) {
- /* If the stream still thinks there's data left to upload. */
+ if(!ctx || !ctx->h2)
+ goto out;
- stream->upload_left = 0; /* DONE! */
+ if(stream->upload_left) {
+ /* If the stream still thinks there's data left to upload. */
+ stream->upload_left = 0; /* DONE! */
- /* resume sending here to trigger the callback to get called again so
- that it can signal EOF to nghttp2 */
- (void)nghttp2_session_resume_data(h2, stream->stream_id);
- (void)h2_process_pending_input(data, httpc, &result);
- }
+ /* resume sending here to trigger the callback to get called again so
+ that it can signal EOF to nghttp2 */
+ (void)nghttp2_session_resume_data(ctx->h2, stream->stream_id);
+ (void)h2_process_pending_input(cf, data, &result);
+ }
- /* If nghttp2 still has pending frames unsent */
- if(nghttp2_session_want_write(h2)) {
- struct SingleRequest *k = &data->req;
- int rv;
+ /* If nghttp2 still has pending frames unsent */
+ if(nghttp2_session_want_write(ctx->h2)) {
+ struct SingleRequest *k = &data->req;
+ int rv;
- H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
+ DEBUGF(LOG_CF(data, cf, "HTTP/2 still wants to send data"));
- /* and attempt to send the pending frames */
- rv = h2_session_send(data, h2);
- if(rv)
- result = CURLE_SEND_ERROR;
+ /* and attempt to send the pending frames */
+ rv = h2_session_send(cf, data);
+ if(rv)
+ result = CURLE_SEND_ERROR;
- if(nghttp2_session_want_write(h2)) {
- /* re-set KEEP_SEND to make sure we are called again */
- k->keepon |= KEEP_SEND;
- }
+ if(nghttp2_session_want_write(ctx->h2)) {
+ /* re-set KEEP_SEND to make sure we are called again */
+ k->keepon |= KEEP_SEND;
}
}
+
+out:
return result;
}
-static ssize_t http2_handle_stream_close(struct connectdata *conn,
+static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct HTTP *stream, CURLcode *err)
{
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
- if(httpc->pause_stream_id == stream->stream_id) {
- httpc->pause_stream_id = 0;
+ if(ctx->pause_stream_id == stream->stream_id) {
+ ctx->pause_stream_id = 0;
}
- drained_transfer(data, httpc);
+ drained_transfer(cf, data);
- if(httpc->pause_stream_id == 0) {
- if(h2_process_pending_input(data, httpc, err) != 0) {
+ if(ctx->pause_stream_id == 0) {
+ if(h2_process_pending_input(cf, data, err) != 0) {
return -1;
}
}
- DEBUGASSERT(data->state.drain == 0);
-
- /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
- stream->closed = FALSE;
if(stream->error == NGHTTP2_REFUSED_STREAM) {
- H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
- stream->stream_id));
- connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new "
+ "connection", stream->stream_id));
+ connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
data->state.refused_stream = TRUE;
*err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
return -1;
@@ -1562,6 +1605,11 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
*err = CURLE_HTTP2_STREAM;
return -1;
}
+ else if(stream->reset) {
+ failf(data, "HTTP/2 stream %u was reset", stream->stream_id);
+ *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ return -1;
+ }
if(!stream->bodystarted) {
failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
@@ -1597,10 +1645,24 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
stream->close_handled = TRUE;
- H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] closed cleanly", stream->stream_id));
return 0;
}
+static int sweight_wanted(const struct Curl_easy *data)
+{
+ /* 0 weight is not set by user and we take the nghttp2 default one */
+ return data->set.priority.weight?
+ data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
+}
+
+static int sweight_in_effect(const struct Curl_easy *data)
+{
+ /* 0 weight is not set by user and we take the nghttp2 default one */
+ return data->state.priority.weight?
+ data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
+}
+
/*
* h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
* and dependency to the peer. It also stores the updated values in the state
@@ -1610,14 +1672,14 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
static void h2_pri_spec(struct Curl_easy *data,
nghttp2_priority_spec *pri_spec)
{
- struct HTTP *depstream = (data->set.stream_depends_on?
- data->set.stream_depends_on->req.p.http:NULL);
+ struct Curl_data_priority *prio = &data->set.priority;
+ struct HTTP *depstream = (prio->parent?
+ prio->parent->req.p.http:NULL);
int32_t depstream_id = depstream? depstream->stream_id:0;
- nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
- data->set.stream_depends_e);
- data->state.stream_weight = data->set.stream_weight;
- data->state.stream_depends_e = data->set.stream_depends_e;
- data->state.stream_depends_on = data->set.stream_depends_on;
+ nghttp2_priority_spec_init(pri_spec, depstream_id,
+ sweight_wanted(data),
+ data->set.priority.exclusive);
+ data->state.priority = *prio;
}
/*
@@ -1625,53 +1687,81 @@ static void h2_pri_spec(struct Curl_easy *data,
* dependency settings and if so it submits a PRIORITY frame with the updated
* info.
*/
-static int h2_session_send(struct Curl_easy *data,
- nghttp2_session *h2)
+static CURLcode h2_session_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
- set_transfer(httpc, data);
- if((data->set.stream_weight != data->state.stream_weight) ||
- (data->set.stream_depends_e != data->state.stream_depends_e) ||
- (data->set.stream_depends_on != data->state.stream_depends_on) ) {
+ int rv = 0;
+
+ if((sweight_wanted(data) != sweight_in_effect(data)) ||
+ (data->set.priority.exclusive != data->state.priority.exclusive) ||
+ (data->set.priority.parent != data->state.priority.parent) ) {
/* send new weight and/or dependency */
nghttp2_priority_spec pri_spec;
- int rv;
h2_pri_spec(data, &pri_spec);
-
- H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
- stream->stream_id, data));
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Queuing PRIORITY",
+ stream->stream_id));
DEBUGASSERT(stream->stream_id != -1);
- rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
- &pri_spec);
+ rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->stream_id, &pri_spec);
if(rv)
- return rv;
+ goto out;
}
- return nghttp2_session_send(h2);
+ rv = nghttp2_session_send(ctx->h2);
+out:
+ if(nghttp2_is_fatal(rv)) {
+ DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
+ nghttp2_strerror(rv), rv));
+ return CURLE_SEND_ERROR;
+ }
+ return flush_output(cf, data);
}
-static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
- char *mem, size_t len, CURLcode *err)
+static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
{
- ssize_t nread;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct HTTP *stream = data->req.p.http;
-
- (void)sockindex; /* we always do HTTP2 on sockindex 0 */
-
- if(should_close_session(httpc)) {
- H2BUGF(infof(data,
- "http2_recv: nothing to do in this session"));
- if(conn->bits.close) {
+ ssize_t nread = -1;
+ struct cf_call_data save;
+ bool conn_is_closed = FALSE;
+
+ CF_DATA_SAVE(save, cf, data);
+
+ /* If the h2 session has told us to GOAWAY with an error AND
+ * indicated the highest stream id it has processes AND
+ * the stream we are trying to read has a higher id, this
+ * means we will most likely not receive any more for it.
+ * Treat this as if the server explicitly had RST the stream */
+ if((ctx->goaway && ctx->goaway_error &&
+ ctx->last_stream_id > 0 &&
+ ctx->last_stream_id < stream->stream_id)) {
+ stream->reset = TRUE;
+ }
+
+ /* If a stream is RST, it does not matter what state the h2 session
+ * is in, our answer to receiving data is always the same. */
+ if(stream->reset) {
+ *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ nread = -1;
+ goto out;
+ }
+
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "http2_recv: nothing to do in this session"));
+ if(cf->conn->bits.close) {
/* already marked for closure, return OK and we're done */
+ drained_transfer(cf, data);
*err = CURLE_OK;
- return 0;
+ nread = 0;
+ goto out;
}
*err = CURLE_HTTP2;
- return -1;
+ nread = -1;
+ goto out;
}
/* Nullify here because we call nghttp2_session_send() and they
@@ -1690,74 +1780,67 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
size_t left =
Curl_dyn_len(&stream->header_recvbuf) - stream->nread_header_recvbuf;
size_t ncopy = CURLMIN(len, left);
- memcpy(mem, Curl_dyn_ptr(&stream->header_recvbuf) +
+ memcpy(buf, Curl_dyn_ptr(&stream->header_recvbuf) +
stream->nread_header_recvbuf, ncopy);
stream->nread_header_recvbuf += ncopy;
- H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
- (int)ncopy));
- return ncopy;
+ DEBUGF(LOG_CF(data, cf, "recv: Got %d bytes from header_recvbuf",
+ (int)ncopy));
+ nread = ncopy;
+ goto out;
}
- H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
- data, stream->stream_id,
- nghttp2_session_get_local_window_size(httpc->h2),
- nghttp2_session_get_stream_local_window_size(httpc->h2,
- stream->stream_id)
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv: win %u/%u",
+ stream->stream_id,
+ nghttp2_session_get_local_window_size(ctx->h2),
+ nghttp2_session_get_stream_local_window_size(ctx->h2,
+ stream->stream_id)
));
- if((data->state.drain) && stream->memlen) {
- H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u (%p => %p)",
- stream->memlen, stream->stream_id,
- stream->mem, mem));
- if(mem != stream->mem) {
+ if(stream->memlen) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: DRAIN %zu bytes (%p => %p)",
+ stream->stream_id, stream->memlen,
+ (void *)stream->mem, (void *)buf));
+ if(buf != stream->mem) {
/* if we didn't get the same buffer this time, we must move the data to
the beginning */
- memmove(mem, stream->mem, stream->memlen);
+ memmove(buf, stream->mem, stream->memlen);
stream->len = len - stream->memlen;
- stream->mem = mem;
+ stream->mem = buf;
}
- if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
+
+ if(ctx->pause_stream_id == stream->stream_id && !stream->pausedata) {
/* We have paused nghttp2, but we have no pause data (see
on_data_chunk_recv). */
- httpc->pause_stream_id = 0;
- if(h2_process_pending_input(data, httpc, err) != 0) {
- return -1;
+ ctx->pause_stream_id = 0;
+ if(h2_process_pending_input(cf, data, err) != 0) {
+ nread = -1;
+ goto out;
}
}
}
else if(stream->pausedata) {
- DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
+ DEBUGASSERT(ctx->pause_stream_id == stream->stream_id);
nread = CURLMIN(len, stream->pauselen);
- memcpy(mem, stream->pausedata, nread);
+ memcpy(buf, stream->pausedata, nread);
stream->pausedata += nread;
stream->pauselen -= nread;
+ drain_this(cf, data);
if(stream->pauselen == 0) {
- H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
- DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
- httpc->pause_stream_id = 0;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Unpaused", stream->stream_id));
+ DEBUGASSERT(ctx->pause_stream_id == stream->stream_id);
+ ctx->pause_stream_id = 0;
stream->pausedata = NULL;
stream->pauselen = 0;
-
- /* When NGHTTP2_ERR_PAUSE is returned from
- data_source_read_callback, we might not process DATA frame
- fully. Calling nghttp2_session_mem_recv() again will
- continue to process DATA frame, but if there is no incoming
- frames, then we have to call it again with 0-length data.
- Without this, on_stream_close callback will not be called,
- and stream could be hanged. */
- if(h2_process_pending_input(data, httpc, err) != 0) {
- return -1;
- }
}
- H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
- nread, stream->stream_id));
- return nread;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: returns unpaused %zd bytes",
+ stream->stream_id, nread));
+ goto out;
}
- else if(httpc->pause_stream_id) {
+ else if(ctx->pause_stream_id) {
/* If a stream paused nghttp2_session_mem_recv previously, and has
not processed all data, it still refers to the buffer in
nghttp2_session. If we call nghttp2_session_mem_recv(), we may
@@ -1766,156 +1849,192 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
socket is not read. But it seems that usually streams are
notified with its drain property, and socket is read again
quickly. */
- if(stream->closed)
+ if(stream->closed) {
/* closed overrides paused */
- return 0;
- H2BUGF(infof(data, "stream %u is paused, pause id: %u",
- stream->stream_id, httpc->pause_stream_id));
+ drained_transfer(cf, data);
+ nread = 0;
+ goto out;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is paused, pause h2sid: %u",
+ stream->stream_id, ctx->pause_stream_id));
*err = CURLE_AGAIN;
- return -1;
+ nread = -1;
+ goto out;
}
else {
- /* remember where to store incoming data for this stream and how big the
- buffer is */
- stream->mem = mem;
+ /* We have nothing buffered for `data` and no other stream paused
+ * the processing of incoming data, we can therefore read new data
+ * from the network.
+ * If DATA is coming for this stream, we want to store it ad the
+ * `buf` passed in right away - saving us a copy.
+ */
+ stream->mem = buf;
stream->len = len;
stream->memlen = 0;
- if(httpc->inbuflen == 0) {
- nread = ((Curl_recv *)httpc->recv_underlying)(
- data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
-
- if(nread == -1) {
- if(*err != CURLE_AGAIN)
- failf(data, "Failed receiving HTTP2 data");
- else if(stream->closed)
- /* received when the stream was already closed! */
- return http2_handle_stream_close(conn, data, stream, err);
-
+ if(ctx->inbuflen > 0) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] %zd bytes in inbuf",
+ stream->stream_id, ctx->inbuflen - ctx->nread_inbuf));
+ if(h2_process_pending_input(cf, data, err))
return -1;
- }
+ }
- if(nread == 0) {
- if(!stream->closed) {
- /* This will happen when the server or proxy server is SIGKILLed
- during data transfer. We should emit an error since our data
- received may be incomplete. */
- failf(data, "HTTP/2 stream %u was not closed cleanly before"
- " end of the underlying stream",
- stream->stream_id);
- *err = CURLE_HTTP2_STREAM;
- return -1;
+ while(stream->memlen == 0 && /* have no data for this stream */
+ !stream->closed && /* and it is not closed/reset */
+ !ctx->pause_stream_id && /* we are not paused either */
+ ctx->inbuflen == 0 && /* and out input buffer is empty */
+ !conn_is_closed) { /* and connection is not closed */
+ /* Receive data from the "lower" filters */
+ nread = Curl_conn_cf_recv(cf->next, data, ctx->inbuf, H2_BUFSIZE, err);
+ if(nread < 0) {
+ DEBUGASSERT(*err);
+ if(*err == CURLE_AGAIN) {
+ break;
}
-
- H2BUGF(infof(data, "end of stream"));
- *err = CURLE_OK;
- return 0;
+ failf(data, "Failed receiving HTTP2 data");
+ conn_is_closed = TRUE;
+ }
+ else if(nread == 0) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] underlying connection is closed",
+ stream->stream_id));
+ conn_is_closed = TRUE;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] read %zd from connection",
+ stream->stream_id, nread));
+ ctx->inbuflen = nread;
+ DEBUGASSERT(ctx->nread_inbuf == 0);
+ if(h2_process_pending_input(cf, data, err))
+ return -1;
}
-
- H2BUGF(infof(data, "nread=%zd", nread));
-
- httpc->inbuflen = nread;
-
- DEBUGASSERT(httpc->nread_inbuf == 0);
- }
- else {
- nread = httpc->inbuflen - httpc->nread_inbuf;
- (void)nread; /* silence warning, used in debug */
- H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
- nread));
}
- if(h2_process_pending_input(data, httpc, err))
- return -1;
}
+
if(stream->memlen) {
ssize_t retlen = stream->memlen;
- H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
- retlen, stream->stream_id));
+
+ /* TODO: all this buffer handling is very brittle */
+ stream->len += stream->memlen;
stream->memlen = 0;
- if(httpc->pause_stream_id == stream->stream_id) {
+ if(ctx->pause_stream_id == stream->stream_id) {
/* data for this stream is returned now, but this stream caused a pause
already so we need it called again asap */
- H2BUGF(infof(data, "Data returned for PAUSED stream %u",
- stream->stream_id));
- }
- else if(!stream->closed) {
- drained_transfer(data, httpc);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] Data returned for PAUSED stream",
+ stream->stream_id));
+ drain_this(cf, data);
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
- else
+ else if(stream->closed) {
+ if(stream->reset || stream->error) {
+ nread = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
+ }
/* this stream is closed, trigger a another read ASAP to detect that */
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is closed now, run again",
+ stream->stream_id));
+ drain_this(cf, data);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+ else {
+ drained_transfer(cf, data);
+ }
- return retlen;
+ *err = CURLE_OK;
+ nread = retlen;
+ goto out;
+ }
+
+ if(conn_is_closed && !stream->closed) {
+ /* underlying connection is closed and we have nothing for the stream.
+ * Treat this as a RST */
+ stream->closed = stream->reset = TRUE;
+ failf(data, "HTTP/2 stream %u was not closed cleanly before"
+ " end of the underlying connection",
+ stream->stream_id);
+ }
+
+ if(stream->closed) {
+ nread = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
+ }
+
+ if(!data->state.drain && Curl_conn_cf_data_pending(cf->next, data)) {
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] pending data, set drain",
+ stream->stream_id));
+ drain_this(cf, data);
}
- if(stream->closed)
- return http2_handle_stream_close(conn, data, stream, err);
*err = CURLE_AGAIN;
- H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
- stream->stream_id));
- return -1;
+ nread = -1;
+out:
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv -> %zd, %d",
+ stream->stream_id, nread, *err));
+ CF_DATA_RESTORE(cf, save);
+ return nread;
}
-static ssize_t http2_send(struct Curl_easy *data, int sockindex,
- const void *mem, size_t len, CURLcode *err)
+static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
{
/*
* Currently, we send request in this function, but this function is also
* used to send request body. It would be nice to add dedicated function for
* request.
*/
+ struct cf_h2_ctx *ctx = cf->ctx;
int rv;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
struct HTTP *stream = data->req.p.http;
nghttp2_nv *nva = NULL;
size_t nheader;
nghttp2_data_provider data_prd;
int32_t stream_id;
- nghttp2_session *h2 = httpc->h2;
nghttp2_priority_spec pri_spec;
CURLcode result;
struct h2h3req *hreq;
+ struct cf_call_data save;
+ ssize_t nwritten;
- (void)sockindex;
-
- H2BUGF(infof(data, "http2_send len=%zu", len));
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) start", len));
if(stream->stream_id != -1) {
if(stream->close_handled) {
infof(data, "stream %u closed", stream->stream_id);
*err = CURLE_HTTP2_STREAM;
- return -1;
+ nwritten = -1;
+ goto out;
}
else if(stream->closed) {
- return http2_handle_stream_close(conn, data, stream, err);
+ nwritten = http2_handle_stream_close(cf, data, stream, err);
+ goto out;
}
/* If stream_id != -1, we have dispatched request HEADERS, and now
are going to send or sending request body in DATA frame */
- stream->upload_mem = mem;
+ stream->upload_mem = buf;
stream->upload_len = len;
- rv = nghttp2_session_resume_data(h2, stream->stream_id);
+ rv = nghttp2_session_resume_data(ctx->h2, stream->stream_id);
if(nghttp2_is_fatal(rv)) {
*err = CURLE_SEND_ERROR;
- return -1;
+ nwritten = -1;
+ goto out;
}
- rv = h2_session_send(data, h2);
- if(nghttp2_is_fatal(rv)) {
- *err = CURLE_SEND_ERROR;
- return -1;
+ result = h2_session_send(cf, data);
+ if(result) {
+ *err = result;
+ nwritten = -1;
+ goto out;
}
- len -= stream->upload_len;
- /* Nullify here because we call nghttp2_session_send() and they
- might refer to the old buffer. */
+ nwritten = (ssize_t)len - (ssize_t)stream->upload_len;
stream->upload_mem = NULL;
stream->upload_len = 0;
- if(should_close_session(httpc)) {
- H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
*err = CURLE_HTTP2;
- return -1;
+ nwritten = -1;
+ goto out;
}
if(stream->upload_left) {
@@ -1923,29 +2042,40 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
following API will make nghttp2_session_want_write() return
nonzero if remote window allows it, which then libcurl checks
socket is writable or not. See http2_perform_getsock(). */
- nghttp2_session_resume_data(h2, stream->stream_id);
+ nghttp2_session_resume_data(ctx->h2, stream->stream_id);
}
-#ifdef DEBUG_HTTP2
- if(!len) {
- infof(data, "http2_send: easy %p (stream %u) win %u/%u",
- data, stream->stream_id,
- nghttp2_session_get_remote_window_size(httpc->h2),
- nghttp2_session_get_stream_remote_window_size(httpc->h2,
- stream->stream_id)
- );
-
+ if(!nwritten) {
+ size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
+ stream->stream_id);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_send: win %u/%zu",
+ stream->stream_id,
+ nghttp2_session_get_remote_window_size(ctx->h2), rwin));
+ if(rwin == 0) {
+ /* We cannot upload more as the stream's remote window size
+ * is 0. We need to receive WIN_UPDATEs before we can continue.
+ */
+ data->req.keepon |= KEEP_SEND_HOLD;
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] holding send as remote flow "
+ "window is exhausted", stream->stream_id));
+ }
}
- infof(data, "http2_send returns %zu for stream %u", len,
- stream->stream_id);
-#endif
- return len;
- }
-
- result = Curl_pseudo_headers(data, mem, len, &hreq);
+ DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_send returns %zd ",
+ stream->stream_id, nwritten));
+
+ /* handled writing BODY for open stream. */
+ goto out;
+ }
+ /* Stream has not been opened yet. `buf` is expected to contain
+ * request headers. */
+ /* TODO: this assumes that the `buf` and `len` we are called with
+ * is *all* HEADERs and no body. We have no way to determine here
+ * if that is indeed the case. */
+ result = Curl_pseudo_headers(data, buf, len, NULL, &hreq);
if(result) {
*err = result;
- return -1;
+ nwritten = -1;
+ goto out;
}
nheader = hreq->entries;
@@ -1953,7 +2083,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
if(!nva) {
Curl_pseudo_free(hreq);
*err = CURLE_OUT_OF_MEMORY;
- return -1;
+ nwritten = -1;
+ goto out;
}
else {
unsigned int i;
@@ -1969,8 +2100,8 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
h2_pri_spec(data, &pri_spec);
- H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
- nghttp2_session_check_request_allowed(h2), (void *)data));
+ DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)",
+ nghttp2_session_check_request_allowed(ctx->h2), (void *)data));
switch(data->state.httpreq) {
case HTTPREQ_POST:
@@ -1985,42 +2116,43 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
data_prd.read_callback = data_source_read_callback;
data_prd.source.ptr = NULL;
- stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
&data_prd, data);
break;
default:
- stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
+ stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
NULL, data);
}
Curl_safefree(nva);
if(stream_id < 0) {
- H2BUGF(infof(data,
- "http2_send() nghttp2_submit_request error (%s)%u",
- nghttp2_strerror(stream_id), stream_id));
+ DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
+ nghttp2_strerror(stream_id), stream_id));
*err = CURLE_SEND_ERROR;
- return -1;
+ nwritten = -1;
+ goto out;
}
infof(data, "Using Stream ID: %u (easy handle %p)",
stream_id, (void *)data);
stream->stream_id = stream_id;
+ /* See TODO above. We assume that the whole buf was consumed by
+ * generating the request headers. */
+ nwritten = len;
- rv = h2_session_send(data, h2);
- if(rv) {
- H2BUGF(infof(data,
- "http2_send() nghttp2_session_send error (%s)%d",
- nghttp2_strerror(rv), rv));
-
- *err = CURLE_SEND_ERROR;
- return -1;
+ result = h2_session_send(cf, data);
+ if(result) {
+ *err = result;
+ nwritten = -1;
+ goto out;
}
- if(should_close_session(httpc)) {
- H2BUGF(infof(data, "http2_send: nothing to do in this session"));
+ if(should_close_session(ctx)) {
+ DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
*err = CURLE_HTTP2;
- return -1;
+ nwritten = -1;
+ goto out;
}
/* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
@@ -2030,169 +2162,126 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
results that no writable socket check is performed. To workaround this,
we issue nghttp2_session_resume_data() here to bring back DATA
transmission from deferred state. */
- nghttp2_session_resume_data(h2, stream->stream_id);
+ nghttp2_session_resume_data(ctx->h2, stream->stream_id);
- return len;
+out:
+ CF_DATA_RESTORE(cf, save);
+ return nwritten;
}
-CURLcode Curl_http2_setup(struct Curl_easy *data,
- struct connectdata *conn)
+static int cf_h2_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *sock)
{
- CURLcode result;
- struct http_conn *httpc = &conn->proto.httpc;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
struct HTTP *stream = data->req.p.http;
+ int bitmap = GETSOCK_BLANK;
+ struct cf_call_data save;
- DEBUGASSERT(data->state.buffer);
+ CF_DATA_SAVE(save, cf, data);
+ sock[0] = Curl_conn_cf_get_socket(cf, data);
- stream->stream_id = -1;
+ if(!(k->keepon & KEEP_RECV_PAUSE))
+ /* Unless paused - in an HTTP/2 connection we can basically always get a
+ frame so we should always be ready for one */
+ bitmap |= GETSOCK_READSOCK(0);
- Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
- Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
+ /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
+ there's a window to send data in */
+ if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) ||
+ nghttp2_session_want_write(ctx->h2)) &&
+ (nghttp2_session_get_remote_window_size(ctx->h2) &&
+ nghttp2_session_get_stream_remote_window_size(ctx->h2,
+ stream->stream_id)))
+ bitmap |= GETSOCK_WRITESOCK(0);
- stream->upload_left = 0;
- stream->upload_mem = NULL;
- stream->upload_len = 0;
- stream->mem = data->state.buffer;
- stream->len = data->set.buffer_size;
+ CF_DATA_RESTORE(cf, save);
+ return bitmap;
+}
- multi_connchanged(data->multi);
- /* below this point only connection related inits are done, which only needs
- to be done once per connection */
- if((conn->handler == &Curl_handler_http2_ssl) ||
- (conn->handler == &Curl_handler_http2))
- return CURLE_OK; /* already done */
+static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
- if(conn->handler->flags & PROTOPT_SSL)
- conn->handler = &Curl_handler_http2_ssl;
- else
- conn->handler = &Curl_handler_http2;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
- result = http2_init(data, conn);
- if(result) {
- Curl_dyn_free(&stream->header_recvbuf);
- return result;
+ /* Connect the lower filters first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
}
- infof(data, "Using HTTP2, server supports multiplexing");
+ *done = FALSE;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 20;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ CF_DATA_SAVE(save, cf, data);
+ if(!ctx->h2) {
+ result = cf_h2_ctx_init(cf, data, FALSE);
+ if(result)
+ goto out;
+ }
- httpc->inbuflen = 0;
- httpc->nread_inbuf = 0;
+ if(-1 == h2_process_pending_input(cf, data, &result)) {
+ result = CURLE_HTTP2;
+ goto out;
+ }
- httpc->pause_stream_id = 0;
- httpc->drain_total = 0;
+ *done = TRUE;
+ cf->connected = TRUE;
+ result = CURLE_OK;
- return CURLE_OK;
+out:
+ CF_DATA_RESTORE(cf, save);
+ return result;
}
-CURLcode Curl_http2_switched(struct Curl_easy *data,
- const char *mem, size_t nread)
+static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- CURLcode result;
- struct connectdata *conn = data->conn;
- struct http_conn *httpc = &conn->proto.httpc;
- int rv;
- struct HTTP *stream = data->req.p.http;
-
- result = Curl_http2_setup(data, conn);
- if(result)
- return result;
+ struct cf_h2_ctx *ctx = cf->ctx;
- httpc->recv_underlying = conn->recv[FIRSTSOCKET];
- httpc->send_underlying = conn->send[FIRSTSOCKET];
- conn->recv[FIRSTSOCKET] = http2_recv;
- conn->send[FIRSTSOCKET] = http2_send;
+ if(ctx) {
+ struct cf_call_data save;
- if(data->req.upgr101 == UPGR101_RECEIVED) {
- /* stream 1 is opened implicitly on upgrade */
- stream->stream_id = 1;
- /* queue SETTINGS frame (again) */
- rv = nghttp2_session_upgrade2(httpc->h2, httpc->binsettings, httpc->binlen,
- data->state.httpreq == HTTPREQ_HEAD, NULL);
- if(rv) {
- failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
-
- rv = nghttp2_session_set_stream_user_data(httpc->h2,
- stream->stream_id,
- data);
- if(rv) {
- infof(data, "http/2: failed to set user_data for stream %u",
- stream->stream_id);
- DEBUGASSERT(0);
- }
- }
- else {
- populate_settings(data, httpc);
-
- /* stream ID is unknown at this point */
- stream->stream_id = -1;
- rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
- httpc->local_settings,
- httpc->local_settings_num);
- if(rv) {
- failf(data, "nghttp2_submit_settings() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
+ CF_DATA_SAVE(save, cf, data);
+ cf_h2_ctx_clear(ctx);
+ CF_DATA_RESTORE(cf, save);
}
+}
- rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
- HTTP2_HUGE_WINDOW_SIZE);
- if(rv) {
- failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
+static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
- /* we are going to copy mem to httpc->inbuf. This is required since
- mem is part of buffer pointed by stream->mem, and callbacks
- called by nghttp2_session_mem_recv() will write stream specific
- data into stream->mem, overwriting data already there. */
- if(H2_BUFSIZE < nread) {
- failf(data, "connection buffer size is too small to store data following "
- "HTTP Upgrade response header: buflen=%d, datalen=%zu",
- H2_BUFSIZE, nread);
- return CURLE_HTTP2;
+ (void)data;
+ if(ctx) {
+ cf_h2_ctx_free(ctx);
+ cf->ctx = NULL;
}
-
- infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
- " after upgrade: len=%zu",
- nread);
-
- if(nread)
- memcpy(httpc->inbuf, mem, nread);
-
- httpc->inbuflen = nread;
-
- DEBUGASSERT(httpc->nread_inbuf == 0);
-
- if(-1 == h2_process_pending_input(data, httpc, &result))
- return CURLE_HTTP2;
-
- return CURLE_OK;
}
-CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
+static CURLcode http2_data_pause(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool pause)
{
+ struct cf_h2_ctx *ctx = cf->ctx;
+
DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- /* if it isn't HTTP/2, we're done */
- if(!(data->conn->handler->protocol & PROTO_FAMILY_HTTP) ||
- !data->conn->proto.httpc.h2)
- return CURLE_OK;
#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
- else {
+ if(ctx && ctx->h2) {
struct HTTP *stream = data->req.p.http;
- struct http_conn *httpc = &data->conn->proto.httpc;
uint32_t window = !pause * HTTP2_HUGE_WINDOW_SIZE;
- int rv = nghttp2_session_set_local_window_size(httpc->h2,
+ CURLcode result;
+
+ int rv = nghttp2_session_set_local_window_size(ctx->h2,
NGHTTP2_FLAG_NONE,
stream->stream_id,
window);
@@ -2203,9 +2292,9 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
}
/* make sure the window update gets sent */
- rv = h2_session_send(data, httpc->h2);
- if(rv)
- return CURLE_SEND_ERROR;
+ result = h2_session_send(cf, data);
+ if(result)
+ return result;
DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
window, stream->stream_id));
@@ -2214,7 +2303,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
{
/* read out the stream local window again */
uint32_t window2 =
- nghttp2_session_get_stream_local_window_size(httpc->h2,
+ nghttp2_session_get_stream_local_window_size(ctx->h2,
stream->stream_id);
DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
window2, stream->stream_id));
@@ -2225,86 +2314,327 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
return CURLE_OK;
}
-CURLcode Curl_http2_add_child(struct Curl_easy *parent,
- struct Curl_easy *child,
- bool exclusive)
+static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
{
- if(parent) {
- struct Curl_http2_dep **tail;
- struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
- if(!dep)
- return CURLE_OUT_OF_MEMORY;
- dep->data = child;
-
- if(parent->set.stream_dependents && exclusive) {
- struct Curl_http2_dep *node = parent->set.stream_dependents;
- while(node) {
- node->data->set.stream_depends_on = child;
- node = node->next;
- }
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
- tail = &child->set.stream_dependents;
- while(*tail)
- tail = &(*tail)->next;
+ (void)arg2;
- DEBUGASSERT(!*tail);
- *tail = parent->set.stream_dependents;
- parent->set.stream_dependents = 0;
- }
+ CF_DATA_SAVE(save, cf, data);
+ switch(event) {
+ case CF_CTRL_DATA_SETUP: {
+ result = http2_data_setup(cf, data);
+ break;
+ }
+ case CF_CTRL_DATA_PAUSE: {
+ result = http2_data_pause(cf, data, (arg1 != 0));
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ result = http2_data_done_send(cf, data);
+ break;
+ }
+ case CF_CTRL_DATA_DONE: {
+ http2_data_done(cf, data, arg1 != 0);
+ break;
+ }
+ default:
+ break;
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
- tail = &parent->set.stream_dependents;
- while(*tail) {
- (*tail)->data->set.stream_depends_e = FALSE;
- tail = &(*tail)->next;
- }
+static bool cf_h2_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ if(ctx && ctx->inbuflen > 0 && ctx->nread_inbuf > ctx->inbuflen)
+ return TRUE;
+ return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
+}
+
+static bool cf_h2_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ struct cf_call_data save;
- DEBUGASSERT(!*tail);
- *tail = dep;
+ CF_DATA_SAVE(save, cf, data);
+ result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
+ DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d",
+ result, *input_pending));
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ CURLcode result;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ result = http2_send_ping(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static CURLcode cf_h2_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+ size_t effective_max;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT:
+ DEBUGASSERT(pres1);
+
+ CF_DATA_SAVE(save, cf, data);
+ if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
+ /* the limit is what we have in use right now */
+ effective_max = CONN_INUSE(cf->conn);
+ }
+ else {
+ effective_max = ctx->max_concurrent_streams;
+ }
+ *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
+ CF_DATA_RESTORE(cf, save);
+ return CURLE_OK;
+ default:
+ break;
}
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
- child->set.stream_depends_on = parent;
- child->set.stream_depends_e = exclusive;
- return CURLE_OK;
+struct Curl_cftype Curl_cft_nghttp2 = {
+ "HTTP/2",
+ CF_TYPE_MULTIPLEX,
+ CURL_LOG_DEFAULT,
+ cf_h2_destroy,
+ cf_h2_connect,
+ cf_h2_close,
+ Curl_cf_def_get_host,
+ cf_h2_get_select_socks,
+ cf_h2_data_pending,
+ cf_h2_send,
+ cf_h2_recv,
+ cf_h2_cntrl,
+ cf_h2_is_alive,
+ cf_h2_keep_alive,
+ cf_h2_query,
+};
+
+static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_h2_ctx *ctx;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(data->conn);
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx)
+ goto out;
+
+ result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ result = CURLE_OK;
+
+out:
+ if(result)
+ cf_h2_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
}
-void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
+static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- struct Curl_http2_dep *last = 0;
- struct Curl_http2_dep *data = parent->set.stream_dependents;
- DEBUGASSERT(child->set.stream_depends_on == parent);
+ struct Curl_cfilter *cf_h2 = NULL;
+ struct cf_h2_ctx *ctx;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx)
+ goto out;
+
+ result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
+ if(result)
+ goto out;
+
+ Curl_conn_cf_insert_after(cf, cf_h2);
+ result = CURLE_OK;
- while(data && data->data != child) {
- last = data;
- data = data->next;
+out:
+ if(result)
+ cf_h2_ctx_free(ctx);
+ return result;
+}
+
+bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data)
+{
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_nghttp2)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
}
+ return FALSE;
+}
- DEBUGASSERT(data);
+bool Curl_conn_is_http2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
+}
- if(data) {
- if(last) {
- last->next = data->next;
- }
- else {
- parent->set.stream_dependents = data->next;
+bool Curl_http2_may_switch(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ (void)sockindex;
+ if(!Curl_conn_is_http2(data, conn, sockindex) &&
+ data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* We don't support HTTP/2 proxies yet. Also it's debatable
+ whether or not this setting should apply to HTTP/2 proxies. */
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
+ return FALSE;
}
- free(data);
+#endif
+ return TRUE;
+ }
+ return FALSE;
+}
+
+CURLcode Curl_http2_switch(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2")));
+
+ result = http2_cfilter_add(&cf, data, conn, sockindex);
+ if(result)
+ return result;
+
+ result = cf_h2_ctx_init(cf, data, FALSE);
+ if(result)
+ return result;
+
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf, data, FALSE, &done);
}
+ return CURLE_OK;
+}
+
+CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf_h2;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_cf_is_http2(cf, data));
- child->set.stream_depends_on = 0;
- child->set.stream_depends_e = FALSE;
+ result = http2_cfilter_insert_after(cf, data);
+ if(result)
+ return result;
+
+ cf_h2 = cf->next;
+ result = cf_h2_ctx_init(cf_h2, data, FALSE);
+ if(result)
+ return result;
+
+ cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf_h2->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
+ }
+ return CURLE_OK;
}
-void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
+CURLcode Curl_http2_upgrade(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex,
+ const char *mem, size_t nread)
{
- while(data->set.stream_dependents) {
- struct Curl_easy *tmp = data->set.stream_dependents->data;
- Curl_http2_remove_child(data, tmp);
- if(data->set.stream_depends_on)
- Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
+ struct Curl_cfilter *cf;
+ struct cf_h2_ctx *ctx;
+ CURLcode result;
+
+ DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2")));
+ DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
+
+ result = http2_cfilter_add(&cf, data, conn, sockindex);
+ if(result)
+ return result;
+
+ DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
+ ctx = cf->ctx;
+
+ result = cf_h2_ctx_init(cf, data, TRUE);
+ if(result)
+ return result;
+
+ if(nread) {
+ /* we are going to copy mem to httpc->inbuf. This is required since
+ mem is part of buffer pointed by stream->mem, and callbacks
+ called by nghttp2_session_mem_recv() will write stream specific
+ data into stream->mem, overwriting data already there. */
+ if(H2_BUFSIZE < nread) {
+ failf(data, "connection buffer size is too small to store data "
+ "following HTTP Upgrade response header: buflen=%d, datalen=%zu",
+ H2_BUFSIZE, nread);
+ return CURLE_HTTP2;
+ }
+
+ infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
+ " after upgrade: len=%zu", nread);
+ DEBUGASSERT(ctx->nread_inbuf == 0);
+ memcpy(ctx->inbuf, mem, nread);
+ ctx->inbuflen = nread;
}
- if(data->set.stream_depends_on)
- Curl_http2_remove_child(data->set.stream_depends_on, data);
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ multi_connchanged(data->multi);
+
+ if(cf->next) {
+ bool done;
+ return Curl_conn_cf_connect(cf, data, FALSE, &done);
+ }
+ return CURLE_OK;
}
/* Only call this function for a transfer that already got an HTTP/2
@@ -2312,7 +2642,7 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
bool Curl_h2_http_1_1_error(struct Curl_easy *data)
{
struct HTTP *stream = data->req.p.http;
- return (stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
+ return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
}
#else /* !USE_NGHTTP2 */
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h
index 966bf75da0..f78fbf04e0 100644
--- a/Utilities/cmcurl/lib/http2.h
+++ b/Utilities/cmcurl/lib/http2.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -40,44 +40,41 @@ void Curl_http2_ver(char *p, size_t len);
const char *Curl_http2_strerror(uint32_t err);
-CURLcode Curl_http2_init(struct connectdata *conn);
-void Curl_http2_init_state(struct UrlState *state);
-void Curl_http2_init_userset(struct UserDefined *set);
CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
struct Curl_easy *data);
-CURLcode Curl_http2_setup(struct Curl_easy *data, struct connectdata *conn);
-CURLcode Curl_http2_switched(struct Curl_easy *data,
- const char *ptr, size_t nread);
-/* called from http_setup_conn */
-void Curl_http2_setup_conn(struct connectdata *conn);
-void Curl_http2_setup_req(struct Curl_easy *data);
-void Curl_http2_done(struct Curl_easy *data, bool premature);
-CURLcode Curl_http2_done_sending(struct Curl_easy *data,
- struct connectdata *conn);
-CURLcode Curl_http2_add_child(struct Curl_easy *parent,
- struct Curl_easy *child,
- bool exclusive);
-void Curl_http2_remove_child(struct Curl_easy *parent,
- struct Curl_easy *child);
-void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
-CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause);
/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
bool Curl_h2_http_1_1_error(struct Curl_easy *data);
+
+bool Curl_conn_is_http2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+bool Curl_cf_is_http2(struct Curl_cfilter *cf, const struct Curl_easy *data);
+
+bool Curl_http2_may_switch(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex);
+
+CURLcode Curl_http2_switch(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex);
+
+CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data);
+
+CURLcode Curl_http2_upgrade(struct Curl_easy *data,
+ struct connectdata *conn, int sockindex,
+ const char *ptr, size_t nread);
+
+extern struct Curl_cftype Curl_cft_nghttp2;
+
#else /* USE_NGHTTP2 */
+
+#define Curl_cf_is_http2(a,b) FALSE
+#define Curl_conn_is_http2(a,b,c) FALSE
+#define Curl_http2_may_switch(a,b,c) FALSE
+
#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_setup(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_setup_conn(x) Curl_nop_stmt
-#define Curl_http2_setup_req(x)
-#define Curl_http2_init_state(x)
-#define Curl_http2_init_userset(x)
-#define Curl_http2_done(x,y)
-#define Curl_http2_done_sending(x,y) (void)y
-#define Curl_http2_add_child(x, y, z)
-#define Curl_http2_remove_child(x, y)
-#define Curl_http2_cleanup_dependencies(x)
-#define Curl_http2_stream_pause(x, y)
+#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_h2_http_1_1_error(x) 0
#endif
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 8c6d1c9dc8..7d50cfff8f 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,9 +48,9 @@
do { \
ret = Curl_hmacit(Curl_HMAC_SHA256, \
(unsigned char *)k, \
- (unsigned int)kl, \
+ kl, \
(unsigned char *)d, \
- (unsigned int)dl, o); \
+ dl, o); \
if(ret) { \
goto fail; \
} \
@@ -58,13 +58,15 @@
#define TIMESTAMP_SIZE 17
-static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
+/* hex-encoded with trailing null */
+#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1)
+
+static void sha256_to_hex(char *dst, unsigned char *sha)
{
int i;
- DEBUGASSERT(dst_l >= 65);
- for(i = 0; i < 32; ++i) {
- msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+ for(i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+ msnprintf(dst + (i * 2), SHA256_HEX_LENGTH - (i * 2), "%02x", sha[i]);
}
}
@@ -135,6 +137,7 @@ static CURLcode make_headers(struct Curl_easy *data,
char *timestamp,
char *provider1,
char **date_header,
+ char *content_sha256_header,
struct dynbuf *canonical_headers,
struct dynbuf *signed_headers)
{
@@ -189,6 +192,13 @@ static CURLcode make_headers(struct Curl_easy *data,
}
+ if (*content_sha256_header) {
+ tmp_head = curl_slist_append(head, content_sha256_header);
+ if(!tmp_head)
+ goto fail;
+ head = tmp_head;
+ }
+
for(l = data->set.headers; l; l = l->next) {
tmp_head = curl_slist_append(head, l->data);
if(!tmp_head)
@@ -267,6 +277,9 @@ fail:
}
#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
+/* add 2 for ": " between header name and value */
+#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \
+ SHA256_HEX_LENGTH)
/* try to parse a payload hash from the content-sha256 header */
static char *parse_content_sha_hdr(struct Curl_easy *data,
@@ -300,6 +313,63 @@ static char *parse_content_sha_hdr(struct Curl_easy *data,
return value;
}
+static CURLcode calc_payload_hash(struct Curl_easy *data,
+ unsigned char *sha_hash, char *sha_hex)
+{
+ const char *post_data = data->set.postfields;
+ size_t post_data_len = 0;
+ CURLcode result;
+
+ if(post_data) {
+ if(data->set.postfieldsize < 0)
+ post_data_len = strlen(post_data);
+ else
+ post_data_len = (size_t)data->set.postfieldsize;
+ }
+ result = Curl_sha256it(sha_hash, (const unsigned char *) post_data,
+ post_data_len);
+ if(!result)
+ sha256_to_hex(sha_hex, sha_hash);
+ return result;
+}
+
+#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
+
+static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
+ Curl_HttpReq httpreq, char *provider1,
+ unsigned char *sha_hash,
+ char *sha_hex, char *header)
+{
+ bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD);
+ /* The request method or filesize indicate no request payload */
+ bool empty_payload = (empty_method || data->set.filesize == 0);
+ /* The POST payload is in memory */
+ bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
+ CURLcode ret = CURLE_OUT_OF_MEMORY;
+
+ if(empty_payload || post_payload) {
+ /* Calculate a real hash when we know the request payload */
+ ret = calc_payload_hash(data, sha_hash, sha_hex);
+ if(ret)
+ goto fail;
+ }
+ else {
+ /* Fall back to s3's UNSIGNED-PAYLOAD */
+ size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1;
+ DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */
+ memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len);
+ sha_hex[len] = 0;
+ }
+
+ /* format the required content-sha256 header */
+ msnprintf(header, CONTENT_SHA256_HDR_LEN,
+ "x-%s-content-sha256: %s", provider1, sha_hex);
+
+ ret = CURLE_OK;
+fail:
+ return ret;
+}
+
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
{
CURLcode ret = CURLE_OUT_OF_MEMORY;
@@ -310,6 +380,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char provider1[MAX_SIGV4_LEN + 1]="";
char region[MAX_SIGV4_LEN + 1]="";
char service[MAX_SIGV4_LEN + 1]="";
+ bool sign_as_s3 = false;
const char *hostname = conn->host.name;
time_t clock;
struct tm tm;
@@ -318,20 +389,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
struct dynbuf canonical_headers;
struct dynbuf signed_headers;
char *date_header = NULL;
+ Curl_HttpReq httpreq;
+ const char *method = NULL;
char *payload_hash = NULL;
size_t payload_hash_len = 0;
- const char *post_data = data->set.postfields;
- size_t post_data_len = 0;
- unsigned char sha_hash[32];
- char sha_hex[65];
+ unsigned char sha_hash[SHA256_DIGEST_LENGTH];
+ char sha_hex[SHA256_HEX_LENGTH];
+ char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
char *canonical_request = NULL;
char *request_type = NULL;
char *credential_scope = NULL;
char *str_to_sign = NULL;
const char *user = data->state.aptr.user ? data->state.aptr.user : "";
char *secret = NULL;
- unsigned char sign0[32] = {0};
- unsigned char sign1[32] = {0};
+ unsigned char sign0[SHA256_DIGEST_LENGTH] = {0};
+ unsigned char sign1[SHA256_DIGEST_LENGTH] = {0};
char *auth_headers = NULL;
DEBUGASSERT(!proxy);
@@ -408,6 +480,29 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
}
}
+ Curl_http_method(data, conn, &method, &httpreq);
+
+ /* AWS S3 requires a x-amz-content-sha256 header, and supports special
+ * values like UNSIGNED-PAYLOAD */
+ sign_as_s3 = (strcasecompare(provider0, "aws") &&
+ strcasecompare(service, "s3"));
+
+ payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
+
+ if(!payload_hash) {
+ if(sign_as_s3)
+ ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
+ sha_hex, content_sha256_hdr);
+ else
+ ret = calc_payload_hash(data, sha_hash, sha_hex);
+ if(ret)
+ goto fail;
+
+ payload_hash = sha_hex;
+ /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
+ payload_hash_len = strlen(sha_hex);
+ }
+
#ifdef DEBUGBUILD
{
char *force_timestamp = getenv("CURL_FORCETIME");
@@ -429,54 +524,37 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
}
ret = make_headers(data, hostname, timestamp, provider1,
- &date_header, &canonical_headers, &signed_headers);
+ &date_header, content_sha256_hdr,
+ &canonical_headers, &signed_headers);
if(ret)
goto fail;
ret = CURLE_OUT_OF_MEMORY;
+ if(*content_sha256_hdr) {
+ /* make_headers() needed this without the \r\n for canonicalization */
+ size_t hdrlen = strlen(content_sha256_hdr);
+ DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr));
+ memcpy(content_sha256_hdr + hdrlen, "\r\n", 3);
+ }
+
memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
- payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
-
- if(!payload_hash) {
- if(post_data) {
- if(data->set.postfieldsize < 0)
- post_data_len = strlen(post_data);
- else
- post_data_len = (size_t)data->set.postfieldsize;
- }
- if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
- post_data_len))
- goto fail;
-
- sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
- payload_hash = sha_hex;
- payload_hash_len = strlen(sha_hex);
- }
-
- {
- Curl_HttpReq httpreq;
- const char *method;
-
- Curl_http_method(data, conn, &method, &httpreq);
-
- canonical_request =
- curl_maprintf("%s\n" /* HTTPRequestMethod */
- "%s\n" /* CanonicalURI */
- "%s\n" /* CanonicalQueryString */
- "%s\n" /* CanonicalHeaders */
- "%s\n" /* SignedHeaders */
- "%.*s", /* HashedRequestPayload in hex */
- method,
- data->state.up.path,
- data->state.up.query ? data->state.up.query : "",
- Curl_dyn_ptr(&canonical_headers),
- Curl_dyn_ptr(&signed_headers),
- (int)payload_hash_len, payload_hash);
- if(!canonical_request)
- goto fail;
- }
+ canonical_request =
+ curl_maprintf("%s\n" /* HTTPRequestMethod */
+ "%s\n" /* CanonicalURI */
+ "%s\n" /* CanonicalQueryString */
+ "%s\n" /* CanonicalHeaders */
+ "%s\n" /* SignedHeaders */
+ "%.*s", /* HashedRequestPayload in hex */
+ method,
+ data->state.up.path,
+ data->state.up.query ? data->state.up.query : "",
+ Curl_dyn_ptr(&canonical_headers),
+ Curl_dyn_ptr(&signed_headers),
+ (int)payload_hash_len, payload_hash);
+ if(!canonical_request)
+ goto fail;
/* provider 0 lowercase */
Curl_strntolower(provider0, provider0, strlen(provider0));
@@ -493,7 +571,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
strlen(canonical_request)))
goto fail;
- sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
+ sha256_to_hex(sha_hex, sha_hash);
/* provider 0 uppercase */
Curl_strntoupper(provider0, provider0, strlen(provider0));
@@ -527,20 +605,22 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
- sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
+ sha256_to_hex(sha_hex, sign0);
/* provider 0 uppercase */
auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
- "%s\r\n",
+ "%s\r\n"
+ "%s", /* optional sha256 header includes \r\n */
provider0,
user,
credential_scope,
Curl_dyn_ptr(&signed_headers),
sha_hex,
- date_header);
+ date_header,
+ content_sha256_hdr);
if(!auth_headers) {
goto fail;
}
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h
index 85755e937b..57cc5706eb 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.h
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c
index c344e6d39a..bda00d3833 100644
--- a/Utilities/cmcurl/lib/http_chunks.c
+++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_chunks.h b/Utilities/cmcurl/lib/http_chunks.h
index 2cf5507c21..ed50713162 100644
--- a/Utilities/cmcurl/lib/http_chunks.h
+++ b/Utilities/cmcurl/lib/http_chunks.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index f015f78d92..8daad99e32 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h
index eea90b7439..7d5cfc1bfd 100644
--- a/Utilities/cmcurl/lib/http_digest.h
+++ b/Utilities/cmcurl/lib/http_digest.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c
index 5909f85b0d..153e3d4ab8 100644
--- a/Utilities/cmcurl/lib/http_negotiate.c
+++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_negotiate.h b/Utilities/cmcurl/lib/http_negotiate.h
index 6e2096c697..76d8356132 100644
--- a/Utilities/cmcurl/lib/http_negotiate.h
+++ b/Utilities/cmcurl/lib/http_negotiate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c
index a19cc0d589..b845ddf37f 100644
--- a/Utilities/cmcurl/lib/http_ntlm.c
+++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_ntlm.h b/Utilities/cmcurl/lib/http_ntlm.h
index cec63b82c3..f37572baec 100644
--- a/Utilities/cmcurl/lib/http_ntlm.h
+++ b/Utilities/cmcurl/lib/http_ntlm.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index e30730acab..9f214a305e 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,7 +26,7 @@
#include "http_proxy.h"
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+#if !defined(CURL_DISABLE_PROXY)
#include <curl/curl.h>
#ifdef USE_HYPER
@@ -49,6 +49,9 @@
#include "curl_memory.h"
#include "memdebug.h"
+
+#if !defined(CURL_DISABLE_HTTP)
+
typedef enum {
TUNNEL_INIT, /* init/default/no tunnel state */
TUNNEL_CONNECT, /* CONNECT request is being send */
@@ -63,8 +66,7 @@ struct tunnel_state {
int sockindex;
const char *hostname;
int remote_port;
- struct HTTP http_proxy;
- struct HTTP *prot_save;
+ struct HTTP CONNECT;
struct dynbuf rcvbuf;
struct dynbuf req;
size_t nsend;
@@ -149,17 +151,6 @@ static CURLcode tunnel_init(struct tunnel_state **pts,
Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
- /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
- * member conn->proto.http; we want [protocol] through HTTP and we have
- * to change the member temporarily for connecting to the HTTP
- * proxy. After Curl_proxyCONNECT we have to set back the member to the
- * original pointer
- *
- * This function might be called several times in the multi interface case
- * if the proxy's CONNECT response is not instant.
- */
- ts->prot_save = data->req.p.http;
- data->req.p.http = &ts->http_proxy;
*pts = ts;
connkeep(conn, "HTTP proxy CONNECT");
return tunnel_reinit(ts, conn, data);
@@ -183,34 +174,39 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
/* entering this one */
switch(new_state) {
case TUNNEL_INIT:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
tunnel_reinit(ts, cf->conn, data);
break;
case TUNNEL_CONNECT:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
ts->tunnel_state = TUNNEL_CONNECT;
ts->keepon = KEEPON_CONNECT;
Curl_dyn_reset(&ts->rcvbuf);
break;
case TUNNEL_RECEIVE:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
ts->tunnel_state = TUNNEL_RECEIVE;
break;
case TUNNEL_RESPONSE:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
ts->tunnel_state = TUNNEL_RESPONSE;
break;
case TUNNEL_ESTABLISHED:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
/* FALLTHROUGH */
case TUNNEL_FAILED:
+ DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
ts->tunnel_state = new_state;
Curl_dyn_reset(&ts->rcvbuf);
Curl_dyn_reset(&ts->req);
/* restore the protocol pointer */
- data->req.p.http = ts->prot_save;
data->info.httpcode = 0; /* clear it as it might've been used for the
proxy */
/* If a proxy-authorization header was used for the proxy, then we should
@@ -271,10 +267,11 @@ static CURLcode CONNECT_host(struct Curl_easy *data,
}
#ifndef USE_HYPER
-static CURLcode start_CONNECT(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode start_CONNECT(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts)
{
+ struct connectdata *conn = cf->conn;
char *hostheader = NULL;
char *host = NULL;
const char *httpv;
@@ -338,7 +335,8 @@ static CURLcode start_CONNECT(struct Curl_easy *data,
goto out;
/* Send the connect request to the proxy */
- result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0,
+ result = Curl_buffer_send(&ts->req, data, &ts->CONNECT,
+ &data->info.request_size, 0,
ts->sockindex);
ts->headerlines = 0;
@@ -356,7 +354,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data,
bool *done)
{
struct SingleRequest *k = &data->req;
- struct HTTP *http = data->req.p.http;
+ struct HTTP *http = &ts->CONNECT;
CURLcode result = CURLE_OK;
if(http->sending != HTTPSEND_REQUEST)
@@ -377,7 +375,7 @@ static CURLcode send_CONNECT(struct Curl_easy *data,
result = Curl_write(data,
conn->writesockfd, /* socket to send to */
k->upload_fromhere, /* buffer pointer */
- ts->nsend, /* buffer size */
+ ts->nsend, /* buffer size */
&bytes_written); /* actually sent */
if(result)
goto out;
@@ -398,13 +396,14 @@ out:
return result;
}
-static CURLcode on_resp_header(struct Curl_easy *data,
+static CURLcode on_resp_header(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
const char *header)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- int subversion = 0;
+ (void)cf;
if((checkprefix("WWW-Authenticate:", header) &&
(401 == k->httpcode)) ||
@@ -416,8 +415,7 @@ static CURLcode on_resp_header(struct Curl_easy *data,
if(!auth)
return CURLE_OUT_OF_MEMORY;
- DEBUGF(infof(data, "CONNECT: fwd auth header '%s'",
- header));
+ DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header));
result = Curl_http_input_auth(data, proxy, auth);
free(auth);
@@ -462,23 +460,26 @@ static CURLcode on_resp_header(struct Curl_easy *data,
STRCONST("Proxy-Connection:"),
STRCONST("close")))
ts->close_connection = TRUE;
- else if(2 == sscanf(header, "HTTP/1.%d %d",
- &subversion,
- &k->httpcode)) {
+ else if(!strncmp(header, "HTTP/1.", 7) &&
+ ((header[7] == '0') || (header[7] == '1')) &&
+ (header[8] == ' ') &&
+ ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
+ !ISDIGIT(header[12])) {
/* store the HTTP code from the proxy */
- data->info.httpproxycode = k->httpcode;
+ data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
+ (header[10] - '0') * 10 + (header[11] - '0');
}
return result;
}
-static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
bool *done)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
- curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
char *linep;
size_t perline;
int error;
@@ -634,7 +635,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
/* without content-length or chunked encoding, we
can't keep the connection alive since the close is
the end signal so we bail out at once instead */
- DEBUGF(infof(data, "CONNECT: no content-length or chunked"));
+ DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked"));
ts->keepon = KEEPON_DONE;
}
}
@@ -647,7 +648,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
continue;
}
- result = on_resp_header(data, ts, linep);
+ result = on_resp_header(cf, data, ts, linep);
if(result)
return result;
@@ -667,12 +668,13 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
#else /* USE_HYPER */
/* The Hyper version of CONNECT */
-static CURLcode start_CONNECT(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode start_CONNECT(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts)
{
+ struct connectdata *conn = cf->conn;
struct hyptransfer *h = &data->hyp;
- curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
+ curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
hyper_io *io = NULL;
hyper_request *req = NULL;
hyper_headers *headers = NULL;
@@ -914,8 +916,8 @@ error:
return result;
}
-static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
- struct connectdata *conn,
+static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
struct tunnel_state *ts,
bool *done)
{
@@ -925,7 +927,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
(void)ts;
*done = FALSE;
- result = Curl_hyper_stream(data, conn, &didwhat, done,
+ result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
CURL_CSELECT_IN | CURL_CSELECT_OUT);
if(result || !*done)
return result;
@@ -972,7 +974,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
switch(ts->tunnel_state) {
case TUNNEL_INIT:
/* Prepare the CONNECT request and make a first attempt to send. */
- result = start_CONNECT(data, cf->conn, ts);
+ DEBUGF(LOG_CF(data, cf, "CONNECT start"));
+ result = start_CONNECT(cf, data, ts);
if(result)
goto out;
tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
@@ -980,6 +983,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_CONNECT:
/* see that the request is completely sent */
+ DEBUGF(LOG_CF(data, cf, "CONNECT send"));
result = send_CONNECT(data, cf->conn, ts, &done);
if(result || !done)
goto out;
@@ -988,7 +992,8 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
case TUNNEL_RECEIVE:
/* read what is there */
- result = recv_CONNECT_resp(data, cf->conn, ts, &done);
+ DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
+ result = recv_CONNECT_resp(cf, data, ts, &done);
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
goto out;
@@ -1001,24 +1006,29 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case TUNNEL_RESPONSE:
+ DEBUGF(LOG_CF(data, cf, "CONNECT response"));
if(data->req.newurl) {
/* not the "final" response, we need to do a follow up request.
* If the other side indicated a connection close, or if someone
- * else told us to close this connection, do so now. */
+ * else told us to close this connection, do so now.
+ */
if(ts->close_connection || conn->bits.close) {
- /* Close the filter chain and trigger connect, non-blocking
- * again, so the process is ongoing. This will
- * a) the close resets our tunnel state
- * b) the connect makes sure that there will be a socket
- * to select on again.
- * We return and expect to be called again. */
+ /* Close this filter and the sub-chain, re-connect the
+ * sub-chain and continue. Closing this filter will
+ * reset our tunnel state. To avoid recursion, we return
+ * and expect to be called again.
+ */
+ DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open"));
infof(data, "Connect me again please");
- Curl_conn_close(data, cf->sockindex);
- result = cf->next->cft->connect(cf->next, data, FALSE, &done);
+ Curl_conn_cf_close(cf, data);
+ connkeep(conn, "HTTP proxy CONNECT");
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
goto out;
}
- /* staying on this connection, reset state */
- tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+ else {
+ /* staying on this connection, reset state */
+ tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+ }
}
break;
@@ -1063,10 +1073,12 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
+ DEBUGF(LOG_CF(data, cf, "connect"));
result = cf->next->cft->connect(cf->next, data, blocking, done);
if(result || !*done)
return result;
+ DEBUGF(LOG_CF(data, cf, "subchain is connected"));
/* TODO: can we do blocking? */
/* We want "seamless" operations through HTTP proxy tunnel */
@@ -1117,22 +1129,21 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
curl_socket_t *socks)
{
struct tunnel_state *ts = cf->ctx;
- struct connectdata *conn = cf->conn;
int fds;
- DEBUGASSERT(conn);
fds = cf->next->cft->get_select_socks(cf->next, data, socks);
if(!fds && cf->next->connected && !cf->connected) {
/* If we are not connected, but the filter "below" is
* and not waiting on something, we are tunneling. */
- socks[0] = conn->sock[cf->sockindex];
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
if(ts) {
/* when we've sent a CONNECT to a proxy, we should rather either
wait for the socket to become readable to be able to get the
response headers or if we're still sending the request, wait
for write. */
- if(ts->http_proxy.sending == HTTPSEND_REQUEST)
+ if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
return GETSOCK_WRITESOCK(0);
+ }
return GETSOCK_READSOCK(0);
}
return GETSOCK_WRITESOCK(0);
@@ -1140,24 +1151,18 @@ static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
return fds;
}
-static void http_proxy_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- if(cf->ctx) {
- tunnel_free(cf, data);
- }
-}
-
static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- http_proxy_cf_detach_data(cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ tunnel_free(cf, data);
}
static void http_proxy_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
+ DEBUGF(LOG_CF(data, cf, "close"));
cf->connected = FALSE;
cf->next->cft->close(cf->next, data);
if(cf->ctx) {
@@ -1166,11 +1171,11 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf,
}
-static const struct Curl_cftype cft_http_proxy = {
+struct Curl_cftype Curl_cft_http_proxy = {
"HTTP-PROXY",
CF_TYPE_IP_CONNECT,
+ 0,
http_proxy_cf_destroy,
- Curl_cf_def_setup,
http_proxy_cf_connect,
http_proxy_cf_close,
http_proxy_cf_get_host,
@@ -1178,8 +1183,10 @@ static const struct Curl_cftype cft_http_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- http_proxy_cf_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
@@ -1189,31 +1196,73 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_http_proxy, NULL);
+ result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
+CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ (void)data;
+ result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
+#endif /* ! CURL_DISABLE_HTTP */
-static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
- struct Curl_easy *data)
+
+typedef enum {
+ HAPROXY_INIT, /* init/default/no tunnel state */
+ HAPROXY_SEND, /* data_out being sent */
+ HAPROXY_DONE /* all work done */
+} haproxy_state;
+
+struct cf_haproxy_ctx {
+ int state;
+ struct dynbuf data_out;
+};
+
+static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
- struct dynbuf req;
+ DEBUGASSERT(ctx);
+ ctx->state = HAPROXY_INIT;
+ Curl_dyn_reset(&ctx->data_out);
+}
+
+static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
+{
+ if(ctx) {
+ Curl_dyn_free(&ctx->data_out);
+ free(ctx);
+ }
+}
+
+static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
+ struct Curl_easy *data)
+{
+ struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *tcp_version;
- Curl_dyn_init(&req, DYN_HAXPROXY);
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(ctx->state == HAPROXY_INIT);
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
/* the buffer is large enough to hold this! */
- result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n"));
+ result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
/* Emit the correct prefix for IPv6 */
tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
- result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
+ result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
tcp_version,
data->info.conn_local_ip,
data->info.conn_primary_ip,
@@ -1223,19 +1272,18 @@ static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
#ifdef USE_UNIX_SOCKETS
}
#endif /* USE_UNIX_SOCKETS */
-
- if(!result)
- result = Curl_buffer_send(&req, data, &data->info.request_size,
- 0, FIRSTSOCKET);
return result;
}
-static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
+static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
+ struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
+ size_t len;
+ DEBUGASSERT(ctx);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@@ -1245,28 +1293,120 @@ static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
if(result || !*done)
return result;
- result = send_haproxy_header(cf, data);
- *done = (!result);
+ switch(ctx->state) {
+ case HAPROXY_INIT:
+ result = cf_haproxy_date_out_set(cf, data);
+ if(result)
+ goto out;
+ ctx->state = HAPROXY_SEND;
+ /* FALLTHROUGH */
+ case HAPROXY_SEND:
+ len = Curl_dyn_len(&ctx->data_out);
+ if(len > 0) {
+ ssize_t written = Curl_conn_send(data, cf->sockindex,
+ Curl_dyn_ptr(&ctx->data_out),
+ len, &result);
+ if(written < 0)
+ goto out;
+ Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
+ if(Curl_dyn_len(&ctx->data_out) > 0) {
+ result = CURLE_OK;
+ goto out;
+ }
+ }
+ ctx->state = HAPROXY_DONE;
+ /* FALLTHROUGH */
+ default:
+ Curl_dyn_free(&ctx->data_out);
+ break;
+ }
+
+out:
+ *done = (!result) && (ctx->state == HAPROXY_DONE);
cf->connected = *done;
return result;
}
-static const struct Curl_cftype cft_haproxy = {
+static void cf_haproxy_destroy(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)data;
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ cf_haproxy_ctx_free(cf->ctx);
+}
+
+static void cf_haproxy_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ DEBUGF(LOG_CF(data, cf, "close"));
+ cf->connected = FALSE;
+ cf_haproxy_ctx_reset(cf->ctx);
+ if(cf->next)
+ cf->next->cft->close(cf->next, data);
+}
+
+static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ int fds;
+
+ fds = cf->next->cft->get_select_socks(cf->next, data, socks);
+ if(!fds && cf->next->connected && !cf->connected) {
+ /* If we are not connected, but the filter "below" is
+ * and not waiting on something, we are sending. */
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
+ return GETSOCK_WRITESOCK(0);
+ }
+ return fds;
+}
+
+
+struct Curl_cftype Curl_cft_haproxy = {
"HAPROXY",
0,
- Curl_cf_def_destroy_this,
- Curl_cf_def_setup,
- haproxy_cf_connect,
- Curl_cf_def_close,
+ 0,
+ cf_haproxy_destroy,
+ cf_haproxy_connect,
+ cf_haproxy_close,
Curl_cf_def_get_host,
- Curl_cf_def_get_select_socks,
+ cf_haproxy_get_select_socks,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- Curl_cf_def_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
+static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf = NULL;
+ struct cf_haproxy_ctx *ctx;
+ CURLcode result;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->state = HAPROXY_INIT;
+ Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
+
+ result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
+ if(result)
+ goto out;
+ ctx = NULL;
+
+out:
+ cf_haproxy_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
+}
+
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
@@ -1274,10 +1414,28 @@ CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_haproxy, NULL);
- if(!result)
- Curl_conn_cf_add(data, conn, sockindex, cf);
+ result = cf_haproxy_create(&cf, data);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+
+out:
+ return result;
+}
+
+CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_haproxy_create(&cf, data);
+ if(result)
+ goto out;
+ Curl_conn_cf_insert_after(cf_at, cf);
+
+out:
return result;
}
-#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */
+#endif /* !CURL_DISABLE_PROXY */
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h
index dfdc0e72ba..f573da273c 100644
--- a/Utilities/cmcurl/lib/http_proxy.h
+++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,8 +27,9 @@
#include "curl_setup.h"
#include "urldata.h"
-#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
+#if !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP)
/* Default proxy timeout in milliseconds */
#define PROXY_TIMEOUT (3600*1000)
@@ -36,10 +37,22 @@ CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_http_proxy;
+
+#endif /* !CURL_DISABLE_HTTP */
+
CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_haproxy;
+
+#endif /* !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_HTTP_PROXY_H */
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c
index 6255221ae1..5f4b07e018 100644
--- a/Utilities/cmcurl/lib/idn.c
+++ b/Utilities/cmcurl/lib/idn.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -116,7 +116,7 @@ bool Curl_is_ASCII_name(const char *hostname)
* Curl_idn_decode() returns an allocated IDN decoded string if it was
* possible. NULL on error.
*/
-static char *Curl_idn_decode(const char *input)
+static char *idn_decode(const char *input)
{
char *decoded = NULL;
#ifdef USE_LIBIDN2
@@ -144,24 +144,29 @@ static char *Curl_idn_decode(const char *input)
return decoded;
}
+char *Curl_idn_decode(const char *input)
+{
+ char *d = idn_decode(input);
+#ifdef USE_LIBIDN2
+ if(d) {
+ char *c = strdup(d);
+ idn2_free(d);
+ d = c;
+ }
+#endif
+ return d;
+}
+
/*
* Frees data allocated by idnconvert_hostname()
*/
void Curl_free_idnconverted_hostname(struct hostname *host)
{
-#if defined(USE_LIBIDN2)
if(host->encalloc) {
- idn2_free(host->encalloc); /* must be freed with idn2_free() since this was
- allocated by libidn */
+ /* must be freed with idn2_free() if allocated by libidn */
+ Curl_idn_free(host->encalloc);
host->encalloc = NULL;
}
-#elif defined(USE_WIN32_IDN)
- free(host->encalloc); /* must be freed with free() since this was
- allocated by Curl_win32_idn_to_ascii */
- host->encalloc = NULL;
-#else
- (void)host;
-#endif
}
#endif /* USE_IDN */
@@ -177,8 +182,13 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host)
#ifdef USE_IDN
/* Check name for non-ASCII and convert hostname if we can */
if(!Curl_is_ASCII_name(host->name)) {
- char *decoded = Curl_idn_decode(host->name);
+ char *decoded = idn_decode(host->name);
if(decoded) {
+ if(!*decoded) {
+ /* zero length is a bad host name */
+ Curl_idn_free(decoded);
+ return CURLE_URL_MALFORMAT;
+ }
/* successful */
host->encalloc = decoded;
/* change the name pointer to point to the encoded hostname */
@@ -190,4 +200,3 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host)
#endif
return CURLE_OK;
}
-
diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h
index 2d04efcdff..6c0bbb7109 100644
--- a/Utilities/cmcurl/lib/idn.h
+++ b/Utilities/cmcurl/lib/idn.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -32,7 +32,15 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host);
#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
#define USE_IDN
void Curl_free_idnconverted_hostname(struct hostname *host);
+char *Curl_idn_decode(const char *input);
+#ifdef USE_LIBIDN2
+#define Curl_idn_free(x) idn2_free(x)
+#else
+#define Curl_idn_free(x) free(x)
+#endif
+
#else
#define Curl_free_idnconverted_hostname(x)
+#define Curl_idn_decode(x) NULL
#endif
#endif /* HEADER_CURL_IDN_H */
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c
index c29194878f..6bf0ce16f7 100644
--- a/Utilities/cmcurl/lib/if2ip.c
+++ b/Utilities/cmcurl/lib/if2ip.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/if2ip.h b/Utilities/cmcurl/lib/if2ip.h
index 5d15459e98..1f973505c0 100644
--- a/Utilities/cmcurl/lib/if2ip.h
+++ b/Utilities/cmcurl/lib/if2ip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2020, 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index bd4c6f2203..c2f675d4b2 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -475,15 +475,17 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
/* Start the SSL connection */
struct imap_conn *imapc = &conn->proto.imapc;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ imapc->ssldone = ssldone;
if(imapc->state != IMAP_UPGRADETLS)
state(data, IMAP_UPGRADETLS);
@@ -952,7 +954,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
line += wordlen;
}
}
- else if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* PREAUTH is not compatible with STARTTLS. */
if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
/* Switch to TLS connection now */
@@ -1386,8 +1388,10 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
struct imap_conn *imapc = &conn->proto.imapc;
if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &imapc->ssldone);
- if(result || !imapc->ssldone)
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ imapc->ssldone = ssldone;
+ if(result || !ssldone)
return result;
}
@@ -1774,7 +1778,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
/* Calculate the tag based on the connection ID and command ID */
msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
'A' + curlx_sltosi(data->conn->connection_id % 26),
- (++imapc->cmdid)%1000);
+ ++imapc->cmdid);
/* start with a blank buffer */
Curl_dyn_reset(&imapc->dyn);
diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h
index 43cc1e98fc..784ee97e55 100644
--- a/Utilities/cmcurl/lib/imap.h
+++ b/Utilities/cmcurl/lib/imap.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -72,19 +72,19 @@ struct IMAP {
struct */
struct imap_conn {
struct pingpong pp;
- imapstate state; /* Always use imap.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
- bool preauth; /* Is this connection PREAUTH? */
struct SASL sasl; /* SASL-related parameters */
- unsigned int preftype; /* Preferred authentication type */
- unsigned int cmdid; /* Last used command ID */
- char resptag[5]; /* Response tag to wait for */
- bool tls_supported; /* StartTLS capability supported by server */
- bool login_disabled; /* LOGIN command disabled by server */
- bool ir_supported; /* Initial response supported by server */
+ struct dynbuf dyn; /* for the IMAP commands */
char *mailbox; /* The last selected mailbox */
char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */
- struct dynbuf dyn; /* for the IMAP commands */
+ imapstate state; /* Always use imap.c:state() to change state! */
+ char resptag[5]; /* Response tag to wait for */
+ unsigned char preftype; /* Preferred authentication type */
+ unsigned char cmdid; /* Last used command ID */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(preauth); /* Is this connection PREAUTH? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(login_disabled); /* LOGIN command disabled by server */
+ BIT(ir_supported); /* Initial response supported by server */
};
extern const struct Curl_handler Curl_handler_imap;
@@ -96,6 +96,6 @@ extern const struct Curl_handler Curl_handler_imaps;
/* Authentication type values */
#define IMAP_TYPE_NONE 0
-#define IMAP_TYPE_ANY ~0U
+#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL)
#endif /* HEADER_CURL_IMAP_H */
diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c
index 024f8da36d..770ed3a59b 100644
--- a/Utilities/cmcurl/lib/inet_ntop.c
+++ b/Utilities/cmcurl/lib/inet_ntop.c
@@ -42,6 +42,15 @@
#define INT16SZ 2
/*
+ * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
+ * sure we have _some_ value for AF_INET6 without polluting our fake value
+ * everywhere.
+ */
+#if !defined(ENABLE_IPV6) && !defined(AF_INET6)
+#define AF_INET6 (AF_INET + 1)
+#endif
+
+/*
* Format an IPv4 address, more or less like inet_ntop().
*
* Returns `dst' (as a const)
@@ -72,7 +81,6 @@ static char *inet_ntop4 (const unsigned char *src, char *dst, size_t size)
return dst;
}
-#ifdef ENABLE_IPV6
/*
* Convert IPv6 binary address into presentation (printable) format.
*/
@@ -168,7 +176,6 @@ static char *inet_ntop6 (const unsigned char *src, char *dst, size_t size)
strcpy(dst, tmp);
return dst;
}
-#endif /* ENABLE_IPV6 */
/*
* Convert a network format address to presentation format.
@@ -187,10 +194,8 @@ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
switch(af) {
case AF_INET:
return inet_ntop4((const unsigned char *)src, buf, size);
-#ifdef ENABLE_IPV6
case AF_INET6:
return inet_ntop6((const unsigned char *)src, buf, size);
-#endif
default:
errno = EAFNOSUPPORT;
return NULL;
diff --git a/Utilities/cmcurl/lib/inet_ntop.h b/Utilities/cmcurl/lib/inet_ntop.h
index 18fbd8ba3a..7c3ead4341 100644
--- a/Utilities/cmcurl/lib/inet_ntop.h
+++ b/Utilities/cmcurl/lib/inet_ntop.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/inet_pton.c b/Utilities/cmcurl/lib/inet_pton.c
index 47fb77834d..7d3c698795 100644
--- a/Utilities/cmcurl/lib/inet_pton.c
+++ b/Utilities/cmcurl/lib/inet_pton.c
@@ -1,6 +1,6 @@
/* This is from the BIND 4.9.4 release, modified to compile by itself */
-/* Copyright (c) 2003 - 2022 by Internet Software Consortium.
+/* Copyright (c) Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -39,14 +39,21 @@
#define INT16SZ 2
/*
+ * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
+ * sure we have _some_ value for AF_INET6 without polluting our fake value
+ * everywhere.
+ */
+#if !defined(ENABLE_IPV6) && !defined(AF_INET6)
+#define AF_INET6 (AF_INET + 1)
+#endif
+
+/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static int inet_pton4(const char *src, unsigned char *dst);
-#ifdef ENABLE_IPV6
static int inet_pton6(const char *src, unsigned char *dst);
-#endif
/* int
* inet_pton(af, src, dst)
@@ -70,10 +77,8 @@ Curl_inet_pton(int af, const char *src, void *dst)
switch(af) {
case AF_INET:
return (inet_pton4(src, (unsigned char *)dst));
-#ifdef ENABLE_IPV6
case AF_INET6:
return (inet_pton6(src, (unsigned char *)dst));
-#endif
default:
errno = EAFNOSUPPORT;
return (-1);
@@ -135,7 +140,6 @@ inet_pton4(const char *src, unsigned char *dst)
return (1);
}
-#ifdef ENABLE_IPV6
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
@@ -234,6 +238,5 @@ inet_pton6(const char *src, unsigned char *dst)
memcpy(dst, tmp, IN6ADDRSZ);
return (1);
}
-#endif /* ENABLE_IPV6 */
#endif /* HAVE_INET_PTON */
diff --git a/Utilities/cmcurl/lib/inet_pton.h b/Utilities/cmcurl/lib/inet_pton.h
index 92ae93ea1f..82fde7e2eb 100644
--- a/Utilities/cmcurl/lib/inet_pton.h
+++ b/Utilities/cmcurl/lib/inet_pton.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c
index 08a682533d..a71779ab04 100644
--- a/Utilities/cmcurl/lib/krb5.c
+++ b/Utilities/cmcurl/lib/krb5.c
@@ -2,7 +2,7 @@
*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
- * Copyright (c) 2004 - 2022 Daniel Stenberg
+ * Copyright (C) Daniel Stenberg
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
@@ -46,6 +46,8 @@
#endif
#include "urldata.h"
+#include "cfilters.h"
+#include "cf-socket.h"
#include "curl_base64.h"
#include "ftp.h"
#include "curl_gssapi.h"
@@ -207,8 +209,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
gss_ctx_id_t *context = app_data;
struct gss_channel_bindings_struct chan;
size_t base64_sz = 0;
- struct sockaddr_in **remote_addr =
- (struct sockaddr_in **)&conn->ip_addr->ai_addr;
+ struct sockaddr_in *remote_addr =
+ (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr;
char *stringp;
if(getsockname(conn->sock[FIRSTSOCKET],
@@ -220,7 +222,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
chan.acceptor_addrtype = GSS_C_AF_INET;
chan.acceptor_address.length = l - 4;
- chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
+ chan.acceptor_address.value = &remote_addr->sin_addr.s_addr;
chan.application_data.length = 0;
chan.application_data.value = NULL;
@@ -454,15 +456,15 @@ static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
saying whether an error occurred or CURLE_OK if |len| was read. */
static CURLcode
-socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
+socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
{
char *to_p = to;
CURLcode result;
ssize_t nread = 0;
while(len > 0) {
- result = Curl_read_plain(data, fd, to_p, len, &nread);
- if(!result) {
+ nread = Curl_conn_recv(data, sockindex, to_p, len, &result);
+ if(nread > 0) {
len -= nread;
to_p += nread;
}
@@ -480,7 +482,7 @@ socket_read(struct Curl_easy *data, curl_socket_t fd, void *to, size_t len)
CURLcode saying whether an error occurred or CURLE_OK if |len| was
written. */
static CURLcode
-socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
+socket_write(struct Curl_easy *data, int sockindex, const void *to,
size_t len)
{
const char *to_p = to;
@@ -488,8 +490,8 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
ssize_t written;
while(len > 0) {
- result = Curl_write_plain(data, fd, to_p, len, &written);
- if(!result) {
+ written = Curl_conn_send(data, sockindex, to_p, len, &result);
+ if(written > 0) {
len -= written;
to_p += written;
}
@@ -502,7 +504,7 @@ socket_write(struct Curl_easy *data, curl_socket_t fd, const void *to,
return CURLE_OK;
}
-static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
+static CURLcode read_data(struct Curl_easy *data, int sockindex,
struct krb5buffer *buf)
{
struct connectdata *conn = data->conn;
@@ -510,7 +512,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
CURLcode result;
int nread;
- result = socket_read(data, fd, &len, sizeof(len));
+ result = socket_read(data, sockindex, &len, sizeof(len));
if(result)
return result;
@@ -525,7 +527,7 @@ static CURLcode read_data(struct Curl_easy *data, curl_socket_t fd,
if(!len || !buf->data)
return CURLE_OUT_OF_MEMORY;
- result = socket_read(data, fd, buf->data, len);
+ result = socket_read(data, sockindex, buf->data, len);
if(result)
return result;
nread = conn->mech->decode(conn->app_data, buf->data, len,
@@ -554,13 +556,12 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
size_t bytes_read;
size_t total_read = 0;
struct connectdata *conn = data->conn;
- curl_socket_t fd = conn->sock[sockindex];
*err = CURLE_OK;
/* Handle clear text response. */
if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
- return Curl_recv_plain(data, sockindex, buffer, len, err);
+ return Curl_conn_recv(data, sockindex, buffer, len, err);
if(conn->in_buffer.eof_flag) {
conn->in_buffer.eof_flag = 0;
@@ -573,7 +574,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
buffer += bytes_read;
while(len > 0) {
- if(read_data(data, fd, &conn->in_buffer))
+ if(read_data(data, sockindex, &conn->in_buffer))
return -1;
if(conn->in_buffer.size == 0) {
if(bytes_read > 0)
@@ -720,8 +721,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
return 0;
if(buf[3] != '-')
- /* safe to ignore return code */
- (void)sscanf(buf, "%d", &ret_code);
+ ret_code = atoi(buf);
if(buf[decoded_len - 1] == '\n')
buf[decoded_len - 1] = '\0';
@@ -764,8 +764,9 @@ static int sec_set_protection_level(struct Curl_easy *data)
pbsz = strstr(data->state.buffer, "PBSZ=");
if(pbsz) {
- /* ignore return code, use default value if it fails */
- (void)sscanf(pbsz, "PBSZ=%u", &buffer_size);
+ /* stick to default value if the check fails */
+ if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5]))
+ buffer_size = atoi(&pbsz[5]);
if(buffer_size < conn->buffer_size)
conn->buffer_size = buffer_size;
}
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index 92006d6936..595e4b3b3b 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -140,6 +140,14 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp);
#define ldap_err2string ldap_err2stringA
#endif
+#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600)
+/* Workaround for warning:
+ 'type cast' : conversion from 'int' to 'void *' of greater size */
+#undef LDAP_OPT_ON
+#undef LDAP_OPT_OFF
+#define LDAP_OPT_ON ((void *)(size_t)1)
+#define LDAP_OPT_OFF ((void *)(size_t)0)
+#endif
static CURLcode ldap_do(struct Curl_easy *data, bool *done);
diff --git a/Utilities/cmcurl/lib/libcurl.rc b/Utilities/cmcurl/lib/libcurl.rc
index 23134a7102..daa2d62d8a 100644
--- a/Utilities/cmcurl/lib/libcurl.rc
+++ b/Utilities/cmcurl/lib/libcurl.rc
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c
index fa2d366cb4..5b6b0336da 100644
--- a/Utilities/cmcurl/lib/llist.c
+++ b/Utilities/cmcurl/lib/llist.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h
index 2fcb91ca50..320580e33c 100644
--- a/Utilities/cmcurl/lib/llist.h
+++ b/Utilities/cmcurl/lib/llist.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index c13b080d69..318e9da6ae 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -86,11 +86,7 @@
#include "memdebug.h"
-#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
-
-#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
-
-#elif defined(USE_GNUTLS)
+#if defined(USE_GNUTLS)
typedef struct md4_ctx MD4_CTX;
@@ -109,6 +105,10 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
md4_digest(ctx, MD4_DIGEST_SIZE, result);
}
+#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+
#elif defined(AN_APPLE_OS)
typedef CC_MD4_CTX MD4_CTX;
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index 9610e0c778..f57ef391cc 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c
index 15fb491559..d6952a07a1 100644
--- a/Utilities/cmcurl/lib/memdebug.c
+++ b/Utilities/cmcurl/lib/memdebug.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/memdebug.h b/Utilities/cmcurl/lib/memdebug.h
index 7fc90e83a0..c9eb5dc37c 100644
--- a/Utilities/cmcurl/lib/memdebug.h
+++ b/Utilities/cmcurl/lib/memdebug.h
@@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index e3f2821dbe..83846c57dd 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h
index b9ea0f1011..04adf2d247 100644
--- a/Utilities/cmcurl/lib/mime.h
+++ b/Utilities/cmcurl/lib/mime.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index 8a7c17a7ff..5de935b1fe 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1999 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index 8ba826f4fb..47af369147 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2019, Björn Stenberg, <bjorn@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Björn Stenberg, <bjorn@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -122,8 +122,9 @@ static CURLcode mqtt_send(struct Curl_easy *data,
struct MQTT *mq = data->req.p.mqtt;
ssize_t n;
result = Curl_write(data, sockfd, buf, len, &n);
- if(!result)
- Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
+ if(result)
+ return result;
+ Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
if(len != (size_t)n) {
size_t nsend = len - n;
char *sendleftovers = Curl_memdup(&buf[n], nsend);
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h
index c400d9b14e..63961366fc 100644
--- a/Utilities/cmcurl/lib/mqtt.h
+++ b/Utilities/cmcurl/lib/mqtt.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Björn Stenberg, <bjorn@haxx.se>
+ * Copyright (C) Björn Stenberg, <bjorn@haxx.se>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index b96ee7c7ea..731b2598f1 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -445,9 +445,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
sockhash_destroy(&multi->sockhash);
Curl_hash_destroy(&multi->hostcache);
Curl_conncache_destroy(&multi->conn_cache);
- Curl_llist_destroy(&multi->msglist, NULL);
- Curl_llist_destroy(&multi->pending, NULL);
-
free(multi);
return NULL;
}
@@ -459,6 +456,42 @@ struct Curl_multi *curl_multi_init(void)
CURL_DNS_HASH_SIZE);
}
+static void link_easy(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ /* We add the new easy entry last in the list. */
+ data->next = NULL; /* end of the line */
+ if(multi->easyp) {
+ struct Curl_easy *last = multi->easylp;
+ last->next = data;
+ data->prev = last;
+ multi->easylp = data; /* the new last node */
+ }
+ else {
+ /* first node, make prev NULL! */
+ data->prev = NULL;
+ multi->easylp = multi->easyp = data; /* both first and last */
+ }
+}
+
+/* unlink the given easy handle from the linked list of easy handles */
+static void unlink_easy(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ /* make the previous node point to our next */
+ if(data->prev)
+ data->prev->next = data->next;
+ else
+ multi->easyp = data->next; /* point to first node */
+
+ /* make our next point to our previous node */
+ if(data->next)
+ data->next->prev = data->prev;
+ else
+ multi->easylp = data->prev; /* point to last node */
+}
+
+
CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
struct Curl_easy *data)
{
@@ -554,19 +587,7 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
data->psl = &multi->psl;
#endif
- /* We add the new entry last in the list. */
- data->next = NULL; /* end of the line */
- if(multi->easyp) {
- struct Curl_easy *last = multi->easylp;
- last->next = data;
- data->prev = last;
- multi->easylp = data; /* the new last node */
- }
- else {
- /* first node, make prev NULL! */
- data->prev = NULL;
- multi->easylp = multi->easyp = data; /* both first and last */
- }
+ link_easy(multi, data);
/* increase the node-counter */
multi->num_easy++;
@@ -655,6 +676,9 @@ static CURLcode multi_done(struct Curl_easy *data,
result = CURLE_ABORTED_BY_CALLBACK;
}
+ /* Inform connection filters that this transfer is done */
+ Curl_conn_ev_data_done(data, premature);
+
process_pending_handles(data->multi); /* connection / multiplex */
CONNCACHE_LOCK(data);
@@ -709,12 +733,12 @@ static CURLcode multi_done(struct Curl_easy *data,
conn->proxy_negotiate_state == GSS_AUTHRECV)
#endif
) || conn->bits.close
- || (premature && !(conn->handler->flags & PROTOPT_STREAM))) {
+ || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
- ", close=%d, premature=%d, stream=%d",
+ ", close=%d, premature=%d, conn_multiplex=%d",
conn->connection_id,
data->set.reuse_forbid, conn->bits.close, premature,
- (conn->handler->flags & PROTOPT_STREAM)));
+ Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
connclose(conn, "disconnecting");
Curl_conncache_remove_conn(data, conn, FALSE);
CONNCACHE_UNLOCK(data);
@@ -838,10 +862,6 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
Curl_wildcard_dtor(&data->wildcard);
- /* destroy the timeout list that is held in the easy handle, do this *after*
- multi_done() as that may actually call Curl_expire that uses this */
- Curl_llist_destroy(&data->state.timeoutlist, NULL);
-
/* change state without using multistate(), only to make singlesocket() do
what we want */
data->mstate = MSTATE_COMPLETED;
@@ -914,17 +934,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
}
}
- /* make the previous node point to our next */
- if(data->prev)
- data->prev->next = data->next;
- else
- multi->easyp = data->next; /* point to first node */
-
- /* make our next point to our previous node */
- if(data->next)
- data->next->prev = data->prev;
- else
- multi->easylp = data->prev; /* point to last node */
+ unlink_easy(multi, data);
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
@@ -954,7 +964,7 @@ void Curl_detach_connection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
if(conn) {
- Curl_conn_detach_data(conn, data);
+ Curl_conn_ev_data_detach(conn, data);
Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
}
data->conn = NULL;
@@ -973,9 +983,9 @@ void Curl_attach_connection(struct Curl_easy *data,
data->conn = conn;
Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
&data->conn_queue);
- Curl_conn_attach_data(conn, data);
- if(conn->handler->attach)
+ if(conn->handler && conn->handler->attach)
conn->handler->attach(data, conn);
+ Curl_conn_ev_data_attach(conn, data);
}
static int domore_getsock(struct Curl_easy *data,
@@ -1002,11 +1012,7 @@ static int protocol_getsock(struct Curl_easy *data,
{
if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(data, conn, socks);
- /* Backup getsock logic. Since there is a live socket in use, we must wait
- for it or it will be removed from watching when the multi_socket API is
- used. */
- socks[0] = conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
+ return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
}
/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
@@ -1111,6 +1117,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
return CURLM_OK;
}
+#ifdef USE_WINSOCK
+/* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't
+ * be reset this way because an empty datagram would be sent. #9203
+ *
+ * "On Windows the internal state of FD_WRITE as returned from
+ * WSAEnumNetworkEvents is only reset after successful send()."
+ */
+static void reset_socket_fdwrite(curl_socket_t s)
+{
+ int t;
+ int l = (int)sizeof(t);
+ if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM)
+ send(s, NULL, 0, 0);
+}
+#endif
+
#define NUM_POLLS_ON_STACK 10
static CURLMcode multi_wait(struct Curl_multi *multi,
@@ -1232,7 +1254,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
s = sockbunch[i];
#ifdef USE_WINSOCK
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- send(s, NULL, 0, 0); /* reset FD_WRITE */
+ reset_socket_fdwrite(s);
#endif
ufds[nfds].fd = s;
ufds[nfds].events = POLLOUT;
@@ -1266,7 +1288,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
mask |= FD_OOB;
if(extra_fds[i].events & CURL_WAIT_POLLOUT) {
mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
- send(extra_fds[i].fd, NULL, 0, 0); /* reset FD_WRITE */
+ reset_socket_fdwrite(extra_fds[i].fd);
}
if(WSAEventSelect(extra_fds[i].fd, multi->wsa_event, mask) != 0) {
if(ufds_malloc)
@@ -1862,6 +1884,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
multistate(data, MSTATE_COMPLETED);
}
+#ifdef DEBUGBUILD
+ if(!multi->warned) {
+ infof(data, "!!! WARNING !!!");
+ infof(data, "This is a debug build of libcurl, "
+ "do not use in production.");
+ multi->warned = true;
+ }
+#endif
+
do {
/* A "stream" here is a logical stream if the protocol can handle that
(HTTP/2), or the full connection for older protocols */
@@ -2168,7 +2199,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
#ifndef CURL_DISABLE_FTP
/* some steps needed for wildcard matching */
if(data->state.wildcardmatch) {
- struct WildcardData *wc = &data->wildcard;
+ struct WildcardData *wc = data->wildcard;
if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) {
/* skip some states if it is important */
multi_done(data, CURLE_OK, FALSE);
@@ -2320,7 +2351,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch &&
((data->conn->handler->flags & PROTOPT_WILDCARD) == 0)) {
- data->wildcard.state = CURLWC_DONE;
+ data->wildcard->state = CURLWC_DONE;
}
#endif
multistate(data, MSTATE_DONE);
@@ -2550,7 +2581,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
#ifndef CURL_DISABLE_FTP
if(data->state.wildcardmatch) {
- if(data->wildcard.state != CURLWC_DONE) {
+ if(data->wildcard->state != CURLWC_DONE) {
/* if a wildcard is set and we are not ending -> lets start again
with MSTATE_INIT */
multistate(data, MSTATE_INIT);
@@ -2682,18 +2713,25 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
return CURLM_RECURSIVE_API_CALL;
data = multi->easyp;
- while(data) {
+ if(data) {
CURLMcode result;
+ bool nosig = data->set.no_signal;
SIGPIPE_VARIABLE(pipe_st);
-
sigpipe_ignore(data, &pipe_st);
- result = multi_runsingle(multi, &now, data);
+ /* Do the loop and only alter the signal ignore state if the next handle
+ has a different NO_SIGNAL state than the previous */
+ do {
+ if(data->set.no_signal != nosig) {
+ sigpipe_restore(&pipe_st);
+ sigpipe_ignore(data, &pipe_st);
+ nosig = data->set.no_signal;
+ }
+ result = multi_runsingle(multi, &now, data);
+ if(result)
+ returncode = result;
+ data = data->next; /* operate on next handle */
+ } while(data);
sigpipe_restore(&pipe_st);
-
- if(result)
- returncode = result;
-
- data = data->next; /* operate on next handle */
}
/*
@@ -2764,9 +2802,6 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
sockhash_destroy(&multi->sockhash);
Curl_conncache_destroy(&multi->conn_cache);
- Curl_llist_destroy(&multi->msglist, NULL);
- Curl_llist_destroy(&multi->pending, NULL);
-
Curl_hash_destroy(&multi->hostcache);
Curl_psl_destroy(&multi->psl);
@@ -3248,7 +3283,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
multi->push_userp = va_arg(param, void *);
break;
case CURLMOPT_PIPELINING:
- multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX;
+ multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0;
break;
case CURLMOPT_TIMERFUNCTION:
multi->timer_cb = va_arg(param, curl_multi_timer_callback);
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index 5a83656d51..6cda65d442 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -162,14 +162,17 @@ struct Curl_multi {
#define IPV6_DEAD 1
#define IPV6_WORKS 2
unsigned char ipv6_up; /* IPV6_* defined */
- bool multiplexing; /* multiplexing wanted */
- bool recheckstate; /* see Curl_multi_connchanged */
- bool in_callback; /* true while executing a callback */
+ BIT(multiplexing); /* multiplexing wanted */
+ BIT(recheckstate); /* see Curl_multi_connchanged */
+ BIT(in_callback); /* true while executing a callback */
#ifdef USE_OPENSSL
- bool ssl_seeded;
+ BIT(ssl_seeded);
#endif
- bool dead; /* a callback returned error, everything needs to crash and
+ BIT(dead); /* a callback returned error, everything needs to crash and
burn */
+#ifdef DEBUGBUILD
+ BIT(warned); /* true after user warned of DEBUGBUILD */
+#endif
};
#endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h
index 0cb9d4f7f2..cae02cb08c 100644
--- a/Utilities/cmcurl/lib/multiif.h
+++ b/Utilities/cmcurl/lib/multiif.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 4461b8492f..aa1b80ab2f 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h
index 53d0056721..9f2815f3bb 100644
--- a/Utilities/cmcurl/lib/netrc.h
+++ b/Utilities/cmcurl/lib/netrc.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/nonblock.c b/Utilities/cmcurl/lib/nonblock.c
index 8447b6f423..f4eb656128 100644
--- a/Utilities/cmcurl/lib/nonblock.c
+++ b/Utilities/cmcurl/lib/nonblock.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/nonblock.h b/Utilities/cmcurl/lib/nonblock.h
index a42f443a49..4a1a6151f2 100644
--- a/Utilities/cmcurl/lib/nonblock.h
+++ b/Utilities/cmcurl/lib/nonblock.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c
index 9b13fe8956..f1c1ed2c63 100644
--- a/Utilities/cmcurl/lib/noproxy.c
+++ b/Utilities/cmcurl/lib/noproxy.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -119,8 +119,10 @@ enum nametype {
* Checks if the host is in the noproxy list. returns TRUE if it matches and
* therefore the proxy should NOT be used.
****************************************************************/
-bool Curl_check_noproxy(const char *name, const char *no_proxy)
+bool Curl_check_noproxy(const char *name, const char *no_proxy,
+ bool *spacesep)
{
+ *spacesep = FALSE;
/*
* If we don't have a hostname at all, like for example with a FILE
* transfer, we have nothing to interrogate the noproxy list with.
@@ -244,6 +246,15 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
if(match)
return TRUE;
} /* if(tokenlen) */
+ /* pass blanks after pattern */
+ while(ISBLANK(*p))
+ p++;
+ /* if not a comma! */
+ if(*p && (*p != ',')) {
+ *spacesep = TRUE;
+ continue;
+ }
+ /* pass any number of commas */
while(*p == ',')
p++;
} /* while(*p) */
diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h
index 8800a21276..a3a6807722 100644
--- a/Utilities/cmcurl/lib/noproxy.h
+++ b/Utilities/cmcurl/lib/noproxy.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,7 +37,8 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6,
unsigned int bits);
#endif
-bool Curl_check_noproxy(const char *name, const char *no_proxy);
+bool Curl_check_noproxy(const char *name, const char *no_proxy,
+ bool *spacesep);
#endif
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index ab81e57c26..b9feeda058 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2011 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Howard Chu, <hyc@openldap.org>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Howard Chu, <hyc@openldap.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c
index 5ed88195fb..1662dd36b8 100644
--- a/Utilities/cmcurl/lib/parsedate.c
+++ b/Utilities/cmcurl/lib/parsedate.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -212,56 +212,55 @@ static int checkday(const char *check, size_t len)
{
int i;
const char * const *what;
- bool found = FALSE;
if(len > 3)
what = &weekday[0];
- else
+ else if(len == 3)
what = &Curl_wkday[0];
+ else
+ return -1; /* too short */
for(i = 0; i<7; i++) {
- if(strcasecompare(check, what[0])) {
- found = TRUE;
- break;
- }
+ size_t ilen = strlen(what[0]);
+ if((ilen == len) &&
+ strncasecompare(check, what[0], len))
+ return i;
what++;
}
- return found?i:-1;
+ return -1;
}
-static int checkmonth(const char *check)
+static int checkmonth(const char *check, size_t len)
{
int i;
- const char * const *what;
- bool found = FALSE;
+ const char * const *what = &Curl_month[0];
+ if(len != 3)
+ return -1; /* not a month */
- what = &Curl_month[0];
for(i = 0; i<12; i++) {
- if(strcasecompare(check, what[0])) {
- found = TRUE;
- break;
- }
+ if(strncasecompare(check, what[0], 3))
+ return i;
what++;
}
- return found?i:-1; /* return the offset or -1, no real offset is -1 */
+ return -1; /* return the offset or -1, no real offset is -1 */
}
/* return the time zone offset between GMT and the input one, in number
of seconds or -1 if the timezone wasn't found/legal */
-static int checktz(const char *check)
+static int checktz(const char *check, size_t len)
{
unsigned int i;
- const struct tzinfo *what;
- bool found = FALSE;
+ const struct tzinfo *what = tz;
+ if(len > 4) /* longer than any valid timezone */
+ return -1;
- what = tz;
for(i = 0; i< sizeof(tz)/sizeof(tz[0]); i++) {
- if(strcasecompare(check, what->name)) {
- found = TRUE;
- break;
- }
+ size_t ilen = strlen(what->name);
+ if((ilen == len) &&
+ strncasecompare(check, what->name, len))
+ return what->offset*60;
what++;
}
- return found?what->offset*60:-1;
+ return -1;
}
static void skip(const char **date)
@@ -294,6 +293,53 @@ static time_t time2epoch(int sec, int min, int hour,
+ hour) * 60 + min) * 60 + sec;
}
+/* Returns the value of a single-digit or two-digit decimal number, return
+ then pointer to after the number. The 'date' pointer is known to point to a
+ digit. */
+static int oneortwodigit(const char *date, const char **endp)
+{
+ int num = date[0] - '0';
+ if(ISDIGIT(date[1])) {
+ *endp = &date[2];
+ return num*10 + (date[1] - '0');
+ }
+ *endp = &date[1];
+ return num;
+}
+
+
+/* HH:MM:SS or HH:MM and accept single-digits too */
+static bool match_time(const char *date,
+ int *h, int *m, int *s, char **endp)
+{
+ const char *p;
+ int hh, mm, ss = 0;
+ hh = oneortwodigit(date, &p);
+ if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
+ mm = oneortwodigit(&p[1], &p);
+ if(mm < 60) {
+ if((*p == ':') && ISDIGIT(p[1])) {
+ ss = oneortwodigit(&p[1], &p);
+ if(ss <= 60) {
+ /* valid HH:MM:SS */
+ goto match;
+ }
+ }
+ else {
+ /* valid HH:MM */
+ goto match;
+ }
+ }
+ }
+ return FALSE; /* not a time string */
+ match:
+ *h = hh;
+ *m = mm;
+ *s = ss;
+ *endp = (char *)p;
+ return TRUE;
+}
+
/*
* parsedate()
*
@@ -305,6 +351,9 @@ static time_t time2epoch(int sec, int min, int hour,
* PARSEDATE_SOONER - time underflow at the low end of time_t
*/
+/* Wednesday is the longest name this parser knows about */
+#define NAME_LEN 12
+
static int parsedate(const char *date, time_t *output)
{
time_t t = 0;
@@ -327,32 +376,32 @@ static int parsedate(const char *date, time_t *output)
if(ISALPHA(*date)) {
/* a name coming up */
- char buf[32]="";
- size_t len;
- if(sscanf(date, "%31[ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz]", buf))
- len = strlen(buf);
- else
- len = 0;
-
- if(wdaynum == -1) {
- wdaynum = checkday(buf, len);
- if(wdaynum != -1)
- found = TRUE;
- }
- if(!found && (monnum == -1)) {
- monnum = checkmonth(buf);
- if(monnum != -1)
- found = TRUE;
+ size_t len = 0;
+ const char *p = date;
+ while(ISALPHA(*p) && (len < NAME_LEN)) {
+ p++;
+ len++;
}
- if(!found && (tzoff == -1)) {
- /* this just must be a time zone string */
- tzoff = checktz(buf);
- if(tzoff != -1)
- found = TRUE;
- }
+ if(len != NAME_LEN) {
+ if(wdaynum == -1) {
+ wdaynum = checkday(date, len);
+ if(wdaynum != -1)
+ found = TRUE;
+ }
+ if(!found && (monnum == -1)) {
+ monnum = checkmonth(date, len);
+ if(monnum != -1)
+ found = TRUE;
+ }
+ if(!found && (tzoff == -1)) {
+ /* this just must be a time zone string */
+ tzoff = checktz(date, len);
+ if(tzoff != -1)
+ found = TRUE;
+ }
+ }
if(!found)
return PARSEDATE_FAIL; /* bad string */
@@ -362,18 +411,10 @@ static int parsedate(const char *date, time_t *output)
/* a digit */
int val;
char *end;
- int len = 0;
if((secnum == -1) &&
- (3 == sscanf(date, "%02d:%02d:%02d%n",
- &hournum, &minnum, &secnum, &len))) {
- /* time stamp! */
- date += len;
- }
- else if((secnum == -1) &&
- (2 == sscanf(date, "%02d:%02d%n", &hournum, &minnum, &len))) {
- /* time stamp without seconds */
- date += len;
- secnum = 0;
+ match_time(date, &hournum, &minnum, &secnum, &end)) {
+ /* time stamp */
+ date = end;
}
else {
long lval;
diff --git a/Utilities/cmcurl/lib/parsedate.h b/Utilities/cmcurl/lib/parsedate.h
index 4e4347754d..84c37f1675 100644
--- a/Utilities/cmcurl/lib/parsedate.h
+++ b/Utilities/cmcurl/lib/parsedate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 9b9558048a..2f4aa1c346 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h
index cefae073a6..80d3f7718c 100644
--- a/Utilities/cmcurl/lib/pingpong.h
+++ b/Utilities/cmcurl/lib/pingpong.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index ce17f2ac72..36707e5352 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -370,16 +370,18 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
/* Start the SSL connection */
struct pop3_conn *pop3c = &conn->proto.pop3c;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ pop3c->ssldone = ssldone;
if(pop3c->state != POP3_UPGRADETLS)
state(data, POP3_UPGRADETLS);
@@ -769,7 +771,7 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
if(pop3code != '+')
pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
- if(!data->set.use_ssl || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ if(!data->set.use_ssl || Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = pop3_perform_authentication(data, conn);
else if(pop3code == '+' && pop3c->tls_supported)
/* Switch to TLS connection now */
@@ -1056,7 +1058,9 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
struct pop3_conn *pop3c = &conn->proto.pop3c;
if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &pop3c->ssldone);
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ pop3c->ssldone = ssldone;
if(result || !pop3c->ssldone)
return result;
}
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h
index bb0645f9bf..83f0f831e6 100644
--- a/Utilities/cmcurl/lib/pop3.h
+++ b/Utilities/cmcurl/lib/pop3.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -62,16 +62,16 @@ struct POP3 {
struct pop3_conn {
struct pingpong pp;
pop3state state; /* Always use pop3.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
- bool tls_supported; /* StartTLS capability supported by server */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
size_t strip; /* Number of bytes from the start to ignore as
non-body */
struct SASL sasl; /* SASL-related storage */
- unsigned int authtypes; /* Accepted authentication types */
- unsigned int preftype; /* Preferred authentication type */
char *apoptimestamp; /* APOP timestamp from the server greeting */
+ unsigned char authtypes; /* Accepted authentication types */
+ unsigned char preftype; /* Preferred authentication type */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
};
extern const struct Curl_handler Curl_handler_pop3;
@@ -84,7 +84,7 @@ extern const struct Curl_handler Curl_handler_pop3s;
/* Authentication type values */
#define POP3_TYPE_NONE 0
-#define POP3_TYPE_ANY ~0U
+#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
/* This is the 5-bytes End-Of-Body marker for POP3 */
#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index 4a1e1daa81..6092b782c7 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -87,8 +87,6 @@ static char *max5data(curl_off_t bytes, char *max5)
CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE,
(bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) );
-#if (SIZEOF_CURL_OFF_T > 4)
-
else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
/* 'XXXXM' is good until we're at 10000MB or above */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
@@ -111,15 +109,8 @@ static char *max5data(curl_off_t bytes, char *max5)
/* up to 10000PB, display without decimal: XXXXP */
msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "P", bytes/ONE_PETABYTE);
- /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number
- can hold, but our data type is signed so 8192PB will be the maximum. */
-
-#else
-
- else
- msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
-
-#endif
+ /* 16384 petabytes (16 exabytes) is the maximum a 64 bit unsigned number can
+ hold, but our data type is signed so 8192PB will be the maximum. */
return max5;
}
@@ -166,14 +157,11 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data)
/*
*
- * Curl_pgrsTime(). Store the current time at the given label. This fetches a
- * fresh "now" and returns it.
- *
- * @unittest: 1399
+ * Curl_pgrsTimeWas(). Store the timestamp time at the given label.
*/
-struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
+ struct curltime timestamp)
{
- struct curltime now = Curl_now();
timediff_t *delta = NULL;
switch(timer) {
@@ -183,15 +171,15 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
break;
case TIMER_STARTOP:
/* This is set at the start of a transfer */
- data->progress.t_startop = now;
+ data->progress.t_startop = timestamp;
break;
case TIMER_STARTSINGLE:
/* This is set at the start of each single fetch */
- data->progress.t_startsingle = now;
+ data->progress.t_startsingle = timestamp;
data->progress.is_t_startransfer_set = false;
break;
case TIMER_STARTACCEPT:
- data->progress.t_acceptdata = now;
+ data->progress.t_acceptdata = timestamp;
break;
case TIMER_NAMELOOKUP:
delta = &data->progress.t_nslookup;
@@ -214,7 +202,7 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
* changing the t_starttransfer time.
*/
if(data->progress.is_t_startransfer_set) {
- return now;
+ return;
}
else {
data->progress.is_t_startransfer_set = true;
@@ -224,15 +212,30 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
/* this is the normal end-of-transfer thing */
break;
case TIMER_REDIRECT:
- data->progress.t_redirect = Curl_timediff_us(now, data->progress.start);
+ data->progress.t_redirect = Curl_timediff_us(timestamp,
+ data->progress.start);
break;
}
if(delta) {
- timediff_t us = Curl_timediff_us(now, data->progress.t_startsingle);
+ timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle);
if(us < 1)
us = 1; /* make sure at least one microsecond passed */
*delta += us;
}
+}
+
+/*
+ *
+ * Curl_pgrsTime(). Store the current time at the given label. This fetches a
+ * fresh "now" and returns it.
+ *
+ * @unittest: 1399
+ */
+struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
+{
+ struct curltime now = Curl_now();
+
+ Curl_pgrsTimeWas(data, timer, now);
return now;
}
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h
index a129315147..0049cd04be 100644
--- a/Utilities/cmcurl/lib/progress.h
+++ b/Utilities/cmcurl/lib/progress.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -57,6 +57,13 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
curl_off_t limit,
struct curltime start,
struct curltime now);
+/**
+ * Update progress timer with the elapsed time from its start to `timestamp`.
+ * This allows updating timers later and is used by happy eyeballing, where
+ * we only want to record the winner's times.
+ */
+void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
+ struct curltime timestamp);
#define PGRS_HIDE (1<<4)
#define PGRS_UL_SIZE_KNOWN (1<<5)
diff --git a/Utilities/cmcurl/lib/psl.c b/Utilities/cmcurl/lib/psl.c
index 60c98a4ca4..626a203a6d 100644
--- a/Utilities/cmcurl/lib/psl.c
+++ b/Utilities/cmcurl/lib/psl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/psl.h b/Utilities/cmcurl/lib/psl.h
index 34f0a5cae8..23cfa921c4 100644
--- a/Utilities/cmcurl/lib/psl.h
+++ b/Utilities/cmcurl/lib/psl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/quic.h b/Utilities/cmcurl/lib/quic.h
deleted file mode 100644
index b35774735e..0000000000
--- a/Utilities/cmcurl/lib/quic.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef HEADER_CURL_QUIC_H
-#define HEADER_CURL_QUIC_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef ENABLE_QUIC
-#ifdef USE_NGTCP2
-#include "vquic/ngtcp2.h"
-#endif
-#ifdef USE_QUICHE
-#include "vquic/quiche.h"
-#endif
-#ifdef USE_MSH3
-#include "vquic/msh3.h"
-#endif
-
-#include "urldata.h"
-
-/* functions provided by the specific backends */
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen);
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected);
-void Curl_quic_ver(char *p, size_t len);
-CURLcode Curl_quic_done_sending(struct Curl_easy *data);
-void Curl_quic_done(struct Curl_easy *data, bool premature);
-bool Curl_quic_data_pending(const struct Curl_easy *data);
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn, int tempindex);
-CURLcode Curl_quic_idle(struct Curl_easy *data);
-
-#else /* ENABLE_QUIC */
-#define Curl_quic_done_sending(x)
-#define Curl_quic_done(x,y)
-#define Curl_quic_data_pending(x)
-#define Curl_quic_disconnect(x,y,z)
-#endif /* !ENABLE_QUIC */
-
-#endif /* HEADER_CURL_QUIC_H */
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index a549624a86..9abb722d2b 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,6 +30,10 @@
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
+#ifdef HAVE_ARC4RANDOM
+/* Some platforms might have the prototype missing (ubuntu + libressl) */
+uint32_t arc4random(void);
+#endif
#include <curl/curl.h>
#include "vtls/vtls.h"
@@ -143,6 +147,11 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
}
#endif
+#ifdef HAVE_ARC4RANDOM
+ *rnd = (unsigned int)arc4random();
+ return CURLE_OK;
+#endif
+
#if defined(RANDOM_FILE) && !defined(WIN32)
if(!seeded) {
/* if there's a random file to read a seed from, use it */
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index 30fc29615a..cbe05677a1 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rename.c b/Utilities/cmcurl/lib/rename.c
index cfb3699fb7..97a66e947e 100644
--- a/Utilities/cmcurl/lib/rename.c
+++ b/Utilities/cmcurl/lib/rename.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rename.h b/Utilities/cmcurl/lib/rename.h
index 9958e2cd23..04440820c5 100644
--- a/Utilities/cmcurl/lib/rename.h
+++ b/Utilities/cmcurl/lib/rename.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index 75e620d118..aef3560a9a 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -38,6 +38,7 @@
#include "strcase.h"
#include "select.h"
#include "connect.h"
+#include "cfilters.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -134,36 +135,6 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data,
/*
- * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
- * want to block the application forever while receiving a stream. Therefore,
- * we cannot assume that an RTSP socket is dead just because it is readable.
- *
- * Instead, if it is readable, run Curl_connalive() to peek at the socket
- * and distinguish between closed and data.
- */
-static bool rtsp_connisdead(struct Curl_easy *data, struct connectdata *check)
-{
- int sval;
- bool ret_val = TRUE;
-
- sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
- if(sval == 0) {
- /* timeout */
- ret_val = FALSE;
- }
- else if(sval & CURL_CSELECT_ERR) {
- /* socket is in an error state */
- ret_val = TRUE;
- }
- else if(sval & CURL_CSELECT_IN) {
- /* readable with no error. could still be closed */
- ret_val = !Curl_connalive(data, check);
- }
-
- return ret_val;
-}
-
-/*
* Function to check on various aspects of a connection.
*/
static unsigned int rtsp_conncheck(struct Curl_easy *data,
@@ -174,7 +145,8 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data,
(void)data;
if(checks_to_perform & CONNCHECK_ISDEAD) {
- if(rtsp_connisdead(data, conn))
+ bool input_pending;
+ if(!Curl_conn_is_alive(data, conn, &input_pending))
ret_val |= CONNRESULT_DEAD;
}
@@ -592,7 +564,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
}
/* issue the request */
- result = Curl_buffer_send(&req_buffer, data,
+ result = Curl_buffer_send(&req_buffer, data, data->req.p.http,
&data->info.request_size, 0, FIRSTSOCKET);
if(result) {
failf(data, "Failed sending RTSP request");
@@ -784,12 +756,14 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
{
- long CSeq = 0;
-
if(checkprefix("CSeq:", header)) {
- /* Store the received CSeq. Match is verified in rtsp_done */
- int nc = sscanf(&header[4], ": %ld", &CSeq);
- if(nc == 1) {
+ long CSeq = 0;
+ char *endp;
+ char *p = &header[5];
+ while(ISBLANK(*p))
+ p++;
+ CSeq = strtol(p, &endp, 10);
+ if(p != endp) {
struct RTSP *rtsp = data->req.p.rtsp;
rtsp->CSeq_recv = CSeq; /* mark the request */
data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h
index fa6606ac92..6e55616b38 100644
--- a/Utilities/cmcurl/lib/rtsp.h
+++ b/Utilities/cmcurl/lib/rtsp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c
index 2ac0746772..61cce619b4 100644
--- a/Utilities/cmcurl/lib/select.c
+++ b/Utilities/cmcurl/lib/select.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -230,14 +230,14 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
if(readfd0 != CURL_SOCKET_BAD) {
if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
r |= CURL_CSELECT_IN;
- if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+ if(pfd[num].revents & (POLLPRI|POLLNVAL))
r |= CURL_CSELECT_ERR;
num++;
}
if(readfd1 != CURL_SOCKET_BAD) {
if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
r |= CURL_CSELECT_IN2;
- if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
+ if(pfd[num].revents & (POLLPRI|POLLNVAL))
r |= CURL_CSELECT_ERR;
num++;
}
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h
index f2cf8bbd9f..5b1ca23eb1 100644
--- a/Utilities/cmcurl/lib/select.h
+++ b/Utilities/cmcurl/lib/select.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index 63262409bb..2b08271687 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -138,149 +138,6 @@ static size_t convert_lineends(struct Curl_easy *data,
}
#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
- struct postponed_data * const psnd = &(conn->postponed[sockindex]);
- return psnd->buffer && psnd->allocated_size &&
- psnd->recv_size > psnd->recv_processed;
-}
-
-static CURLcode pre_receive_plain(struct Curl_easy *data,
- struct connectdata *conn, int num)
-{
- const curl_socket_t sockfd = conn->sock[num];
- struct postponed_data * const psnd = &(conn->postponed[num]);
- size_t bytestorecv = psnd->allocated_size - psnd->recv_size;
- ssize_t recvedbytes;
-
- /* WinSock will destroy unread received data if send() is
- failed.
- To avoid lossage of received data, recv() must be
- performed before every send() if any incoming data is
- available. However, skip this, if buffer is already full. */
- if((conn->handler->protocol&PROTO_FAMILY_HTTP) != 0 &&
- conn->recv[num] == Curl_conn_recv &&
- (!psnd->buffer || bytestorecv)) {
- const int readymask = Curl_socket_check(sockfd, CURL_SOCKET_BAD,
- CURL_SOCKET_BAD, 0);
- if(readymask != -1 && (readymask & CURL_CSELECT_IN) != 0) {
- /* Have some incoming data */
- if(!psnd->buffer) {
- /* Use buffer double default size for intermediate buffer */
- psnd->allocated_size = 2 * data->set.buffer_size;
- psnd->buffer = malloc(psnd->allocated_size);
- if(!psnd->buffer)
- return CURLE_OUT_OF_MEMORY;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = sockfd; /* Used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
- bytestorecv = psnd->allocated_size;
- }
-
- DEBUGASSERT(psnd->bindsock == sockfd);
- recvedbytes = sread(sockfd, psnd->buffer + psnd->recv_size,
- bytestorecv);
- if(recvedbytes > 0)
- psnd->recv_size += recvedbytes;
- }
- }
- return CURLE_OK;
-}
-
-static ssize_t get_pre_recved(struct connectdata *conn, int num, char *buf,
- size_t len)
-{
- struct postponed_data * const psnd = &(conn->postponed[num]);
- size_t copysize;
- if(!psnd->buffer)
- return 0;
-
- DEBUGASSERT(psnd->allocated_size > 0);
- DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
- DEBUGASSERT(psnd->recv_processed <= psnd->recv_size);
- /* Check and process data that already received and storied in internal
- intermediate buffer */
- if(psnd->recv_size > psnd->recv_processed) {
- DEBUGASSERT(psnd->bindsock == conn->sock[num]);
- copysize = CURLMIN(len, psnd->recv_size - psnd->recv_processed);
- memcpy(buf, psnd->buffer + psnd->recv_processed, copysize);
- psnd->recv_processed += copysize;
- }
- else
- copysize = 0; /* buffer was allocated, but nothing was received */
-
- /* Free intermediate buffer if it has no unprocessed data */
- if(psnd->recv_processed == psnd->recv_size) {
- free(psnd->buffer);
- psnd->buffer = NULL;
- psnd->allocated_size = 0;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = CURL_SOCKET_BAD;
-#endif /* DEBUGBUILD */
- }
- return (ssize_t)copysize;
-}
-#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macros instead of functions when workaround not used */
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
-{
- (void)conn;
- (void)sockindex;
- return false;
-}
-#define pre_receive_plain(d,c,n) CURLE_OK
-#define get_pre_recved(c,n,b,l) 0
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
-/* Curl_infof() is for info message along the way */
-#define MAXINFO 2048
-
-void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
-{
- DEBUGASSERT(!strchr(fmt, '\n'));
- if(data && data->set.verbose) {
- va_list ap;
- int len;
- char buffer[MAXINFO + 2];
- va_start(ap, fmt);
- len = mvsnprintf(buffer, MAXINFO, fmt, ap);
- va_end(ap);
- buffer[len++] = '\n';
- buffer[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, buffer, len);
- }
-}
-
-/* Curl_failf() is for messages stating why we failed.
- * The message SHALL NOT include any LF or CR.
- */
-
-void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
-{
- DEBUGASSERT(!strchr(fmt, '\n'));
- if(data->set.verbose || data->set.errorbuffer) {
- va_list ap;
- int len;
- char error[CURL_ERROR_SIZE + 2];
- va_start(ap, fmt);
- len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
-
- if(data->set.errorbuffer && !data->state.errorbuf) {
- strcpy(data->set.errorbuffer, error);
- data->state.errorbuf = TRUE; /* wrote error string */
- }
- error[len++] = '\n';
- error[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, error, len);
- va_end(ap);
- }
-}
-
/*
* Curl_write() is an internal write function that sends data to the
* server. Works with plain sockets, SCP, SSL or kerberos.
@@ -301,7 +158,7 @@ CURLcode Curl_write(struct Curl_easy *data,
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
conn = data->conn;
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
+ num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
#ifdef CURLDEBUG
{
@@ -338,153 +195,6 @@ CURLcode Curl_write(struct Curl_easy *data,
}
}
-/* Curl_send_plain sends raw data without a size restriction on 'len'. */
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
- const void *mem, size_t len, CURLcode *code)
-{
- struct connectdata *conn;
- curl_socket_t sockfd;
- ssize_t bytes_written;
-
- DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- conn = data->conn;
- sockfd = conn->sock[num];
- /* WinSock will destroy unread received data if send() is
- failed.
- To avoid lossage of received data, recv() must be
- performed before every send() if any incoming data is
- available. */
- if(pre_receive_plain(data, conn, num)) {
- *code = CURLE_OUT_OF_MEMORY;
- return -1;
- }
-
-#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
- if(conn->bits.tcp_fastopen) {
- bytes_written = sendto(sockfd, mem, len, MSG_FASTOPEN,
- conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen);
- conn->bits.tcp_fastopen = FALSE;
- }
- else
-#endif
- bytes_written = swrite(sockfd, mem, len);
-
- *code = CURLE_OK;
- if(-1 == bytes_written) {
- int err = SOCKERRNO;
-
- if(
-#ifdef WSAEWOULDBLOCK
- /* This is how Windows does it */
- (WSAEWOULDBLOCK == err)
-#else
- /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
- due to its inability to send off data without blocking. We therefore
- treat both error codes the same here */
- (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err) ||
- (EINPROGRESS == err)
-#endif
- ) {
- /* this is just a case of EWOULDBLOCK */
- *code = CURLE_AGAIN;
- }
- else {
- char buffer[STRERROR_LEN];
- failf(data, "Send failure: %s",
- Curl_strerror(err, buffer, sizeof(buffer)));
- data->state.os_errno = err;
- *code = CURLE_SEND_ERROR;
- }
- }
- return bytes_written;
-}
-
-/*
- * Curl_write_plain() is an internal write function that sends data to the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_write().
- *
- * This function wraps Curl_send_plain(). The only difference besides the
- * prototype is '*written' (bytes written) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'len' must not be changed.
- */
-CURLcode Curl_write_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- const void *mem,
- size_t len,
- ssize_t *written)
-{
- CURLcode result;
- struct connectdata *conn = data->conn;
- int num;
- DEBUGASSERT(conn);
- DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
- sockfd == conn->sock[SECONDARYSOCKET]);
- if(sockfd != conn->sock[FIRSTSOCKET] &&
- sockfd != conn->sock[SECONDARYSOCKET])
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
- *written = Curl_send_plain(data, num, mem, len, &result);
- if(*written == -1)
- *written = 0;
-
- return result;
-}
-
-/* Curl_recv_plain receives raw data without a size restriction on 'len'. */
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
- size_t len, CURLcode *code)
-{
- struct connectdata *conn;
- curl_socket_t sockfd;
- ssize_t nread;
- DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
- conn = data->conn;
- sockfd = conn->sock[num];
- /* Check and return data that already received and storied in internal
- intermediate buffer */
- nread = get_pre_recved(conn, num, buf, len);
- if(nread > 0) {
- *code = CURLE_OK;
- return nread;
- }
-
- nread = sread(sockfd, buf, len);
-
- *code = CURLE_OK;
- if(-1 == nread) {
- int err = SOCKERRNO;
-
- if(
-#ifdef WSAEWOULDBLOCK
- /* This is how Windows does it */
- (WSAEWOULDBLOCK == err)
-#else
- /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
- due to its inability to send off data without blocking. We therefore
- treat both error codes the same here */
- (EWOULDBLOCK == err) || (EAGAIN == err) || (EINTR == err)
-#endif
- ) {
- /* this is just a case of EWOULDBLOCK */
- *code = CURLE_AGAIN;
- }
- else {
- char buffer[STRERROR_LEN];
- failf(data, "Recv failure: %s",
- Curl_strerror(err, buffer, sizeof(buffer)));
- data->state.os_errno = err;
- *code = CURLE_RECV_ERROR;
- }
- }
- return nread;
-}
-
static CURLcode pausewrite(struct Curl_easy *data,
int type, /* what type of data */
const char *ptr,
@@ -498,8 +208,7 @@ static CURLcode pausewrite(struct Curl_easy *data,
unsigned int i;
bool newtype = TRUE;
- /* If this transfers over HTTP/2, pause the stream! */
- Curl_http2_stream_pause(data, TRUE);
+ Curl_conn_ev_data_pause(data, TRUE);
if(s->tempcount) {
for(i = 0; i< s->tempcount; i++) {
@@ -678,41 +387,6 @@ CURLcode Curl_client_write(struct Curl_easy *data,
}
/*
- * Curl_read_plain() is an internal read function that reads data from the
- * server using plain sockets only. Otherwise meant to have the exact same
- * proto as Curl_read().
- *
- * This function wraps Curl_recv_plain(). The only difference besides the
- * prototype is '*n' (bytes read) is set to 0 on error.
- * 'sockfd' must be one of the connection's two main sockets and the value of
- * 'sizerequested' must not be changed.
- */
-CURLcode Curl_read_plain(struct Curl_easy *data, /* transfer */
- curl_socket_t sockfd, /* read from this socket */
- char *buf, /* store read data here */
- size_t sizerequested, /* max amount to read */
- ssize_t *n) /* amount bytes read */
-{
- CURLcode result;
- struct connectdata *conn = data->conn;
- int num;
- DEBUGASSERT(conn);
- DEBUGASSERT(sockfd == conn->sock[FIRSTSOCKET] ||
- sockfd == conn->sock[SECONDARYSOCKET]);
- if(sockfd != conn->sock[FIRSTSOCKET] &&
- sockfd != conn->sock[SECONDARYSOCKET])
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- num = (sockfd == conn->sock[SECONDARYSOCKET]);
-
- *n = Curl_recv_plain(data, num, buf, sizerequested, &result);
- if(*n == -1)
- *n = 0;
-
- return result;
-}
-
-/*
* Internal read-from-socket function. This is meant to deal with plain
* sockets, SSL sockets and kerberos sockets.
*
@@ -752,30 +426,3 @@ out:
return result;
}
-/* return 0 on success */
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size)
-{
- if(data->set.verbose) {
- static const char s_infotype[CURLINFO_END][3] = {
- "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
- if(data->set.fdebug) {
- bool inCallback = Curl_is_in_callback(data);
- Curl_set_in_callback(data, true);
- (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
- }
- else {
- switch(type) {
- case CURLINFO_TEXT:
- case CURLINFO_HEADER_OUT:
- case CURLINFO_HEADER_IN:
- fwrite(s_infotype[type], 2, 1, data->set.err);
- fwrite(ptr, size, 1, data->set.err);
- break;
- default: /* nada */
- break;
- }
- }
- }
-}
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h
index 8af5c46085..d0c9275705 100644
--- a/Utilities/cmcurl/lib/sendf.h
+++ b/Utilities/cmcurl/lib/sendf.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,26 +26,8 @@
#include "curl_setup.h"
-void Curl_infof(struct Curl_easy *, const char *fmt, ...);
-void Curl_failf(struct Curl_easy *, const char *fmt, ...);
+#include "curl_log.h"
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-
-#if defined(HAVE_VARIADIC_MACROS_C99)
-#define infof(...) Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC)
-#define infof(x...) Curl_nop_stmt
-#else
-#error "missing VARIADIC macro define, fix and rebuild!"
-#endif
-
-#else /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define infof Curl_infof
-
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define failf Curl_failf
#define CLIENTWRITE_BODY (1<<0)
#define CLIENTWRITE_HEADER (1<<1)
@@ -58,20 +40,6 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...);
CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
size_t len) WARN_UNUSED_RESULT;
-bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex);
-
-/* internal read-function, does plain socket only */
-CURLcode Curl_read_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- char *buf,
- size_t sizerequested,
- ssize_t *n);
-
-ssize_t Curl_recv_plain(struct Curl_easy *data, int num, char *buf,
- size_t len, CURLcode *code);
-ssize_t Curl_send_plain(struct Curl_easy *data, int num,
- const void *mem, size_t len, CURLcode *code);
-
/* internal read-function, does plain socket, SSL and krb4 */
CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
char *buf, size_t buffersize,
@@ -83,15 +51,4 @@ CURLcode Curl_write(struct Curl_easy *data,
const void *mem, size_t len,
ssize_t *written);
-/* internal write-function, does plain sockets ONLY */
-CURLcode Curl_write_plain(struct Curl_easy *data,
- curl_socket_t sockfd,
- const void *mem, size_t len,
- ssize_t *written);
-
-/* the function used to output verbose information */
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size);
-
-
#endif /* HEADER_CURL_SENDF_H */
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index b77e95b4e3..6bb88791ca 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -174,7 +174,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val)
*val |= h->protocol;
}
- } while(str++);
+ } while(str && str++);
if(!*val)
/* no protocol listed */
@@ -463,8 +463,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
version_max >= CURL_SSLVERSION_MAX_LAST)
return CURLE_BAD_FUNCTION_ARGUMENT;
- primary->version = version;
- primary->version_max = version_max;
+ primary->version = (unsigned char)version;
+ primary->version_max = (unsigned int)version_max;
}
#else
result = CURLE_NOT_BUILT_IN;
@@ -732,13 +732,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
break;
- case CURLOPT_HTTP200ALIASES:
- /*
- * Set a list of aliases for HTTP 200 in response header
- */
- data->set.http200aliases = va_arg(param, struct curl_slist *);
- break;
-
#if !defined(CURL_DISABLE_COOKIES)
case CURLOPT_COOKIE:
/*
@@ -760,18 +753,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_BAD_FUNCTION_ARGUMENT;
/* append the cookie file name to the list of file names, and deal with
them later */
- cl = curl_slist_append(data->state.cookielist, argptr);
+ cl = curl_slist_append(data->set.cookielist, argptr);
if(!cl) {
- curl_slist_free_all(data->state.cookielist);
- data->state.cookielist = NULL;
+ curl_slist_free_all(data->set.cookielist);
+ data->set.cookielist = NULL;
return CURLE_OUT_OF_MEMORY;
}
- data->state.cookielist = cl; /* store the list for later use */
+ data->set.cookielist = cl; /* store the list for later use */
}
else {
/* clear the list of cookie files */
- curl_slist_free_all(data->state.cookielist);
- data->state.cookielist = NULL;
+ curl_slist_free_all(data->set.cookielist);
+ data->set.cookielist = NULL;
if(!data->share || !data->share->cookies) {
/* throw away all existing cookies if this isn't a shared cookie
@@ -902,22 +895,38 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* the listed enums in curl/curl.h.
*/
arg = va_arg(param, long);
- if(arg < CURL_HTTP_VERSION_NONE)
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ switch(arg) {
+ case CURL_HTTP_VERSION_NONE:
+#ifdef USE_HTTP2
+ /* TODO: this seems an undesirable quirk to force a behaviour on
+ * lower implementations that they should recognize independently? */
+ arg = CURL_HTTP_VERSION_2TLS;
+#endif
+ /* accepted */
+ break;
+ case CURL_HTTP_VERSION_1_0:
+ case CURL_HTTP_VERSION_1_1:
+ /* accepted */
+ break;
+#ifdef USE_HTTP2
+ case CURL_HTTP_VERSION_2_0:
+ case CURL_HTTP_VERSION_2TLS:
+ case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+ /* accepted */
+ break;
+#endif
#ifdef ENABLE_QUIC
- if(arg == CURL_HTTP_VERSION_3)
- ;
- else
+ case CURL_HTTP_VERSION_3:
+ case CURL_HTTP_VERSION_3ONLY:
+ /* accepted */
+ break;
#endif
-#ifndef USE_HTTP2
- if(arg >= CURL_HTTP_VERSION_2)
- return CURLE_UNSUPPORTED_PROTOCOL;
-#else
- if(arg >= CURL_HTTP_VERSION_LAST)
+ default:
+ /* not accepted */
+ if(arg < CURL_HTTP_VERSION_NONE)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
return CURLE_UNSUPPORTED_PROTOCOL;
- if(arg == CURL_HTTP_VERSION_NONE)
- arg = CURL_HTTP_VERSION_2TLS;
-#endif
+ }
data->set.httpwant = (unsigned char)arg;
break;
@@ -944,6 +953,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.http09_allowed = arg ? TRUE : FALSE;
#endif
break;
+
+ case CURLOPT_HTTP200ALIASES:
+ /*
+ * Set a list of aliases for HTTP 200 in response header
+ */
+ data->set.http200aliases = va_arg(param, struct curl_slist *);
+ break;
#endif /* CURL_DISABLE_HTTP */
#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
@@ -1309,6 +1325,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
break;
#endif
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
case CURLOPT_FTP_CREATE_MISSING_DIRS:
/*
* An FTP/SFTP option that modifies an upload to create missing
@@ -1322,6 +1339,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
else
data->set.ftp_create_missing_dirs = (unsigned char)arg;
break;
+
+ case CURLOPT_POSTQUOTE:
+ /*
+ * List of RAW FTP commands to use after a transfer
+ */
+ data->set.postquote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_PREQUOTE:
+ /*
+ * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
+ */
+ data->set.prequote = va_arg(param, struct curl_slist *);
+ break;
+ case CURLOPT_QUOTE:
+ /*
+ * List of RAW FTP commands to use before a transfer
+ */
+ data->set.quote = va_arg(param, struct curl_slist *);
+ break;
+#endif
case CURLOPT_READDATA:
/*
* FILE pointer to read the file to be uploaded from. Or possibly
@@ -1431,7 +1468,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_TIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.timeout = (unsigned int)uarg;
break;
@@ -1449,7 +1486,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_CONNECTTIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.connecttimeout = (unsigned int)uarg;
break;
@@ -1460,7 +1497,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* The maximum time for curl to wait for FTP server connect
*/
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.accepttimeout = (unsigned int)uarg;
break;
@@ -1506,24 +1543,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
- case CURLOPT_POSTQUOTE:
- /*
- * List of RAW FTP commands to use after a transfer
- */
- data->set.postquote = va_arg(param, struct curl_slist *);
- break;
- case CURLOPT_PREQUOTE:
- /*
- * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
- */
- data->set.prequote = va_arg(param, struct curl_slist *);
- break;
- case CURLOPT_QUOTE:
- /*
- * List of RAW FTP commands to use before a transfer
- */
- data->set.quote = va_arg(param, struct curl_slist *);
- break;
case CURLOPT_RESOLVE:
/*
* List of HOST:PORT:[addresses] strings to populate the DNS cache with
@@ -1871,16 +1890,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = va_arg(param, long);
if((arg < 0) || (arg > 65535))
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.localportrange = curlx_sltosi(arg);
+ data->set.localportrange = curlx_sltous(arg);
break;
case CURLOPT_GSSAPI_DELEGATION:
/*
* GSS-API credential delegation bitmask
*/
- arg = va_arg(param, long);
- if(arg < CURLGSSAPI_DELEGATION_NONE)
- return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.gssapi_delegation = arg;
+ uarg = va_arg(param, unsigned long);
+ data->set.gssapi_delegation = (unsigned char)uarg&
+ (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG);
break;
case CURLOPT_SSL_VERIFYPEER:
/*
@@ -2260,9 +2278,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->cookies = NULL;
#endif
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts == data->hsts)
+ data->hsts = NULL;
+#endif
+#ifdef USE_SSL
if(data->share->sslsession == data->state.session)
data->state.session = NULL;
-
+#endif
#ifdef USE_LIBPSL
if(data->psl == &data->share->psl)
data->psl = data->multi? &data->multi->psl: NULL;
@@ -2296,10 +2319,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->cookies = data->share->cookies;
}
#endif /* CURL_DISABLE_HTTP */
+#ifndef CURL_DISABLE_HSTS
+ if(data->share->hsts) {
+ /* first free the private one if any */
+ Curl_hsts_cleanup(&data->hsts);
+ data->hsts = data->share->hsts;
+ }
+#endif /* CURL_DISABLE_HTTP */
+#ifdef USE_SSL
if(data->share->sslsession) {
data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
data->state.session = data->share->sslsession;
}
+#endif
#ifdef USE_LIBPSL
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
data->psl = &data->share->psl;
@@ -2337,7 +2369,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = va_arg(param, long);
if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.use_ssl = (curl_usessl)arg;
+ data->set.use_ssl = (unsigned char)arg;
break;
case CURLOPT_SSL_OPTIONS:
@@ -2515,6 +2547,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
+ case CURLOPT_SSH_KNOWNHOSTS:
+ /*
+ * Store the file name to read known hosts from.
+ */
+ result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
+ va_arg(param, char *));
+ break;
+#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
/*
* Option to allow for the SHA256 of the host public key to be checked
@@ -2524,14 +2564,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
va_arg(param, char *));
break;
- case CURLOPT_SSH_KNOWNHOSTS:
- /*
- * Store the file name to read known hosts from.
- */
- result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
- va_arg(param, char *));
- break;
-#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOSTKEYFUNCTION:
/* the callback to check the hostkey without the knownhost file */
data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback);
@@ -2544,6 +2576,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.ssh_hostkeyfunc_userp = va_arg(param, void *);
break;
#endif
+
case CURLOPT_SSH_KEYFUNCTION:
/* setting to NULL is fine since the ssh.c functions themselves will
then revert to use the internal default */
@@ -2590,7 +2623,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.new_file_perms = (unsigned int)arg;
break;
-
+#endif
+#ifdef USE_SSH
case CURLOPT_NEW_DIRECTORY_PERMS:
/*
* Uses these permissions instead of 0755
@@ -2815,7 +2849,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.fnmatch = va_arg(param, curl_fnmatch_callback);
break;
case CURLOPT_CHUNK_DATA:
- data->wildcard.customptr = va_arg(param, void *);
+ data->set.wildcardptr = va_arg(param, void *);
break;
case CURLOPT_FNMATCH_DATA:
data->set.fnmatch_data = va_arg(param, void *);
@@ -2825,52 +2859,33 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_TLSAUTH_USERNAME:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME] &&
- !data->set.ssl.primary.authtype)
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to SRP */
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_USERNAME:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
- !data->set.proxy_ssl.primary.authtype)
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default to
- SRP */
break;
#endif
case CURLOPT_TLSAUTH_PASSWORD:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME] &&
- !data->set.ssl.primary.authtype)
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_PASSWORD:
result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY],
va_arg(param, char *));
- if(data->set.str[STRING_TLSAUTH_USERNAME_PROXY] &&
- !data->set.proxy_ssl.primary.authtype)
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP; /* default */
break;
#endif
case CURLOPT_TLSAUTH_TYPE:
argptr = va_arg(param, char *);
- if(!argptr ||
- strncasecompare(argptr, "SRP", strlen("SRP")))
- data->set.ssl.primary.authtype = CURL_TLSAUTH_SRP;
- else
- data->set.ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ if(argptr && !strncasecompare(argptr, "SRP", strlen("SRP")))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLSAUTH_TYPE:
argptr = va_arg(param, char *);
- if(!argptr ||
- strncasecompare(argptr, "SRP", strlen("SRP")))
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_SRP;
- else
- data->set.proxy_ssl.primary.authtype = CURL_TLSAUTH_NONE;
+ if(argptr || !strncasecompare(argptr, "SRP", strlen("SRP")))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
break;
#endif
#endif
@@ -2956,29 +2971,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
case CURLOPT_STREAM_WEIGHT:
-#ifndef USE_NGHTTP2
- return CURLE_NOT_BUILT_IN;
-#else
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
arg = va_arg(param, long);
if((arg >= 1) && (arg <= 256))
- data->set.stream_weight = (int)arg;
+ data->set.priority.weight = (int)arg;
break;
+#else
+ return CURLE_NOT_BUILT_IN;
#endif
case CURLOPT_STREAM_DEPENDS:
case CURLOPT_STREAM_DEPENDS_E:
{
-#ifndef USE_NGHTTP2
- return CURLE_NOT_BUILT_IN;
-#else
struct Curl_easy *dep = va_arg(param, struct Curl_easy *);
if(!dep || GOOD_EASY_HANDLE(dep)) {
- if(data->set.stream_depends_on) {
- Curl_http2_remove_child(data->set.stream_depends_on, data);
- }
- Curl_http2_add_child(dep, data, (option == CURLOPT_STREAM_DEPENDS_E));
+ return Curl_data_priority_add_child(dep, data,
+ option == CURLOPT_STREAM_DEPENDS_E);
}
break;
-#endif
}
case CURLOPT_CONNECT_TO:
data->set.connect_to = va_arg(param, struct curl_slist *);
@@ -2988,7 +2997,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
break;
case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
uarg = va_arg(param, unsigned long);
- if(uarg >= UINT_MAX)
+ if(uarg > UINT_MAX)
uarg = UINT_MAX;
data->set.happy_eyeballs_timeout = (unsigned int)uarg;
break;
@@ -3049,19 +3058,39 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_HSTSWRITEDATA:
data->set.hsts_write_userp = va_arg(param, void *);
break;
- case CURLOPT_HSTS:
+ case CURLOPT_HSTS: {
+ struct curl_slist *h;
if(!data->hsts) {
data->hsts = Curl_hsts_init();
if(!data->hsts)
return CURLE_OUT_OF_MEMORY;
}
argptr = va_arg(param, char *);
- result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
- if(result)
- return result;
- if(argptr)
- (void)Curl_hsts_loadfile(data, data->hsts, argptr);
+ if(argptr) {
+ result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr);
+ if(result)
+ return result;
+ /* this needs to build a list of file names to read from, so that it can
+ read them later, as we might get a shared HSTS handle to load them
+ into */
+ h = curl_slist_append(data->set.hstslist, argptr);
+ if(!h) {
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->set.hstslist = h; /* store the list for later use */
+ }
+ else {
+ /* clear the list of HSTS files */
+ curl_slist_free_all(data->set.hstslist);
+ data->set.hstslist = NULL;
+ if(!data->share || !data->share->hsts)
+ /* throw away the HSTS cache unless shared */
+ Curl_hsts_cleanup(&data->hsts);
+ }
break;
+ }
case CURLOPT_HSTS_CTRL:
arg = va_arg(param, long);
if(arg & CURLHSTS_ENABLE) {
diff --git a/Utilities/cmcurl/lib/setopt.h b/Utilities/cmcurl/lib/setopt.h
index ffc77a71dc..3c14a05e37 100644
--- a/Utilities/cmcurl/lib/setopt.h
+++ b/Utilities/cmcurl/lib/setopt.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h
index 7854397860..759583466c 100644
--- a/Utilities/cmcurl/lib/setup-os400.h
+++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -205,7 +205,7 @@ extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status,
extern int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen);
extern int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen);
extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags,
- struct sockaddr *dstaddr, int addrlen);
+ const struct sockaddr *dstaddr, int addrlen);
extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags,
struct sockaddr *fromaddr, int *addrlen);
extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen);
diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h
index b570683d78..46657b2cd4 100644
--- a/Utilities/cmcurl/lib/setup-vms.h
+++ b/Utilities/cmcurl/lib/setup-vms.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/setup-win32.h b/Utilities/cmcurl/lib/setup-win32.h
index bc5f8efc3c..13948389a4 100644
--- a/Utilities/cmcurl/lib/setup-win32.h
+++ b/Utilities/cmcurl/lib/setup-win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c
index c96a9fc61f..fdfd631ef9 100644
--- a/Utilities/cmcurl/lib/sha256.c
+++ b/Utilities/cmcurl/lib/sha256.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/share.c b/Utilities/cmcurl/lib/share.c
index 1a083e72a0..c0a8d806f3 100644
--- a/Utilities/cmcurl/lib/share.c
+++ b/Utilities/cmcurl/lib/share.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,9 +29,11 @@
#include "share.h"
#include "psl.h"
#include "vtls/vtls.h"
-#include "curl_memory.h"
+#include "hsts.h"
-/* The last #include file should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
#include "memdebug.h"
struct Curl_share *
@@ -89,6 +91,18 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(!share->hsts) {
+ share->hsts = Curl_hsts_init();
+ if(!share->hsts)
+ res = CURLSHE_NOMEM;
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
if(!share->sslsession) {
@@ -141,6 +155,16 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...)
#endif
break;
+ case CURL_LOCK_DATA_HSTS:
+#ifndef CURL_DISABLE_HSTS
+ if(share->hsts) {
+ Curl_hsts_cleanup(&share->hsts);
+ }
+#else /* CURL_DISABLE_HSTS */
+ res = CURLSHE_NOT_BUILT_IN;
+#endif
+ break;
+
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
Curl_safefree(share->sslsession);
@@ -207,6 +231,10 @@ curl_share_cleanup(struct Curl_share *share)
Curl_cookie_cleanup(share->cookies);
#endif
+#ifndef CURL_DISABLE_HSTS
+ Curl_hsts_cleanup(&share->hsts);
+#endif
+
#ifdef USE_SSL
if(share->sslsession) {
size_t i;
diff --git a/Utilities/cmcurl/lib/share.h b/Utilities/cmcurl/lib/share.h
index 32be41691a..7f55aac853 100644
--- a/Utilities/cmcurl/lib/share.h
+++ b/Utilities/cmcurl/lib/share.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,10 +59,14 @@ struct Curl_share {
#ifdef USE_LIBPSL
struct PslCache psl;
#endif
-
+#ifndef CURL_DISABLE_HSTS
+ struct hsts *hsts;
+#endif
+#ifdef USE_SSL
struct Curl_ssl_session *sslsession;
size_t max_ssl_sessions;
long sessionage;
+#endif
};
CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h
index d12b31764d..48761ad0fd 100644
--- a/Utilities/cmcurl/lib/sigpipe.h
+++ b/Utilities/cmcurl/lib/sigpipe.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -50,7 +50,6 @@ static void sigpipe_ignore(struct Curl_easy *data,
if(!data->set.no_signal) {
struct sigaction action;
/* first, extract the existing situation */
- memset(&ig->old_pipe_act, 0, sizeof(struct sigaction));
sigaction(SIGPIPE, NULL, &ig->old_pipe_act);
action = ig->old_pipe_act;
/* ignore this signal */
diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c
index 6c80722c77..366b247609 100644
--- a/Utilities/cmcurl/lib/slist.c
+++ b/Utilities/cmcurl/lib/slist.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/slist.h b/Utilities/cmcurl/lib/slist.h
index 4e5834c90a..9561fd0226 100644
--- a/Utilities/cmcurl/lib/slist.h
+++ b/Utilities/cmcurl/lib/slist.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 48d5a2fe00..076200472e 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -25,8 +25,7 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
- (SIZEOF_CURL_OFF_T > 4)
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
#define BUILDING_CURL_SMB_C
@@ -763,6 +762,11 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
void *msg = NULL;
const struct smb_nt_create_response *smb_m;
+ if(data->set.upload && (data->state.infilesize < 0)) {
+ failf(data, "SMB upload needs to know the size up front");
+ return CURLE_SEND_ERROR;
+ }
+
/* Start the request */
if(req->state == SMB_REQUESTING) {
result = smb_send_tree_connect(data);
@@ -993,6 +997,7 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data,
/* The share must be present */
if(!slash) {
Curl_safefree(smbc->share);
+ failf(data, "missing share in URL path for SMB");
return CURLE_URL_MALFORMAT;
}
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h
index 919f3ac142..c35f3e9700 100644
--- a/Utilities/cmcurl/lib/smb.h
+++ b/Utilities/cmcurl/lib/smb.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
- * Copyright (C) 2018 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index 6d0783f458..7a030308d4 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -398,15 +398,17 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
struct connectdata *conn = data->conn;
struct smtp_conn *smtpc = &conn->proto.smtpc;
CURLcode result;
+ bool ssldone = FALSE;
- if(!Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
}
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
+ smtpc->ssldone = ssldone;
if(smtpc->state != SMTP_UPGRADETLS)
state(data, SMTP_UPGRADETLS);
@@ -891,7 +893,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
if(smtpcode/100 != 2 && smtpcode != 1) {
if(data->set.use_ssl <= CURLUSESSL_TRY
- || Curl_conn_is_ssl(data, FIRSTSOCKET))
+ || Curl_conn_is_ssl(conn, FIRSTSOCKET))
result = smtp_perform_helo(data, conn);
else {
failf(data, "Remote access denied: %d", smtpcode);
@@ -956,7 +958,7 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
}
if(smtpcode != 1) {
- if(data->set.use_ssl && !Curl_conn_is_ssl(data, FIRSTSOCKET)) {
+ if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* We don't have a SSL/TLS connection yet, but SSL is requested */
if(smtpc->tls_supported)
/* Switch to TLS connection now */
@@ -1288,7 +1290,9 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
struct smtp_conn *smtpc = &conn->proto.smtpc;
if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &smtpc->ssldone);
+ bool ssldone = FALSE;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ smtpc->ssldone = ssldone;
if(result || !smtpc->ssldone)
return result;
}
diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h
index 24c5589e43..7a04c21549 100644
--- a/Utilities/cmcurl/lib/smtp.h
+++ b/Utilities/cmcurl/lib/smtp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2009 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -57,28 +57,28 @@ struct SMTP {
curl_pp_transfer transfer;
char *custom; /* Custom Request */
struct curl_slist *rcpt; /* Recipient list */
- bool rcpt_had_ok; /* Whether any of RCPT TO commands (depends on
- total number of recipients) succeeded so far */
- bool trailing_crlf; /* Specifies if the trailing CRLF is present */
int rcpt_last_error; /* The last error received for RCPT TO command */
size_t eob; /* Number of bytes of the EOB (End Of Body) that
have been received so far */
+ BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on
+ total number of recipients) succeeded so far */
+ BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */
};
/* smtp_conn is used for struct connection-oriented data in the connectdata
struct */
struct smtp_conn {
struct pingpong pp;
+ struct SASL sasl; /* SASL-related storage */
smtpstate state; /* Always use smtp.c:state() to change state! */
- bool ssldone; /* Is connect() over SSL done? */
char *domain; /* Client address/name to send in the EHLO */
- struct SASL sasl; /* SASL-related storage */
- bool tls_supported; /* StartTLS capability supported by server */
- bool size_supported; /* If server supports SIZE extension according to
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(size_supported); /* If server supports SIZE extension according to
RFC 1870 */
- bool utf8_supported; /* If server supports SMTPUTF8 extension according
+ BIT(utf8_supported); /* If server supports SMTPUTF8 extension according
to RFC 6531 */
- bool auth_supported; /* AUTH capability supported by server */
+ BIT(auth_supported); /* AUTH capability supported by server */
};
extern const struct Curl_handler Curl_handler_smtp;
diff --git a/Utilities/cmcurl/lib/sockaddr.h b/Utilities/cmcurl/lib/sockaddr.h
index 77ec833ee0..5a6bb207dc 100644
--- a/Utilities/cmcurl/lib/sockaddr.h
+++ b/Utilities/cmcurl/lib/sockaddr.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c
index 0f8798f087..b94c9843e6 100644
--- a/Utilities/cmcurl/lib/socketpair.c
+++ b/Utilities/cmcurl/lib/socketpair.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -65,7 +65,7 @@ int Curl_socketpair(int domain, int type, int protocol,
union {
struct sockaddr_in inaddr;
struct sockaddr addr;
- } a, a2;
+ } a;
curl_socket_t listener;
curl_socklen_t addrlen = sizeof(a.inaddr);
int reuse = 1;
@@ -85,9 +85,22 @@ int Curl_socketpair(int domain, int type, int protocol,
socks[0] = socks[1] = CURL_SOCKET_BAD;
+#if defined(WIN32) || defined(__CYGWIN__)
+ /* don't set SO_REUSEADDR on Windows */
+ (void)reuse;
+#ifdef SO_EXCLUSIVEADDRUSE
+ {
+ int exclusive = 1;
+ if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1)
+ goto error;
+ }
+#endif
+#else
if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
goto error;
+#endif
if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
goto error;
if(getsockname(listener, &a.addr, &addrlen) == -1 ||
@@ -107,24 +120,59 @@ int Curl_socketpair(int domain, int type, int protocol,
pfd[0].fd = listener;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
- (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */
+ (void)Curl_poll(pfd, 1, 1000); /* one second */
socks[1] = accept(listener, NULL, NULL);
if(socks[1] == CURL_SOCKET_BAD)
goto error;
+ else {
+ struct curltime check;
+ struct curltime start = Curl_now();
+ char *p = (char *)&check;
+ size_t s = sizeof(check);
- /* verify that nothing else connected */
- addrlen = sizeof(a.inaddr);
- if(getsockname(socks[0], &a.addr, &addrlen) == -1 ||
- addrlen < (int)sizeof(a.inaddr))
- goto error;
- addrlen = sizeof(a2.inaddr);
- if(getpeername(socks[1], &a2.addr, &addrlen) == -1 ||
- addrlen < (int)sizeof(a2.inaddr))
- goto error;
- if(a.inaddr.sin_family != a2.inaddr.sin_family ||
- a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr ||
- a.inaddr.sin_port != a2.inaddr.sin_port)
- goto error;
+ /* write data to the socket */
+ swrite(socks[0], &start, sizeof(start));
+ /* verify that we read the correct data */
+ do {
+ ssize_t nread;
+
+ pfd[0].fd = socks[1];
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ (void)Curl_poll(pfd, 1, 1000); /* one second */
+
+ nread = sread(socks[1], p, s);
+ if(nread == -1) {
+ int sockerr = SOCKERRNO;
+ /* Don't block forever */
+ if(Curl_timediff(Curl_now(), start) > (60 * 1000))
+ goto error;
+ if(
+#ifdef WSAEWOULDBLOCK
+ /* This is how Windows does it */
+ (WSAEWOULDBLOCK == sockerr)
+#else
+ /* errno may be EWOULDBLOCK or on some systems EAGAIN when it
+ returned due to its inability to send off data without
+ blocking. We therefore treat both error codes the same here */
+ (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
+ (EINTR == sockerr) || (EINPROGRESS == sockerr)
+#endif
+ ) {
+ continue;
+ }
+ goto error;
+ }
+ s -= nread;
+ if(s) {
+ p += nread;
+ continue;
+ }
+ if(memcmp(&start, &check, sizeof(check)))
+ goto error;
+ break;
+ } while(1);
+ }
sclose(listener);
return 0;
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h
index de70df673a..306ab5dc4c 100644
--- a/Utilities/cmcurl/lib/socketpair.h
+++ b/Utilities/cmcurl/lib/socketpair.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index d491e089be..95c2b004c9 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -89,8 +89,8 @@ struct socks_state {
*
* This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
*/
-int Curl_blockread_all(struct Curl_easy *data, /* transfer */
- curl_socket_t sockfd, /* read from this socket */
+int Curl_blockread_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data, /* transfer */
char *buf, /* store read data here */
ssize_t buffersize, /* max amount to read */
ssize_t *n) /* amount bytes read */
@@ -98,6 +98,8 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */
ssize_t nread = 0;
ssize_t allread = 0;
int result;
+ CURLcode err = CURLE_OK;
+
*n = 0;
for(;;) {
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
@@ -108,15 +110,19 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */
}
if(!timeout_ms)
timeout_ms = TIMEDIFF_T_MAX;
- if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
+ if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) {
result = ~CURLE_OK;
break;
}
- result = Curl_read_plain(data, sockfd, buf, buffersize, &nread);
- if(CURLE_AGAIN == result)
- continue;
- if(result)
- break;
+ nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err);
+ if(nread <= 0) {
+ result = err;
+ if(CURLE_AGAIN == err)
+ continue;
+ if(err) {
+ break;
+ }
+ }
if(buffersize == nread) {
allread += nread;
@@ -192,6 +198,68 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data,
#endif
}
+static CURLproxycode socks_state_send(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data,
+ CURLproxycode failcode,
+ const char *description)
+{
+ ssize_t nwritten;
+ CURLcode result;
+
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
+ sx->outstanding, &result);
+ if(nwritten <= 0) {
+ if(CURLE_AGAIN == result) {
+ return CURLPX_OK;
+ }
+ else if(CURLE_OK == result) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ failf(data, "Failed to send %s: %s", description,
+ curl_easy_strerror(result));
+ return failcode;
+ }
+ DEBUGASSERT(sx->outstanding >= nwritten);
+ /* not done, remain in state */
+ sx->outstanding -= nwritten;
+ sx->outp += nwritten;
+ return CURLPX_OK;
+}
+
+static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
+ struct socks_state *sx,
+ struct Curl_easy *data,
+ CURLproxycode failcode,
+ const char *description)
+{
+ ssize_t nread;
+ CURLcode result;
+
+ nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
+ sx->outstanding, &result);
+ if(nread <= 0) {
+ if(CURLE_AGAIN == result) {
+ return CURLPX_OK;
+ }
+ else if(CURLE_OK == result) {
+ /* connection closed */
+ failf(data, "connection to proxy closed");
+ return CURLPX_CLOSED;
+ }
+ failf(data, "SOCKS4: Failed receiving %s: %s", description,
+ curl_easy_strerror(result));
+ return failcode;
+ }
+ /* remain in reading state */
+ DEBUGASSERT(sx->outstanding >= nread);
+ sx->outstanding -= nread;
+ sx->outp += nread;
+ return CURLPX_OK;
+}
+
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
@@ -212,10 +280,8 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
unsigned char *socksreq = (unsigned char *)data->state.buffer;
CURLcode result;
- curl_socket_t sockfd = conn->sock[cf->sockindex];
+ CURLproxycode presult;
struct Curl_dns_entry *dns = NULL;
- ssize_t actualread;
- ssize_t written;
/* make sure that the buffer is at least 600 bytes */
DEBUGASSERT(READBUFFER_MIN >= 600);
@@ -250,7 +316,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* DNS resolve only for SOCKS4, not SOCKS4a */
if(!protocol4a) {
enum resolve_t rc =
- Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns);
+ Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
@@ -375,19 +441,14 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
/* Send request */
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS4 connect request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != sx->outstanding) {
- /* not done, remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "SOCKS4 connect request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
-
/* done sending! */
sx->outstanding = 8; /* receive data size */
sx->outp = socksreq;
@@ -396,22 +457,12 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
/* Receive response */
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "SOCKS4: Failed receiving connect request ack: %s",
- curl_easy_strerror(result));
- return CURLPX_RECV_CONNECT;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+ "connect request ack");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
/* remain in reading state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_DONE);
@@ -518,10 +569,8 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
unsigned char *socksreq = (unsigned char *)data->state.buffer;
char dest[256] = "unknown"; /* printable hostname:port */
int idx;
- ssize_t actualread;
- ssize_t written;
CURLcode result;
- curl_socket_t sockfd = conn->sock[cf->sockindex];
+ CURLproxycode presult;
bool socks5_resolve_local =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
const size_t hostname_len = strlen(sx->hostname);
@@ -567,30 +616,25 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
/* write the number of authentication methods */
socksreq[1] = (unsigned char) (idx - 2);
- result = Curl_write_plain(data, sockfd, socksreq, idx, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to send initial SOCKS5 request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != idx) {
- sxstate(sx, data, CONNECT_SOCKS_SEND);
- sx->outstanding = idx - written;
- sx->outp = &socksreq[written];
+ sx->outp = socksreq;
+ sx->outstanding = idx;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "initial SOCKS5 request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_SOCKS_READ);
goto CONNECT_SOCKS_READ_INIT;
case CONNECT_SOCKS_SEND:
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to send initial SOCKS5 request.");
- return CURLPX_SEND_CONNECT;
- }
- if(written != sx->outstanding) {
- /* not done, remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
+ "initial SOCKS5 request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
/* FALLTHROUGH */
@@ -600,21 +644,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sx->outp = socksreq; /* store it here */
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to receive initial SOCKS5 response.");
- return CURLPX_RECV_CONNECT;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "Connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
+ "initial SOCKS5 response");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
/* remain in reading state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
return CURLPX_OK;
}
else if(socksreq[0] != 5) {
@@ -634,7 +669,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else if(allow_gssapi && (socksreq[1] == 1)) {
sxstate(sx, data, CONNECT_GSSAPI_INIT);
- result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data);
+ result = Curl_SOCKS5_gssapi_negotiate(cf, data);
if(result) {
failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
return CURLPX_GSSAPI;
@@ -713,16 +748,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
}
/* FALLTHROUGH */
case CONNECT_AUTH_SEND:
- result = Curl_write_plain(data, sockfd, sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS5 sub-negotiation request.");
- return CURLPX_SEND_AUTH;
- }
- if(sx->outstanding != written) {
- /* remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
+ "SOCKS5 sub-negotiation request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in sending state */
return CURLPX_OK;
}
sx->outp = socksreq;
@@ -730,21 +761,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_AUTH_READ);
/* FALLTHROUGH */
case CONNECT_AUTH_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
- return CURLPX_RECV_AUTH;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
+ "SOCKS5 sub-negotiation response");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
/* ignore the first (VER) byte */
@@ -761,7 +783,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
case CONNECT_REQ_INIT:
if(socks5_resolve_local) {
enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
- FALSE, &dns);
+ TRUE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
@@ -909,16 +931,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_REQ_SENDING);
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
- result = Curl_write_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &written);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to send SOCKS5 connect request.");
- return CURLPX_SEND_REQUEST;
- }
- if(sx->outstanding != written) {
- /* remain in state */
- sx->outstanding -= written;
- sx->outp += written;
+ presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
+ "SOCKS5 connect request");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in send state */
return CURLPX_OK;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@@ -932,25 +950,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
sxstate(sx, data, CONNECT_REQ_READ);
/* FALLTHROUGH */
case CONNECT_REQ_READ:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to receive SOCKS5 connect request ack.");
- return CURLPX_RECV_REQACK;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
+ "SOCKS5 connect request ack");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
-
- if(socksreq[0] != 5) { /* version */
+ else if(socksreq[0] != 5) { /* version */
failf(data,
"SOCKS5 reply has wrong version, version should be 5.");
return CURLPX_BAD_VERSION;
@@ -1031,21 +1039,12 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
#endif
/* FALLTHROUGH */
case CONNECT_REQ_READ_MORE:
- result = Curl_read_plain(data, sockfd, (char *)sx->outp,
- sx->outstanding, &actualread);
- if(result && (CURLE_AGAIN != result)) {
- failf(data, "Failed to receive SOCKS5 connect request ack.");
- return CURLPX_RECV_ADDRESS;
- }
- else if(!result && !actualread) {
- /* connection closed */
- failf(data, "connection to proxy closed");
- return CURLPX_CLOSED;
- }
- else if(actualread != sx->outstanding) {
- /* remain in state */
- sx->outstanding -= actualread;
- sx->outp += actualread;
+ presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
+ "SOCKS5 connect request address");
+ if(CURLPX_OK != presult)
+ return presult;
+ else if(sx->outstanding) {
+ /* remain in reading state */
return CURLPX_OK;
}
sxstate(sx, data, CONNECT_DONE);
@@ -1151,7 +1150,6 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
result = connect_SOCKS(cf, sx, data);
if(!result && sx->state == CONNECT_DONE) {
cf->connected = TRUE;
- Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]);
Curl_verboseconnect(data, conn);
socks_proxy_cf_free(cf);
}
@@ -1171,7 +1169,7 @@ static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
if(!fds && cf->next->connected && !cf->connected && sx) {
/* If we are not connected, the filter below is and has nothing
* to wait on, we determine what to wait for. */
- socks[0] = cf->conn->sock[cf->sockindex];
+ socks[0] = Curl_conn_cf_get_socket(cf, data);
switch(sx->state) {
case CONNECT_RESOLVING:
case CONNECT_SOCKS_READ:
@@ -1205,13 +1203,6 @@ static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
socks_proxy_cf_free(cf);
}
-static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- (void)data;
- socks_proxy_cf_free(cf);
-}
-
static void socks_cf_get_host(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char **phost,
@@ -1229,11 +1220,11 @@ static void socks_cf_get_host(struct Curl_cfilter *cf,
}
}
-static const struct Curl_cftype cft_socks_proxy = {
+struct Curl_cftype Curl_cft_socks_proxy = {
"SOCKS-PROXYY",
CF_TYPE_IP_CONNECT,
+ 0,
socks_proxy_cf_destroy,
- Curl_cf_def_setup,
socks_proxy_cf_connect,
socks_proxy_cf_close,
socks_cf_get_host,
@@ -1241,8 +1232,10 @@ static const struct Curl_cftype cft_socks_proxy = {
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
- Curl_cf_def_attach_data,
- socks_proxy_cf_detach_data,
+ Curl_cf_def_cntrl,
+ Curl_cf_def_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
@@ -1252,10 +1245,23 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
struct Curl_cfilter *cf;
CURLcode result;
- result = Curl_cf_create(&cf, &cft_socks_proxy, NULL);
+ result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
return result;
}
+CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ (void)data;
+ result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
#endif /* CURL_DISABLE_PROXY */
diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h
index 2e2fa18f81..ba5b54a7bd 100644
--- a/Utilities/cmcurl/lib/socks.h
+++ b/Utilities/cmcurl/lib/socks.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,8 +37,8 @@
*
* This is STUPID BLOCKING behavior
*/
-int Curl_blockread_all(struct Curl_easy *data,
- curl_socket_t sockfd,
+int Curl_blockread_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
char *buf,
ssize_t buffersize,
ssize_t *n);
@@ -47,7 +47,7 @@ int Curl_blockread_all(struct Curl_easy *data,
/*
* This function handles the SOCKS5 GSS-API negotiation and initialization
*/
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data);
#endif
@@ -55,6 +55,11 @@ CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
+extern struct Curl_cftype Curl_cft_socks_proxy;
+
#endif /* CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_SOCKS_H */
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index f14099febf..2ede8c7c29 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,6 +30,7 @@
#include "curl_gssapi.h"
#include "urldata.h"
#include "sendf.h"
+#include "cfilters.h"
#include "connect.h"
#include "timeval.h"
#include "socks.h"
@@ -101,14 +102,14 @@ static int check_gss_err(struct Curl_easy *data,
return 0;
}
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- curl_socket_t sock = conn->sock[sockindex];
+ struct connectdata *conn = cf->conn;
+ curl_socket_t sock = conn->sock[cf->sockindex];
CURLcode code;
ssize_t actualread;
- ssize_t written;
+ ssize_t nwritten;
int result;
OM_uint32 gss_major_status, gss_minor_status, gss_status;
OM_uint32 gss_ret_flags;
@@ -203,8 +204,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
us_length = htons((short)gss_send_token.length);
memcpy(socksreq + 2, &us_length, sizeof(short));
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
- if(code || (4 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API authentication request.");
gss_release_name(&gss_status, &server);
gss_release_buffer(&gss_status, &gss_recv_token);
@@ -213,10 +214,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
- code = Curl_write_plain(data, sock, (char *)gss_send_token.value,
- gss_send_token.length, &written);
-
- if(code || ((ssize_t)gss_send_token.length != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data,
+ (char *)gss_send_token.value,
+ gss_send_token.length, &code);
+ if(code || ((ssize_t)gss_send_token.length != nwritten)) {
failf(data, "Failed to send GSS-API authentication token.");
gss_release_name(&gss_status, &server);
gss_release_buffer(&gss_status, &gss_recv_token);
@@ -242,7 +243,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
* +----+------+-----+----------------+
*/
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive GSS-API authentication response.");
gss_release_name(&gss_status, &server);
@@ -281,7 +282,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
gss_recv_token.length, &actualread);
if(result || (actualread != us_length)) {
@@ -410,8 +411,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
- if(code || (4 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
+ if(code || (4 != nwritten)) {
failf(data, "Failed to send GSS-API encryption request.");
gss_release_buffer(&gss_status, &gss_w_token);
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -420,17 +421,18 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- code = Curl_write_plain(data, sock, socksreq, 1, &written);
- if(code || ( 1 != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
+ if(code || ( 1 != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_COULDNT_CONNECT;
}
}
else {
- code = Curl_write_plain(data, sock, (char *)gss_w_token.value,
- gss_w_token.length, &written);
- if(code || ((ssize_t)gss_w_token.length != written)) {
+ nwritten = Curl_conn_cf_send(cf->next, data,
+ (char *)gss_w_token.value,
+ gss_w_token.length, &code);
+ if(code || ((ssize_t)gss_w_token.length != nwritten)) {
failf(data, "Failed to send GSS-API encryption type.");
gss_release_buffer(&gss_status, &gss_w_token);
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -439,7 +441,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
gss_release_buffer(&gss_status, &gss_w_token);
}
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive GSS-API encryption response.");
gss_delete_sec_context(&gss_status, &gss_context, NULL);
@@ -470,7 +472,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
gss_delete_sec_context(&gss_status, &gss_context, NULL);
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)gss_recv_token.value,
+ result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value,
gss_recv_token.length, &actualread);
if(result || (actualread != us_length)) {
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index 210a0dfbc5..d1200ea037 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012, 2011, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -29,6 +29,7 @@
#include "urldata.h"
#include "sendf.h"
+#include "cfilters.h"
#include "connect.h"
#include "strerror.h"
#include "timeval.h"
@@ -62,11 +63,11 @@ static int check_sspi_err(struct Curl_easy *data,
}
/* This is the SSPI-using version of this function */
-CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
+CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct connectdata *conn = data->conn;
- curl_socket_t sock = conn->sock[sockindex];
+ struct connectdata *conn = cf->conn;
+ curl_socket_t sock = conn->sock[cf->sockindex];
CURLcode code;
ssize_t actualread;
ssize_t written;
@@ -206,7 +207,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
us_length = htons((short)sspi_send_token.cbBuffer);
memcpy(socksreq + 2, &us_length, sizeof(short));
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
if(code || (4 != written)) {
failf(data, "Failed to send SSPI authentication request.");
free(service_name);
@@ -219,8 +220,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
- code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &written);
+ written = Curl_conn_cf_send(cf->next, data,
+ (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &code);
if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
failf(data, "Failed to send SSPI authentication token.");
free(service_name);
@@ -260,7 +262,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
* +----+------+-----+----------------+
*/
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI authentication response.");
free(service_name);
@@ -300,7 +302,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
s_pSecFn->DeleteSecurityContext(&sspi_context);
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)sspi_recv_token.pvBuffer,
+ result = Curl_blockread_all(cf, data, (char *)sspi_recv_token.pvBuffer,
sspi_recv_token.cbBuffer, &actualread);
if(result || (actualread != us_length)) {
@@ -468,7 +470,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
memcpy(socksreq + 2, &us_length, sizeof(short));
}
- code = Curl_write_plain(data, sock, (char *)socksreq, 4, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, &code);
if(code || (4 != written)) {
failf(data, "Failed to send SSPI encryption request.");
if(sspi_send_token.pvBuffer)
@@ -479,7 +481,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
if(data->set.socks5_gssapi_nec) {
memcpy(socksreq, &gss_enc, 1);
- code = Curl_write_plain(data, sock, (char *)socksreq, 1, &written);
+ written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, &code);
if(code || (1 != written)) {
failf(data, "Failed to send SSPI encryption type.");
s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -487,8 +489,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
}
}
else {
- code = Curl_write_plain(data, sock, (char *)sspi_send_token.pvBuffer,
- sspi_send_token.cbBuffer, &written);
+ written = Curl_conn_cf_send(cf->next, data,
+ (char *)sspi_send_token.pvBuffer,
+ sspi_send_token.cbBuffer, &code);
if(code || (sspi_send_token.cbBuffer != (size_t)written)) {
failf(data, "Failed to send SSPI encryption type.");
if(sspi_send_token.pvBuffer)
@@ -500,7 +503,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
s_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer);
}
- result = Curl_blockread_all(data, sock, (char *)socksreq, 4, &actualread);
+ result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread);
if(result || (actualread != 4)) {
failf(data, "Failed to receive SSPI encryption response.");
s_pSecFn->DeleteSecurityContext(&sspi_context);
@@ -532,7 +535,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_OUT_OF_MEMORY;
}
- result = Curl_blockread_all(data, sock, (char *)sspi_w_token[0].pvBuffer,
+ result = Curl_blockread_all(cf, data, (char *)sspi_w_token[0].pvBuffer,
sspi_w_token[0].cbBuffer, &actualread);
if(result || (actualread != us_length)) {
diff --git a/Utilities/cmcurl/lib/speedcheck.c b/Utilities/cmcurl/lib/speedcheck.c
index 3ddc43d2de..580efbde75 100644
--- a/Utilities/cmcurl/lib/speedcheck.c
+++ b/Utilities/cmcurl/lib/speedcheck.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/speedcheck.h b/Utilities/cmcurl/lib/speedcheck.h
index cb44eb04ec..bff2f32b77 100644
--- a/Utilities/cmcurl/lib/speedcheck.h
+++ b/Utilities/cmcurl/lib/speedcheck.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/splay.c b/Utilities/cmcurl/lib/splay.c
index 33b44aa1c6..48e079b32a 100644
--- a/Utilities/cmcurl/lib/splay.c
+++ b/Utilities/cmcurl/lib/splay.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/splay.h b/Utilities/cmcurl/lib/splay.h
index 015e2ca52f..dd1d07ac2e 100644
--- a/Utilities/cmcurl/lib/splay.h
+++ b/Utilities/cmcurl/lib/splay.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1997 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strcase.c b/Utilities/cmcurl/lib/strcase.c
index 7fb9c80870..7c0b4ef909 100644
--- a/Utilities/cmcurl/lib/strcase.c
+++ b/Utilities/cmcurl/lib/strcase.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strcase.h b/Utilities/cmcurl/lib/strcase.h
index 192e0da099..8c50bbcba6 100644
--- a/Utilities/cmcurl/lib/strcase.h
+++ b/Utilities/cmcurl/lib/strcase.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c
index ac22b6ddaf..07a61391ae 100644
--- a/Utilities/cmcurl/lib/strdup.c
+++ b/Utilities/cmcurl/lib/strdup.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -37,7 +37,7 @@
#include "memdebug.h"
#ifndef HAVE_STRDUP
-char *curlx_strdup(const char *str)
+char *Curl_strdup(const char *str)
{
size_t len;
char *newstr;
diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h
index fb46808b83..c3430b54d5 100644
--- a/Utilities/cmcurl/lib/strdup.h
+++ b/Utilities/cmcurl/lib/strdup.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,7 +26,7 @@
#include "curl_setup.h"
#ifndef HAVE_STRDUP
-extern char *curlx_strdup(const char *str);
+char *Curl_strdup(const char *str);
#endif
#ifdef WIN32
wchar_t* Curl_wcsdup(const wchar_t* src);
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index b9a51e26b9..3ec10e3695 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2004 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -550,6 +550,9 @@ curl_url_strerror(CURLUcode error)
case CURLUE_BAD_USER:
return "Bad user";
+ case CURLUE_LACKS_IDN:
+ return "libcurl lacks IDN support";
+
case CURLUE_LAST:
break;
}
diff --git a/Utilities/cmcurl/lib/strerror.h b/Utilities/cmcurl/lib/strerror.h
index 658f16c10e..399712f8eb 100644
--- a/Utilities/cmcurl/lib/strerror.h
+++ b/Utilities/cmcurl/lib/strerror.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtok.c b/Utilities/cmcurl/lib/strtok.c
index 6120bcc28e..d8e1e8183f 100644
--- a/Utilities/cmcurl/lib/strtok.c
+++ b/Utilities/cmcurl/lib/strtok.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtok.h b/Utilities/cmcurl/lib/strtok.h
index 641a3daed8..321cba2326 100644
--- a/Utilities/cmcurl/lib/strtok.h
+++ b/Utilities/cmcurl/lib/strtok.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtoofft.c b/Utilities/cmcurl/lib/strtoofft.c
index fb8d92196f..077b25792e 100644
--- a/Utilities/cmcurl/lib/strtoofft.c
+++ b/Utilities/cmcurl/lib/strtoofft.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/strtoofft.h b/Utilities/cmcurl/lib/strtoofft.h
index 311dae4403..34d293ba38 100644
--- a/Utilities/cmcurl/lib/strtoofft.h
+++ b/Utilities/cmcurl/lib/strtoofft.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/system_win32.c b/Utilities/cmcurl/lib/system_win32.c
index bede9c7dcd..0cdaf3b2fe 100644
--- a/Utilities/cmcurl/lib/system_win32.c
+++ b/Utilities/cmcurl/lib/system_win32.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h
index 167804e3c5..24899cb2d5 100644
--- a/Utilities/cmcurl/lib/system_win32.h
+++ b/Utilities/cmcurl/lib/system_win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 22bc81e755..e4ffd853ac 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -770,22 +770,32 @@ static void printsub(struct Curl_easy *data,
}
}
+static bool str_is_nonascii(const char *str)
+{
+ size_t len = strlen(str);
+ while(len--) {
+ if(*str & 0x80)
+ return TRUE;
+ str++;
+ }
+ return FALSE;
+}
+
static CURLcode check_telnet_options(struct Curl_easy *data)
{
struct curl_slist *head;
struct curl_slist *beg;
- char option_keyword[128] = "";
- char option_arg[256] = "";
struct TELNET *tn = data->req.p.telnet;
- struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
- int binary_option;
/* Add the user name as an environment variable if it
was given on the command line */
if(data->state.aptr.user) {
- msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
- beg = curl_slist_append(tn->telnet_vars, option_arg);
+ char buffer[256];
+ if(str_is_nonascii(data->conn->user))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
+ beg = curl_slist_append(tn->telnet_vars, buffer);
if(!beg) {
curl_slist_free_all(tn->telnet_vars);
tn->telnet_vars = NULL;
@@ -795,68 +805,100 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
}
- for(head = data->set.telnet_options; head; head = head->next) {
- if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
- option_keyword, option_arg) == 2) {
-
- /* Terminal type */
- if(strcasecompare(option_keyword, "TTYPE")) {
- strncpy(tn->subopt_ttype, option_arg, 31);
- tn->subopt_ttype[31] = 0; /* String termination */
- tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ for(head = data->set.telnet_options; head && !result; head = head->next) {
+ size_t olen;
+ char *option = head->data;
+ char *arg;
+ char *sep = strchr(option, '=');
+ if(sep) {
+ olen = sep - option;
+ arg = ++sep;
+ if(str_is_nonascii(arg))
continue;
- }
+ switch(olen) {
+ case 5:
+ /* Terminal type */
+ if(strncasecompare(option, "TTYPE", 5)) {
+ strncpy(tn->subopt_ttype, arg, 31);
+ tn->subopt_ttype[31] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
+ }
+ else
+ result = CURLE_UNKNOWN_OPTION;
+ break;
- /* Display variable */
- if(strcasecompare(option_keyword, "XDISPLOC")) {
- strncpy(tn->subopt_xdisploc, option_arg, 127);
- tn->subopt_xdisploc[127] = 0; /* String termination */
- tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
- continue;
- }
+ case 8:
+ /* Display variable */
+ if(strncasecompare(option, "XDISPLOC", 8)) {
+ strncpy(tn->subopt_xdisploc, arg, 127);
+ tn->subopt_xdisploc[127] = 0; /* String termination */
+ tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
+ }
+ else
+ result = CURLE_UNKNOWN_OPTION;
+ break;
- /* Environment variable */
- if(strcasecompare(option_keyword, "NEW_ENV")) {
- beg = curl_slist_append(tn->telnet_vars, option_arg);
- if(!beg) {
- result = CURLE_OUT_OF_MEMORY;
- break;
+ case 7:
+ /* Environment variable */
+ if(strncasecompare(option, "NEW_ENV", 7)) {
+ beg = curl_slist_append(tn->telnet_vars, arg);
+ if(!beg) {
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+ tn->telnet_vars = beg;
+ tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
}
- tn->telnet_vars = beg;
- tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
- continue;
- }
+ else
+ result = CURLE_UNKNOWN_OPTION;
+ break;
- /* Window Size */
- if(strcasecompare(option_keyword, "WS")) {
- if(sscanf(option_arg, "%hu%*[xX]%hu",
- &tn->subopt_wsx, &tn->subopt_wsy) == 2)
- tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
- else {
- failf(data, "Syntax error in telnet option: %s", head->data);
- result = CURLE_SETOPT_OPTION_SYNTAX;
- break;
+ case 2:
+ /* Window Size */
+ if(strncasecompare(option, "WS", 2)) {
+ char *p;
+ unsigned long x = strtoul(arg, &p, 10);
+ unsigned long y = 0;
+ if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
+ p++;
+ y = strtoul(p, NULL, 10);
+ if(y && (y <= 0xffff)) {
+ tn->subopt_wsx = (unsigned short)x;
+ tn->subopt_wsy = (unsigned short)y;
+ tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+ }
+ }
+ if(!y) {
+ failf(data, "Syntax error in telnet option: %s", head->data);
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+ }
}
- continue;
- }
+ else
+ result = CURLE_UNKNOWN_OPTION;
+ break;
- /* To take care or not of the 8th bit in data exchange */
- if(strcasecompare(option_keyword, "BINARY")) {
- binary_option = atoi(option_arg);
- if(binary_option != 1) {
- tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
- tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+ case 6:
+ /* To take care or not of the 8th bit in data exchange */
+ if(strncasecompare(option, "BINARY", 6)) {
+ int binary_option = atoi(arg);
+ if(binary_option != 1) {
+ tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+ tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+ }
}
- continue;
+ else
+ result = CURLE_UNKNOWN_OPTION;
+ break;
+ default:
+ failf(data, "Unknown telnet option %s", head->data);
+ result = CURLE_UNKNOWN_OPTION;
+ break;
}
-
- failf(data, "Unknown telnet option %s", head->data);
- result = CURLE_UNKNOWN_OPTION;
- break;
}
- failf(data, "Syntax error in telnet option: %s", head->data);
- result = CURLE_SETOPT_OPTION_SYNTAX;
- break;
+ else {
+ failf(data, "Syntax error in telnet option: %s", head->data);
+ result = CURLE_SETOPT_OPTION_SYNTAX;
+ }
}
if(result) {
@@ -881,8 +923,6 @@ static void suboption(struct Curl_easy *data)
ssize_t bytes_written;
size_t len;
int err;
- char varname[128] = "";
- char varval[128] = "";
struct TELNET *tn = data->req.p.telnet;
struct connectdata *conn = data->conn;
@@ -920,19 +960,18 @@ static void suboption(struct Curl_easy *data)
for(v = tn->telnet_vars; v; v = v->next) {
size_t tmplen = (strlen(v->data) + 1);
- /* Add the variable only if it fits */
+ /* Add the variable if it fits */
if(len + tmplen < (int)sizeof(temp)-6) {
- int rv;
- char sep[2] = "";
- varval[0] = 0;
- rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
- if(rv == 1)
+ char *s = strchr(v->data, ',');
+ if(!s)
len += msnprintf((char *)&temp[len], sizeof(temp) - len,
- "%c%s", CURL_NEW_ENV_VAR, varname);
- else if(rv >= 2)
+ "%c%s", CURL_NEW_ENV_VAR, v->data);
+ else {
+ size_t vlen = s - v->data;
len += msnprintf((char *)&temp[len], sizeof(temp) - len,
- "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
- CURL_NEW_ENV_VALUE, varval);
+ "%c%.*s%c%s", CURL_NEW_ENV_VAR,
+ (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
+ }
}
}
msnprintf((char *)&temp[len], sizeof(temp) - len,
diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h
index 6dd99b48dc..30782d8375 100644
--- a/Utilities/cmcurl/lib/telnet.h
+++ b/Utilities/cmcurl/lib/telnet.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index 9e6d9490ed..164d3c723c 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,6 +48,7 @@
#include "urldata.h"
#include <curl/curl.h>
+#include "cf-socket.h"
#include "transfer.h"
#include "sendf.h"
#include "tftp.h"
@@ -529,8 +530,8 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
not have a size_t argument, like older unixes that want an 'int' */
senddata = sendto(state->sockfd, (void *)state->spacket.data,
(SEND_TYPE_ARG3)sbytes, 0,
- data->conn->ip_addr->ai_addr,
- data->conn->ip_addr->ai_addrlen);
+ &data->conn->remote_addr->sa_addr,
+ data->conn->remote_addr->addrlen);
if(senddata != (ssize_t)sbytes) {
char buffer[STRERROR_LEN];
failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
@@ -1014,7 +1015,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
state->requested_blksize = blksize;
((struct sockaddr *)&state->local_addr)->sa_family =
- (CURL_SA_FAMILY_T)(conn->ip_addr->ai_family);
+ (CURL_SA_FAMILY_T)(conn->remote_addr->family);
tftp_set_timeouts(state);
@@ -1033,7 +1034,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
* IPv4 and IPv6...
*/
int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr,
- conn->ip_addr->ai_addrlen);
+ conn->remote_addr->addrlen);
if(rc) {
char buffer[STRERROR_LEN];
failf(data, "bind() failed; %s",
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h
index 3f1fda6382..5d2d5da615 100644
--- a/Utilities/cmcurl/lib/tftp.h
+++ b/Utilities/cmcurl/lib/tftp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timediff.c b/Utilities/cmcurl/lib/timediff.c
index c5893187dd..1b762bbd3e 100644
--- a/Utilities/cmcurl/lib/timediff.c
+++ b/Utilities/cmcurl/lib/timediff.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timediff.h b/Utilities/cmcurl/lib/timediff.h
index 90e547457f..fb318d4f2b 100644
--- a/Utilities/cmcurl/lib/timediff.h
+++ b/Utilities/cmcurl/lib/timediff.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c
index 647d7b0fc5..dca1c6fe50 100644
--- a/Utilities/cmcurl/lib/timeval.c
+++ b/Utilities/cmcurl/lib/timeval.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h
index 8d4fef4e17..92e484ad1d 100644
--- a/Utilities/cmcurl/lib/timeval.h
+++ b/Utilities/cmcurl/lib/timeval.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index ba0410fc51..a28395233c 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,6 +73,7 @@
#include "url.h"
#include "getinfo.h"
#include "vtls/vtls.h"
+#include "vquic/vquic.h"
#include "select.h"
#include "multiif.h"
#include "connect.h"
@@ -367,27 +368,12 @@ static int data_pending(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC)
- return Curl_quic_data_pending(data);
-#endif
-
if(conn->handler->protocol&PROTO_FAMILY_FTP)
return Curl_conn_data_pending(data, SECONDARYSOCKET);
/* in the case of libssh2, we can never be really sure that we have emptied
its internal buffers so we MUST always try until we get EAGAIN back */
return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
-#ifdef USE_NGHTTP2
- /* For HTTP/2, we may read up everything including response body
- with header fields in Curl_http_readwrite_headers. If no
- content-length is provided, curl waits for the connection
- close, which we emulate it using conn->proto.httpc.closed =
- TRUE. The thing is if we read everything, then http2_recv won't
- be called and we cannot signal the HTTP/2 stream has closed. As
- a workaround, we return nonzero here to call http2_recv. */
- ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20) ||
-#endif
Curl_conn_data_pending(data, FIRSTSOCKET);
}
@@ -454,29 +440,16 @@ static CURLcode readwrite_data(struct Curl_easy *data,
bool is_empty_data = FALSE;
size_t buffersize = data->set.buffer_size;
size_t bytestoread = buffersize;
-#ifdef USE_NGHTTP2
- bool is_http2 = ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- (conn->httpversion == 20));
-#endif
- bool is_http3 =
-#ifdef ENABLE_QUIC
- ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- (conn->httpversion == 30));
-#else
- FALSE;
-#endif
-
- if(
-#ifdef USE_NGHTTP2
- /* For HTTP/2, read data without caring about the content length. This
- is safe because body in HTTP/2 is always segmented thanks to its
- framing layer. Meanwhile, we have to call Curl_read to ensure that
- http2_handle_stream_close is called when we read all incoming bytes
- for a particular stream. */
- !is_http2 &&
-#endif
- !is_http3 && /* Same reason mentioned above. */
- k->size != -1 && !k->header) {
+ /* For HTTP/2 and HTTP/3, read data without caring about the content
+ length. This is safe because body in HTTP/2 is always segmented
+ thanks to its framing layer. Meanwhile, we have to call Curl_read
+ to ensure that http2_handle_stream_close is called when we read all
+ incoming bytes for a particular stream. */
+ bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
+ bool data_eof_handled = is_http3
+ || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
+
+ if(!data_eof_handled && k->size != -1 && !k->header) {
/* make sure we don't read too much */
curl_off_t totalleft = k->size - k->bytecount;
if(totalleft < (curl_off_t)bytestoread)
@@ -499,7 +472,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
else {
/* read nothing but since we wanted nothing we consider this an OK
situation to proceed from */
- DEBUGF(infof(data, "readwrite_data: we're done"));
+ DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done")));
nread = 0;
}
@@ -518,14 +491,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
buf[nread] = 0;
}
else {
- /* if we receive 0 or less here, either the http2 stream is closed or the
+ /* if we receive 0 or less here, either the data transfer is done or the
server closed the connection and we bail out from this! */
-#ifdef USE_NGHTTP2
- if(is_http2 && !nread)
- DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
- else
-#endif
- if(is_http3 && !nread)
+ if(data_eof_handled)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
@@ -776,8 +744,8 @@ static CURLcode readwrite_data(struct Curl_easy *data,
k->keepon &= ~KEEP_RECV;
}
- if(k->keepon & KEEP_RECV_PAUSE) {
- /* this is a paused transfer */
+ if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
+ /* this is a paused or stopped transfer */
break;
}
@@ -799,19 +767,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
}
out:
- DEBUGF(infof(data, "readwrite_data(handle=%p) -> %d", data, result));
+ if(result)
+ DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result));
return result;
}
CURLcode Curl_done_sending(struct Curl_easy *data,
struct SingleRequest *k)
{
- struct connectdata *conn = data->conn;
k->keepon &= ~KEEP_SEND; /* we're done writing */
/* These functions should be moved into the handler struct! */
- Curl_http2_done_sending(data, conn);
- Curl_quic_done_sending(data);
+ Curl_conn_ev_data_done_send(data);
return CURLE_OK;
}
@@ -1013,7 +980,15 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
if(result)
return result;
- win_update_buffer_size(conn->writesockfd);
+#if defined(WIN32) && defined(USE_WINSOCK)
+ {
+ struct curltime n = Curl_now();
+ if(Curl_timediff(n, k->last_sndbuf_update) > 1000) {
+ win_update_buffer_size(conn->writesockfd);
+ k->last_sndbuf_update = n;
+ }
+ }
+#endif
if(k->pendingheader) {
/* parts of what was sent was header */
@@ -1088,6 +1063,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
{
struct SingleRequest *k = &data->req;
CURLcode result;
+ struct curltime now;
int didwhat = 0;
curl_socket_t fd_read;
@@ -1113,6 +1089,8 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->state.drain) {
select_res |= CURL_CSELECT_IN;
DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ select_res |= CURL_CSELECT_OUT;
}
#endif
@@ -1155,7 +1133,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
#endif
- k->now = Curl_now();
+ now = Curl_now();
if(!didwhat) {
/* no read no write, this is a timeout? */
if(k->exp100 == EXP100_AWAITING_CONTINUE) {
@@ -1172,7 +1150,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
*/
- timediff_t ms = Curl_timediff(k->now, k->start100);
+ timediff_t ms = Curl_timediff(now, k->start100);
if(ms >= data->set.expect_100_timeout) {
/* we've waited long enough, continue anyway */
k->exp100 = EXP100_SEND_DATA;
@@ -1182,35 +1160,31 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
}
-#ifdef ENABLE_QUIC
- if(conn->transport == TRNSPRT_QUIC) {
- result = Curl_quic_idle(data);
- if(result)
- goto out;
- }
-#endif
+ result = Curl_conn_ev_data_idle(data);
+ if(result)
+ goto out;
}
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, k->now);
+ result = Curl_speedcheck(data, now);
if(result)
goto out;
if(k->keepon) {
- if(0 > Curl_timeleft(data, &k->now, FALSE)) {
+ if(0 > Curl_timeleft(data, &now, FALSE)) {
if(k->size != -1) {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
CURL_FORMAT_CURL_OFF_T " bytes received",
- Curl_timediff(k->now, data->progress.t_startsingle),
+ Curl_timediff(now, data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
- Curl_timediff(k->now, data->progress.t_startsingle),
+ Curl_timediff(now, data->progress.t_startsingle),
k->bytecount);
}
result = CURLE_OPERATION_TIMEDOUT;
@@ -1260,11 +1234,11 @@ CURLcode Curl_readwrite(struct connectdata *conn,
}
/* Now update the "done" boolean we return */
- *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
- KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;
+ *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE;
result = CURLE_OK;
out:
- DEBUGF(infof(data, "Curl_readwrite(handle=%p) -> %d", data, result));
+ if(result)
+ DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result));
return result;
}
@@ -1377,6 +1351,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
Curl_safefree(data->info.wouldredirect);
+ Curl_data_priority_clear_state(data);
if(data->state.httpreq == HTTPREQ_PUT)
data->state.infilesize = data->set.filesize;
@@ -1389,15 +1364,16 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
else
data->state.infilesize = 0;
-#ifndef CURL_DISABLE_COOKIES
/* If there is a list of cookie files to read, do it now! */
- if(data->state.cookielist)
- Curl_cookie_loadfiles(data);
-#endif
+ Curl_cookie_loadfiles(data);
+
/* If there is a list of host pairs to deal with */
if(data->state.resolve)
result = Curl_loadhostpairs(data);
+ /* If there is a list of hsts files to read */
+ Curl_hsts_loadfiles(data);
+
if(!result) {
/* Allow data->set.use_port to set which port to use. This needs to be
* disabled for example when we follow Location: headers to URLs using
@@ -1425,7 +1401,13 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
#ifndef CURL_DISABLE_FTP
data->state.wildcardmatch = data->set.wildcard_enabled;
if(data->state.wildcardmatch) {
- struct WildcardData *wc = &data->wildcard;
+ struct WildcardData *wc;
+ if(!data->wildcard) {
+ data->wildcard = calloc(1, sizeof(struct WildcardData));
+ if(!data->wildcard)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ wc = data->wildcard;
if(wc->state < CURLWC_INIT) {
result = Curl_wildcard_init(wc); /* init wildcard structures */
if(result)
@@ -1433,7 +1415,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
#endif
- Curl_http2_init_state(&data->state);
result = Curl_hsts_loadcb(data, data->hsts);
}
@@ -1871,7 +1852,7 @@ Curl_setup_transfer(
httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
(http->sending == HTTPSEND_REQUEST));
- if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) {
+ if(conn->bits.multiplex || conn->httpversion >= 20 || httpsending) {
/* when multiplexing, the read/write sockets need to be the same! */
conn->sockfd = sockindex == -1 ?
((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h
index 4092508729..536ac249b7 100644
--- a/Utilities/cmcurl/lib/transfer.h
+++ b/Utilities/cmcurl/lib/transfer.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 3ab63a068f..f7b4bbbe90 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -288,33 +288,6 @@ static const struct Curl_handler * const protocols[] = {
(struct Curl_handler *) NULL
};
-/*
- * Dummy handler for undefined protocol schemes.
- */
-
-static const struct Curl_handler Curl_handler_dummy = {
- "<no protocol>", /* scheme */
- ZERO_NULL, /* setup_connection */
- ZERO_NULL, /* do_it */
- ZERO_NULL, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- ZERO_NULL, /* proto_getsock */
- ZERO_NULL, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ZERO_NULL, /* perform_getsock */
- ZERO_NULL, /* disconnect */
- ZERO_NULL, /* readwrite */
- ZERO_NULL, /* connection_check */
- ZERO_NULL, /* attach connection */
- 0, /* defport */
- 0, /* protocol */
- 0, /* family */
- PROTOPT_NONE /* flags */
-};
-
void Curl_freeset(struct Curl_easy *data)
{
/* Free all dynamic strings stored in the data->set substructure. */
@@ -341,6 +314,11 @@ void Curl_freeset(struct Curl_easy *data)
data->state.url = NULL;
Curl_mime_cleanpart(&data->set.mimepost);
+
+#ifndef CURL_DISABLE_COOKIES
+ curl_slist_free_all(data->set.cookielist);
+ data->set.cookielist = NULL;
+#endif
}
/* free the URL pieces */
@@ -434,7 +412,11 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(&data->asi);
Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
- Curl_hsts_cleanup(&data->hsts);
+#ifndef CURL_DISABLE_HSTS
+ if(!data->share || !data->share->hsts)
+ Curl_hsts_cleanup(&data->hsts);
+ curl_slist_free_all(data->set.hstslist); /* clean up list */
+#endif
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
@@ -445,7 +427,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_resolver_cancel(data);
Curl_resolver_cleanup(data->state.async.resolver);
- Curl_http2_cleanup_dependencies(data);
+ Curl_data_priority_cleanup(data);
/* No longer a dirty share, if it exists */
if(data->share) {
@@ -531,11 +513,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
/* Timeout every 24 hours by default */
set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
- set->proxyport = 0;
- set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
set->httpauth = CURLAUTH_BASIC; /* defaults to basic */
#ifndef CURL_DISABLE_PROXY
+ set->proxyport = 0;
+ set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
/* SOCKS5 proxy auth defaults to username/password + GSS-API */
set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
@@ -556,11 +538,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
#endif
set->ssl.primary.verifypeer = TRUE;
set->ssl.primary.verifyhost = TRUE;
-#ifdef USE_TLS_SRP
- set->ssl.primary.authtype = CURL_TLSAUTH_NONE;
-#endif
- /* defaults to any auth type */
+#ifdef USE_SSH
+ /* defaults to any auth type */
set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
+ set->new_directory_perms = 0755; /* Default permissions */
+#endif
set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by
default */
#ifndef CURL_DISABLE_PROXY
@@ -568,7 +550,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
#endif
set->new_file_perms = 0644; /* Default permissions */
- set->new_directory_perms = 0755; /* Default permissions */
set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP |
CURLPROTO_FTPS;
@@ -631,14 +612,15 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->maxage_conn = 118;
set->maxlifetime_conn = 0;
set->http09_allowed = FALSE;
- set->httpwant =
#ifdef USE_HTTP2
- CURL_HTTP_VERSION_2TLS
+ set->httpwant = CURL_HTTP_VERSION_2TLS
#else
- CURL_HTTP_VERSION_1_1
+ set->httpwant = CURL_HTTP_VERSION_1_1
#endif
;
- Curl_http2_init_userset(set);
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ memset(&set->priority, 0, sizeof(set->priority));
+#endif
set->quick_exit = 0L;
return result;
}
@@ -698,45 +680,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
return result;
}
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-static void conn_reset_postponed_data(struct connectdata *conn, int num)
-{
- struct postponed_data * const psnd = &(conn->postponed[num]);
- if(psnd->buffer) {
- DEBUGASSERT(psnd->allocated_size > 0);
- DEBUGASSERT(psnd->recv_size <= psnd->allocated_size);
- DEBUGASSERT(psnd->recv_size ?
- (psnd->recv_processed < psnd->recv_size) :
- (psnd->recv_processed == 0));
- DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD);
- free(psnd->buffer);
- psnd->buffer = NULL;
- psnd->allocated_size = 0;
- psnd->recv_size = 0;
- psnd->recv_processed = 0;
-#ifdef DEBUGBUILD
- psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
- }
- else {
- DEBUGASSERT(psnd->allocated_size == 0);
- DEBUGASSERT(psnd->recv_size == 0);
- DEBUGASSERT(psnd->recv_processed == 0);
- DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD);
- }
-}
-
-static void conn_reset_all_postponed_data(struct connectdata *conn)
-{
- conn_reset_postponed_data(conn, 0);
- conn_reset_postponed_data(conn, 1);
-}
-#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-/* Use "do-nothing" macro instead of function when workaround not used */
-#define conn_reset_all_postponed_data(c) do {} while(0)
-#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
-
-
static void conn_shutdown(struct Curl_easy *data)
{
DEBUGASSERT(data);
@@ -777,14 +720,13 @@ static void conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->sasl_authzid);
Curl_safefree(conn->options);
Curl_safefree(conn->oauth_bearer);
+#ifndef CURL_DISABLE_HTTP
Curl_dyn_free(&conn->trailer);
+#endif
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
Curl_safefree(conn->hostname_resolve);
Curl_safefree(conn->secondaryhostname);
-
- conn_reset_all_postponed_data(conn);
- Curl_llist_destroy(&conn->easyq, NULL);
Curl_safefree(conn->localdev);
Curl_free_primary_ssl_config(&conn->ssl_config);
@@ -854,7 +796,7 @@ void Curl_disconnect(struct Curl_easy *data,
disconnect and shutdown */
Curl_attach_connection(data, conn);
- if(conn->handler->disconnect)
+ if(conn->handler && conn->handler->disconnect)
/* This is set if protocol-specific cleanups should be made */
conn->handler->disconnect(data, conn, dead_connection);
@@ -867,24 +809,6 @@ void Curl_disconnect(struct Curl_easy *data,
}
/*
- * This function should return TRUE if the socket is to be assumed to
- * be dead. Most commonly this happens when the server has closed the
- * connection due to inactivity.
- */
-static bool SocketIsDead(curl_socket_t sock)
-{
- int sval;
- bool ret_val = TRUE;
-
- sval = SOCKET_READABLE(sock, 0);
- if(sval == 0)
- /* timeout */
- ret_val = FALSE;
-
- return ret_val;
-}
-
-/*
* IsMultiplexingPossible()
*
* Return a bitmask with the available multiplexing options for the given
@@ -1014,8 +938,20 @@ static bool extract_if_dead(struct connectdata *conn,
}
else {
- /* Use the general method for determining the death of a connection */
- dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
+ bool input_pending;
+
+ dead = !Curl_conn_is_alive(data, conn, &input_pending);
+ if(input_pending) {
+ /* For reuse, we want a "clean" connection state. The includes
+ * that we expect - in general - no waiting input data. Input
+ * waiting might be a TLS Notify Close, for example. We reject
+ * that.
+ * For protocols where data from other other end may arrive at
+ * any time (HTTP/2 PING for example), the protocol handler needs
+ * to install its own `connection_check` callback.
+ */
+ dead = TRUE;
+ }
}
if(dead) {
@@ -1220,14 +1156,14 @@ ConnectionExists(struct Curl_easy *data,
continue;
}
}
+ }
- if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
- foundPendingCandidate = TRUE;
- /* Don't pick a connection that hasn't connected yet */
- infof(data, "Connection #%ld isn't open enough, can't reuse",
- check->connection_id);
- continue;
- }
+ if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
+ foundPendingCandidate = TRUE;
+ /* Don't pick a connection that hasn't connected yet */
+ infof(data, "Connection #%ld isn't open enough, can't reuse",
+ check->connection_id);
+ continue;
}
#ifdef USE_UNIX_SOCKETS
@@ -1341,17 +1277,37 @@ ConnectionExists(struct Curl_easy *data,
}
}
+ /* GSS delegation differences do not actually affect every connection
+ and auth method, but this check takes precaution before efficiency */
+ if(needle->gssapi_delegation != check->gssapi_delegation)
+ continue;
+
/* If multiplexing isn't enabled on the h2 connection and h1 is
explicitly requested, handle it: */
if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
- (check->httpversion >= 20) &&
- (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ (((check->httpversion >= 20) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_2_0))
+ || ((check->httpversion >= 30) &&
+ (data->state.httpwant < CURL_HTTP_VERSION_3))))
continue;
-
- if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) {
+#ifdef USE_SSH
+ else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
if(!ssh_config_matches(needle, check))
continue;
}
+#endif
+#ifndef CURL_DISABLE_FTP
+ else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
+ /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
+ if(Curl_timestrcmp(needle->proto.ftpc.account,
+ check->proto.ftpc.account) ||
+ Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
+ check->proto.ftpc.alternative_to_user) ||
+ (needle->proto.ftpc.use_ssl != check->proto.ftpc.use_ssl) ||
+ (needle->proto.ftpc.ccc != check->proto.ftpc.ccc))
+ continue;
+ }
+#endif
if((needle->handler->flags&PROTOPT_SSL)
#ifndef CURL_DISABLE_PROXY
@@ -1467,9 +1423,8 @@ ConnectionExists(struct Curl_easy *data,
#ifdef USE_NGHTTP2
/* If multiplexed, make sure we don't go over concurrency limit */
if(check->bits.multiplex) {
- /* Multiplexed connections can only be HTTP/2 for now */
- struct http_conn *httpc = &check->proto.httpc;
- if(multiplexed >= httpc->settings.max_concurrent_streams) {
+ if(multiplexed >= Curl_conn_get_max_concurrent(data, check,
+ FIRSTSOCKET)) {
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
multiplexed);
continue;
@@ -1543,23 +1498,13 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
if(!conn)
return NULL;
- conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined
- already from start to avoid NULL
- situations and checks */
-
/* and we setup a few fields in case we end up actually using this struct */
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
- conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
- conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connection_id = -1; /* no ID */
conn->port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown at this point */
-#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD)
- conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
- conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */
/* Default protocol-independent behavior doesn't support persistent
connections, so we set this to force-close. Protocols that support
@@ -1644,11 +1589,11 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->fclosesocket = data->set.fclosesocket;
conn->closesocket_client = data->set.closesocket_client;
conn->lastused = Curl_now(); /* used now */
+ conn->gssapi_delegation = data->set.gssapi_delegation;
return conn;
error:
- Curl_llist_destroy(&conn->easyq, NULL);
free(conn->localdev);
free(conn);
return NULL;
@@ -2329,7 +2274,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
goto error;
}
- /* path will be "/", if no path was was found */
+ /* path will be "/", if no path was found */
if(strcmp("/", path)) {
is_unix_proxy = TRUE;
free(host);
@@ -2412,6 +2357,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
char *socksproxy = NULL;
char *no_proxy = NULL;
CURLcode result = CURLE_OK;
+ bool spacesep = FALSE;
/*************************************************************
* Extract the user and password from the authentication string
@@ -2458,7 +2404,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
}
if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
- data->set.str[STRING_NOPROXY] : no_proxy)) {
+ data->set.str[STRING_NOPROXY] : no_proxy,
+ &spacesep)) {
Curl_safefree(proxy);
Curl_safefree(socksproxy);
}
@@ -2467,6 +2414,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
/* if the host is not in the noproxy list, detect proxy. */
proxy = detect_proxy(data, conn);
#endif /* CURL_DISABLE_HTTP */
+ if(spacesep)
+ infof(data, "space-separated NOPROXY patterns are deprecated");
Curl_safefree(no_proxy);
@@ -2795,7 +2744,7 @@ static CURLcode override_login(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
/* no user was set but a password, set a blank user */
- if(userp && !*userp && *passwdp) {
+ if(!*userp && *passwdp) {
*userp = strdup("");
if(!*userp)
return CURLE_OUT_OF_MEMORY;
@@ -3345,12 +3294,6 @@ static void reuse_conn(struct Curl_easy *data,
struct connectdata *temp,
struct connectdata *existing)
{
- /* 'local_ip' and 'local_port' get filled with local's numerical
- ip address and port number whenever an outgoing connection is
- **established** from the primary socket to a remote address. */
- char local_ip[MAX_IPADR_LEN] = "";
- int local_port = -1;
-
/* get the user+password information from the temp struct since it may
* be new for this request even when we re-use an existing connection */
if(temp->user) {
@@ -3382,6 +3325,20 @@ static void reuse_conn(struct Curl_easy *data,
}
#endif
+ /* Finding a connection for reuse in the cache matches, among other
+ * things on the "remote-relevant" hostname. This is not necessarily
+ * the authority of the URL, e.g. conn->host. For example:
+ * - we use a proxy (not tunneling). we want to send all requests
+ * that use the same proxy on this connection.
+ * - we have a "connect-to" setting that may redirect the hostname of
+ * a new request to the same remote endpoint of an existing conn.
+ * We want to reuse an existing conn to the remote endpoint.
+ * Since connection reuse does not match on conn->host necessarily, we
+ * switch `existing` conn to `temp` conn's host settings.
+ * TODO: is this correct in the case of TLS connections that have
+ * used the original hostname in SNI to negotiate? Do we send
+ * requests for another host through the different SNI?
+ */
Curl_free_idnconverted_hostname(&existing->host);
Curl_free_idnconverted_hostname(&existing->conn_to_host);
Curl_safefree(existing->host.rawalloc);
@@ -3398,15 +3355,6 @@ static void reuse_conn(struct Curl_easy *data,
existing->hostname_resolve = temp->hostname_resolve;
temp->hostname_resolve = NULL;
- /* persist connection info in session handle */
- if(existing->transport == TRNSPRT_TCP) {
- Curl_conninfo_local(data, existing->sock[FIRSTSOCKET],
- local_ip, &local_port);
- }
- Curl_persistconninfo(data, existing, local_ip, local_port);
-
- conn_reset_all_postponed_data(temp); /* free buffers */
-
/* re-use init */
existing->bits.reuse = TRUE; /* yes, we're re-using here */
@@ -3880,6 +3828,13 @@ static CURLcode create_conn(struct Curl_easy *data,
* Resolve the address of the server or proxy
*************************************************************/
result = resolve_server(data, conn, async);
+ if(result)
+ goto out;
+
+ /* Everything general done, inform filters that they need
+ * to prepare for a data transfer.
+ */
+ result = Curl_conn_ev_data_setup(data);
out:
return result;
@@ -4007,7 +3962,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
data->state.httpreq = HTTPREQ_HEAD;
k->start = Curl_now(); /* start time */
- k->now = k->start; /* current time is now */
k->header = TRUE; /* assume header */
k->bytecount = 0;
k->ignorebody = FALSE;
@@ -4018,3 +3972,103 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
return CURLE_OK;
}
+
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+
+#ifdef USE_NGHTTP2
+
+static void priority_remove_child(struct Curl_easy *parent,
+ struct Curl_easy *child)
+{
+ struct Curl_data_prio_node **pnext = &parent->set.priority.children;
+ struct Curl_data_prio_node *pnode = parent->set.priority.children;
+
+ DEBUGASSERT(child->set.priority.parent == parent);
+ while(pnode && pnode->data != child) {
+ pnext = &pnode->next;
+ pnode = pnode->next;
+ }
+
+ DEBUGASSERT(pnode);
+ if(pnode) {
+ *pnext = pnode->next;
+ free(pnode);
+ }
+
+ child->set.priority.parent = 0;
+ child->set.priority.exclusive = FALSE;
+}
+
+CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
+ struct Curl_easy *child,
+ bool exclusive)
+{
+ if(child->set.priority.parent) {
+ priority_remove_child(child->set.priority.parent, child);
+ }
+
+ if(parent) {
+ struct Curl_data_prio_node **tail;
+ struct Curl_data_prio_node *pnode;
+
+ pnode = calloc(1, sizeof(*pnode));
+ if(!pnode)
+ return CURLE_OUT_OF_MEMORY;
+ pnode->data = child;
+
+ if(parent->set.priority.children && exclusive) {
+ /* exclusive: move all existing children underneath the new child */
+ struct Curl_data_prio_node *node = parent->set.priority.children;
+ while(node) {
+ node->data->set.priority.parent = child;
+ node = node->next;
+ }
+
+ tail = &child->set.priority.children;
+ while(*tail)
+ tail = &(*tail)->next;
+
+ DEBUGASSERT(!*tail);
+ *tail = parent->set.priority.children;
+ parent->set.priority.children = 0;
+ }
+
+ tail = &parent->set.priority.children;
+ while(*tail) {
+ (*tail)->data->set.priority.exclusive = FALSE;
+ tail = &(*tail)->next;
+ }
+
+ DEBUGASSERT(!*tail);
+ *tail = pnode;
+ }
+
+ child->set.priority.parent = parent;
+ child->set.priority.exclusive = exclusive;
+ return CURLE_OK;
+}
+
+#endif /* USE_NGHTTP2 */
+
+void Curl_data_priority_cleanup(struct Curl_easy *data)
+{
+#ifdef USE_NGHTTP2
+ while(data->set.priority.children) {
+ struct Curl_easy *tmp = data->set.priority.children->data;
+ priority_remove_child(data, tmp);
+ if(data->set.priority.parent)
+ Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE);
+ }
+
+ if(data->set.priority.parent)
+ priority_remove_child(data->set.priority.parent, data);
+#endif
+ (void)data;
+}
+
+void Curl_data_priority_clear_state(struct Curl_easy *data)
+{
+ memset(&data->state.priority, 0, sizeof(data->state.priority));
+}
+
+#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h
index 1a03c564c6..3b58df403e 100644
--- a/Utilities/cmcurl/lib/url.h
+++ b/Utilities/cmcurl/lib/url.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -59,4 +59,20 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
void Curl_verboseconnect(struct Curl_easy *data, struct connectdata *conn);
#endif
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+void Curl_data_priority_cleanup(struct Curl_easy *data);
+void Curl_data_priority_clear_state(struct Curl_easy *data);
+#else
+#define Curl_data_priority_cleanup(x)
+#define Curl_data_priority_clear_state(x)
+#endif /* !(defined(USE_HTTP2) || defined(USE_HTTP3)) */
+
+#ifdef USE_NGHTTP2
+CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
+ struct Curl_easy *child,
+ bool exclusive);
+#else
+#define Curl_data_priority_add_child(x, y, z) CURLE_NOT_BUILT_IN
+#endif
+
#endif /* HEADER_CURL_URL_H */
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h
index 43a83ef6e4..28e5dd7ea4 100644
--- a/Utilities/cmcurl/lib/urlapi-int.h
+++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index b96af35ad2..62e3233064 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,6 +33,7 @@
#include "inet_pton.h"
#include "inet_ntop.h"
#include "strdup.h"
+#include "idn.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -56,6 +57,15 @@
/* scheme is not URL encoded, the longest libcurl supported ones are... */
#define MAX_SCHEME_LEN 40
+/*
+ * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
+ * sure we have _some_ value for AF_INET6 without polluting our fake value
+ * everywhere.
+ */
+#if !defined(ENABLE_IPV6) && !defined(AF_INET6)
+#define AF_INET6 (AF_INET + 1)
+#endif
+
/* Internal representation of CURLU. Point to URL-encoded strings. */
struct Curl_URL {
char *scheme;
@@ -116,14 +126,11 @@ static const char *find_host_sep(const char *url)
}
/*
- * Decide in an encoding-independent manner whether a character in a URL must
- * be escaped. This is used in urlencode_str().
+ * Decide whether a character in a URL must be escaped.
*/
-static bool urlchar_needs_escaping(int c)
-{
- return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
-}
+#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)))
+static const char hexdigits[] = "0123456789abcdef";
/* urlencode_str() writes data into an output dynbuf and URL-encodes the
* spaces in the source URL accordingly.
*
@@ -167,7 +174,10 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
left = FALSE;
if(urlchar_needs_escaping(*iptr)) {
- if(Curl_dyn_addf(o, "%%%02x", *iptr))
+ char out[3]={'%'};
+ out[1] = hexdigits[*iptr>>4];
+ out[2] = hexdigits[*iptr & 0xf];
+ if(Curl_dyn_addn(o, out, 3))
return CURLUE_OUT_OF_MEMORY;
}
else {
@@ -492,35 +502,21 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
bool has_scheme)
{
- char *portptr = NULL;
- char endbracket;
- int len;
+ char *portptr;
char *hostname = Curl_dyn_ptr(host);
/*
* Find the end of an IPv6 address, either on the ']' ending bracket or
* a percent-encoded zone index.
*/
- if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
- &endbracket, &len)) {
- if(']' == endbracket)
- portptr = &hostname[len];
- else if('%' == endbracket) {
- int zonelen = len;
- if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
- if(']' != endbracket)
- return CURLUE_BAD_IPV6;
- portptr = &hostname[--zonelen + len + 1];
- }
- else
- return CURLUE_BAD_IPV6;
- }
- else
+ if(hostname[0] == '[') {
+ portptr = strchr(hostname, ']');
+ if(!portptr)
return CURLUE_BAD_IPV6;
-
+ portptr++;
/* this is a RFC2732-style specified IP-address */
- if(portptr && *portptr) {
+ if(*portptr) {
if(*portptr != ':')
- return CURLUE_BAD_IPV6;
+ return CURLUE_BAD_PORT_NUMBER;
}
else
portptr = NULL;
@@ -584,11 +580,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
hostname++;
hlen -= 2;
- if(hostname[hlen] != ']')
- return CURLUE_BAD_IPV6;
-
- /* only valid letters are ok */
+ /* only valid IPv6 letters are ok */
len = strspn(hostname, l);
+
if(hlen != len) {
hlen = len;
if(hostname[len] == '%') {
@@ -602,8 +596,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
while(*h && (*h != ']') && (i < 15))
zoneid[i++] = *h++;
if(!i || (']' != *h))
- /* impossible to reach? */
- return CURLUE_MALFORMED_INPUT;
+ return CURLUE_BAD_IPV6;
zoneid[i] = 0;
u->zoneid = strdup(zoneid);
if(!u->zoneid)
@@ -615,7 +608,8 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
return CURLUE_BAD_IPV6;
/* hostname is fine */
}
-#ifdef ENABLE_IPV6
+
+ /* Check the IPv6 address. */
{
char dest[16]; /* fits a binary IPv6 address */
char norm[MAX_IPADR_LEN];
@@ -632,11 +626,10 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
}
hostname[hlen] = ']'; /* restore ending bracket */
}
-#endif
}
else {
/* letters from the second string are not ok */
- len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()");
+ len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%");
if(hlen != len)
/* hostname with bad content */
return CURLUE_BAD_HOSTNAME;
@@ -782,25 +775,28 @@ static CURLUcode decode_host(struct dynbuf *host)
*
* RETURNS
*
- * an allocated dedotdotified output string
+ * Zero for success and 'out' set to an allocated dedotdotified string.
*/
-UNITTEST char *dedotdotify(const char *input, size_t clen);
-UNITTEST char *dedotdotify(const char *input, size_t clen)
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
+UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
{
- char *out = malloc(clen + 1);
char *outptr;
const char *orginput = input;
char *queryp;
+ char *out;
+
+ *outp = NULL;
+ /* the path always starts with a slash, and a slash has not dot */
+ if((clen < 2) || !memchr(input, '.', clen))
+ return 0;
+
+ out = malloc(clen + 1);
if(!out)
- return NULL; /* out of memory */
+ return 1; /* out of memory */
*out = 0; /* null-terminates, for inputs like "./" */
outptr = out;
- if(!*input)
- /* zero length input string, return that */
- return out;
-
/*
* To handle query-parts properly, we must find it and remove it during the
* dotdot-operation and then append it again at the end to the output
@@ -905,7 +901,8 @@ UNITTEST char *dedotdotify(const char *input, size_t clen)
memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */
}
- return out;
+ *outp = out;
+ return 0; /* success */
}
static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
@@ -1152,7 +1149,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
size_t qlen = strlen(query) - fraglen; /* includes '?' */
pathlen = strlen(path) - qlen - fraglen;
if(qlen > 1) {
- if(qlen && (flags & CURLU_URLENCODE)) {
+ if(flags & CURLU_URLENCODE) {
struct dynbuf enc;
Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
/* skip the leading question mark */
@@ -1199,8 +1196,8 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
path = u->path = Curl_dyn_ptr(&enc);
}
- if(!pathlen) {
- /* there is no path left, unset */
+ if(pathlen <= 1) {
+ /* there is no path left or just the slash, unset */
path = NULL;
}
else {
@@ -1224,13 +1221,16 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(!(flags & CURLU_PATH_AS_IS)) {
/* remove ../ and ./ sequences according to RFC3986 */
- char *newp = dedotdotify((char *)path, pathlen);
- if(!newp) {
+ char *dedot;
+ int err = dedotdotify((char *)path, pathlen, &dedot);
+ if(err) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
}
- free(u->path);
- u->path = newp;
+ if(dedot) {
+ free(u->path);
+ u->path = dedot;
+ }
}
}
@@ -1350,7 +1350,7 @@ void curl_url_cleanup(CURLU *u)
} \
} while(0)
-CURLU *curl_url_dup(CURLU *in)
+CURLU *curl_url_dup(const CURLU *in)
{
struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1);
if(u) {
@@ -1371,14 +1371,15 @@ CURLU *curl_url_dup(CURLU *in)
return NULL;
}
-CURLUcode curl_url_get(CURLU *u, CURLUPart what,
+CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
char **part, unsigned int flags)
{
- char *ptr;
+ const char *ptr;
CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
char portbuf[7];
bool urldecode = (flags & CURLU_URLDECODE)?1:0;
bool urlencode = (flags & CURLU_URLENCODE)?1:0;
+ bool punycode = FALSE;
bool plusdecode = FALSE;
(void)flags;
if(!u)
@@ -1408,6 +1409,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
case CURLUPART_HOST:
ptr = u->host;
ifmissing = CURLUE_NO_HOST;
+ punycode = (flags & CURLU_PUNYCODE)?1:0;
break;
case CURLUPART_ZONEID:
ptr = u->zoneid;
@@ -1439,11 +1441,8 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
break;
case CURLUPART_PATH:
ptr = u->path;
- if(!ptr) {
- ptr = u->path = strdup("/");
- if(!u->path)
- return CURLUE_OUT_OF_MEMORY;
- }
+ if(!ptr)
+ ptr = "/";
break;
case CURLUPART_QUERY:
ptr = u->query;
@@ -1460,6 +1459,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
char *options = u->options;
char *port = u->port;
char *allochost = NULL;
+ punycode = (flags & CURLU_PUNYCODE)?1:0;
if(u->scheme && strcasecompare("file", u->scheme)) {
url = aprintf("file://%s%s%s",
u->path,
@@ -1514,6 +1514,17 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
if(!allochost)
return CURLUE_OUT_OF_MEMORY;
}
+ else if(punycode) {
+ if(!Curl_is_ASCII_name(u->host)) {
+#ifndef USE_IDN
+ return CURLUE_LACKS_IDN;
+#else
+ allochost = Curl_idn_decode(u->host);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+#endif
+ }
+ }
else {
/* only encode '%' in output host name */
char *host = u->host;
@@ -1541,8 +1552,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
return CURLUE_OUT_OF_MEMORY;
host++;
}
- free(u->host);
- u->host = Curl_dyn_ptr(&enc);
+ allochost = Curl_dyn_ptr(&enc);
}
}
@@ -1611,6 +1621,19 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
free(*part);
*part = Curl_dyn_ptr(&enc);
}
+ else if(punycode) {
+ if(!Curl_is_ASCII_name(u->host)) {
+#ifndef USE_IDN
+ return CURLUE_LACKS_IDN;
+#else
+ char *allochost = Curl_idn_decode(*part);
+ if(!allochost)
+ return CURLUE_OUT_OF_MEMORY;
+ free(*part);
+ *part = allochost;
+#endif
+ }
+ }
return CURLUE_OK;
}
@@ -1807,7 +1830,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
return CURLUE_OUT_OF_MEMORY;
}
else {
- result = Curl_dyn_addf(&enc, "%%%02x", *i);
+ char out[3]={'%'};
+ out[1] = hexdigits[*i>>4];
+ out[2] = hexdigits[*i & 0xf];
+ result = Curl_dyn_addn(&enc, out, 3);
if(result)
return CURLUE_OUT_OF_MEMORY;
}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index 3d7545c689..8b54518d20 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -113,23 +113,6 @@ typedef unsigned int curl_prot_t;
input easier and better. */
#define CURL_MAX_INPUT_LENGTH 8000000
-/* Macros intended for DEBUGF logging, use like:
- * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
- * and it will output:
- * [CONN-1-0][CF-SSL] this filter very much rocks
- * on connection #1 with sockindex 0 for filter of type "SSL". */
-#define DMSG(d,msg) \
- "[CONN-%ld] "msg, (d)->conn->connection_id
-#define DMSGI(d,i,msg) \
- "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
-#define CMSG(c,msg) \
- "[CONN-%ld] "msg, (conn)->connection_id
-#define CMSGI(c,i,msg) \
- "[CONN-%ld-%d] "msg, (conn)->connection_id, (i)
-#define CFMSG(cf,msg) \
- "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
- (cf)->sockindex, (cf)->cft->name
-
#include "cookie.h"
#include "psl.h"
@@ -185,10 +168,10 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
#include "rtsp.h"
#include "smb.h"
#include "mqtt.h"
-#include "wildcard.h"
+#include "ftplistparser.h"
#include "multihandle.h"
-#include "quic.h"
#include "c-hyper.h"
+#include "cf-socket.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@@ -268,8 +251,6 @@ typedef enum {
struct ssl_backend_data;
struct ssl_primary_config {
- long version; /* what version the client wants to use */
- long version_max; /* max supported version the client wants to use */
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* certificate to verify peer against */
char *issuercert; /* optional issuer certificate filename */
@@ -284,10 +265,11 @@ struct ssl_primary_config {
#ifdef USE_TLS_SRP
char *username; /* TLS username (for, e.g., SRP) */
char *password; /* TLS password (for, e.g., SRP) */
- enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */
#endif
char *curves; /* list of curves to use */
unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */
+ unsigned int version_max; /* max supported version the client wants to use */
+ unsigned char version; /* what version the client wants to use */
BIT(verifypeer); /* set TRUE if this is desired */
BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
BIT(verifystatus); /* set TRUE if certificate status must be checked */
@@ -648,7 +630,6 @@ struct SingleRequest {
curl_off_t pendingheader; /* this many bytes left to send is actually
header and not body */
struct curltime start; /* transfer started at this time */
- struct curltime now; /* current time */
enum {
HEADER_NORMAL, /* no bad header at all */
HEADER_PARTHEADER, /* part of the chunk is a bad header, the rest
@@ -706,7 +687,12 @@ struct SingleRequest {
#ifndef CURL_DISABLE_DOH
struct dohdata *doh; /* DoH specific data for this request */
#endif
+#if defined(WIN32) && defined(USE_WINSOCK)
+ struct curltime last_sndbuf_update; /* last time readwrite_upload called
+ win_update_buffer_size */
+#endif
unsigned char setcookies;
+ unsigned char writer_stack_depth; /* Unencoding stack depth. */
BIT(header); /* incoming data has HTTP header */
BIT(content_range); /* set TRUE if Content-Range: was found */
BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
@@ -783,8 +769,8 @@ struct Curl_handler {
/* This function *MAY* be set to a protocol-dependent function that is run
* by the curl_disconnect(), as a step in the disconnection. If the handler
* is called because the connection has been considered dead,
- * dead_connection is set to TRUE. The connection is already disassociated
- * from the transfer here.
+ * dead_connection is set to TRUE. The connection is (again) associated with
+ * the transfer here.
*/
CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *,
bool dead_connection);
@@ -830,7 +816,7 @@ struct Curl_handler {
#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per
request instead of per connection */
#define PROTOPT_ALPN (1<<8) /* set ALPN for this */
-#define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */
+/* (1<<9) was PROTOPT_STREAM, now free */
#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field
of the URL */
#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a
@@ -848,20 +834,6 @@ struct Curl_handler {
#define CONNRESULT_NONE 0 /* No extra information. */
#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
-struct postponed_data {
- char *buffer; /* Temporal store for received data during
- sending, must be freed */
- size_t allocated_size; /* Size of temporal store */
- size_t recv_size; /* Size of received data during sending */
- size_t recv_processed; /* Size of processed part of postponed data */
-#ifdef DEBUGBUILD
- curl_socket_t bindsock;/* Structure must be bound to specific socket,
- used only for DEBUGASSERT */
-#endif /* DEBUGBUILD */
-};
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
-
struct proxy_info {
struct hostname host;
int port;
@@ -909,16 +881,9 @@ struct connectdata {
there is no name resolve done. */
struct Curl_dns_entry *dns_entry;
- /* 'ip_addr' is the particular IP we connected to. It points to a struct
- within the DNS cache, so this pointer is only valid as long as the DNS
- cache entry remains locked. It gets unlocked in multi_done() */
- struct Curl_addrinfo *ip_addr;
- struct Curl_addrinfo *tempaddr[2]; /* for happy eyeballs */
-
-#ifdef ENABLE_QUIC
- struct quicsocket hequic[2]; /* two, for happy eyeballs! */
- struct quicsocket *quic;
-#endif
+ /* 'remote_addr' is the particular IP we connected to. it is owned, set
+ * and NULLed by the connected socket filter (if there is one). */
+ const struct Curl_sockaddr_ex *remote_addr;
struct hostname host;
char *hostname_resolve; /* host name to resolve to address, allocated */
@@ -947,31 +912,16 @@ struct connectdata {
struct curltime lastused; /* when returned to the connection cache */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
transfer when doing FTP */
- curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
- int tempfamily[2]; /* family used for the temp sockets */
Curl_recv *recv[2];
Curl_send *send[2];
struct Curl_cfilter *cfilter[2]; /* connection filters */
-#ifdef USE_RECV_BEFORE_SEND_WORKAROUND
- struct postponed_data postponed[2]; /* two buffers for two sockets */
-#endif /* USE_RECV_BEFORE_SEND_WORKAROUND */
struct ssl_primary_config ssl_config;
#ifndef CURL_DISABLE_PROXY
struct ssl_primary_config proxy_ssl_config;
#endif
struct ConnectBits bits; /* various state-flags for this connection */
- /* connecttime: when connect() is called on the current IP address. Used to
- be able to track when to move on to try next IP - but only when the multi
- interface is used. */
- struct curltime connecttime;
-
- /* The field below gets set in Curl_connecthost */
- /* how long time in milliseconds to spend on trying to connect to each IP
- address, per family */
- timediff_t timeoutms_per_addr[2];
-
const struct Curl_handler *handler; /* Connection's protocol handler */
const struct Curl_handler *given; /* The protocol first given */
@@ -1034,16 +984,15 @@ struct connectdata {
struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
#endif
+#ifndef CURL_DISABLE_HTTP
/* for chunked-encoded trailer */
struct dynbuf trailer;
+#endif
union {
#ifndef CURL_DISABLE_FTP
struct ftp_conn ftpc;
#endif
-#ifndef CURL_DISABLE_HTTP
- struct http_conn httpc;
-#endif
#ifdef USE_SSH
struct ssh_conn sshc;
#endif
@@ -1070,6 +1019,9 @@ struct connectdata {
#ifndef CURL_DISABLE_MQTT
struct mqtt_conn mqtt;
#endif
+#ifdef USE_WEBSOCKETS
+ struct ws_conn ws;
+#endif
} proto;
struct connectbundle *bundle; /* The bundle we are member of */
@@ -1086,14 +1038,13 @@ struct connectdata {
that subsequent bound-requested connections aren't accidentally re-using
wrong connections. */
char *localdev;
- int localportrange;
+ unsigned short localportrange;
int cselect_bits; /* bitmask of socket events */
int waitfor; /* current READ/WRITE bits to wait for */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
int socks5_gssapi_enctype;
#endif
- /* The field below gets set in Curl_connecthost */
- int num_addr; /* number of addresses to try to connect to */
+ /* The field below gets set in connect.c:connecthost() */
int port; /* which port to use locally - to connect to */
int remote_port; /* the remote port, not the proxy port! */
int conn_to_port; /* the remote port to connect to. valid only if
@@ -1110,6 +1061,7 @@ struct connectdata {
unsigned char ip_version; /* copied from the Curl_easy at creation time */
unsigned char httpversion; /* the HTTP version*10 reported by the server */
unsigned char connect_only;
+ unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */
};
/* The end of connectdata. */
@@ -1238,10 +1190,29 @@ struct auth {
should be RFC compliant */
};
-struct Curl_http2_dep {
- struct Curl_http2_dep *next;
+#ifdef USE_NGHTTP2
+struct Curl_data_prio_node {
+ struct Curl_data_prio_node *next;
struct Curl_easy *data;
};
+#endif
+
+/**
+ * Priority information for an easy handle in relation to others
+ * on the same connection.
+ * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218
+ */
+struct Curl_data_priority {
+#ifdef USE_NGHTTP2
+ /* tree like dependencies only implemented in nghttp2 */
+ struct Curl_easy *parent;
+ struct Curl_data_prio_node *children;
+#endif
+ int weight;
+#ifdef USE_NGHTTP2
+ BIT(exclusive);
+#endif
+};
/*
* This struct is for holding data that was attempted to get sent to the user's
@@ -1270,6 +1241,7 @@ typedef enum {
EXPIRE_TOOFAST,
EXPIRE_QUIC,
EXPIRE_FTP_ACCEPT,
+ EXPIRE_ALPN_EYEBALLS,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
@@ -1389,24 +1361,17 @@ struct UrlState {
size_t drain; /* Increased when this stream has data to read, even if its
socket is not necessarily is readable. Decreased when
checked. */
+ struct Curl_data_priority priority; /* shallow copy of data->set */
#endif
curl_read_callback fread_func; /* read callback/function */
void *in; /* CURLOPT_READDATA */
-#ifdef USE_HTTP2
- struct Curl_easy *stream_depends_on;
- int stream_weight;
-#endif
CURLU *uh; /* URL handle for the current parsed URL */
struct urlpieces up;
unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
is this */
char *url; /* work URL, copied from UserDefined */
char *referer; /* referer string */
-#ifndef CURL_DISABLE_COOKIES
- struct curl_slist *cookielist; /* list of cookie files set by
- curl_easy_setopt(COOKIEFILE) calls */
-#endif
struct curl_slist *resolve; /* set to point to the set.resolve list when
this should be dealt with in pretransfer */
#ifndef CURL_DISABLE_HTTP
@@ -1414,7 +1379,7 @@ struct UrlState {
struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
headers */
struct Curl_llist httphdrs; /* received headers */
- struct curl_header headerout; /* for external purposes */
+ struct curl_header headerout[2]; /* for external purposes */
struct Curl_header_store *prevhead; /* the latest added header */
trailers_state trailers_state; /* whether we are sending trailers
and what stage are we at */
@@ -1471,7 +1436,6 @@ struct UrlState {
BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE
when multi_done() is called, to prevent multi_done() to get
invoked twice when the multi interface is used. */
- BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
BIT(cookie_engine);
BIT(prefer_ascii); /* ASCII rather than binary */
@@ -1620,15 +1584,9 @@ struct UserDefined {
void *out; /* CURLOPT_WRITEDATA */
void *in_set; /* CURLOPT_READDATA */
void *writeheader; /* write the header to this if non-NULL */
- unsigned short proxyport; /* If non-zero, use this port number by
- default. If the proxy string features a
- ":[port]" that one will override this. */
unsigned short use_port; /* which port to use (when not using default) */
unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */
unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */
-#ifndef CURL_DISABLE_PROXY
- unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
-#endif
long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1
for infinity */
@@ -1638,8 +1596,9 @@ struct UserDefined {
of strlen(), and then the data *may* be binary
(contain zero bytes) */
unsigned short localport; /* local port number to bind to */
- int localportrange; /* number of additional port numbers to test in case the
- 'localport' one can't be bind()ed */
+ unsigned short localportrange; /* number of additional port numbers to test
+ in case the 'localport' one can't be
+ bind()ed */
curl_write_callback fwrite_func; /* function that stores the output */
curl_write_callback fwrite_header; /* function that stores headers */
curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */
@@ -1661,7 +1620,13 @@ struct UserDefined {
void *prereq_userp; /* pre-initial request user data */
void *seek_client; /* pointer to pass to the seek callback */
+#ifndef CURL_DISABLE_COOKIES
+ struct curl_slist *cookielist; /* list of cookie files set by
+ curl_easy_setopt(COOKIEFILE) calls */
+#endif
#ifndef CURL_DISABLE_HSTS
+ struct curl_slist *hstslist; /* list of HSTS files set by
+ curl_easy_setopt(HSTS) calls */
curl_hstsread_callback hsts_read;
void *hsts_read_userp;
curl_hstswrite_callback hsts_write;
@@ -1688,17 +1653,8 @@ struct UserDefined {
download */
curl_off_t set_resume_from; /* continue [ftp] transfer from here */
struct curl_slist *headers; /* linked list of extra headers */
- struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
struct curl_httppost *httppost; /* linked list of old POST data */
curl_mimepart mimepost; /* MIME/POST data. */
- struct curl_slist *quote; /* after connection is established */
- struct curl_slist *postquote; /* after the transfer */
- struct curl_slist *prequote; /* before the transfer, after type */
- struct curl_slist *source_quote; /* 3rd party quote */
- struct curl_slist *source_prequote; /* in 3rd party transfer mode - before
- the transfer on source host */
- struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
- the transfer on source host */
#ifndef CURL_DISABLE_TELNET
struct curl_slist *telnet_options; /* linked list of telnet options */
#endif
@@ -1708,13 +1664,18 @@ struct UserDefined {
the hostname and port to connect to */
time_t timevalue; /* what time to compare with */
unsigned char timecondition; /* kind of time comparison: curl_TimeCond */
- unsigned char proxytype; /* what kind of proxy: curl_proxytype */
unsigned char method; /* what kind of HTTP request: Curl_HttpReq */
unsigned char httpwant; /* when non-zero, a specific HTTP version requested
to be used in the library's request(s) */
struct ssl_config_data ssl; /* user defined SSL stuff */
#ifndef CURL_DISABLE_PROXY
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
+ struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */
+ unsigned short proxyport; /* If non-zero, use this port number by
+ default. If the proxy string features a
+ ":[port]" that one will override this. */
+ unsigned char proxytype; /* what kind of proxy: curl_proxytype */
+ unsigned char socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
#endif
struct ssl_general_config general_ssl; /* general user defined SSL stuff */
int dns_cache_timeout; /* DNS cache timeout (seconds) */
@@ -1722,7 +1683,9 @@ struct UserDefined {
unsigned int upload_buffer_size; /* size of upload buffer to use,
keep it >= CURL_MAX_WRITE_SIZE */
void *private_data; /* application-private data */
+#ifndef CURL_DISABLE_HTTP
struct curl_slist *http200aliases; /* linked list of aliases for http200 */
+#endif
unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
file 0 - whatever, 1 - v2, 2 - v6 */
curl_off_t max_filesize; /* Maximum file size to download */
@@ -1732,26 +1695,30 @@ struct UserDefined {
unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */
unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */
#endif
- /* Desppie the name ftp_create_missing_dirs is for FTP(S) and SFTP
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
+ struct curl_slist *quote; /* after connection is established */
+ struct curl_slist *postquote; /* after the transfer */
+ struct curl_slist *prequote; /* before the transfer, after type */
+ /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP
1 - create directories that don't exist
2 - the same but also allow MKD to fail once
*/
unsigned char ftp_create_missing_dirs;
+#endif
#ifdef USE_LIBSSH2
curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
void *ssh_hostkeyfunc_userp; /* custom pointer to callback */
#endif
-
+#ifdef USE_SSH
curl_sshkeycallback ssh_keyfunc; /* key matching callback */
void *ssh_keyfunc_userp; /* custom pointer to callback */
+ int ssh_auth_types; /* allowed SSH auth types */
+ unsigned int new_directory_perms; /* when creating remote dirs */
+#endif
#ifndef CURL_DISABLE_NETRC
unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */
#endif
- curl_usessl use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
- IMAP or POP3 or others! */
unsigned int new_file_perms; /* when creating remote files */
- unsigned int new_directory_perms; /* when creating remote dirs */
- int ssh_auth_types; /* allowed SSH auth types */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
struct curl_blob *blobs[BLOB_LAST];
#ifdef ENABLE_IPV6
@@ -1759,8 +1726,9 @@ struct UserDefined {
#endif
curl_prot_t allowed_protocols;
curl_prot_t redir_protocols;
+#ifndef CURL_DISABLE_MIME
unsigned int mime_options; /* Mime option flags. */
-
+#endif
#ifndef CURL_DISABLE_RTSP
void *rtp_out; /* write RTP to this if non-NULL */
/* Common RTSP header options */
@@ -1774,9 +1742,11 @@ struct UserDefined {
curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds
to pattern (e.g. if WILDCARDMATCH is on) */
void *fnmatch_data;
+ void *wildcardptr;
#endif
- long gssapi_delegation; /* GSS-API credential delegation, see the
- documentation of CURLOPT_GSSAPI_DELEGATION */
+ /* GSS-API credential delegation, see the documentation of
+ CURLOPT_GSSAPI_DELEGATION */
+ unsigned char gssapi_delegation;
int tcp_keepidle; /* seconds in idle before sending keepalive probe */
int tcp_keepintvl; /* seconds between TCP keepalive probes */
@@ -1784,10 +1754,8 @@ struct UserDefined {
size_t maxconnects; /* Max idle connections in the connection cache */
long expect_100_timeout; /* in milliseconds */
-#ifdef USE_HTTP2
- struct Curl_easy *stream_depends_on;
- int stream_weight;
- struct Curl_http2_dep *stream_dependents;
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
+ struct Curl_data_priority priority;
#endif
curl_resolver_start_callback resolver_start; /* optional callback called
before resolver start */
@@ -1798,8 +1766,10 @@ struct UserDefined {
struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
#endif
CURLU *uh; /* URL handle for the current parsed URL */
+#ifndef CURL_DISABLE_HTTP
void *trailer_data; /* pointer to pass to trailer data callback */
curl_trailer_callback trailer_callback; /* trailing data callback */
+#endif
char keep_post; /* keep POSTs as POSTs after a 30x request; each
bit represents a request, from 301 to 303 */
#ifndef CURL_DISABLE_SMTP
@@ -1807,6 +1777,8 @@ struct UserDefined {
BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
recipients */
#endif
+ unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
+ IMAP or POP3 or others! (type: curl_usessl)*/
unsigned char connect_only; /* make connection/request, then let
application use the socket */
BIT(is_fread_set); /* has read callback been set to non-NULL? */
@@ -1877,7 +1849,6 @@ struct UserDefined {
BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers
from user callbacks */
BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */
- BIT(stream_depends_e); /* set or don't set the Exclusive bit */
BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1
header */
BIT(abstract_unix_socket);
@@ -1969,7 +1940,7 @@ struct Curl_easy {
struct UrlState state; /* struct for fields used for state info and
other dynamic purposes */
#ifndef CURL_DISABLE_FTP
- struct WildcardData wildcard; /* wildcard download state info */
+ struct WildcardData *wildcard; /* wildcard download state info */
#endif
struct PureInfo info; /* stats, reports and info data */
struct curl_tlssessioninfo tsi; /* Information about the TLS session, only
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c
index b82b171467..c651fc5145 100644
--- a/Utilities/cmcurl/lib/vauth/cleartext.c
+++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,7 +28,8 @@
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
- !defined(CURL_DISABLE_POP3)
+ !defined(CURL_DISABLE_POP3) || \
+ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c
index 475d31b8d7..5894ed4bcf 100644
--- a/Utilities/cmcurl/lib/vauth/cram.c
+++ b/Utilities/cmcurl/lib/vauth/cram.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index c81ce10958..b7a0d92a31 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h
index d785bdd91b..68fdb28c47 100644
--- a/Utilities/cmcurl/lib/vauth/digest.h
+++ b/Utilities/cmcurl/lib/vauth/digest.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 6c95a3ef70..8fb8669393 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/gsasl.c b/Utilities/cmcurl/lib/vauth/gsasl.c
index a73c644434..c7d0a8d3b2 100644
--- a/Utilities/cmcurl/lib/vauth/gsasl.c
+++ b/Utilities/cmcurl/lib/vauth/gsasl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Simon Josefsson, <simon@josefsson.org>, et al.
+ * Copyright (C) Simon Josefsson, <simon@josefsson.org>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index bac78049dc..65eb3e1b50 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index 015bc66a35..c487149b9d 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index 0141e173fd..2a5d4a4908 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.h b/Utilities/cmcurl/lib/vauth/ntlm.h
index 14ebba2440..31ce921cd1 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.h
+++ b/Utilities/cmcurl/lib/vauth/ntlm.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index 193576acaa..5118963f4d 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/oauth2.c b/Utilities/cmcurl/lib/vauth/oauth2.c
index 1604b303e0..a4adbdcf15 100644
--- a/Utilities/cmcurl/lib/vauth/oauth2.c
+++ b/Utilities/cmcurl/lib/vauth/oauth2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,8 @@
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
- !defined(CURL_DISABLE_POP3)
+ !defined(CURL_DISABLE_POP3) || \
+ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
index 25dff967d1..e1d52b755c 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
index d845caca6c..d3245d0b18 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/vauth.c b/Utilities/cmcurl/lib/vauth/vauth.c
index 58fe05139d..62fc7c40fe 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.c
+++ b/Utilities/cmcurl/lib/vauth/vauth.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index c310c66375..e17d7aad6e 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2014 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index c80841c166..5800ad3c48 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,12 +24,16 @@
#include "curl_setup.h"
+#ifdef USE_NGHTTP2
+#include <nghttp2/nghttp2.h>
+#endif
+
#include <curl/curl.h>
#include "urldata.h"
#include "vtls/vtls.h"
#include "http2.h"
#include "vssh/ssh.h"
-#include "quic.h"
+#include "vquic/vquic.h"
#include "curl_printf.h"
#include "easy_lock.h"
@@ -58,7 +62,15 @@
#endif
#ifdef HAVE_BROTLI
+#if defined(__GNUC__)
+/* Ignore -Wvla warnings in brotli headers */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wvla"
+#endif
#include <brotli/decode.h>
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
#endif
#ifdef HAVE_ZSTD
@@ -353,8 +365,7 @@ static const char * const protocols[] = {
#ifdef USE_SSH
"sftp",
#endif
-#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
- (SIZEOF_CURL_OFF_T > 4)
+#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
"smb",
# ifdef USE_SSL
"smbs",
diff --git a/Utilities/cmcurl/lib/version_win32.c b/Utilities/cmcurl/lib/version_win32.c
index e8f14f9dff..872d5b4f3c 100644
--- a/Utilities/cmcurl/lib/version_win32.c
+++ b/Utilities/cmcurl/lib/version_win32.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/version_win32.h b/Utilities/cmcurl/lib/version_win32.h
index 7a9a6a14f1..3899174a31 100644
--- a/Utilities/cmcurl/lib/version_win32.h
+++ b/Utilities/cmcurl/lib/version_win32.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2016 - 2022, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) Steve Holme, <steve_holme@hotmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c
new file mode 100644
index 0000000000..530899977d
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -0,0 +1,850 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_MSH3
+
+#include "urldata.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "curl_log.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "h2h3.h"
+#include "curl_msh3.h"
+#include "socketpair.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DEBUG_CF 1
+
+#if DEBUG_CF && defined(DEBUGBUILD)
+#define CF_DEBUGF(x) x
+#else
+#define CF_DEBUGF(x) do { } while(0)
+#endif
+
+#define MSH3_REQ_INIT_BUF_LEN 16384
+#define MSH3_REQ_MAX_BUF_LEN 0x100000
+
+#ifdef _WIN32
+#define msh3_lock CRITICAL_SECTION
+#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
+#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
+#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
+#define msh3_lock_release(lock) LeaveCriticalSection(lock)
+#else /* !_WIN32 */
+#include <pthread.h>
+#define msh3_lock pthread_mutex_t
+#define msh3_lock_initialize(lock) do { \
+ pthread_mutexattr_t attr; \
+ pthread_mutexattr_init(&attr); \
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
+ pthread_mutex_init(lock, &attr); \
+ pthread_mutexattr_destroy(&attr); \
+}while(0)
+#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
+#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
+#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
+#endif /* _WIN32 */
+
+
+static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
+ void *IfContext);
+static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
+ void *IfContext);
+static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
+ void *IfContext,
+ MSH3_REQUEST *Request);
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header);
+static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t *Length,
+ const uint8_t *Data);
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError);
+static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
+ void *IfContext);
+static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
+ void *IfContext, void *SendContext);
+
+
+void Curl_msh3_ver(char *p, size_t len)
+{
+ uint32_t v[4];
+ MsH3Version(v);
+ (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
+}
+
+#define SP_LOCAL 0
+#define SP_REMOTE 1
+
+struct cf_msh3_ctx {
+ MSH3_API *api;
+ MSH3_CONNECTION *qconn;
+ struct Curl_sockaddr_ex addr;
+ curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
+ char l_ip[MAX_IPADR_LEN]; /* local IP as string */
+ int l_port; /* local port number */
+ struct curltime connect_started; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ /* Flags written by msh3/msquic thread */
+ bool handshake_complete;
+ bool handshake_succeeded;
+ bool connected;
+ /* Flags written by curl thread */
+ BIT(verbose);
+ BIT(active);
+};
+
+static const MSH3_CONNECTION_IF msh3_conn_if = {
+ msh3_conn_connected,
+ msh3_conn_shutdown_complete,
+ msh3_conn_new_request
+};
+
+static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
+ void *IfContext)
+{
+ struct cf_msh3_ctx *ctx = IfContext;
+ (void)Connection;
+ if(ctx->verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: connected\n"));
+ ctx->handshake_succeeded = true;
+ ctx->connected = true;
+ ctx->handshake_complete = true;
+}
+
+static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
+ void *IfContext)
+{
+ struct cf_msh3_ctx *ctx = IfContext;
+ (void)Connection;
+ if(ctx->verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: shutdown complete\n"));
+ ctx->connected = false;
+ ctx->handshake_complete = true;
+}
+
+static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
+ void *IfContext,
+ MSH3_REQUEST *Request)
+{
+ (void)Connection;
+ (void)IfContext;
+ (void)Request;
+}
+
+static const MSH3_REQUEST_IF msh3_request_if = {
+ msh3_header_received,
+ msh3_data_received,
+ msh3_complete,
+ msh3_shutdown_complete,
+ msh3_data_sent
+};
+
+static CURLcode msh3_data_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ (void)cf;
+
+ DEBUGASSERT(stream);
+ if(!stream->recv_buf) {
+ DEBUGF(LOG_CF(data, cf, "req: setup"));
+ stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
+ if(!stream->recv_buf) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ stream->req = ZERO_NULL;
+ msh3_lock_initialize(&stream->recv_lock);
+ stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
+ stream->recv_buf_max = MSH3_REQ_MAX_BUF_LEN;
+ stream->recv_header_len = 0;
+ stream->recv_header_complete = false;
+ stream->recv_data_len = 0;
+ stream->recv_data_complete = false;
+ stream->recv_error = CURLE_OK;
+ }
+ return CURLE_OK;
+}
+
+/* Requires stream->recv_lock to be held */
+static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
+{
+ uint8_t *new_recv_buf;
+ const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+
+ if(cur_recv_len + len > stream->recv_buf_alloc) {
+ size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
+ do {
+ new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
+ } while(cur_recv_len + len > new_recv_buf_alloc_len);
+ CF_DEBUGF(fprintf(stderr, "* enlarging buffer to %zu\n",
+ new_recv_buf_alloc_len));
+ new_recv_buf = malloc(new_recv_buf_alloc_len);
+ if(!new_recv_buf) {
+ CF_DEBUGF(fprintf(stderr, "* FAILED: enlarging buffer to %zu\n",
+ new_recv_buf_alloc_len));
+ return false;
+ }
+ if(cur_recv_len) {
+ memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
+ }
+ stream->recv_buf_alloc = new_recv_buf_alloc_len;
+ free(stream->recv_buf);
+ stream->recv_buf = new_recv_buf;
+ }
+ return true;
+}
+
+static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
+ void *IfContext,
+ const MSH3_HEADER *Header)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ size_t total_len;
+ (void)Request;
+
+ if(stream->recv_header_complete) {
+ CF_DEBUGF(fprintf(stderr, "* ignoring header after data\n"));
+ return;
+ }
+
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if((Header->NameLength == 7) &&
+ !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
+ total_len = 10 + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
+ (int)Header->NameLength, Header->Name));
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value);
+ }
+ else {
+ total_len = 4 + Header->NameLength + Header->ValueLength;
+ if(!msh3request_ensure_room(stream, total_len)) {
+ CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
+ (int)Header->NameLength, Header->Name));
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ msnprintf((char *)stream->recv_buf + stream->recv_header_len,
+ stream->recv_buf_alloc - stream->recv_header_len,
+ "%.*s: %.*s\r\n",
+ (int)Header->NameLength, Header->Name,
+ (int)Header->ValueLength, Header->Value);
+ }
+
+ stream->recv_header_len += total_len;
+ data->state.drain = 1;
+
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
+ void *IfContext, uint32_t *Length,
+ const uint8_t *Data)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
+
+ (void)Request;
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: received %u. %zu buffered, "
+ "%zu allocated\n",
+ *Length, cur_recv_len, stream->recv_buf_alloc));
+ /* TODO - Update this code to limit data bufferring by `stream->recv_buf_max`
+ and return `false` when we reach that limit. Then, when curl drains some
+ of the buffer, making room, call MsH3RequestSetReceiveEnabled to enable
+ receive callbacks again. */
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if(!stream->recv_header_complete) {
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: Headers complete!\n"));
+ if(!msh3request_ensure_room(stream, 2)) {
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ stream->recv_buf[stream->recv_header_len++] = '\r';
+ stream->recv_buf[stream->recv_header_len++] = '\n';
+ stream->recv_header_complete = true;
+ cur_recv_len += 2;
+ }
+ if(!msh3request_ensure_room(stream, *Length)) {
+ stream->recv_error = CURLE_OUT_OF_MEMORY;
+ goto release_lock;
+ }
+ memcpy(stream->recv_buf + cur_recv_len, Data, *Length);
+ stream->recv_data_len += (size_t)*Length;
+ data->state.drain = 1;
+
+release_lock:
+ msh3_lock_release(&stream->recv_lock);
+ return true;
+}
+
+static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
+ bool Aborted, uint64_t AbortError)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+
+ (void)Request;
+ (void)AbortError;
+ if(data && data->set.verbose)
+ CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: complete, aborted=%s\n",
+ Aborted ? "true" : "false"));
+ msh3_lock_acquire(&stream->recv_lock);
+ if(Aborted) {
+ stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
+ }
+ stream->recv_header_complete = true;
+ stream->recv_data_complete = true;
+ msh3_lock_release(&stream->recv_lock);
+}
+
+static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
+ void *IfContext)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ (void)Request;
+ (void)stream;
+}
+
+static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
+ void *IfContext, void *SendContext)
+{
+ struct Curl_easy *data = IfContext;
+ struct HTTP *stream = data->req.p.http;
+ (void)Request;
+ (void)stream;
+ (void)SendContext;
+}
+
+static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ size_t outsize = 0;
+
+ (void)cf;
+ DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
+
+ if(stream->recv_error) {
+ failf(data, "request aborted");
+ data->state.drain = 0;
+ *err = stream->recv_error;
+ return -1;
+ }
+
+ *err = CURLE_OK;
+ msh3_lock_acquire(&stream->recv_lock);
+
+ if(stream->recv_header_len) {
+ outsize = len;
+ if(stream->recv_header_len < outsize) {
+ outsize = stream->recv_header_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_header_len + stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_header_len + stream->recv_data_len - outsize);
+ }
+ stream->recv_header_len -= outsize;
+ DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of header", outsize));
+ }
+ else if(stream->recv_data_len) {
+ outsize = len;
+ if(stream->recv_data_len < outsize) {
+ outsize = stream->recv_data_len;
+ }
+ memcpy(buf, stream->recv_buf, outsize);
+ if(outsize < stream->recv_data_len) {
+ memmove(stream->recv_buf, stream->recv_buf + outsize,
+ stream->recv_data_len - outsize);
+ }
+ stream->recv_data_len -= outsize;
+ DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of data", outsize));
+ if(stream->recv_data_len == 0 && stream->recv_data_complete)
+ data->state.drain = 1;
+ }
+ else if(stream->recv_data_complete) {
+ DEBUGF(LOG_CF(data, cf, "req: receive complete"));
+ data->state.drain = 0;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
+ *err = CURLE_AGAIN;
+ outsize = -1;
+ }
+
+ msh3_lock_release(&stream->recv_lock);
+
+ return (ssize_t)outsize;
+}
+
+static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ struct h2h3req *hreq;
+ size_t hdrlen = 0;
+ size_t sentlen = 0;
+
+ /* Sizes must match for cast below to work" */
+ DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
+ DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
+
+ if(!stream->req) {
+ /* The first send on the request contains the headers and possibly some
+ data. Parse out the headers and create the request, then if there is
+ any data left over go ahead and send it too. */
+
+ *err = msh3_data_setup(cf, data);
+ if(*err) {
+ failf(data, "could not setup data");
+ return -1;
+ }
+
+ *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
+ if(*err) {
+ failf(data, "Curl_pseudo_headers failed");
+ return -1;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries));
+ stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
+ (MSH3_HEADER*)hreq->header, hreq->entries,
+ hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
+ MSH3_REQUEST_FLAG_NONE);
+ Curl_pseudo_free(hreq);
+ if(!stream->req) {
+ failf(data, "request open failed");
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ *err = CURLE_OK;
+ return len;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
+ if(len > 0xFFFFFFFF) {
+ /* msh3 doesn't support size_t sends currently. */
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ /* TODO - Need an explicit signal to know when to FIN. */
+ if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len,
+ stream)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ /* TODO - msh3/msquic will hold onto this memory until the send complete
+ event. How do we make sure curl doesn't free it until then? */
+ sentlen += len;
+ *err = CURLE_OK;
+ return sentlen;
+}
+
+static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ int bitmap = GETSOCK_BLANK;
+
+ if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
+ socks[0] = ctx->sock[SP_LOCAL];
+
+ if(stream->recv_error) {
+ bitmap |= GETSOCK_READSOCK(0);
+ data->state.drain = 1;
+ }
+ else if(stream->recv_header_len || stream->recv_data_len) {
+ bitmap |= GETSOCK_READSOCK(0);
+ data->state.drain = 1;
+ }
+ }
+ DEBUGF(LOG_CF(data, cf, "select_sock %u -> %d",
+ (uint32_t)data->state.drain, bitmap));
+
+ return bitmap;
+}
+
+static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ (void)cf;
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %hhu",
+ (bool)(stream->recv_header_len || stream->recv_data_len)));
+ return stream->recv_header_len || stream->recv_data_len;
+}
+
+static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ /* use this socket from now on */
+ cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
+ /* the first socket info gets set at conn and data */
+ if(cf->sockindex == FIRSTSOCKET) {
+ cf->conn->remote_addr = &ctx->addr;
+ #ifdef ENABLE_IPV6
+ cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
+ #endif
+ Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
+ }
+ ctx->active = TRUE;
+}
+
+static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_SETUP:
+ result = msh3_data_setup(cf, data);
+ break;
+ case CF_CTRL_DATA_DONE:
+ DEBUGF(LOG_CF(data, cf, "req: done"));
+ if(stream) {
+ if(stream->recv_buf) {
+ Curl_safefree(stream->recv_buf);
+ msh3_lock_uninitialize(&stream->recv_lock);
+ }
+ if(stream->req) {
+ MsH3RequestClose(stream->req);
+ stream->req = ZERO_NULL;
+ }
+ }
+ break;
+ case CF_CTRL_DATA_DONE_SEND:
+ DEBUGF(LOG_CF(data, cf, "req: send done"));
+ stream->upload_done = TRUE;
+ break;
+ case CF_CTRL_CONN_INFO_UPDATE:
+ DEBUGF(LOG_CF(data, cf, "req: update info"));
+ cf_msh3_active(cf, data);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ bool verify = !!cf->conn->ssl_config.verifypeer;
+ MSH3_ADDR addr = {0};
+ memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
+ MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
+ ctx->verbose = (data && data->set.verbose);
+
+ if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) {
+ /* TODO: need a way to provide trust anchors to MSH3 */
+#ifdef DEBUGBUILD
+ /* we need this for our test cases to run */
+ DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
+ "switching off verifypeer in DEBUG mode"));
+ verify = 0;
+#else
+ DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
+ "attempting with built-in verification"));
+#endif
+ }
+
+ DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
+ cf->conn->host.name, (int)cf->conn->remote_port, verify));
+
+ ctx->api = MsH3ApiOpen();
+ if(!ctx->api) {
+ failf(data, "can't create msh3 api");
+ return CURLE_FAILED_INIT;
+ }
+
+ ctx->qconn = MsH3ConnectionOpen(ctx->api,
+ &msh3_conn_if,
+ ctx,
+ cf->conn->host.name,
+ &addr,
+ !verify);
+ if(!ctx->qconn) {
+ failf(data, "can't create msh3 connection");
+ if(ctx->api) {
+ MsH3ApiClose(ctx->api);
+ ctx->api = NULL;
+ }
+ return CURLE_FAILED_INIT;
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
+ if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ return CURLE_COULDNT_CONNECT;
+ }
+ }
+
+ *done = FALSE;
+ if(!ctx->qconn) {
+ ctx->connect_started = Curl_now();
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ }
+
+ if(ctx->handshake_complete) {
+ ctx->handshake_at = Curl_now();
+ if(ctx->handshake_succeeded) {
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ Curl_pgrsTime(data, TIMER_APPCONNECT);
+ }
+ else {
+ failf(data, "failed to connect, handshake failed");
+ result = CURLE_COULDNT_CONNECT;
+ }
+ }
+
+out:
+ return result;
+}
+
+static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ (void)data;
+ if(ctx) {
+ DEBUGF(LOG_CF(data, cf, "destroying"));
+ if(ctx->qconn)
+ MsH3ConnectionClose(ctx->qconn);
+ if(ctx->api)
+ MsH3ApiClose(ctx->api);
+
+ if(ctx->active) {
+ /* We share our socket at cf->conn->sock[cf->sockindex] when active.
+ * If it is no longer there, someone has stolen (and hopefully
+ * closed it) and we just forget about it.
+ */
+ if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
+ DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
+ (int)ctx->sock[SP_LOCAL]));
+ cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
+ "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ }
+ if(cf->sockindex == FIRSTSOCKET)
+ cf->conn->remote_addr = NULL;
+ }
+ if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
+ sclose(ctx->sock[SP_LOCAL]);
+ }
+ if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
+ sclose(ctx->sock[SP_REMOTE]);
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+ }
+}
+
+static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ cf_msh3_close(cf, data);
+ free(cf->ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ /* TODO: we do not have access to this so far, fake it */
+ (void)ctx;
+ *pres1 = 100;
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ /* we do not know when the first byte arrived */
+ if(cf->connected)
+ *when = ctx->handshake_at;
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ if(cf->connected)
+ *when = ctx->handshake_at;
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ struct cf_msh3_ctx *ctx = cf->ctx;
+
+ (void)data;
+ *input_pending = FALSE;
+ return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
+ ctx->connected;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_msh3_destroy,
+ cf_msh3_connect,
+ cf_msh3_close,
+ Curl_cf_def_get_host,
+ cf_msh3_get_select_socks,
+ cf_msh3_data_pending,
+ cf_msh3_send,
+ cf_msh3_recv,
+ cf_msh3_data_event,
+ cf_msh3_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_msh3_query,
+};
+
+CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_msh3_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ (void)ai; /* TODO: msh3 resolves itself? */
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
+ ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
+ ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+bool Curl_conn_is_msh3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif /* USE_MSH3 */
diff --git a/Utilities/cmcurl/lib/vquic/msh3.h b/Utilities/cmcurl/lib/vquic/curl_msh3.h
index ce884d92dd..33931f59bb 100644
--- a/Utilities/cmcurl/lib/vquic/msh3.h
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_MSH3_H
-#define HEADER_CURL_VQUIC_MSH3_H
+#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H
+#define HEADER_CURL_VQUIC_CURL_MSH3_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,11 +30,17 @@
#include <msh3.h>
-struct quicsocket {
- MSH3_API* api;
- MSH3_CONNECTION* conn;
-};
+void Curl_msh3_ver(char *p, size_t len);
+
+CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_msh3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
#endif /* USE_MSQUIC */
-#endif /* HEADER_CURL_VQUIC_MSH3_H */
+#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
new file mode 100644
index 0000000000..d2d0a3a5af
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -0,0 +1,2550 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_NGTCP2
+#include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <ngtcp2/ngtcp2_crypto_boringssl.h>
+#else
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#endif
+#include "vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+#include "vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
+#include "vtls/wolfssl.h"
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "dynbuf.h"
+#include "select.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "h2h3.h"
+#include "vtls/keylog.h"
+#include "vtls/vtls.h"
+#include "curl_ngtcp2.h"
+
+#include "warnless.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define H3_ALPN_H3_29 "\x5h3-29"
+#define H3_ALPN_H3 "\x2h3"
+
+/*
+ * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
+ * It is used as a circular buffer. Add new bytes at the end until it reaches
+ * the far end, then start over at index 0 again.
+ */
+
+#define H3_SEND_SIZE (256*1024)
+struct h3out {
+ uint8_t buf[H3_SEND_SIZE];
+ size_t used; /* number of bytes used in the buffer */
+ size_t windex; /* index in the buffer where to start writing the next
+ data block */
+};
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
+#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
+
+#ifdef USE_OPENSSL
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+#elif defined(USE_GNUTLS)
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+#elif defined(USE_WOLFSSL)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+#endif
+
+
+/*
+ * Store ngtcp2 version info in this buffer.
+ */
+void Curl_ngtcp2_ver(char *p, size_t len)
+{
+ const ngtcp2_info *ng2 = ngtcp2_version(0);
+ const nghttp3_info *ht3 = nghttp3_version(0);
+ (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
+ ng2->version_str, ht3->version_str);
+}
+
+struct cf_ngtcp2_ctx {
+ struct cf_quic_ctx q;
+ ngtcp2_path connected_path;
+ ngtcp2_conn *qconn;
+ ngtcp2_cid dcid;
+ ngtcp2_cid scid;
+ uint32_t version;
+ ngtcp2_settings settings;
+ ngtcp2_transport_params transport_params;
+ ngtcp2_connection_close_error last_error;
+ ngtcp2_crypto_conn_ref conn_ref;
+#ifdef USE_OPENSSL
+ SSL_CTX *sslctx;
+ SSL *ssl;
+#elif defined(USE_GNUTLS)
+ struct gtls_instance *gtls;
+#elif defined(USE_WOLFSSL)
+ WOLFSSL_CTX *sslctx;
+ WOLFSSL *ssl;
+#endif
+ struct cf_call_data call_data;
+ nghttp3_conn *h3conn;
+ nghttp3_settings h3settings;
+ int qlogfd;
+ struct curltime started_at; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ struct curltime reconnect_at; /* time the next attempt should start */
+ BIT(got_first_byte); /* if first byte was received */
+};
+
+/* How to access `call_data` from a cf_ngtcp2 filter */
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
+
+
+/* ngtcp2 default congestion controller does not perform pacing. Limit
+ the maximum packet burst to MAX_PKT_BURST packets. */
+#define MAX_PKT_BURST 10
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data);
+
+static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
+{
+ struct Curl_cfilter *cf = conn_ref->user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ return ctx->qconn;
+}
+
+static ngtcp2_tstamp timestamp(void)
+{
+ struct curltime ct = Curl_now();
+ return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+#ifdef DEBUG_NGTCP2
+static void quic_printf(void *user_data, const char *fmt, ...)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
+ (void)ctx; /* TODO: need an easy handle to infof() message */
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void qlog_callback(void *user_data, uint32_t flags,
+ const void *data, size_t datalen)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ (void)flags;
+ if(ctx->qlogfd != -1) {
+ ssize_t rc = write(ctx->qlogfd, data, datalen);
+ if(rc == -1) {
+ /* on write error, stop further write attempts */
+ close(ctx->qlogfd);
+ ctx->qlogfd = -1;
+ }
+ }
+
+}
+
+static void quic_settings(struct cf_ngtcp2_ctx *ctx,
+ struct Curl_easy *data)
+{
+ ngtcp2_settings *s = &ctx->settings;
+ ngtcp2_transport_params *t = &ctx->transport_params;
+ size_t stream_win_size = CURL_MAX_READ_SIZE;
+
+ ngtcp2_settings_default(s);
+ ngtcp2_transport_params_default(t);
+#ifdef DEBUG_NGTCP2
+ s->log_printf = quic_printf;
+#else
+ s->log_printf = NULL;
+#endif
+
+ (void)data;
+ s->initial_ts = timestamp();
+ s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
+ s->max_window = 100 * stream_win_size;
+ s->max_stream_window = stream_win_size;
+
+ t->initial_max_data = 10 * stream_win_size;
+ t->initial_max_stream_data_bidi_local = stream_win_size;
+ t->initial_max_stream_data_bidi_remote = stream_win_size;
+ t->initial_max_stream_data_uni = stream_win_size;
+ t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
+ t->initial_max_streams_uni = QUIC_MAX_STREAMS;
+ t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
+ if(ctx->qlogfd != -1) {
+ s->qlog.write = qlog_callback;
+ }
+}
+
+#ifdef USE_OPENSSL
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#elif defined(USE_GNUTLS)
+static int keylog_callback(gnutls_session_t session, const char *label,
+ const gnutls_datum_t *secret)
+{
+ gnutls_datum_t crandom;
+ gnutls_datum_t srandom;
+
+ gnutls_session_get_random(session, &crandom, &srandom);
+ if(crandom.size != 32) {
+ return -1;
+ }
+
+ Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+ return 0;
+}
+#elif defined(USE_WOLFSSL)
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#endif
+#endif
+
+static int init_ngh3_conn(struct Curl_cfilter *cf);
+
+#ifdef USE_OPENSSL
+static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
+ struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct connectdata *conn = cf->conn;
+ CURLcode result = CURLE_FAILED_INIT;
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+ if(!ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
+ goto out;
+ }
+#else
+ if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
+ goto out;
+ }
+#endif
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_curves_list failed");
+ goto out;
+ }
+#else
+ if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ goto out;
+ }
+
+ if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ goto out;
+ }
+#endif
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
+ if(result)
+ goto out;
+
+ /* OpenSSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ *pssl_ctx = result? NULL : ssl_ctx;
+ if(result && ssl_ctx)
+ SSL_CTX_free(ssl_ctx);
+ return result;
+}
+
+static CURLcode quic_set_client_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ SSL_CTX *ssl_ctx = ctx->sslctx;
+ const struct ssl_config_data *ssl_config;
+
+ ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
+ DEBUGASSERT(ssl_config);
+
+ if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
+ || ssl_config->cert_type) {
+ return Curl_ossl_set_client_cert(
+ data, ssl_ctx, ssl_config->primary.clientcert,
+ ssl_config->primary.cert_blob, ssl_config->cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd);
+ }
+
+ return CURLE_OK;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+
+ (void)data;
+ DEBUGASSERT(!ctx->ssl);
+ ctx->ssl = SSL_new(ctx->sslctx);
+
+ SSL_set_app_data(ctx->ssl, &ctx->conn_ref);
+ SSL_set_connect_state(ctx->ssl);
+ SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+ return CURLE_OK;
+}
+#elif defined(USE_GNUTLS)
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ gnutls_datum_t alpn[2];
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = cf->conn->host.name;
+ long * const pverifyresult = &data->set.ssl.certverifyresult;
+ int rc;
+
+ DEBUGASSERT(ctx->gtls == NULL);
+ ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
+ if(!ctx->gtls)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl,
+ hostname, ctx->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
+
+ if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+ DEBUGF(LOG_CF(data, cf,
+ "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
+ if(rc < 0) {
+ DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+ gnutls_strerror(rc)));
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
+ }
+
+ /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
+ alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
+ alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
+ alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
+ alpn[1].size = sizeof(H3_ALPN_H3) - 2;
+
+ gnutls_alpn_set_protocols(ctx->gtls->session,
+ alpn, 2, GNUTLS_ALPN_MANDATORY);
+ return CURLE_OK;
+}
+#elif defined(USE_WOLFSSL)
+
+static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx,
+ struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct connectdata *conn = cf->conn;
+ CURLcode result = CURLE_FAILED_INIT;
+ WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
+
+ if(!ssl_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
+ failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
+ goto out;
+ }
+
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
+ char error_buffer[256];
+ ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
+ failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+ goto out;
+ }
+
+ if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+#if defined(HAVE_SECRET_CALLBACK)
+ wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+#else
+ failf(data, "wolfSSL was built without keylog callback");
+ goto out;
+#endif
+ }
+
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ if(conn->ssl_config.CAfile || conn->ssl_config.CApath) {
+ /* tell wolfSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ goto out;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use wolfssl's built-in default as fallback */
+ wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ else {
+ wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ goto out;
+ }
+ }
+ result = CURLE_OK;
+
+out:
+ *pssl_ctx = result? NULL : ssl_ctx;
+ if(result && ssl_ctx)
+ SSL_CTX_free(ssl_ctx);
+ return result;
+}
+
+/** SSL callbacks ***/
+
+static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = cf->conn->host.name;
+
+ (void)data;
+ DEBUGASSERT(!ctx->ssl);
+ ctx->ssl = wolfSSL_new(ctx->sslctx);
+
+ wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref);
+ wolfSSL_set_connect_state(ctx->ssl);
+ wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
+
+ alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+ alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
+ if(alpn)
+ wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
+ hostname, (unsigned short)strlen(hostname));
+
+ return CURLE_OK;
+}
+#endif /* defined(USE_WOLFSSL) */
+
+static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
+{
+ (void)user_data;
+ (void)tconn;
+ return 0;
+}
+
+static void report_consumed_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ size_t consumed)
+{
+ struct HTTP *stream = data->req.p.http;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+
+ /* the HTTP/1.1 response headers are written to the buffer, but
+ * consuming those does not count against flow control. */
+ if(stream->recv_buf_nonflow) {
+ if(consumed >= stream->recv_buf_nonflow) {
+ consumed -= stream->recv_buf_nonflow;
+ stream->recv_buf_nonflow = 0;
+ }
+ else {
+ stream->recv_buf_nonflow -= consumed;
+ consumed = 0;
+ }
+ }
+ if(consumed > 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes",
+ stream->stream3_id, consumed));
+ ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->stream3_id,
+ consumed);
+ ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
+ }
+ if(!stream->closed && data->state.drain
+ && !stream->memlen
+ && !Curl_dyn_len(&stream->overflow)) {
+ /* nothing buffered any more */
+ data->state.drain = 0;
+ }
+}
+
+static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ nghttp3_ssize nconsumed;
+ int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
+ struct Curl_easy *data = stream_user_data;
+ (void)offset;
+ (void)data;
+
+ nconsumed =
+ nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd",
+ stream_id, buflen, nconsumed));
+ if(nconsumed < 0) {
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error,
+ nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* number of bytes inside buflen which consists of framing overhead
+ * including QPACK HEADERS. In other words, it does not consume payload of
+ * DATA frame. */
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
+ ngtcp2_conn_extend_max_offset(tconn, nconsumed);
+
+ return 0;
+}
+
+static int
+cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t offset, uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)stream_id;
+ (void)tconn;
+ (void)offset;
+ (void)datalen;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+ int64_t stream3_id, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+
+ (void)tconn;
+ (void)data;
+ /* stream is closed... */
+
+ if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+ app_error_code = NGHTTP3_H3_NO_ERROR;
+ }
+
+ rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id,
+ app_error_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%"
+ PRIu64 ") -> %d", stream3_id, app_error_code, rv));
+ if(rv) {
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data = stream_user_data;
+ int rv;
+ (void)tconn;
+ (void)final_size;
+ (void)app_error_code;
+ (void)data;
+
+ rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)tconn;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
+ uint64_t max_streams,
+ void *user_data)
+{
+ (void)tconn;
+ (void)max_streams;
+ (void)user_data;
+
+ return 0;
+}
+
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)tconn;
+ (void)max_data;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static void cb_rand(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx)
+{
+ CURLcode result;
+ (void)rand_ctx;
+
+ result = Curl_rand(NULL, dest, destlen);
+ if(result) {
+ /* cb_rand is only used for non-cryptographic context. If Curl_rand
+ failed, just fill 0 and call it *random*. */
+ memset(dest, 0, destlen);
+ }
+}
+
+static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data)
+{
+ CURLcode result;
+ (void)tconn;
+ (void)user_data;
+
+ result = Curl_rand(NULL, cid->data, cidlen);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ cid->datalen = cidlen;
+
+ result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+}
+
+static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
+ void *user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ (void)tconn;
+
+ if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
+ return 0;
+ }
+
+ if(init_ngh3_conn(cf) != CURLE_OK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static ngtcp2_callbacks ng_callbacks = {
+ ngtcp2_crypto_client_initial_cb,
+ NULL, /* recv_client_initial */
+ ngtcp2_crypto_recv_crypto_data_cb,
+ cb_handshake_completed,
+ NULL, /* recv_version_negotiation */
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ cb_recv_stream_data,
+ cb_acked_stream_data_offset,
+ NULL, /* stream_open */
+ cb_stream_close,
+ NULL, /* recv_stateless_reset */
+ ngtcp2_crypto_recv_retry_cb,
+ cb_extend_max_local_streams_bidi,
+ NULL, /* extend_max_local_streams_uni */
+ cb_rand,
+ cb_get_new_connection_id,
+ NULL, /* remove_connection_id */
+ ngtcp2_crypto_update_key_cb, /* update_key */
+ NULL, /* path_validation */
+ NULL, /* select_preferred_addr */
+ cb_stream_reset,
+ NULL, /* extend_max_remote_streams_bidi */
+ NULL, /* extend_max_remote_streams_uni */
+ cb_extend_max_stream_data,
+ NULL, /* dcid_status */
+ NULL, /* handshake_confirmed */
+ NULL, /* recv_new_token */
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ NULL, /* recv_datagram */
+ NULL, /* ack_datagram */
+ NULL, /* lost_datagram */
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ cb_stream_stop_sending,
+ NULL, /* version_negotiation */
+ cb_recv_rx_key,
+ NULL, /* recv_tx_key */
+ NULL, /* early_data_rejected */
+};
+
+static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
+ int rv = GETSOCK_BLANK;
+ struct HTTP *stream = data->req.p.http;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ socks[0] = ctx->q.sockfd;
+
+ /* in an HTTP/3 connection we can basically always get a frame so we should
+ always be ready for one */
+ rv |= GETSOCK_READSOCK(0);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND &&
+ (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
+ ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
+ ngtcp2_conn_get_max_data_left(ctx->qconn) &&
+ nghttp3_conn_is_stream_writable(ctx->h3conn, stream->stream3_id))
+ rv |= GETSOCK_WRITESOCK(0);
+
+ DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
+ rv, (int)socks[0]));
+ CF_DATA_RESTORE(cf, save);
+ return rv;
+}
+
+static void notify_drain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ if(!data->state.drain) {
+ data->state.drain = 1;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
+}
+
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)conn;
+ (void)stream_id;
+ (void)app_error_code;
+ (void)cf;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRIx64 ")",
+ stream_id, app_error_code));
+ stream->closed = TRUE;
+ stream->error3 = app_error_code;
+ if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) {
+ /* TODO: we do not get a specific error when the remote end closed
+ * the response before it was complete. */
+ stream->reset = TRUE;
+ }
+ notify_drain(cf, data);
+ return 0;
+}
+
+/*
+ * write_resp_raw() copies response data in raw format to the `data`'s
+ * receive buffer. If not enough space is available, it appends to the
+ * `data`'s overflow buffer.
+ */
+static CURLcode write_resp_raw(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem, size_t memlen,
+ bool flow)
+{
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ const char *buf = mem;
+ size_t ncopy = memlen;
+ /* copy as much as possible to the receive buffer */
+ if(stream->len) {
+ size_t len = CURLMIN(ncopy, stream->len);
+ memcpy(stream->mem + stream->memlen, buf, len);
+ stream->len -= len;
+ stream->memlen += len;
+ buf += len;
+ ncopy -= len;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
+ " to data buffer", stream->stream3_id, len));
+ }
+ /* copy the rest to the overflow buffer */
+ if(ncopy) {
+ result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
+ " to overflow buffer -> %d",
+ stream->stream3_id, ncopy, result));
+ notify_drain(cf, data);
+ }
+
+ if(!flow)
+ stream->recv_buf_nonflow += memlen;
+ if(CF_DATA_CURRENT(cf) != data) {
+ notify_drain(cf, data);
+ }
+ return result;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ CURLcode result;
+
+ (void)conn;
+ (void)stream3_id;
+
+ result = write_resp_raw(cf, data, buf, buflen, TRUE);
+ return result? -1 : 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id,
+ size_t consumed, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ (void)conn;
+ (void)stream_user_data;
+
+ /* nghttp3 has consumed bytes on the QUIC stream and we need to
+ * tell the QUIC connection to increase its flow control */
+ ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed);
+ ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
+ return 0;
+}
+
+/* Decode HTTP status code. Returns -1 if no valid status code was
+ decoded. (duplicate from http2.c) */
+static int decode_status_code(const uint8_t *value, size_t len)
+{
+ int i;
+ int res;
+
+ if(len != 3) {
+ return -1;
+ }
+
+ res = 0;
+
+ for(i = 0; i < 3; ++i) {
+ char c = value[i];
+
+ if(c < '0' || c > '9') {
+ return -1;
+ }
+
+ res *= 10;
+ res += c - '0';
+ }
+
+ return res;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+ int fin, void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)fin;
+ (void)cf;
+
+ /* add a CRLF only if we've received some headers */
+ if(stream->firstheader) {
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d",
+ stream_id, stream->status_code));
+ if(stream->status_code / 100 != 1) {
+ stream->bodystarted = TRUE;
+ }
+ return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+ nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ CURLcode result = CURLE_OK;
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)flags;
+ (void)cf;
+
+ if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
+ char line[14]; /* status line is always 13 characters long */
+ size_t ncopy;
+
+ DEBUGASSERT(!stream->firstheader);
+ stream->status_code = decode_status_code(h3val.base, h3val.len);
+ DEBUGASSERT(stream->status_code != -1);
+ ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
+ stream->status_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s",
+ stream_id, line));
+ result = write_resp_raw(cf, data, line, ncopy, FALSE);
+ if(result) {
+ return -1;
+ }
+ stream->firstheader = TRUE;
+ }
+ else {
+ /* store as an HTTP1-style header */
+ DEBUGASSERT(stream->firstheader);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s",
+ stream_id, (int)h3name.len, h3name.base,
+ (int)h3val.len, h3val.base));
+ result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, ": ", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
+ if(result) {
+ return -1;
+ }
+ result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
+ if(result) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ (void)conn;
+ (void)stream_user_data;
+
+ rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code);
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data) {
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data = stream_user_data;
+ int rv;
+ (void)conn;
+ (void)data;
+
+ rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id,
+ app_error_code);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+ if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static nghttp3_callbacks ngh3_callbacks = {
+ cb_h3_acked_stream_data, /* acked_stream_data */
+ cb_h3_stream_close,
+ cb_h3_recv_data,
+ cb_h3_deferred_consume,
+ NULL, /* begin_headers */
+ cb_h3_recv_header,
+ cb_h3_end_headers,
+ NULL, /* begin_trailers */
+ cb_h3_recv_header,
+ NULL, /* end_trailers */
+ cb_h3_stop_sending,
+ NULL, /* end_stream */
+ cb_h3_reset_stream,
+ NULL /* shutdown */
+};
+
+static int init_ngh3_conn(struct Curl_cfilter *cf)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result;
+ int rc;
+ int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
+
+ if(ngtcp2_conn_get_max_local_streams_uni(ctx->qconn) < 3) {
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+
+ nghttp3_settings_default(&ctx->h3settings);
+
+ rc = nghttp3_conn_client_new(&ctx->h3conn,
+ &ngh3_callbacks,
+ &ctx->h3settings,
+ nghttp3_mem_default(),
+ cf);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id,
+ qpack_dec_stream_id);
+ if(rc) {
+ result = CURLE_QUIC_CONNECT_ERROR;
+ goto fail;
+ }
+
+ return CURLE_OK;
+ fail:
+
+ return result;
+}
+
+static void drain_overflow_buffer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+ size_t overlen = Curl_dyn_len(&stream->overflow);
+ size_t ncopy = CURLMIN(overlen, stream->len);
+
+ (void)cf;
+ if(ncopy > 0) {
+ memcpy(stream->mem + stream->memlen,
+ Curl_dyn_ptr(&stream->overflow), ncopy);
+ stream->len -= ncopy;
+ stream->memlen += ncopy;
+ if(ncopy != overlen)
+ /* make the buffer only keep the tail */
+ (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
+ else {
+ Curl_dyn_reset(&stream->overflow);
+ }
+ }
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+
+ (void)cf;
+
+ if(stream->reset) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
+ *err = CURLE_PARTIAL_FILE;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
+ stream->stream3_id, *err));
+ goto out;
+ }
+ else if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was not closed cleanly: (err 0x%" PRIx64
+ ")",
+ stream->stream3_id, stream->error3);
+ *err = CURLE_HTTP3;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+
+ if(!stream->bodystarted) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->stream3_id);
+ *err = CURLE_HTTP3;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
+ " -> %d", stream->stream3_id, *err));
+ }
+ *err = CURLE_OK;
+ nread = 0;
+
+out:
+ data->state.drain = 0;
+ return nread;
+}
+
+/* incoming data frames on the h3 stream */
+static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+ struct cf_call_data save;
+
+ (void)ctx;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(ctx->qconn);
+ DEBUGASSERT(ctx->h3conn);
+ *err = CURLE_OK;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) start",
+ stream->stream3_id, len));
+ /* TODO: this implementation of response DATA buffering is fragile.
+ * It makes the following assumptions:
+ * - the `buf` passed here has the same lifetime as the easy handle
+ * - data returned in `buf` from this call is immediately used and `buf`
+ * can be overwritten during any handling of other transfers at
+ * this connection.
+ */
+ if(!stream->memlen) {
+ /* `buf` was not known before or is currently not used by stream,
+ * assign it (again). */
+ stream->mem = buf;
+ stream->len = len;
+ }
+
+ /* if there's data in the overflow buffer, move as much
+ as possible to the receive buffer now */
+ drain_overflow_buffer(cf, data);
+
+ if(cf_process_ingress(cf, data)) {
+ *err = CURLE_RECV_ERROR;
+ nread = -1;
+ goto out;
+ }
+
+ if(stream->memlen) {
+ nread = stream->memlen;
+ /* reset to allow more data to come */
+ /* TODO: very brittle buffer use design:
+ * - stream->mem has now `nread` bytes of response data
+ * - we assume that the caller will use those immediately and
+ * we can overwrite that with new data on our next invocation from
+ * anywhere.
+ */
+ stream->mem = buf;
+ stream->memlen = 0;
+ stream->len = len;
+ /* extend the stream window with the data we're consuming and send out
+ any additional packets to tell the server that we can receive more */
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> %zd bytes",
+ stream->stream3_id, nread));
+ report_consumed_data(cf, data, nread);
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ nread = -1;
+ }
+ goto out;
+ }
+
+ if(stream->closed) {
+ nread = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> EAGAIN",
+ stream->stream3_id));
+ *err = CURLE_AGAIN;
+ nread = -1;
+out:
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ nread = -1;
+ goto out;
+ }
+
+ CF_DATA_RESTORE(cf, save);
+ return nread;
+}
+
+/* this amount of data has now been acked on this stream */
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.p.http;
+ (void)user_data;
+
+ (void)cf;
+ if(!data->set.postfields) {
+ stream->h3out->used -= datalen;
+ DEBUGF(LOG_CF(data, cf, "cb_h3_acked_stream_data, %"PRIu64" bytes, "
+ "%zd left unacked", datalen, stream->h3out->used));
+ DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
+
+ if(stream->h3out->used == 0) {
+ int rv = nghttp3_conn_resume_stream(conn, stream_id);
+ if(rv) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ }
+ return 0;
+}
+
+static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_cfilter *cf = user_data;
+ struct Curl_easy *data = stream_user_data;
+ size_t nread;
+ struct HTTP *stream = data->req.p.http;
+ (void)cf;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)veccnt;
+
+ if(data->set.postfields) {
+ vec[0].base = data->set.postfields;
+ vec[0].len = data->state.infilesize;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 1;
+ }
+
+ if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+
+ nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
+ if(nread > 0) {
+ /* nghttp3 wants us to hold on to the data until it tells us it is okay to
+ delete it. Append the data at the end of the h3out buffer. Since we can
+ only return consecutive data, copy the amount that fits and the next
+ part comes in next invoke. */
+ struct h3out *out = stream->h3out;
+ if(nread + out->windex > H3_SEND_SIZE)
+ nread = H3_SEND_SIZE - out->windex;
+
+ memcpy(&out->buf[out->windex], stream->upload_mem, nread);
+
+ /* that's the chunk we return to nghttp3 */
+ vec[0].base = &out->buf[out->windex];
+ vec[0].len = nread;
+
+ out->windex += nread;
+ out->used += nread;
+
+ if(out->windex == H3_SEND_SIZE)
+ out->windex = 0; /* wrap */
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data->state.infilesize != -1) {
+ stream->upload_left -= nread;
+ if(!stream->upload_left)
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
+ nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ out->used));
+ }
+ if(stream->upload_done && !stream->upload_len &&
+ (stream->upload_left <= 0)) {
+ DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction sets EOF"));
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return nread ? 1 : 0;
+ }
+ else if(!nread) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ return 1;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode h3_stream_open(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ CURLcode result = CURLE_OK;
+ nghttp3_nv *nva = NULL;
+ int64_t stream3_id;
+ int rc = 0;
+ struct h3out *h3out = NULL;
+ struct h2h3req *hreq = NULL;
+
+ rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream3_id, NULL);
+ if(rc) {
+ failf(data, "can get bidi streams");
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ stream->h3req = TRUE;
+ Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
+ stream->recv_buf_nonflow = 0;
+
+ result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(nghttp3_nv) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].namelen = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].valuelen = hreq->header[i].valuelen;
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT: {
+ nghttp3_data_reader data_reader;
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ data_reader.read_data = cb_h3_readfunction;
+
+ h3out = calloc(sizeof(struct h3out), 1);
+ if(!h3out) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ stream->h3out = h3out;
+
+ rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
+ nva, nheader, &data_reader, data);
+ if(rc)
+ goto fail;
+ break;
+ }
+ default:
+ stream->upload_left = 0; /* nothing left to send */
+ rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
+ nva, nheader, NULL, data);
+ if(rc)
+ goto fail;
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
+ stream3_id, (void *)data);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
+ stream3_id, data->state.url));
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ if(rc) {
+ switch(rc) {
+ case NGHTTP3_ERR_CONN_CLOSING:
+ DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+ "connection is closing", stream->stream3_id));
+ result = CURLE_RECV_ERROR;
+ break;
+ default:
+ DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+ stream->stream3_id, rc, ngtcp2_strerror(rc)));
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ }
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+
+static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ ssize_t sent = 0;
+ struct HTTP *stream = data->req.p.http;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGASSERT(cf->connected);
+ DEBUGASSERT(ctx->qconn);
+ DEBUGASSERT(ctx->h3conn);
+ *err = CURLE_OK;
+
+ if(stream->closed) {
+ *err = CURLE_HTTP3;
+ sent = -1;
+ goto out;
+ }
+
+ if(!stream->h3req) {
+ CURLcode result = h3_stream_open(cf, data, buf, len);
+ if(result) {
+ DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", result));
+ sent = -1;
+ goto out;
+ }
+ /* Assume that mem of length len only includes HTTP/1.1 style
+ header fields. In other words, it does not contain request
+ body. */
+ sent = len;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "ngh3_stream_send() wants to send %zd bytes",
+ len));
+ if(!stream->upload_len) {
+ stream->upload_mem = buf;
+ stream->upload_len = len;
+ (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
+ }
+ else {
+ *err = CURLE_AGAIN;
+ sent = -1;
+ goto out;
+ }
+ }
+
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ sent = -1;
+ goto out;
+ }
+
+ /* Reset post upload buffer after resumed. */
+ if(stream->upload_mem) {
+ if(data->set.postfields) {
+ sent = len;
+ }
+ else {
+ sent = len - stream->upload_len;
+ }
+
+ stream->upload_mem = NULL;
+ stream->upload_len = 0;
+
+ if(sent == 0) {
+ *err = CURLE_AGAIN;
+ sent = -1;
+ goto out;
+ }
+ }
+out:
+ CF_DATA_RESTORE(cf, save);
+ return sent;
+}
+
+static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ const char *hostname, *disp_hostname;
+ int port;
+ char *snihost;
+
+ Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port);
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ if(cf->conn->ssl_config.verifyhost) {
+#ifdef USE_OPENSSL
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(ctx->ssl);
+ if(!server_cert) {
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ return result;
+#elif defined(USE_GNUTLS)
+ result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+ &cf->conn->ssl_config, &data->set.ssl,
+ hostname, disp_hostname,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE)
+ return CURLE_PEER_FAILED_VERIFICATION;
+#endif
+ infof(data, "Verified certificate just fine");
+ }
+ else
+ infof(data, "Skipped certificate verification");
+#ifdef USE_OPENSSL
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, ctx->ssl);
+#endif
+ return result;
+}
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ ssize_t recvd;
+ int rv;
+ uint8_t buf[65536];
+ int bufsize = (int)sizeof(buf);
+ size_t pktcount = 0, total_recvd = 0;
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ ngtcp2_path path;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_pkt_info pi = { 0 };
+
+ for(;;) {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
+ goto out;
+ }
+ if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ const char *r_ip;
+ int r_port;
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ failf(data, "ngtcp2: connection to %s port %u refused",
+ r_ip, r_port);
+ return CURLE_COULDNT_CONNECT;
+ }
+ failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd (errno=%d)",
+ recvd, SOCKERRNO);
+ return CURLE_RECV_ERROR;
+ }
+
+ if(recvd > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+
+ ++pktcount;
+ total_recvd += recvd;
+
+ ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
+ ctx->q.local_addrlen);
+ ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr,
+ remote_addrlen);
+
+ rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, buf, recvd, ts);
+ if(rv) {
+ DEBUGF(LOG_CF(data, cf, "ingress, read_pkt -> %s",
+ ngtcp2_strerror(rv)));
+ if(!ctx->last_error.error_code) {
+ if(rv == NGTCP2_ERR_CRYPTO) {
+ ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ &ctx->last_error,
+ ngtcp2_conn_get_tls_alert(ctx->qconn), NULL, 0);
+ }
+ else {
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &ctx->last_error, rv, NULL, 0);
+ }
+ }
+
+ if(rv == NGTCP2_ERR_CRYPTO)
+ /* this is a "TLS problem", but a failed certificate verification
+ is a common reason for this */
+ return CURLE_PEER_FAILED_VERIFICATION;
+ return CURLE_RECV_ERROR;
+ }
+ }
+
+out:
+ (void)pktcount;
+ (void)total_recvd;
+ DEBUGF(LOG_CF(data, cf, "ingress, recvd %zu packets with %zd bytes",
+ pktcount, total_recvd));
+ return CURLE_OK;
+}
+
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rv;
+ size_t sent;
+ ngtcp2_ssize outlen;
+ uint8_t *outpos = ctx->q.pktbuf;
+ size_t max_udp_payload_size =
+ ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn);
+ size_t path_max_udp_payload_size =
+ ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn);
+ size_t max_pktcnt =
+ CURLMIN(MAX_PKT_BURST, ctx->q.pktbuflen / max_udp_payload_size);
+ size_t pktcnt = 0;
+ size_t gsolen = 0; /* this disables gso until we have a clue */
+ ngtcp2_path_storage ps;
+ ngtcp2_tstamp ts = timestamp();
+ ngtcp2_tstamp expiry;
+ ngtcp2_duration timeout;
+ int64_t stream_id;
+ nghttp3_ssize veccnt;
+ int fin;
+ nghttp3_vec vec[16];
+ ngtcp2_ssize ndatalen;
+ uint32_t flags;
+ CURLcode curlcode;
+
+ rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts);
+ if(rv) {
+ failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
+ ngtcp2_strerror(rv));
+ ngtcp2_connection_close_error_set_transport_error_liberr(&ctx->last_error,
+ rv, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+
+ if(ctx->q.num_blocked_pkt) {
+ curlcode = vquic_send_blocked_pkt(cf, data, &ctx->q);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+
+ ngtcp2_path_storage_zero(&ps);
+
+ for(;;) {
+ veccnt = 0;
+ stream_id = -1;
+ fin = 0;
+
+ if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
+ veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
+ sizeof(vec) / sizeof(vec[0]));
+ if(veccnt < 0) {
+ failf(data, "nghttp3_conn_writev_stream returned error: %s",
+ nghttp3_strerror((int)veccnt));
+ ngtcp2_connection_close_error_set_application_error(
+ &ctx->last_error,
+ nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
+ (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
+ outlen = ngtcp2_conn_writev_stream(ctx->qconn, &ps.path, NULL, outpos,
+ max_udp_payload_size,
+ &ndatalen, flags, stream_id,
+ (const ngtcp2_vec *)vec, veccnt, ts);
+ if(outlen == 0) {
+ /* ngtcp2 does not want to send more packets, if the buffer is
+ * not empty, send that now */
+ if(outpos != ctx->q.pktbuf) {
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - ctx->q.pktbuf - sent,
+ gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ }
+ /* done for now */
+ goto out;
+ }
+ if(outlen < 0) {
+ switch(outlen) {
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ assert(ndatalen == -1);
+ nghttp3_conn_block_stream(ctx->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ assert(ndatalen == -1);
+ nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id);
+ continue;
+ case NGTCP2_ERR_WRITE_MORE:
+ /* ngtcp2 wants to send more. update the flow of the stream whose data
+ * is in the buffer and continue */
+ assert(ndatalen >= 0);
+ rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ default:
+ assert(ndatalen == -1);
+ failf(data, "ngtcp2_conn_writev_stream returned error: %s",
+ ngtcp2_strerror((int)outlen));
+ ngtcp2_connection_close_error_set_transport_error_liberr(
+ &ctx->last_error, (int)outlen, NULL, 0);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else if(ndatalen >= 0) {
+ /* ngtcp2 thinks it has added all it wants. Update the stream */
+ rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
+ if(rv) {
+ failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ }
+
+ /* advance to the end of the buffered packet data */
+ outpos += outlen;
+
+ if(pktcnt == 0) {
+ /* first packet buffer chunk. use this as gsolen. It's how ngtcp2
+ * indicates the intended segment size. */
+ gsolen = outlen;
+ }
+ else if((size_t)outlen > gsolen ||
+ (gsolen > path_max_udp_payload_size && (size_t)outlen != gsolen)) {
+ /* Packet larger than path_max_udp_payload_size is PMTUD probe
+ packet and it might not be sent because of EMSGSIZE. Send
+ them separately to minimize the loss. */
+ /* send the pktbuf *before* the last addition */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - outlen - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ /* blocked, add the pktbuf *before* and *at* the last addition
+ * separately to the blocked packages */
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - outlen - ctx->q.pktbuf - sent, gsolen);
+ vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* send the pktbuf *at* the last addition */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, outpos - outlen, outlen,
+ outlen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ assert(0 == sent);
+ vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* pktbuf has been completely sent */
+ pktcnt = 0;
+ outpos = ctx->q.pktbuf;
+ continue;
+ }
+
+ if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
+ /* enough packets or last one is shorter than the intended
+ * segment size, indicating that it is time to send. */
+ curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outpos - ctx->q.pktbuf, gsolen, &sent);
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
+ outpos - ctx->q.pktbuf - sent, gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ return curlcode;
+ }
+ /* pktbuf has been completely sent */
+ pktcnt = 0;
+ outpos = ctx->q.pktbuf;
+ }
+ }
+
+out:
+ /* non-errored exit. check when we should run again. */
+ expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+ if(expiry != UINT64_MAX) {
+ if(expiry <= ts) {
+ timeout = 0;
+ }
+ else {
+ timeout = expiry - ts;
+ if(timeout % NGTCP2_MILLISECONDS) {
+ timeout += NGTCP2_MILLISECONDS;
+ }
+ }
+ Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ /* We may have received more data than we're able to hold in the receive
+ buffer and allocated an overflow buffer. Since it's possible that
+ there's no more data coming on the socket, we need to keep reading
+ until the overflow buffer is empty. */
+ const struct HTTP *stream = data->req.p.http;
+ (void)cf;
+ return Curl_dyn_len(&stream->overflow) > 0;
+}
+
+static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_DONE: {
+ struct HTTP *stream = data->req.p.http;
+ Curl_dyn_free(&stream->overflow);
+ free(stream->h3out);
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ struct HTTP *stream = data->req.p.http;
+ stream->upload_done = TRUE;
+ (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
+ break;
+ }
+ case CF_CTRL_DATA_IDLE:
+ if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) {
+ if(cf_flush_egress(cf, data)) {
+ result = CURLE_SEND_ERROR;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
+{
+ struct cf_call_data save = ctx->call_data;
+
+ if(ctx->qlogfd != -1) {
+ close(ctx->qlogfd);
+ }
+#ifdef USE_OPENSSL
+ if(ctx->ssl)
+ SSL_free(ctx->ssl);
+ if(ctx->sslctx)
+ SSL_CTX_free(ctx->sslctx);
+#elif defined(USE_GNUTLS)
+ if(ctx->gtls) {
+ if(ctx->gtls->cred)
+ gnutls_certificate_free_credentials(ctx->gtls->cred);
+ if(ctx->gtls->session)
+ gnutls_deinit(ctx->gtls->session);
+ free(ctx->gtls);
+ }
+#elif defined(USE_WOLFSSL)
+ if(ctx->ssl)
+ wolfSSL_free(ctx->ssl);
+ if(ctx->sslctx)
+ wolfSSL_CTX_free(ctx->sslctx);
+#endif
+ vquic_ctx_free(&ctx->q);
+ if(ctx->h3conn)
+ nghttp3_conn_del(ctx->h3conn);
+ if(ctx->qconn)
+ ngtcp2_conn_del(ctx->qconn);
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->qlogfd = -1;
+ ctx->call_data = save;
+}
+
+static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ if(ctx && ctx->qconn) {
+ char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
+ ngtcp2_tstamp ts;
+ ngtcp2_ssize rc;
+
+ DEBUGF(LOG_CF(data, cf, "close"));
+ ts = timestamp();
+ rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
+ NULL, /* pkt_info */
+ (uint8_t *)buffer, sizeof(buffer),
+ &ctx->last_error, ts);
+ if(rc > 0) {
+ while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) &&
+ SOCKERRNO == EINTR);
+ }
+
+ cf_ngtcp2_ctx_clear(ctx);
+ }
+
+ cf->connected = FALSE;
+ CF_DATA_RESTORE(cf, save);
+}
+
+static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
+ DEBUGF(LOG_CF(data, cf, "destroy"));
+ if(ctx) {
+ cf_ngtcp2_ctx_clear(ctx);
+ free(ctx);
+ }
+ cf->ctx = NULL;
+ /* No CF_DATA_RESTORE(cf, save) possible */
+ (void)save;
+}
+
+/*
+ * Might be called twice for happy eyeballs.
+ */
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ int rc;
+ int rv;
+ CURLcode result;
+ const struct Curl_sockaddr_ex *sockaddr;
+ int qfd;
+
+ ctx->version = NGTCP2_PROTO_VER_MAX;
+#ifdef USE_OPENSSL
+ result = quic_ssl_ctx(&ctx->sslctx, cf, data);
+ if(result)
+ return result;
+
+ result = quic_set_client_cert(cf, data);
+ if(result)
+ return result;
+#elif defined(USE_WOLFSSL)
+ result = quic_ssl_ctx(&ctx->sslctx, cf, data);
+ if(result)
+ return result;
+#endif
+
+ result = quic_init_ssl(cf, data);
+ if(result)
+ return result;
+
+ ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ ctx->scid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
+ ctx->qlogfd = qfd; /* -1 if failure above */
+ quic_settings(ctx, data);
+
+ result = vquic_ctx_init(&ctx->q,
+ NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
+ if(result)
+ return result;
+
+ Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+ &sockaddr, NULL, NULL, NULL, NULL);
+ ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
+ rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
+ &ctx->q.local_addrlen);
+ if(rv == -1)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ ngtcp2_addr_init(&ctx->connected_path.local,
+ (struct sockaddr *)&ctx->q.local_addr,
+ ctx->q.local_addrlen);
+ ngtcp2_addr_init(&ctx->connected_path.remote,
+ &sockaddr->sa_addr, sockaddr->addrlen);
+
+ rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid,
+ &ctx->connected_path,
+ NGTCP2_PROTO_VER_V1, &ng_callbacks,
+ &ctx->settings, &ctx->transport_params,
+ NULL, cf);
+ if(rc)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+#ifdef USE_GNUTLS
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session);
+#else
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl);
+#endif
+
+ ngtcp2_connection_close_error_default(&ctx->last_error);
+
+ ctx->conn_ref.get_conn = get_conn;
+ ctx->conn_ref.user_data = cf;
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct cf_call_data save;
+ struct curltime now;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect the UDP filter first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+
+ CF_DATA_SAVE(save, cf, data);
+
+ if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+ /* Not time yet to attempt the next connect */
+ DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ goto out;
+ }
+
+ if(!ctx->qconn) {
+ ctx->started_at = now;
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ result = cf_flush_egress(cf, data);
+ /* we do not expect to be able to recv anything yet */
+ goto out;
+ }
+
+ result = cf_process_ingress(cf, data);
+ if(result)
+ goto out;
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ goto out;
+
+ if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
+ ctx->handshake_at = now;
+ DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at)));
+ result = qng_verify_peer(cf, data);
+ if(!result) {
+ DEBUGF(LOG_CF(data, cf, "peer verified"));
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ }
+ }
+
+out:
+ if(result == CURLE_RECV_ERROR && ctx->qconn &&
+ ngtcp2_conn_is_in_draining_period(ctx->qconn)) {
+ /* When a QUIC server instance is shutting down, it may send us a
+ * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+ * state.
+ * This may be a stopping of the service or it may be that the server
+ * is reloading and a new instance will start serving soon.
+ * In any case, we tear down our socket and start over with a new one.
+ * We re-open the underlying UDP cf right now, but do not start
+ * connecting until called again.
+ */
+ int reconn_delay_ms = 200;
+
+ DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms));
+ Curl_conn_cf_close(cf->next, data);
+ cf_ngtcp2_ctx_clear(ctx);
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ if(!result && *done) {
+ *done = FALSE;
+ ctx->reconnect_at = now;
+ ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
+ Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
+ result = CURLE_OK;
+ }
+ }
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(result) {
+ const char *r_ip;
+ int r_port;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ infof(data, "QUIC connect to %s port %u failed: %s",
+ r_ip, r_port, curl_easy_strerror(result));
+ }
+#endif
+ DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+ CF_DATA_RESTORE(cf, save);
+ return result;
+}
+
+static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ struct cf_call_data save;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ const ngtcp2_transport_params *rp;
+ DEBUGASSERT(pres1);
+
+ CF_DATA_SAVE(save, cf, data);
+ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
+ if(rp)
+ *pres1 = (rp->initial_max_streams_bidi > INT_MAX)?
+ INT_MAX : (int)rp->initial_max_streams_bidi;
+ else /* not arrived yet? */
+ *pres1 = Curl_multi_max_concurrent_streams(data->multi);
+ DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1));
+ CF_DATA_RESTORE(cf, save);
+ return CURLE_OK;
+ }
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ if(ctx->got_first_byte)
+ *when = ctx->first_byte_at;
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ if(cf->connected)
+ *when = ctx->handshake_at;
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ bool alive = TRUE;
+
+ *input_pending = FALSE;
+ if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+ return FALSE;
+
+ if(*input_pending) {
+ /* This happens before we've sent off a request and the connection is
+ not in use by any other transfer, there shouldn't be any data here,
+ only "protocol frames" */
+ *input_pending = FALSE;
+ Curl_attach_connection(data, cf->conn);
+ if(cf_process_ingress(cf, data))
+ alive = FALSE;
+ else {
+ alive = TRUE;
+ }
+ Curl_detach_connection(data);
+ }
+
+ return alive;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_ngtcp2_destroy,
+ cf_ngtcp2_connect,
+ cf_ngtcp2_close,
+ Curl_cf_def_get_host,
+ cf_ngtcp2_get_select_socks,
+ cf_ngtcp2_data_pending,
+ cf_ngtcp2_send,
+ cf_ngtcp2_recv,
+ cf_ngtcp2_data_event,
+ cf_ngtcp2_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_ngtcp2_query,
+};
+
+CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_ngtcp2_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->qlogfd = -1;
+ cf_ngtcp2_ctx_clear(ctx);
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+ if(result)
+ goto out;
+
+ result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+ if(result)
+ goto out;
+
+ cf->conn = conn;
+ udp_cf->conn = cf->conn;
+ udp_cf->sockindex = cf->sockindex;
+ cf->next = udp_cf;
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ if(udp_cf)
+ Curl_conn_cf_discard(udp_cf, data);
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+ return result;
+}
+
+bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/Utilities/cmcurl/lib/wildcard.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
index a3e24b6784..8813ec9a77 100644
--- a/Utilities/cmcurl/lib/wildcard.c
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
@@ -1,3 +1,5 @@
+#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H
+#define HEADER_CURL_VQUIC_CURL_NGTCP2_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -5,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,52 +26,36 @@
#include "curl_setup.h"
-#ifndef CURL_DISABLE_FTP
+#ifdef USE_NGTCP2
-#include "wildcard.h"
-#include "llist.h"
-#include "fileinfo.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#ifdef HAVE_NETINET_UDP_H
+#include <netinet/udp.h>
+#endif
-static void fileinfo_dtor(void *user, void *element)
-{
- (void)user;
- Curl_fileinfo_cleanup(element);
-}
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <nghttp3/nghttp3.h>
+#ifdef USE_OPENSSL
+#include <openssl/ssl.h>
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#endif
-CURLcode Curl_wildcard_init(struct WildcardData *wc)
-{
- Curl_llist_init(&wc->filelist, fileinfo_dtor);
- wc->state = CURLWC_INIT;
+struct Curl_cfilter;
- return CURLE_OK;
-}
+#include "urldata.h"
-void Curl_wildcard_dtor(struct WildcardData *wc)
-{
- if(!wc)
- return;
+void Curl_ngtcp2_ver(char *p, size_t len);
- if(wc->dtor) {
- wc->dtor(wc->protdata);
- wc->dtor = ZERO_NULL;
- wc->protdata = NULL;
- }
- DEBUGASSERT(wc->protdata == NULL);
+CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
- Curl_llist_destroy(&wc->filelist, NULL);
+bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+#endif
-
- free(wc->path);
- wc->path = NULL;
- free(wc->pattern);
- wc->pattern = NULL;
-
- wc->customptr = NULL;
- wc->state = CURLWC_INIT;
-}
-
-#endif /* if disabled */
+#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
new file mode 100644
index 0000000000..87a221cc18
--- /dev/null
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -0,0 +1,1464 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QUICHE
+#include <quiche.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include "urldata.h"
+#include "cfilters.h"
+#include "cf-socket.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "strcase.h"
+#include "multiif.h"
+#include "connect.h"
+#include "progress.h"
+#include "strerror.h"
+#include "vquic.h"
+#include "vquic_int.h"
+#include "curl_quiche.h"
+#include "transfer.h"
+#include "h2h3.h"
+#include "vtls/openssl.h"
+#include "vtls/keylog.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
+
+/* how many UDP packets to send max in one call */
+#define MAX_PKT_BURST 10
+#define MAX_UDP_PAYLOAD_SIZE 1452
+
+/*
+ * Store quiche version info in this buffer.
+ */
+void Curl_quiche_ver(char *p, size_t len)
+{
+ (void)msnprintf(p, len, "quiche/%s", quiche_version());
+}
+
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+
+static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+
+ SSL_CTX_set_alpn_protos(ssl_ctx,
+ (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+
+ {
+ struct connectdata *conn = data->conn;
+ if(conn->ssl_config.verifypeer) {
+ const char * const ssl_cafile = conn->ssl_config.CAfile;
+ const char * const ssl_capath = conn->ssl_config.CApath;
+ if(ssl_cafile || ssl_capath) {
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+ /* tell OpenSSL where to find CA certificates that are used to verify
+ the server's certificate. */
+ if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return NULL;
+ }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
+#ifdef CURL_CA_FALLBACK
+ else {
+ /* verifying the peer without any CA certificates won't work so
+ use openssl's built-in default as fallback */
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+ }
+#endif
+ }
+ }
+ return ssl_ctx;
+}
+
+struct quic_handshake {
+ char *buf; /* pointer to the buffer */
+ size_t alloclen; /* size of allocation */
+ size_t len; /* size of content in buffer */
+ size_t nread; /* how many bytes have been read */
+};
+
+struct h3_event_node {
+ struct h3_event_node *next;
+ quiche_h3_event *ev;
+};
+
+struct cf_quiche_ctx {
+ struct cf_quic_ctx q;
+ quiche_conn *qconn;
+ quiche_config *cfg;
+ quiche_h3_conn *h3c;
+ quiche_h3_config *h3config;
+ uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
+ SSL_CTX *sslctx;
+ SSL *ssl;
+ struct curltime started_at; /* time the current attempt started */
+ struct curltime handshake_at; /* time connect handshake finished */
+ struct curltime first_byte_at; /* when first byte was recvd */
+ struct curltime reconnect_at; /* time the next attempt should start */
+ BIT(goaway); /* got GOAWAY from server */
+ BIT(got_first_byte); /* if first byte was received */
+};
+
+
+#ifdef DEBUG_QUICHE
+static void quiche_debug_log(const char *line, void *argp)
+{
+ (void)argp;
+ fprintf(stderr, "%s\n", line);
+}
+#endif
+
+static void h3_clear_pending(struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ if(stream->pending) {
+ struct h3_event_node *node, *next;
+ for(node = stream->pending; node; node = next) {
+ next = node->next;
+ quiche_h3_event_free(node->ev);
+ free(node);
+ }
+ stream->pending = NULL;
+ }
+}
+
+static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
+{
+ if(ctx) {
+ vquic_ctx_free(&ctx->q);
+ if(ctx->qconn)
+ quiche_conn_free(ctx->qconn);
+ if(ctx->h3config)
+ quiche_h3_config_free(ctx->h3config);
+ if(ctx->h3c)
+ quiche_h3_conn_free(ctx->h3c);
+ if(ctx->cfg)
+ quiche_config_free(ctx->cfg);
+ memset(ctx, 0, sizeof(*ctx));
+ }
+}
+
+static void notify_drain(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ (void)cf;
+ data->state.drain = 1;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+}
+
+static CURLcode h3_add_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int64_t stream3_id, quiche_h3_event *ev)
+{
+ struct Curl_easy *mdata;
+ struct h3_event_node *node, **pnext;
+
+ DEBUGASSERT(data->multi);
+ for(mdata = data->multi->easyp; mdata; mdata = mdata->next) {
+ if(mdata->req.p.http && mdata->req.p.http->stream3_id == stream3_id) {
+ break;
+ }
+ }
+
+ if(!mdata) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] event discarded, easy handle "
+ "not found", stream3_id));
+ quiche_h3_event_free(ev);
+ return CURLE_OK;
+ }
+
+ node = calloc(sizeof(*node), 1);
+ if(!node) {
+ quiche_h3_event_free(ev);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ node->ev = ev;
+ /* append to process them in order of arrival */
+ pnext = &mdata->req.p.http->pending;
+ while(*pnext) {
+ pnext = &((*pnext)->next);
+ }
+ *pnext = node;
+ notify_drain(cf, mdata);
+ return CURLE_OK;
+}
+
+struct h3h1header {
+ char *dest;
+ size_t destlen; /* left to use */
+ size_t nlen; /* used */
+};
+
+static int cb_each_header(uint8_t *name, size_t name_len,
+ uint8_t *value, size_t value_len,
+ void *argp)
+{
+ struct h3h1header *headers = (struct h3h1header *)argp;
+ size_t olen = 0;
+
+ if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
+ msnprintf(headers->dest,
+ headers->destlen, "HTTP/3 %.*s \r\n",
+ (int) value_len, value);
+ }
+ else if(!headers->nlen) {
+ return CURLE_HTTP3;
+ }
+ else {
+ msnprintf(headers->dest,
+ headers->destlen, "%.*s: %.*s\r\n",
+ (int)name_len, name, (int) value_len, value);
+ }
+ olen = strlen(headers->dest);
+ headers->destlen -= olen;
+ headers->nlen += olen;
+ headers->dest += olen;
+ return 0;
+}
+
+static ssize_t cf_recv_body(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread;
+ size_t offset = 0;
+
+ if(!stream->firstbody) {
+ /* add a header-body separator CRLF */
+ offset = 2;
+ }
+ nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ (unsigned char *)buf + offset, len - offset);
+ if(nread >= 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][DATA] len=%zd",
+ stream->stream3_id, nread));
+ if(!stream->firstbody) {
+ stream->firstbody = TRUE;
+ buf[0] = '\r';
+ buf[1] = '\n';
+ nread += offset;
+ }
+ }
+ else if(nread == -1) {
+ *err = CURLE_AGAIN;
+ stream->h3_recving_data = FALSE;
+ }
+ else {
+ failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]",
+ nread, stream->stream3_id);
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ streamclose(cf->conn, "Reset of stream");
+ stream->h3_recving_data = FALSE;
+ nread = -1;
+ *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ }
+ return nread;
+}
+
+#ifdef DEBUGBUILD
+static const char *cf_ev_name(quiche_h3_event *ev)
+{
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ return "HEADERS";
+ case QUICHE_H3_EVENT_DATA:
+ return "DATA";
+ case QUICHE_H3_EVENT_RESET:
+ return "RESET";
+ case QUICHE_H3_EVENT_FINISHED:
+ return "FINISHED";
+ case QUICHE_H3_EVENT_GOAWAY:
+ return "GOAWAY";
+ default:
+ return "Unknown";
+ }
+}
+#else
+#define cf_ev_name(x) ""
+#endif
+
+static ssize_t h3_process_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ int64_t stream3_id,
+ quiche_h3_event *ev,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = 0;
+ int rc;
+ struct h3h1header headers;
+
+ DEBUGASSERT(stream3_id == stream->stream3_id);
+
+ *err = CURLE_OK;
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ stream->h3_got_header = TRUE;
+ headers.dest = buf;
+ headers.destlen = len;
+ headers.nlen = 0;
+ rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
+ if(rc) {
+ failf(data, "Error %d in HTTP/3 response header for stream[%"PRId64"]",
+ rc, stream3_id);
+ *err = CURLE_RECV_ERROR;
+ recvd = -1;
+ break;
+ }
+ recvd = headers.nlen;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS] len=%zd",
+ stream3_id, recvd));
+ break;
+
+ case QUICHE_H3_EVENT_DATA:
+ DEBUGASSERT(!stream->closed);
+ stream->h3_recving_data = TRUE;
+ recvd = cf_recv_body(cf, data, buf, len, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ break;
+
+ case QUICHE_H3_EVENT_RESET:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id));
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ /* streamclose(cf->conn, "Reset of stream");*/
+ stream->h3_recving_data = FALSE;
+ break;
+
+ case QUICHE_H3_EVENT_FINISHED:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id));
+ stream->closed = TRUE;
+ /* streamclose(cf->conn, "End of stream");*/
+ stream->h3_recving_data = FALSE;
+ break;
+
+ case QUICHE_H3_EVENT_GOAWAY:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id));
+ break;
+
+ default:
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d",
+ stream3_id, quiche_h3_event_type(ev)));
+ break;
+ }
+ return recvd;
+}
+
+static ssize_t h3_process_pending(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ struct h3_event_node *node = stream->pending, **pnext = &stream->pending;
+ ssize_t recvd = 0, erecvd;
+
+ *err = CURLE_OK;
+ DEBUGASSERT(stream);
+ while(node && len) {
+ erecvd = h3_process_event(cf, data, buf, len,
+ stream->stream3_id, node->ev, err);
+ quiche_h3_event_free(node->ev);
+ *pnext = node->next;
+ free(node);
+ node = *pnext;
+ if(erecvd < 0) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] process event -> %d",
+ stream->stream3_id, *err));
+ return erecvd;
+ }
+ recvd += erecvd;
+ *err = CURLE_OK;
+ buf += erecvd;
+ len -= erecvd;
+ }
+ return recvd;
+}
+
+static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
+ uint8_t buf[65536];
+ int bufsize = (int)sizeof(buf);
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ quiche_recv_info recv_info;
+ ssize_t recvd, nread;
+ ssize_t total = 0, pkts = 0;
+
+ DEBUGASSERT(ctx->qconn);
+
+ /* in case the timeout expired */
+ quiche_conn_on_timeout(ctx->qconn);
+
+ do {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd < 0) {
+ if((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)) {
+ break;
+ }
+ if(SOCKERRNO == ECONNREFUSED) {
+ const char *r_ip;
+ int r_port;
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ failf(data, "quiche: connection to %s:%u refused",
+ r_ip, r_port);
+ return CURLE_COULDNT_CONNECT;
+ }
+ failf(data, "quiche: recvfrom() unexpectedly returned %zd "
+ "(errno: %d, socket %d)", recvd, SOCKERRNO, ctx->q.sockfd);
+ return CURLE_RECV_ERROR;
+ }
+
+ total += recvd;
+ ++pkts;
+ if(recvd > 0 && !ctx->got_first_byte) {
+ ctx->first_byte_at = Curl_now();
+ ctx->got_first_byte = TRUE;
+ }
+ recv_info.from = (struct sockaddr *) &remote_addr;
+ recv_info.from_len = remote_addrlen;
+ recv_info.to = (struct sockaddr *) &ctx->q.local_addr;
+ recv_info.to_len = ctx->q.local_addrlen;
+
+ nread = quiche_conn_recv(ctx->qconn, buf, recvd, &recv_info);
+ if(nread < 0) {
+ if(QUICHE_ERR_DONE == nread) {
+ DEBUGF(LOG_CF(data, cf, "ingress, quiche is DONE"));
+ return CURLE_OK;
+ }
+ else if(QUICHE_ERR_TLS_FAIL == nread) {
+ long verify_ok = SSL_get_verify_result(ctx->ssl);
+ if(verify_ok != X509_V_OK) {
+ failf(data, "SSL certificate problem: %s",
+ X509_verify_cert_error_string(verify_ok));
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ else {
+ failf(data, "quiche_conn_recv() == %zd", nread);
+ return CURLE_RECV_ERROR;
+ }
+ }
+ else if(nread < recvd) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, quiche only "
+ "accepted %zd/%zd bytes",
+ stream3_id, nread, recvd));
+ }
+
+ } while(pkts < 1000); /* arbitrary */
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] ingress, recvd %zd bytes "
+ "in %zd packets", stream3_id, total, pkts));
+ return CURLE_OK;
+}
+
+/*
+ * flush_egress drains the buffers and sends off data.
+ * Calls failf() on errors.
+ */
+static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int64_t stream3_id = data->req.p.http? data->req.p.http->stream3_id : -1;
+ quiche_send_info send_info;
+ ssize_t outlen, total_len = 0;
+ size_t max_udp_payload_size =
+ quiche_conn_max_send_udp_payload_size(ctx->qconn);
+ size_t gsolen = max_udp_payload_size;
+ size_t sent, pktcnt = 0;
+ CURLcode result;
+ int64_t timeout_ns;
+
+ ctx->q.no_gso = TRUE;
+ if(ctx->q.num_blocked_pkt) {
+ result = vquic_send_blocked_pkt(cf, data, &ctx->q);
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, still not "
+ "able to send blocked packet", stream3_id));
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ goto out;
+ }
+ }
+
+ for(;;) {
+ outlen = quiche_conn_send(ctx->qconn, ctx->q.pktbuf, max_udp_payload_size,
+ &send_info);
+ if(outlen == QUICHE_ERR_DONE) {
+ result = CURLE_OK;
+ goto out;
+ }
+
+ if(outlen < 0) {
+ failf(data, "quiche_conn_send returned %zd", outlen);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+
+ /* send the pktbuf *before* the last addition */
+ result = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
+ outlen, gsolen, &sent);
+ ++pktcnt;
+ total_len += outlen;
+ if(result) {
+ if(result == CURLE_AGAIN) {
+ /* blocked, add the pktbuf *before* and *at* the last addition
+ * separately to the blocked packages */
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, pushing blocked "
+ "packet with %zd bytes", stream3_id, outlen));
+ vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf, outlen, gsolen);
+ Curl_expire(data, 1, EXPIRE_QUIC);
+ return CURLE_OK;
+ }
+ goto out;
+ }
+ }
+
+out:
+ timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn);
+ if(timeout_ns % 1000000)
+ timeout_ns += 1000000;
+ /* expire resolution is milliseconds */
+ Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC);
+ if(pktcnt)
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] egress, sent %zd packets "
+ "with %zd bytes", stream3_id, pktcnt, total_len));
+ return result;
+}
+
+static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nread = -1;
+
+ if(stream->reset) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
+ *err = stream->h3_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
+ stream->stream3_id, *err));
+ goto out;
+ }
+
+ if(!stream->h3_got_header) {
+ failf(data,
+ "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
+ " all response header fields, treated as error",
+ stream->stream3_id);
+ /* *err = CURLE_PARTIAL_FILE; */
+ *err = CURLE_RECV_ERROR;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
+ " -> %d", stream->stream3_id, *err));
+ goto out;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
+ " -> %d", stream->stream3_id, *err));
+ }
+ *err = CURLE_OK;
+ nread = 0;
+
+out:
+ return nread;
+}
+
+static CURLcode cf_poll_events(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ quiche_h3_event *ev;
+
+ /* Take in the events and distribute them to the transfers. */
+ while(1) {
+ int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev);
+ if(stream3_id < 0) {
+ /* nothing more to do */
+ break;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, queue event %s "
+ "for [h3sid=%"PRId64"]",
+ stream? stream->stream3_id : -1, cf_ev_name(ev),
+ stream3_id));
+ if(h3_add_event(cf, data, stream3_id, ev) != CURLE_OK) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ return CURLE_OK;
+}
+
+static ssize_t cf_recv_transfer_data(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t len,
+ CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = -1;
+ size_t offset = 0;
+
+ if(stream->h3_recving_data) {
+ /* try receiving body first */
+ recvd = cf_recv_body(cf, data, buf, len, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ if(recvd > 0) {
+ offset = recvd;
+ }
+ }
+
+ if(offset < len && stream->pending) {
+ /* process any pending events for `data` first. if there are,
+ * return so the transfer can handle those. We do not want to
+ * progress ingress while events are pending here. */
+ recvd = h3_process_pending(cf, data, buf + offset, len - offset, err);
+ if(recvd < 0) {
+ if(*err != CURLE_AGAIN)
+ return -1;
+ recvd = 0;
+ }
+ if(recvd > 0) {
+ offset += recvd;
+ }
+ }
+
+ if(offset) {
+ *err = CURLE_OK;
+ return offset;
+ }
+ *err = CURLE_AGAIN;
+ return 0;
+}
+
+static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct HTTP *stream = data->req.p.http;
+ ssize_t recvd = -1;
+
+ *err = CURLE_AGAIN;
+
+ recvd = cf_recv_transfer_data(cf, data, buf, len, err);
+ if(recvd)
+ goto out;
+ if(stream->closed) {
+ recvd = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+
+ /* we did get nothing from the quiche buffers or pending events.
+ * Take in more data from the connection, any error is fatal */
+ if(cf_process_ingress(cf, data)) {
+ DEBUGF(LOG_CF(data, cf, "h3_stream_recv returns on ingress"));
+ *err = CURLE_RECV_ERROR;
+ recvd = -1;
+ goto out;
+ }
+ /* poll quiche and distribute the events to the transfers */
+ *err = cf_poll_events(cf, data);
+ if(*err) {
+ recvd = -1;
+ goto out;
+ }
+
+ /* try to receive again for this transfer */
+ recvd = cf_recv_transfer_data(cf, data, buf, len, err);
+ if(recvd)
+ goto out;
+ if(stream->closed) {
+ recvd = recv_closed_stream(cf, data, err);
+ goto out;
+ }
+ recvd = -1;
+ *err = CURLE_AGAIN;
+ data->state.drain = 0;
+
+out:
+ if(cf_flush_egress(cf, data)) {
+ DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed"));
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv -> %zd, err=%d",
+ stream->stream3_id, recvd, *err));
+ if(recvd > 0)
+ notify_drain(cf, data);
+ return recvd;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode cf_http_request(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *mem,
+ size_t len)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ size_t nheader;
+ int64_t stream3_id;
+ quiche_h3_header *nva = NULL;
+ CURLcode result = CURLE_OK;
+ struct h2h3req *hreq = NULL;
+
+ stream->h3req = TRUE; /* send off! */
+ stream->closed = FALSE;
+ stream->reset = FALSE;
+
+ result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
+ if(result)
+ goto fail;
+ nheader = hreq->entries;
+
+ nva = malloc(sizeof(quiche_h3_header) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ else {
+ unsigned int i;
+ for(i = 0; i < nheader; i++) {
+ nva[i].name = (unsigned char *)hreq->header[i].name;
+ nva[i].name_len = hreq->header[i].namelen;
+ nva[i].value = (unsigned char *)hreq->header[i].value;
+ nva[i].value_len = hreq->header[i].valuelen;
+ }
+ }
+
+ switch(data->state.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ stream->upload_done = !stream->upload_left;
+ stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
+ stream->upload_done);
+ break;
+ default:
+ stream->upload_left = 0;
+ stream->upload_done = TRUE;
+ stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader,
+ TRUE);
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ if(stream3_id < 0) {
+ if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
+ DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) rejected "
+ "with H3_ERR_STREAM_BLOCKED",
+ data->state.url, (long)stream->upload_left));
+ result = CURLE_AGAIN;
+ goto fail;
+ }
+ else {
+ DEBUGF(LOG_CF(data, cf, "send_request(%s, body_len=%ld) -> %" PRId64,
+ data->state.url, (long)stream->upload_left, stream3_id));
+ }
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
+ stream3_id, (void *)data);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
+ stream3_id, data->state.url));
+
+ Curl_pseudo_free(hreq);
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ Curl_pseudo_free(hreq);
+ return result;
+}
+
+static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+ ssize_t nwritten;
+
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) start",
+ stream->h3req? stream->stream3_id : -1, len));
+ *err = cf_process_ingress(cf, data);
+ if(*err)
+ return -1;
+
+ if(!stream->h3req) {
+ CURLcode result = cf_http_request(cf, data, buf, len);
+ if(result) {
+ *err = result;
+ return -1;
+ }
+ nwritten = len;
+ }
+ else {
+ nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ (uint8_t *)buf, len, FALSE);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu) -> %zd",
+ stream->stream3_id, len, nwritten));
+ if(nwritten == QUICHE_H3_ERR_DONE) {
+ /* no error, nothing to do (flow control?) */
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ }
+ else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
+ DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> exceeds size", len));
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ }
+ else if(nwritten < 0) {
+ DEBUGF(LOG_CF(data, cf, "send_body(len=%zu) -> SEND_ERROR", len));
+ *err = CURLE_SEND_ERROR;
+ nwritten = -1;
+ }
+ else {
+ *err = CURLE_OK;
+ }
+ }
+
+ if(cf_flush_egress(cf, data)) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ return nwritten;
+}
+
+static bool stream_is_writeable(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct HTTP *stream = data->req.p.http;
+
+ /* surely, there must be a better way */
+ quiche_stream_iter *qiter = quiche_conn_writable(ctx->qconn);
+ if(qiter) {
+ uint64_t stream_id;
+ while(quiche_stream_iter_next(qiter, &stream_id)) {
+ if(stream_id == (uint64_t)stream->stream3_id)
+ return TRUE;
+ }
+ quiche_stream_iter_free(qiter);
+ }
+ return FALSE;
+}
+
+static int cf_quiche_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ struct SingleRequest *k = &data->req;
+ int rv = GETSOCK_BLANK;
+
+ socks[0] = ctx->q.sockfd;
+
+ /* in an HTTP/3 connection we can basically always get a frame so we should
+ always be ready for one */
+ rv |= GETSOCK_READSOCK(0);
+
+ /* we're still uploading or the HTTP/3 layer wants to send data */
+ if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ && stream_is_writeable(cf, data))
+ rv |= GETSOCK_WRITESOCK(0);
+
+ return rv;
+}
+
+/*
+ * Called from transfer.c:data_pending to know if we should keep looping
+ * to receive more data from the connection.
+ */
+static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
+{
+ struct HTTP *stream = data->req.p.http;
+
+ if(stream->pending) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] has event pending", stream->stream3_id));
+ return TRUE;
+ }
+ if(stream->h3_recving_data) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] is receiving DATA", stream->stream3_id));
+ return TRUE;
+ }
+ if(data->state.drain) {
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf,
+ "[h3sid=%"PRId64"] is draining", stream->stream3_id));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_DONE: {
+ struct HTTP *stream = data->req.p.http;
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is %s",
+ stream->stream3_id, arg1? "cancelled" : "done"));
+ h3_clear_pending(data);
+ break;
+ }
+ case CF_CTRL_DATA_DONE_SEND: {
+ struct HTTP *stream = data->req.p.http;
+ ssize_t sent;
+ stream->upload_done = TRUE;
+ sent = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->stream3_id,
+ NULL, 0, TRUE);
+ DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] send_body FINISHED",
+ stream->stream3_id));
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+ break;
+ }
+ case CF_CTRL_DATA_IDLE:
+ /* anything to do? */
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static CURLcode cf_verify_peer(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ cf->conn->httpversion = 30;
+ cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ if(cf->conn->ssl_config.verifyhost) {
+ X509 *server_cert;
+ server_cert = SSL_get_peer_certificate(ctx->ssl);
+ if(!server_cert) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ goto out;
+ }
+ result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
+ X509_free(server_cert);
+ if(result)
+ goto out;
+ }
+ else
+ DEBUGF(LOG_CF(data, cf, "Skipped certificate verification"));
+
+ ctx->h3config = quiche_h3_config_new();
+ if(!ctx->h3config) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Create a new HTTP/3 connection on the QUIC connection. */
+ ctx->h3c = quiche_h3_conn_new_with_transport(ctx->qconn, ctx->h3config);
+ if(!ctx->h3c) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ if(data->set.ssl.certinfo)
+ /* asked to gather certificate info */
+ (void)Curl_ossl_certchain(data, ctx->ssl);
+
+out:
+ if(result) {
+ if(ctx->h3config) {
+ quiche_h3_config_free(ctx->h3config);
+ ctx->h3config = NULL;
+ }
+ if(ctx->h3c) {
+ quiche_h3_conn_free(ctx->h3c);
+ ctx->h3c = NULL;
+ }
+ }
+ return result;
+}
+
+static CURLcode cf_connect_start(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ int rv;
+ CURLcode result;
+ const struct Curl_sockaddr_ex *sockaddr;
+
+ DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
+
+#ifdef DEBUG_QUICHE
+ /* initialize debug log callback only once */
+ static int debug_log_init = 0;
+ if(!debug_log_init) {
+ quiche_enable_debug_logging(quiche_debug_log, NULL);
+ debug_log_init = 1;
+ }
+#endif
+
+ result = vquic_ctx_init(&ctx->q, MAX_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
+ if(result)
+ return result;
+
+ ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
+ if(!ctx->cfg) {
+ failf(data, "can't create quiche config");
+ return CURLE_FAILED_INIT;
+ }
+ quiche_config_set_max_idle_timeout(ctx->cfg, QUIC_IDLE_TIMEOUT);
+ quiche_config_set_initial_max_data(ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_local(
+ ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_remote(
+ ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_uni(ctx->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_initial_max_streams_uni(ctx->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_application_protos(ctx->cfg,
+ (uint8_t *)
+ QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
+ - 1);
+
+ DEBUGASSERT(!ctx->ssl);
+ DEBUGASSERT(!ctx->sslctx);
+ ctx->sslctx = quic_ssl_ctx(data);
+ if(!ctx->sslctx)
+ return CURLE_QUIC_CONNECT_ERROR;
+ ctx->ssl = SSL_new(ctx->sslctx);
+ if(!ctx->ssl)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ SSL_set_app_data(ctx->ssl, cf);
+ SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+
+ result = Curl_rand(data, ctx->scid, sizeof(ctx->scid));
+ if(result)
+ return result;
+
+ Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
+ &sockaddr, NULL, NULL, NULL, NULL);
+ ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
+ rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
+ &ctx->q.local_addrlen);
+ if(rv == -1)
+ return CURLE_QUIC_CONNECT_ERROR;
+
+ ctx->qconn = quiche_conn_new_with_tls((const uint8_t *)ctx->scid,
+ sizeof(ctx->scid), NULL, 0,
+ (struct sockaddr *)&ctx->q.local_addr,
+ ctx->q.local_addrlen,
+ &sockaddr->sa_addr, sockaddr->addrlen,
+ ctx->cfg, ctx->ssl, false);
+ if(!ctx->qconn) {
+ failf(data, "can't create quiche connection");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* Known to not work on Windows */
+#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
+ {
+ int qfd;
+ (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd);
+ if(qfd != -1)
+ quiche_conn_set_qlog_fd(ctx->qconn, qfd,
+ "qlog title", "curl qlog");
+ }
+#endif
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ return result;
+
+ {
+ unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
+ unsigned alpn_len, offset = 0;
+
+ /* Replace each ALPN length prefix by a comma. */
+ while(offset < sizeof(alpn_protocols) - 1) {
+ alpn_len = alpn_protocols[offset];
+ alpn_protocols[offset] = ',';
+ offset += 1 + alpn_len;
+ }
+
+ DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
+ alpn_protocols + 1));
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+ struct curltime now;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ /* Connect the UDP filter first */
+ if(!cf->next->connected) {
+ result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ if(result || !*done)
+ return result;
+ }
+
+ *done = FALSE;
+ now = Curl_now();
+
+ if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
+ /* Not time yet to attempt the next connect */
+ DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+ goto out;
+ }
+
+ if(!ctx->qconn) {
+ result = cf_connect_start(cf, data);
+ if(result)
+ goto out;
+ ctx->started_at = now;
+ result = cf_flush_egress(cf, data);
+ /* we do not expect to be able to recv anything yet */
+ goto out;
+ }
+
+ result = cf_process_ingress(cf, data);
+ if(result)
+ goto out;
+
+ result = cf_flush_egress(cf, data);
+ if(result)
+ goto out;
+
+ if(quiche_conn_is_established(ctx->qconn)) {
+ DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
+ (int)Curl_timediff(now, ctx->started_at)));
+ ctx->handshake_at = now;
+ result = cf_verify_peer(cf, data);
+ if(!result) {
+ DEBUGF(LOG_CF(data, cf, "peer verified"));
+ cf->connected = TRUE;
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ connkeep(cf->conn, "HTTP/3 default");
+ }
+ }
+ else if(quiche_conn_is_draining(ctx->qconn)) {
+ /* When a QUIC server instance is shutting down, it may send us a
+ * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
+ * state.
+ * This may be a stopping of the service or it may be that the server
+ * is reloading and a new instance will start serving soon.
+ * In any case, we tear down our socket and start over with a new one.
+ * We re-open the underlying UDP cf right now, but do not start
+ * connecting until called again.
+ */
+ int reconn_delay_ms = 200;
+
+ DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
+ reconn_delay_ms));
+ Curl_conn_cf_close(cf->next, data);
+ cf_quiche_ctx_clear(ctx);
+ result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ if(!result && *done) {
+ *done = FALSE;
+ ctx->reconnect_at = Curl_now();
+ ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
+ Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
+ result = CURLE_OK;
+ }
+ }
+
+out:
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(result && result != CURLE_AGAIN) {
+ const char *r_ip;
+ int r_port;
+
+ Curl_cf_socket_peek(cf->next, data, NULL, NULL,
+ &r_ip, &r_port, NULL, NULL);
+ infof(data, "connect to %s port %u failed: %s",
+ r_ip, r_port, curl_easy_strerror(result));
+ }
+#endif
+ return result;
+}
+
+static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ (void)data;
+ if(ctx) {
+ if(ctx->qconn) {
+ (void)quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0);
+ /* flushing the egress is not a failsafe way to deliver all the
+ outstanding packets, but we also don't want to get stuck here... */
+ (void)cf_flush_egress(cf, data);
+ }
+ cf_quiche_ctx_clear(ctx);
+ }
+}
+
+static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ (void)data;
+ cf_quiche_ctx_clear(ctx);
+ free(ctx);
+ cf->ctx = NULL;
+}
+
+static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct cf_quiche_ctx *ctx = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_MAX_CONCURRENT: {
+ uint64_t max_streams = CONN_INUSE(cf->conn);
+ if(!ctx->goaway) {
+ max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
+ }
+ *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
+ DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1));
+ return CURLE_OK;
+ }
+ case CF_QUERY_CONNECT_REPLY_MS:
+ if(ctx->got_first_byte) {
+ timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
+ }
+ else
+ *pres1 = -1;
+ return CURLE_OK;
+ case CF_QUERY_TIMER_CONNECT: {
+ struct curltime *when = pres2;
+ if(ctx->got_first_byte)
+ *when = ctx->first_byte_at;
+ return CURLE_OK;
+ }
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ if(cf->connected)
+ *when = ctx->handshake_at;
+ return CURLE_OK;
+ }
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
+}
+
+static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *input_pending)
+{
+ bool alive = TRUE;
+
+ *input_pending = FALSE;
+ if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+ return FALSE;
+
+ if(*input_pending) {
+ /* This happens before we've sent off a request and the connection is
+ not in use by any other transfer, there shouldn't be any data here,
+ only "protocol frames" */
+ *input_pending = FALSE;
+ Curl_attach_connection(data, cf->conn);
+ if(cf_process_ingress(cf, data))
+ alive = FALSE;
+ else {
+ alive = TRUE;
+ }
+ Curl_detach_connection(data);
+ }
+
+ return alive;
+}
+
+struct Curl_cftype Curl_cft_http3 = {
+ "HTTP/3",
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ 0,
+ cf_quiche_destroy,
+ cf_quiche_connect,
+ cf_quiche_close,
+ Curl_cf_def_get_host,
+ cf_quiche_get_select_socks,
+ cf_quiche_data_pending,
+ cf_quiche_send,
+ cf_quiche_recv,
+ cf_quiche_data_event,
+ cf_quiche_conn_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ cf_quiche_query,
+};
+
+CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai)
+{
+ struct cf_quiche_ctx *ctx = NULL;
+ struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
+ CURLcode result;
+
+ (void)data;
+ (void)conn;
+ ctx = calloc(sizeof(*ctx), 1);
+ if(!ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
+ if(result)
+ goto out;
+
+ result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
+ if(result)
+ goto out;
+
+ udp_cf->conn = cf->conn;
+ udp_cf->sockindex = cf->sockindex;
+ cf->next = udp_cf;
+
+out:
+ *pcf = (!result)? cf : NULL;
+ if(result) {
+ if(udp_cf)
+ Curl_conn_cf_discard(udp_cf, data);
+ Curl_safefree(cf);
+ Curl_safefree(ctx);
+ }
+
+ return result;
+}
+
+bool Curl_conn_is_quiche(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
+
+ (void)data;
+ for(; cf; cf = cf->next) {
+ if(cf->cft == &Curl_cft_http3)
+ return TRUE;
+ if(cf->cft->flags & CF_TYPE_IP_CONNECT)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/Utilities/cmcurl/lib/vquic/quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h
index 2da65f5f48..bce781c1bc 100644
--- a/Utilities/cmcurl/lib/vquic/quiche.h
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_QUICHE_H
-#define HEADER_CURL_VQUIC_QUICHE_H
+#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H
+#define HEADER_CURL_VQUIC_CURL_QUICHE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,28 +31,20 @@
#include <quiche.h>
#include <openssl/ssl.h>
-struct quic_handshake {
- char *buf; /* pointer to the buffer */
- size_t alloclen; /* size of allocation */
- size_t len; /* size of content in buffer */
- size_t nread; /* how many bytes have been read */
-};
-
-struct quicsocket {
- quiche_config *cfg;
- quiche_conn *conn;
- quiche_h3_conn *h3c;
- quiche_h3_config *h3config;
- uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
- curl_socket_t sockfd;
- uint32_t version;
- SSL_CTX *sslctx;
- SSL *ssl;
- bool h3_recving; /* TRUE when in h3-body-reading state */
- struct sockaddr_storage local_addr;
- socklen_t local_addrlen;
-};
+struct Curl_cfilter;
+struct Curl_easy;
+
+void Curl_quiche_ver(char *p, size_t len);
+
+CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai);
+
+bool Curl_conn_is_quiche(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
#endif
-#endif /* HEADER_CURL_VQUIC_QUICHE_H */
+#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */
diff --git a/Utilities/cmcurl/lib/vquic/msh3.c b/Utilities/cmcurl/lib/vquic/msh3.c
deleted file mode 100644
index c3e58e726a..0000000000
--- a/Utilities/cmcurl/lib/vquic/msh3.c
+++ /dev/null
@@ -1,527 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_MSH3
-
-#include "urldata.h"
-#include "timeval.h"
-#include "multiif.h"
-#include "sendf.h"
-#include "connect.h"
-#include "h2h3.h"
-#include "msh3.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* #define DEBUG_HTTP3 1 */
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define MSH3_REQ_INIT_BUF_LEN 8192
-
-static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
-static int msh3_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks);
-static CURLcode msh3_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection);
-static unsigned int msh3_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform);
-static Curl_recv msh3_stream_recv;
-static Curl_send msh3_stream_send;
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *IfContext,
- const MSH3_HEADER *Header);
-static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t Length,
- const uint8_t *Data);
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool Aborted, uint64_t AbortError);
-static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
-
-static const struct Curl_handler msh3_curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- msh3_do_it, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- msh3_getsock, /* proto_getsock */
- msh3_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- msh3_getsock, /* perform_getsock */
- msh3_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- msh3_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-static const MSH3_REQUEST_IF msh3_request_if = {
- msh3_header_received,
- msh3_data_received,
- msh3_complete,
- msh3_shutdown
-};
-
-void Curl_quic_ver(char *p, size_t len)
-{
- uint32_t v[4];
- MsH3Version(v);
- (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
-}
-
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- struct quicsocket *qs = &conn->hequic[sockindex];
- bool insecure = !conn->ssl_config.verifypeer;
- memset(qs, 0, sizeof(*qs));
-
- (void)sockfd;
- (void)addr; /* TODO - Pass address along */
- (void)addrlen;
-
- H3BUGF(infof(data, "creating new api/connection"));
-
- qs->api = MsH3ApiOpen();
- if(!qs->api) {
- failf(data, "can't create msh3 api");
- return CURLE_FAILED_INIT;
- }
-
- qs->conn = MsH3ConnectionOpen(qs->api,
- conn->host.name,
- (uint16_t)conn->remote_port,
- insecure);
- if(!qs->conn) {
- failf(data, "can't create msh3 connection");
- if(qs->api) {
- MsH3ApiClose(qs->api);
- }
- return CURLE_FAILED_INIT;
- }
-
- return CURLE_OK;
-}
-
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected)
-{
- struct quicsocket *qs = &conn->hequic[sockindex];
- MSH3_CONNECTION_STATE state;
-
- state = MsH3ConnectionGetState(qs->conn, false);
- if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
- failf(data, "failed to connect, state=%u", (uint32_t)state);
- return CURLE_COULDNT_CONNECT;
- }
-
- if(state == MSH3_CONN_CONNECTED) {
- H3BUGF(infof(data, "connection connected"));
- *connected = true;
- conn->quic = qs;
- conn->recv[sockindex] = msh3_stream_recv;
- conn->send[sockindex] = msh3_stream_send;
- conn->handler = &msh3_curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
- /* TODO - Clean up other happy-eyeballs connection(s)? */
- }
-
- return CURLE_OK;
-}
-
-static int msh3_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks)
-{
- struct HTTP *stream = data->req.p.http;
- int bitmap = GETSOCK_BLANK;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- if(stream->recv_error) {
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
- data->state.drain++;
- }
- else if(stream->recv_header_len || stream->recv_data_len) {
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
- data->state.drain++;
- }
-
- H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
-
- return bitmap;
-}
-
-static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
-{
- struct HTTP *stream = data->req.p.http;
- H3BUGF(infof(data, "msh3_do_it"));
- stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
- if(!stream->recv_buf) {
- return CURLE_OUT_OF_MEMORY;
- }
- stream->req = ZERO_NULL;
- msh3_lock_initialize(&stream->recv_lock);
- stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
- stream->recv_header_len = 0;
- stream->recv_header_complete = false;
- stream->recv_data_len = 0;
- stream->recv_data_complete = false;
- stream->recv_error = CURLE_OK;
- return Curl_http(data, done);
-}
-
-static unsigned int msh3_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- H3BUGF(infof(data, "msh3_conncheck"));
- return CONNRESULT_NONE;
-}
-
-static void disconnect(struct quicsocket *qs)
-{
- if(qs->conn) {
- MsH3ConnectionClose(qs->conn);
- qs->conn = ZERO_NULL;
- }
- if(qs->api) {
- MsH3ApiClose(qs->api);
- qs->api = ZERO_NULL;
- }
-}
-
-static CURLcode msh3_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead_connection)
-{
- (void)data;
- (void)dead_connection;
- H3BUGF(infof(data, "disconnecting (msh3)"));
- disconnect(conn->quic);
- return CURLE_OK;
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
- int tempindex)
-{
- (void)data;
- if(conn->transport == TRNSPRT_QUIC) {
- H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
- disconnect(&conn->hequic[tempindex]);
- }
-}
-
-/* Requires stream->recv_lock to be held */
-static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
-{
- uint8_t *new_recv_buf;
- const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
- if(cur_recv_len + len > stream->recv_buf_alloc) {
- size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
- do {
- new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
- } while(cur_recv_len + len > new_recv_buf_alloc_len);
- new_recv_buf = malloc(new_recv_buf_alloc_len);
- if(!new_recv_buf) {
- return false;
- }
- if(cur_recv_len) {
- memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
- }
- stream->recv_buf_alloc = new_recv_buf_alloc_len;
- free(stream->recv_buf);
- stream->recv_buf = new_recv_buf;
- }
- return true;
-}
-
-static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
- void *IfContext,
- const MSH3_HEADER *Header)
-{
- struct HTTP *stream = IfContext;
- size_t total_len;
- (void)Request;
-
- if(stream->recv_header_complete) {
- H3BUGF(printf("* ignoring header after data\n"));
- return;
- }
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if((Header->NameLength == 7) &&
- !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
- total_len = 9 + Header->ValueLength;
- if(!msh3request_ensure_room(stream, total_len)) {
- /* TODO - handle error */
- goto release_lock;
- }
- msnprintf((char *)stream->recv_buf + stream->recv_header_len,
- stream->recv_buf_alloc - stream->recv_header_len,
- "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
- }
- else {
- total_len = Header->NameLength + 4 + Header->ValueLength;
- if(!msh3request_ensure_room(stream, total_len)) {
- /* TODO - handle error */
- goto release_lock;
- }
- msnprintf((char *)stream->recv_buf + stream->recv_header_len,
- stream->recv_buf_alloc - stream->recv_header_len,
- "%.*s: %.*s\n",
- (int)Header->NameLength, Header->Name,
- (int)Header->ValueLength, Header->Value);
- }
-
- stream->recv_header_len += total_len - 1; /* don't include null-terminator */
-
-release_lock:
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
- void *IfContext, uint32_t Length,
- const uint8_t *Data)
-{
- struct HTTP *stream = IfContext;
- size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
- (void)Request;
- H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
- Length, cur_recv_len, stream->recv_buf_alloc));
- msh3_lock_acquire(&stream->recv_lock);
- if(!stream->recv_header_complete) {
- H3BUGF(printf("* Headers complete!\n"));
- if(!msh3request_ensure_room(stream, 2)) {
- /* TODO - handle error */
- goto release_lock;
- }
- stream->recv_buf[stream->recv_header_len++] = '\r';
- stream->recv_buf[stream->recv_header_len++] = '\n';
- stream->recv_header_complete = true;
- cur_recv_len += 2;
- }
- if(!msh3request_ensure_room(stream, Length)) {
- /* TODO - handle error */
- goto release_lock;
- }
- memcpy(stream->recv_buf + cur_recv_len, Data, Length);
- stream->recv_data_len += (size_t)Length;
-release_lock:
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
- bool Aborted, uint64_t AbortError)
-{
- struct HTTP *stream = IfContext;
- (void)Request;
- (void)AbortError;
- H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
- msh3_lock_acquire(&stream->recv_lock);
- if(Aborted) {
- stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
- }
- stream->recv_header_complete = true;
- stream->recv_data_complete = true;
- msh3_lock_release(&stream->recv_lock);
-}
-
-static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
-{
- struct HTTP *stream = IfContext;
- (void)Request;
- (void)stream;
-}
-
-static ssize_t msh3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- struct h2h3req *hreq;
-
- (void)sockindex;
- /* Sizes must match for cast below to work" */
- DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
-
- H3BUGF(infof(data, "msh3_stream_send %zu", len));
-
- if(!stream->req) {
- *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
- if(*curlcode) {
- failf(data, "Curl_pseudo_headers failed");
- return -1;
- }
- H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
- stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
- (MSH3_HEADER*)hreq->header, hreq->entries);
- Curl_pseudo_free(hreq);
- if(!stream->req) {
- failf(data, "request open failed");
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- *curlcode = CURLE_OK;
- return len;
- }
- H3BUGF(infof(data, "send %zd body bytes on request %p", len,
- (void *)stream->req));
- *curlcode = CURLE_SEND_ERROR;
- return -1;
-}
-
-static ssize_t msh3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- struct HTTP *stream = data->req.p.http;
- size_t outsize = 0;
- (void)sockindex;
- H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
-
- if(stream->recv_error) {
- failf(data, "request aborted");
- *curlcode = stream->recv_error;
- return -1;
- }
-
- msh3_lock_acquire(&stream->recv_lock);
-
- if(stream->recv_header_len) {
- outsize = buffersize;
- if(stream->recv_header_len < outsize) {
- outsize = stream->recv_header_len;
- }
- memcpy(buf, stream->recv_buf, outsize);
- if(outsize < stream->recv_header_len + stream->recv_data_len) {
- memmove(stream->recv_buf, stream->recv_buf + outsize,
- stream->recv_header_len + stream->recv_data_len - outsize);
- }
- stream->recv_header_len -= outsize;
- H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
- }
- else if(stream->recv_data_len) {
- outsize = buffersize;
- if(stream->recv_data_len < outsize) {
- outsize = stream->recv_data_len;
- }
- memcpy(buf, stream->recv_buf, outsize);
- if(outsize < stream->recv_data_len) {
- memmove(stream->recv_buf, stream->recv_buf + outsize,
- stream->recv_data_len - outsize);
- }
- stream->recv_data_len -= outsize;
- H3BUGF(infof(data, "returned %zu bytes of data", outsize));
- }
- else if(stream->recv_data_complete) {
- H3BUGF(infof(data, "receive complete"));
- }
-
- msh3_lock_release(&stream->recv_lock);
-
- return (ssize_t)outsize;
-}
-
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- H3BUGF(infof(data, "Curl_quic_done_sending"));
- if(conn->handler == &msh3_curl_handler_http3) {
- struct HTTP *stream = data->req.p.http;
- stream->upload_done = TRUE;
- }
-
- return CURLE_OK;
-}
-
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- struct HTTP *stream = data->req.p.http;
- (void)premature;
- H3BUGF(infof(data, "Curl_quic_done"));
- if(stream) {
- if(stream->recv_buf) {
- Curl_safefree(stream->recv_buf);
- msh3_lock_uninitialize(&stream->recv_lock);
- }
- if(stream->req) {
- MsH3RequestClose(stream->req);
- stream->req = ZERO_NULL;
- }
- }
-}
-
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- struct HTTP *stream = data->req.p.http;
- H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
- return stream->recv_header_len || stream->recv_data_len;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- (void)data;
- H3BUGF(infof(data, "Curl_quic_idle"));
- return CURLE_OK;
-}
-
-#endif /* USE_MSH3 */
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c
deleted file mode 100644
index f16b469946..0000000000
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.c
+++ /dev/null
@@ -1,2266 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_NGTCP2
-#include <ngtcp2/ngtcp2.h>
-#include <nghttp3/nghttp3.h>
-
-#ifdef USE_OPENSSL
-#include <openssl/err.h>
-#ifdef OPENSSL_IS_BORINGSSL
-#include <ngtcp2/ngtcp2_crypto_boringssl.h>
-#else
-#include <ngtcp2/ngtcp2_crypto_openssl.h>
-#endif
-#include "vtls/openssl.h"
-#elif defined(USE_GNUTLS)
-#include <ngtcp2/ngtcp2_crypto_gnutls.h>
-#include "vtls/gtls.h"
-#elif defined(USE_WOLFSSL)
-#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "ngtcp2.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "vquic.h"
-#include "h2h3.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* #define DEBUG_NGTCP2 */
-#ifdef CURLDEBUG
-#define DEBUG_HTTP3
-#endif
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define H3_ALPN_H3_29 "\x5h3-29"
-#define H3_ALPN_H3 "\x2h3"
-
-/*
- * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
- * It is used as a circular buffer. Add new bytes at the end until it reaches
- * the far end, then start over at index 0 again.
- */
-
-#define H3_SEND_SIZE (256*1024)
-struct h3out {
- uint8_t buf[H3_SEND_SIZE];
- size_t used; /* number of bytes used in the buffer */
- size_t windex; /* index in the buffer where to start writing the next
- data block */
-};
-
-#define QUIC_MAX_STREAMS (256*1024)
-#define QUIC_MAX_DATA (1*1024*1024)
-#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
-
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-/* ngtcp2 default congestion controller does not perform pacing. Limit
- the maximum packet burst to MAX_PKT_BURST packets. */
-#define MAX_PKT_BURST 10
-
-static CURLcode ng_process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs);
-static CURLcode ng_flush_egress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs);
-static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
- uint64_t datalen, void *user_data,
- void *stream_user_data);
-
-static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
-{
- struct quicsocket *qs = conn_ref->user_data;
- return qs->qconn;
-}
-
-static ngtcp2_tstamp timestamp(void)
-{
- struct curltime ct = Curl_now();
- return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
-}
-
-#ifdef DEBUG_NGTCP2
-static void quic_printf(void *user_data, const char *fmt, ...)
-{
- va_list ap;
- (void)user_data; /* TODO, use this to do infof() instead long-term */
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
-}
-#endif
-
-static void qlog_callback(void *user_data, uint32_t flags,
- const void *data, size_t datalen)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- (void)flags;
- if(qs->qlogfd != -1) {
- ssize_t rc = write(qs->qlogfd, data, datalen);
- if(rc == -1) {
- /* on write error, stop further write attempts */
- close(qs->qlogfd);
- qs->qlogfd = -1;
- }
- }
-
-}
-
-static void quic_settings(struct quicsocket *qs,
- uint64_t stream_buffer_size)
-{
- ngtcp2_settings *s = &qs->settings;
- ngtcp2_transport_params *t = &qs->transport_params;
- ngtcp2_settings_default(s);
- ngtcp2_transport_params_default(t);
-#ifdef DEBUG_NGTCP2
- s->log_printf = quic_printf;
-#else
- s->log_printf = NULL;
-#endif
- s->initial_ts = timestamp();
- t->initial_max_stream_data_bidi_local = stream_buffer_size;
- t->initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
- t->initial_max_stream_data_uni = QUIC_MAX_STREAMS;
- t->initial_max_data = QUIC_MAX_DATA;
- t->initial_max_streams_bidi = 1;
- t->initial_max_streams_uni = 3;
- t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
- if(qs->qlogfd != -1) {
- s->qlog.write = qlog_callback;
- }
-}
-
-#ifdef USE_OPENSSL
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
- const gnutls_datum_t *secret)
-{
- gnutls_datum_t crandom;
- gnutls_datum_t srandom;
-
- gnutls_session_get_random(session, &crandom, &srandom);
- if(crandom.size != 32) {
- return -1;
- }
-
- Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
- return 0;
-}
-#elif defined(USE_WOLFSSL)
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#endif
-#endif
-
-static int init_ngh3_conn(struct quicsocket *qs);
-
-#ifdef USE_OPENSSL
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
-#ifdef OPENSSL_IS_BORINGSSL
- if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
- return NULL;
- }
-#else
- if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
- return NULL;
- }
-#endif
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
-#ifdef OPENSSL_IS_BORINGSSL
- if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_curves_list failed");
- return NULL;
- }
-#else
- if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
- return NULL;
- }
-
- if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_groups_list failed");
- return NULL;
- }
-#endif
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
- }
-
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
-
- if(ssl_cafile || ssl_capath) {
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell OpenSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- return ssl_ctx;
-}
-
-static CURLcode quic_set_client_cert(struct Curl_easy *data,
- struct quicsocket *qs)
-{
- SSL_CTX *ssl_ctx = qs->sslctx;
- const struct ssl_config_data *ssl_config;
-
- ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
- DEBUGASSERT(ssl_config);
-
- if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
- || ssl_config->cert_type) {
- return Curl_ossl_set_client_cert(
- data, ssl_ctx, ssl_config->primary.clientcert,
- ssl_config->primary.cert_blob, ssl_config->cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd);
- }
-
- return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
-
- (void)data;
- (void)conn;
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- SSL_set_app_data(qs->ssl, &qs->conn_ref);
- SSL_set_connect_state(qs->ssl);
- SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- SSL_set_tlsext_host_name(qs->ssl, hostname);
- return CURLE_OK;
-}
-#elif defined(USE_GNUTLS)
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- CURLcode result;
- gnutls_datum_t alpn[2];
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
- long * const pverifyresult = &data->set.ssl.certverifyresult;
- int rc;
-
- DEBUGASSERT(qs->gtls == NULL);
- qs->gtls = calloc(1, sizeof(*(qs->gtls)));
- if(!qs->gtls)
- return CURLE_OUT_OF_MEMORY;
-
- result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl,
- hostname, qs->gtls, pverifyresult);
- if(result)
- return result;
-
- gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref);
-
- if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) {
- H3BUGF(fprintf(stderr,
- "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL);
- if(rc < 0) {
- H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
- gnutls_strerror(rc)));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(qs->gtls->session, keylog_callback);
- }
-
- /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
- alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
- alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
- alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
- alpn[1].size = sizeof(H3_ALPN_H3) - 2;
-
- gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY);
-
- return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
-
-static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
-
- if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
- failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
- return NULL;
- }
-
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
-
- if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
- return NULL;
- }
-
- if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
- failf(data, "SSL_CTX_set1_groups_list failed");
- return NULL;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-#else
- failf(data, "wolfSSL was built without keylog callback");
- return NULL;
-#endif
- }
-
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
-
- if(ssl_cafile || ssl_capath) {
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell wolfSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use wolfssl's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- else {
- wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
- }
-
- return ssl_ctx;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode quic_init_ssl(struct quicsocket *qs,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- const uint8_t *alpn = NULL;
- size_t alpnlen = 0;
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = qs->conn->host.name;
-
- (void)data;
- (void)conn;
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- wolfSSL_set_app_data(qs->ssl, &qs->conn_ref);
- wolfSSL_set_connect_state(qs->ssl);
- wolfSSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
-
- alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
- alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
- if(alpn)
- wolfSSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
-
- /* set SNI */
- wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME,
- hostname, (unsigned short)strlen(hostname));
-
- return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
-static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
-{
- (void)user_data;
- (void)tconn;
- return 0;
-}
-
-static void extend_stream_window(ngtcp2_conn *tconn,
- struct HTTP *stream)
-{
- size_t thismuch = stream->unacked_window;
- ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch);
- ngtcp2_conn_extend_max_offset(tconn, thismuch);
- stream->unacked_window = 0;
-}
-
-
-static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
- int64_t stream_id, uint64_t offset,
- const uint8_t *buf, size_t buflen,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- nghttp3_ssize nconsumed;
- int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
- (void)offset;
- (void)stream_user_data;
-
- nconsumed =
- nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
- if(nconsumed < 0) {
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error, nghttp3_err_infer_quic_app_error_code((int)nconsumed),
- NULL, 0);
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- /* number of bytes inside buflen which consists of framing overhead
- * including QPACK HEADERS. In other words, it does not consume payload of
- * DATA frame. */
- ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
- ngtcp2_conn_extend_max_offset(tconn, nconsumed);
-
- return 0;
-}
-
-static int
-cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t offset, uint64_t datalen, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)stream_id;
- (void)tconn;
- (void)offset;
- (void)datalen;
- (void)stream_user_data;
-
- rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
- int64_t stream_id, uint64_t app_error_code,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)stream_user_data;
- /* stream is closed... */
-
- if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
- app_error_code = NGHTTP3_H3_NO_ERROR;
- }
-
- rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
- app_error_code);
- if(rv) {
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t final_size, uint64_t app_error_code,
- void *user_data, void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)final_size;
- (void)app_error_code;
- (void)stream_user_data;
-
- rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)app_error_code;
- (void)stream_user_data;
-
- rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
- uint64_t max_streams,
- void *user_data)
-{
- (void)tconn;
- (void)max_streams;
- (void)user_data;
-
- return 0;
-}
-
-static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
- uint64_t max_data, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- int rv;
- (void)tconn;
- (void)max_data;
- (void)stream_user_data;
-
- rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static void cb_rand(uint8_t *dest, size_t destlen,
- const ngtcp2_rand_ctx *rand_ctx)
-{
- CURLcode result;
- (void)rand_ctx;
-
- result = Curl_rand(NULL, dest, destlen);
- if(result) {
- /* cb_rand is only used for non-cryptographic context. If Curl_rand
- failed, just fill 0 and call it *random*. */
- memset(dest, 0, destlen);
- }
-}
-
-static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
- uint8_t *token, size_t cidlen,
- void *user_data)
-{
- CURLcode result;
- (void)tconn;
- (void)user_data;
-
- result = Curl_rand(NULL, cid->data, cidlen);
- if(result)
- return NGTCP2_ERR_CALLBACK_FAILURE;
- cid->datalen = cidlen;
-
- result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
- if(result)
- return NGTCP2_ERR_CALLBACK_FAILURE;
-
- return 0;
-}
-
-static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
- void *user_data)
-{
- struct quicsocket *qs = (struct quicsocket *)user_data;
- (void)tconn;
-
- if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
- return 0;
- }
-
- if(init_ngh3_conn(qs) != CURLE_OK) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static ngtcp2_callbacks ng_callbacks = {
- ngtcp2_crypto_client_initial_cb,
- NULL, /* recv_client_initial */
- ngtcp2_crypto_recv_crypto_data_cb,
- cb_handshake_completed,
- NULL, /* recv_version_negotiation */
- ngtcp2_crypto_encrypt_cb,
- ngtcp2_crypto_decrypt_cb,
- ngtcp2_crypto_hp_mask_cb,
- cb_recv_stream_data,
- cb_acked_stream_data_offset,
- NULL, /* stream_open */
- cb_stream_close,
- NULL, /* recv_stateless_reset */
- ngtcp2_crypto_recv_retry_cb,
- cb_extend_max_local_streams_bidi,
- NULL, /* extend_max_local_streams_uni */
- cb_rand,
- cb_get_new_connection_id,
- NULL, /* remove_connection_id */
- ngtcp2_crypto_update_key_cb, /* update_key */
- NULL, /* path_validation */
- NULL, /* select_preferred_addr */
- cb_stream_reset,
- NULL, /* extend_max_remote_streams_bidi */
- NULL, /* extend_max_remote_streams_uni */
- cb_extend_max_stream_data,
- NULL, /* dcid_status */
- NULL, /* handshake_confirmed */
- NULL, /* recv_new_token */
- ngtcp2_crypto_delete_crypto_aead_ctx_cb,
- ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
- NULL, /* recv_datagram */
- NULL, /* ack_datagram */
- NULL, /* lost_datagram */
- ngtcp2_crypto_get_path_challenge_data_cb,
- cb_stream_stop_sending,
- NULL, /* version_negotiation */
- cb_recv_rx_key,
- NULL, /* recv_tx_key */
- NULL, /* early_data_rejected */
-};
-
-/*
- * Might be called twice for happy eyeballs.
- */
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr,
- socklen_t addrlen)
-{
- int rc;
- int rv;
- CURLcode result;
- ngtcp2_path path; /* TODO: this must be initialized properly */
- struct quicsocket *qs = &conn->hequic[sockindex];
- char ipbuf[40];
- int port;
- int qfd;
-
- if(qs->conn)
- Curl_quic_disconnect(data, conn, sockindex);
- qs->conn = conn;
-
- /* extract the used address as a string */
- if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
- char buffer[STRERROR_LEN];
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- infof(data, "Connect socket %d over QUIC to %s:%d",
- sockfd, ipbuf, port);
-
- qs->version = NGTCP2_PROTO_VER_MAX;
-#ifdef USE_OPENSSL
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-
- result = quic_set_client_cert(data, qs);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-#endif
-
- result = quic_init_ssl(qs, data, conn);
- if(result)
- return result;
-
- qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
- result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
- if(result)
- return result;
-
- qs->scid.datalen = NGTCP2_MAX_CIDLEN;
- result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
- if(result)
- return result;
-
- (void)Curl_qlogdir(data, qs->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
- qs->qlogfd = qfd; /* -1 if failure above */
- quic_settings(qs, data->set.buffer_size);
-
- qs->local_addrlen = sizeof(qs->local_addr);
- rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
- &qs->local_addrlen);
- if(rv == -1)
- return CURLE_QUIC_CONNECT_ERROR;
-
- ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen);
- ngtcp2_addr_init(&path.remote, addr, addrlen);
-
- rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path,
- NGTCP2_PROTO_VER_V1, &ng_callbacks,
- &qs->settings, &qs->transport_params, NULL, qs);
- if(rc)
- return CURLE_QUIC_CONNECT_ERROR;
-
-#ifdef USE_GNUTLS
- ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session);
-#else
- ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
-#endif
-
- ngtcp2_connection_close_error_default(&qs->last_error);
-
-#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
- qs->no_gso = FALSE;
-#else
- qs->no_gso = TRUE;
-#endif
-
- qs->num_blocked_pkt = 0;
- qs->num_blocked_pkt_sent = 0;
- memset(&qs->blocked_pkt, 0, sizeof(qs->blocked_pkt));
-
- qs->pktbuflen = NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST;
- qs->pktbuf = malloc(qs->pktbuflen);
- if(!qs->pktbuf) {
- ngtcp2_conn_del(qs->qconn);
- qs->qconn = NULL;
- return CURLE_OUT_OF_MEMORY;
- }
-
- qs->conn_ref.get_conn = get_conn;
- qs->conn_ref.user_data = qs;
-
- return CURLE_OK;
-}
-
-/*
- * Store ngtcp2 version info in this buffer.
- */
-void Curl_quic_ver(char *p, size_t len)
-{
- const ngtcp2_info *ng2 = ngtcp2_version(0);
- const nghttp3_info *ht3 = nghttp3_version(0);
- (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
- ng2->version_str, ht3->version_str);
-}
-
-static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t *socks)
-{
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- /* in an HTTP/2 connection we can basically always get a frame so we should
- always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
-
- /* we're still uploading or the HTTP/2 layer wants to send data */
- if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND &&
- (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
- ngtcp2_conn_get_cwnd_left(qs->qconn) &&
- ngtcp2_conn_get_max_data_left(qs->qconn) &&
- nghttp3_conn_is_stream_writable(qs->h3conn, stream->stream3_id))
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
-
- return bitmap;
-}
-
-static void qs_disconnect(struct quicsocket *qs)
-{
- char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
- ngtcp2_tstamp ts;
- ngtcp2_ssize rc;
-
- if(!qs->conn) /* already closed */
- return;
- ts = timestamp();
- rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */
- NULL, /* pkt_info */
- (uint8_t *)buffer, sizeof(buffer),
- &qs->last_error, ts);
- if(rc > 0) {
- while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) &&
- SOCKERRNO == EINTR);
- }
-
- qs->conn = NULL;
- if(qs->qlogfd != -1) {
- close(qs->qlogfd);
- qs->qlogfd = -1;
- }
-#ifdef USE_OPENSSL
- if(qs->ssl)
- SSL_free(qs->ssl);
- qs->ssl = NULL;
- SSL_CTX_free(qs->sslctx);
-#elif defined(USE_GNUTLS)
- if(qs->gtls) {
- if(qs->gtls->cred)
- gnutls_certificate_free_credentials(qs->gtls->cred);
- if(qs->gtls->session)
- gnutls_deinit(qs->gtls->session);
- free(qs->gtls);
- qs->gtls = NULL;
- }
-#elif defined(USE_WOLFSSL)
- if(qs->ssl)
- wolfSSL_free(qs->ssl);
- qs->ssl = NULL;
- wolfSSL_CTX_free(qs->sslctx);
-#endif
- free(qs->pktbuf);
- nghttp3_conn_del(qs->h3conn);
- ngtcp2_conn_del(qs->qconn);
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- int tempindex)
-{
- (void)data;
- if(conn->transport == TRNSPRT_QUIC)
- qs_disconnect(&conn->hequic[tempindex]);
-}
-
-static CURLcode ng_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- (void)dead_connection;
- Curl_quic_disconnect(data, conn, 0);
- Curl_quic_disconnect(data, conn, 1);
- return CURLE_OK;
-}
-
-static unsigned int ng_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- return CONNRESULT_NONE;
-}
-
-static const struct Curl_handler Curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- Curl_http, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- ng_getsock, /* proto_getsock */
- ng_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- ng_getsock, /* perform_getsock */
- ng_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- ng_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- (void)conn;
- (void)stream_id;
- (void)app_error_code;
- (void)user_data;
- H3BUGF(infof(data, "cb_h3_stream_close CALLED"));
-
- stream->closed = TRUE;
- stream->error3 = app_error_code;
- Curl_expire(data, 0, EXPIRE_QUIC);
- /* make sure that ngh3_stream_recv is called again to complete the transfer
- even if there are no more packets to be received from the server. */
- data->state.drain = 1;
- return 0;
-}
-
-/*
- * write_data() copies data to the stream's receive buffer. If not enough
- * space is available in the receive buffer, it copies the rest to the
- * stream's overflow buffer.
- */
-static CURLcode write_data(struct HTTP *stream, const void *mem, size_t memlen)
-{
- CURLcode result = CURLE_OK;
- const char *buf = mem;
- size_t ncopy = memlen;
- /* copy as much as possible to the receive buffer */
- if(stream->len) {
- size_t len = CURLMIN(ncopy, stream->len);
- memcpy(stream->mem, buf, len);
- stream->len -= len;
- stream->memlen += len;
- stream->mem += len;
- buf += len;
- ncopy -= len;
- }
- /* copy the rest to the overflow buffer */
- if(ncopy)
- result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
- return result;
-}
-
-static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
- const uint8_t *buf, size_t buflen,
- void *user_data, void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
-
- result = write_data(stream, buf, buflen);
- if(result) {
- return -1;
- }
- stream->unacked_window += buflen;
- (void)stream_id;
- (void)user_data;
- return 0;
-}
-
-static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
- size_t consumed, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = user_data;
- (void)conn;
- (void)stream_user_data;
- (void)stream_id;
-
- ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
- ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
- return 0;
-}
-
-/* Decode HTTP status code. Returns -1 if no valid status code was
- decoded. (duplicate from http2.c) */
-static int decode_status_code(const uint8_t *value, size_t len)
-{
- int i;
- int res;
-
- if(len != 3) {
- return -1;
- }
-
- res = 0;
-
- for(i = 0; i < 3; ++i) {
- char c = value[i];
-
- if(c < '0' || c > '9') {
- return -1;
- }
-
- res *= 10;
- res += c - '0';
- }
-
- return res;
-}
-
-static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
- int fin, void *user_data, void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
- (void)stream_id;
- (void)user_data;
- (void)fin;
-
- /* add a CRLF only if we've received some headers */
- if(stream->firstheader) {
- result = write_data(stream, "\r\n", 2);
- if(result) {
- return -1;
- }
- }
-
- if(stream->status_code / 100 != 1) {
- stream->bodystarted = TRUE;
- }
- return 0;
-}
-
-static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
- int32_t token, nghttp3_rcbuf *name,
- nghttp3_rcbuf *value, uint8_t flags,
- void *user_data, void *stream_user_data)
-{
- nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
- nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- CURLcode result = CURLE_OK;
- (void)conn;
- (void)stream_id;
- (void)token;
- (void)flags;
- (void)user_data;
-
- if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
- char line[14]; /* status line is always 13 characters long */
- size_t ncopy;
- stream->status_code = decode_status_code(h3val.base, h3val.len);
- DEBUGASSERT(stream->status_code != -1);
- ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
- stream->status_code);
- result = write_data(stream, line, ncopy);
- if(result) {
- return -1;
- }
- }
- else {
- /* store as an HTTP1-style header */
- result = write_data(stream, h3name.base, h3name.len);
- if(result) {
- return -1;
- }
- result = write_data(stream, ": ", 2);
- if(result) {
- return -1;
- }
- result = write_data(stream, h3val.base, h3val.len);
- if(result) {
- return -1;
- }
- result = write_data(stream, "\r\n", 2);
- if(result) {
- return -1;
- }
- }
-
- stream->firstheader = TRUE;
- return 0;
-}
-
-static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data)
-{
- struct quicsocket *qs = user_data;
- int rv;
- (void)conn;
- (void)stream_user_data;
-
- rv = ngtcp2_conn_shutdown_stream_read(qs->qconn, stream_id, app_error_code);
- if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
- uint64_t app_error_code, void *user_data,
- void *stream_user_data) {
- struct quicsocket *qs = user_data;
- int rv;
- (void)conn;
- (void)stream_user_data;
-
- rv = ngtcp2_conn_shutdown_stream_write(qs->qconn, stream_id, app_error_code);
- if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
-
- return 0;
-}
-
-static nghttp3_callbacks ngh3_callbacks = {
- cb_h3_acked_stream_data, /* acked_stream_data */
- cb_h3_stream_close,
- cb_h3_recv_data,
- cb_h3_deferred_consume,
- NULL, /* begin_headers */
- cb_h3_recv_header,
- cb_h3_end_headers,
- NULL, /* begin_trailers */
- cb_h3_recv_header,
- NULL, /* end_trailers */
- cb_h3_stop_sending,
- NULL, /* end_stream */
- cb_h3_reset_stream,
- NULL /* shutdown */
-};
-
-static int init_ngh3_conn(struct quicsocket *qs)
-{
- CURLcode result;
- int rc;
- int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
-
- if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- nghttp3_settings_default(&qs->h3settings);
-
- rc = nghttp3_conn_client_new(&qs->h3conn,
- &ngh3_callbacks,
- &qs->h3settings,
- nghttp3_mem_default(),
- qs);
- if(rc) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
- qpack_dec_stream_id);
- if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
- }
-
- return CURLE_OK;
- fail:
-
- return result;
-}
-
-static Curl_recv ngh3_stream_recv;
-static Curl_send ngh3_stream_send;
-
-static size_t drain_overflow_buffer(struct HTTP *stream)
-{
- size_t overlen = Curl_dyn_len(&stream->overflow);
- size_t ncopy = CURLMIN(overlen, stream->len);
- if(ncopy > 0) {
- memcpy(stream->mem, Curl_dyn_ptr(&stream->overflow), ncopy);
- stream->len -= ncopy;
- stream->mem += ncopy;
- stream->memlen += ncopy;
- if(ncopy != overlen)
- /* make the buffer only keep the tail */
- (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
- else
- Curl_dyn_reset(&stream->overflow);
- }
- return ncopy;
-}
-
-/* incoming data frames on the h3 stream */
-static ssize_t ngh3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- struct connectdata *conn = data->conn;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
-
- if(!stream->memlen) {
- /* remember where to store incoming data for this stream and how big the
- buffer is */
- stream->mem = buf;
- stream->len = buffersize;
- }
- /* else, there's data in the buffer already */
-
- /* if there's data in the overflow buffer from a previous call, copy as much
- as possible to the receive buffer before receiving more */
- drain_overflow_buffer(stream);
-
- if(ng_process_ingress(data, sockfd, qs)) {
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- if(stream->memlen) {
- ssize_t memlen = stream->memlen;
- /* data arrived */
- *curlcode = CURLE_OK;
- /* reset to allow more data to come */
- stream->memlen = 0;
- stream->mem = buf;
- stream->len = buffersize;
- /* extend the stream window with the data we're consuming and send out
- any additional packets to tell the server that we can receive more */
- extend_stream_window(qs->qconn, stream);
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- return memlen;
- }
-
- if(stream->closed) {
- if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
- failf(data,
- "HTTP/3 stream %" PRId64 " was not closed cleanly: (err %" PRIu64
- ")",
- stream->stream3_id, stream->error3);
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- if(!stream->bodystarted) {
- failf(data,
- "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
- " all response header fields, treated as error",
- stream->stream3_id);
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- *curlcode = CURLE_OK;
- return 0;
- }
-
- infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN");
- *curlcode = CURLE_AGAIN;
- return -1;
-}
-
-/* this amount of data has now been acked on this stream */
-static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
- uint64_t datalen, void *user_data,
- void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- struct HTTP *stream = data->req.p.http;
- (void)user_data;
-
- if(!data->set.postfields) {
- stream->h3out->used -= datalen;
- H3BUGF(infof(data,
- "cb_h3_acked_stream_data, %zd bytes, %zd left unacked",
- datalen, stream->h3out->used));
- DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
-
- if(stream->h3out->used == 0) {
- int rv = nghttp3_conn_resume_stream(conn, stream_id);
- if(rv) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
- }
- }
- }
- return 0;
-}
-
-static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
- nghttp3_vec *vec, size_t veccnt,
- uint32_t *pflags, void *user_data,
- void *stream_user_data)
-{
- struct Curl_easy *data = stream_user_data;
- size_t nread;
- struct HTTP *stream = data->req.p.http;
- (void)conn;
- (void)stream_id;
- (void)user_data;
- (void)veccnt;
-
- if(data->set.postfields) {
- vec[0].base = data->set.postfields;
- vec[0].len = data->state.infilesize;
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- return 1;
- }
-
- if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
- return NGHTTP3_ERR_WOULDBLOCK;
- }
-
- nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
- if(nread > 0) {
- /* nghttp3 wants us to hold on to the data until it tells us it is okay to
- delete it. Append the data at the end of the h3out buffer. Since we can
- only return consecutive data, copy the amount that fits and the next
- part comes in next invoke. */
- struct h3out *out = stream->h3out;
- if(nread + out->windex > H3_SEND_SIZE)
- nread = H3_SEND_SIZE - out->windex;
-
- memcpy(&out->buf[out->windex], stream->upload_mem, nread);
-
- /* that's the chunk we return to nghttp3 */
- vec[0].base = &out->buf[out->windex];
- vec[0].len = nread;
-
- out->windex += nread;
- out->used += nread;
-
- if(out->windex == H3_SEND_SIZE)
- out->windex = 0; /* wrap */
- stream->upload_mem += nread;
- stream->upload_len -= nread;
- if(data->state.infilesize != -1) {
- stream->upload_left -= nread;
- if(!stream->upload_left)
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- }
- H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
- nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
- out->used));
- }
- if(stream->upload_done && !stream->upload_len &&
- (stream->upload_left <= 0)) {
- H3BUGF(infof(data, "cb_h3_readfunction sets EOF"));
- *pflags = NGHTTP3_DATA_FLAG_EOF;
- return nread ? 1 : 0;
- }
- else if(!nread) {
- return NGHTTP3_ERR_WOULDBLOCK;
- }
- return 1;
-}
-
-/* Index where :authority header field will appear in request header
- field list. */
-#define AUTHORITY_DST_IDX 3
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- size_t nheader;
- struct quicsocket *qs = conn->quic;
- CURLcode result = CURLE_OK;
- nghttp3_nv *nva = NULL;
- int64_t stream3_id;
- int rc;
- struct h3out *h3out = NULL;
- struct h2h3req *hreq = NULL;
-
- rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
- if(rc) {
- failf(data, "can get bidi streams");
- result = CURLE_SEND_ERROR;
- goto fail;
- }
-
- stream->stream3_id = stream3_id;
- stream->h3req = TRUE; /* senf off! */
- Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
-
- result = Curl_pseudo_headers(data, mem, len, &hreq);
- if(result)
- goto fail;
- nheader = hreq->entries;
-
- nva = malloc(sizeof(nghttp3_nv) * nheader);
- if(!nva) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- else {
- unsigned int i;
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].namelen = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].valuelen = hreq->header[i].valuelen;
- nva[i].flags = NGHTTP3_NV_FLAG_NONE;
- }
- }
-
- switch(data->state.httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- case HTTPREQ_PUT: {
- nghttp3_data_reader data_reader;
- if(data->state.infilesize != -1)
- stream->upload_left = data->state.infilesize;
- else
- /* data sending without specifying the data amount up front */
- stream->upload_left = -1; /* unknown, but not zero */
-
- data_reader.read_data = cb_h3_readfunction;
-
- h3out = calloc(sizeof(struct h3out), 1);
- if(!h3out) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- stream->h3out = h3out;
-
- rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
- nva, nheader, &data_reader, data);
- if(rc) {
- result = CURLE_SEND_ERROR;
- goto fail;
- }
- break;
- }
- default:
- stream->upload_left = 0; /* nothing left to send */
- rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
- nva, nheader, NULL, data);
- if(rc) {
- result = CURLE_SEND_ERROR;
- goto fail;
- }
- break;
- }
-
- Curl_safefree(nva);
-
- infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
- stream3_id, (void *)data);
-
- Curl_pseudo_free(hreq);
- return CURLE_OK;
-
-fail:
- free(nva);
- Curl_pseudo_free(hreq);
- return result;
-}
-static ssize_t ngh3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- ssize_t sent = 0;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
-
- if(stream->closed) {
- *curlcode = CURLE_HTTP3;
- return -1;
- }
-
- if(!stream->h3req) {
- CURLcode result = http_request(data, mem, len);
- if(result) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- /* Assume that mem of length len only includes HTTP/1.1 style
- header fields. In other words, it does not contain request
- body. */
- sent = len;
- }
- else {
- H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes",
- len));
- if(!stream->upload_len) {
- stream->upload_mem = mem;
- stream->upload_len = len;
- (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
- }
- else {
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- }
-
- if(ng_flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- /* Reset post upload buffer after resumed. */
- if(stream->upload_mem) {
- if(data->set.postfields) {
- sent = len;
- }
- else {
- sent = len - stream->upload_len;
- }
-
- stream->upload_mem = NULL;
- stream->upload_len = 0;
-
- if(sent == 0) {
- *curlcode = CURLE_AGAIN;
- return -1;
- }
- }
-
- *curlcode = CURLE_OK;
- return sent;
-}
-
-static CURLcode ng_has_connected(struct Curl_easy *data,
- struct connectdata *conn, int tempindex)
-{
- CURLcode result = CURLE_OK;
- const char *hostname, *disp_hostname;
- int port;
- char *snihost;
-
- Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
- snihost = Curl_ssl_snihost(data, hostname, NULL);
- if(!snihost)
- return CURLE_PEER_FAILED_VERIFICATION;
-
- conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
- conn->send[FIRSTSOCKET] = ngh3_stream_send;
- conn->handler = &Curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
- conn->quic = &conn->hequic[tempindex];
-
- if(conn->ssl_config.verifyhost) {
-#ifdef USE_OPENSSL
- X509 *server_cert;
- server_cert = SSL_get_peer_certificate(conn->quic->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, conn, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
-#elif defined(USE_GNUTLS)
- result = Curl_gtls_verifyserver(data, conn->quic->gtls->session,
- &conn->ssl_config, &data->set.ssl,
- hostname, disp_hostname,
- data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
- if(result)
- return result;
-#elif defined(USE_WOLFSSL)
- if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)
- return CURLE_PEER_FAILED_VERIFICATION;
-#endif
- infof(data, "Verified certificate just fine");
- }
- else
- infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, conn->quic->ssl);
-#endif
- return result;
-}
-
-/*
- * There can be multiple connection attempts going on in parallel.
- */
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *done)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- curl_socket_t sockfd = conn->tempsock[sockindex];
-
- result = ng_process_ingress(data, sockfd, qs);
- if(result)
- goto error;
-
- result = ng_flush_egress(data, sockfd, qs);
- if(result)
- goto error;
-
- if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
- result = ng_has_connected(data, conn, sockindex);
- if(!result)
- *done = TRUE;
- }
-
- return result;
- error:
- (void)qs_disconnect(qs);
- return result;
-
-}
-
-static CURLcode ng_process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs)
-{
- ssize_t recvd;
- int rv;
- uint8_t buf[65536];
- size_t bufsize = sizeof(buf);
- struct sockaddr_storage remote_addr;
- socklen_t remote_addrlen;
- ngtcp2_path path;
- ngtcp2_tstamp ts = timestamp();
- ngtcp2_pkt_info pi = { 0 };
-
- for(;;) {
- remote_addrlen = sizeof(remote_addr);
- while((recvd = recvfrom(sockfd, (char *)buf, bufsize, 0,
- (struct sockaddr *)&remote_addr,
- &remote_addrlen)) == -1 &&
- SOCKERRNO == EINTR)
- ;
- if(recvd == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK)
- break;
-
- failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd", recvd);
- return CURLE_RECV_ERROR;
- }
-
- ngtcp2_addr_init(&path.local, (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen);
- ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr,
- remote_addrlen);
-
- rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts);
- if(rv) {
- if(!qs->last_error.error_code) {
- if(rv == NGTCP2_ERR_CRYPTO) {
- ngtcp2_connection_close_error_set_transport_error_tls_alert(
- &qs->last_error, ngtcp2_conn_get_tls_alert(qs->qconn), NULL, 0);
- }
- else {
- ngtcp2_connection_close_error_set_transport_error_liberr(
- &qs->last_error, rv, NULL, 0);
- }
- }
-
- if(rv == NGTCP2_ERR_CRYPTO)
- /* this is a "TLS problem", but a failed certificate verification
- is a common reason for this */
- return CURLE_PEER_FAILED_VERIFICATION;
- return CURLE_RECV_ERROR;
- }
- }
-
- return CURLE_OK;
-}
-
-static CURLcode do_sendmsg(size_t *sent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen);
-
-static CURLcode send_packet_no_gso(size_t *psent, struct Curl_easy *data,
- int sockfd, struct quicsocket *qs,
- const uint8_t *pkt, size_t pktlen,
- size_t gsolen)
-{
- const uint8_t *p, *end = pkt + pktlen;
- size_t sent;
-
- *psent = 0;
-
- for(p = pkt; p < end; p += gsolen) {
- size_t len = CURLMIN(gsolen, (size_t)(end - p));
- CURLcode curlcode = do_sendmsg(&sent, data, sockfd, qs, p, len, len);
- if(curlcode != CURLE_OK) {
- return curlcode;
- }
- *psent += sent;
- }
-
- return CURLE_OK;
-}
-
-static CURLcode do_sendmsg(size_t *psent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
-#ifdef HAVE_SENDMSG
- struct iovec msg_iov;
- struct msghdr msg = {0};
- ssize_t sent;
-#if defined(__linux__) && defined(UDP_SEGMENT)
- uint8_t msg_ctrl[32];
- struct cmsghdr *cm;
-#endif
-
- *psent = 0;
- msg_iov.iov_base = (uint8_t *)pkt;
- msg_iov.iov_len = pktlen;
- msg.msg_iov = &msg_iov;
- msg.msg_iovlen = 1;
-
-#if defined(__linux__) && defined(UDP_SEGMENT)
- if(pktlen > gsolen) {
- /* Only set this, when we need it. macOS, for example,
- * does not seem to like a msg_control of length 0. */
- msg.msg_control = msg_ctrl;
- assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
- msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
- cm = CMSG_FIRSTHDR(&msg);
- cm->cmsg_level = SOL_UDP;
- cm->cmsg_type = UDP_SEGMENT;
- cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
- *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
- }
-#endif
-
-
- while((sent = sendmsg(sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
- ;
-
- if(sent == -1) {
- switch(SOCKERRNO) {
- case EAGAIN:
-#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
-#endif
- return CURLE_AGAIN;
- case EMSGSIZE:
- /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
- break;
- case EIO:
- if(pktlen > gsolen) {
- /* GSO failure */
- failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
- SOCKERRNO);
- qs->no_gso = TRUE;
- return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen,
- gsolen);
- }
- /* FALLTHROUGH */
- default:
- failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
- return CURLE_SEND_ERROR;
- }
- }
- else {
- assert(pktlen == (size_t)sent);
- }
-#else
- ssize_t sent;
- (void)qs;
- (void)gsolen;
-
- *psent = 0;
-
- while((sent = send(sockfd, (const char *)pkt, pktlen, 0)) == -1 &&
- SOCKERRNO == EINTR)
- ;
-
- if(sent == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
- return CURLE_AGAIN;
- }
- else {
- failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
- if(SOCKERRNO != EMSGSIZE) {
- return CURLE_SEND_ERROR;
- }
- /* UDP datagram is too large; caused by PMTUD. Just let it be
- lost. */
- }
- }
-#endif
-
- *psent = pktlen;
-
- return CURLE_OK;
-}
-
-static CURLcode send_packet(size_t *psent, struct Curl_easy *data, int sockfd,
- struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
- if(qs->no_gso && pktlen > gsolen) {
- return send_packet_no_gso(psent, data, sockfd, qs, pkt, pktlen, gsolen);
- }
-
- return do_sendmsg(psent, data, sockfd, qs, pkt, pktlen, gsolen);
-}
-
-static void push_blocked_pkt(struct quicsocket *qs, const uint8_t *pkt,
- size_t pktlen, size_t gsolen)
-{
- struct blocked_pkt *blkpkt;
-
- assert(qs->num_blocked_pkt <
- sizeof(qs->blocked_pkt) / sizeof(qs->blocked_pkt[0]));
-
- blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt++];
-
- blkpkt->pkt = pkt;
- blkpkt->pktlen = pktlen;
- blkpkt->gsolen = gsolen;
-}
-
-static CURLcode send_blocked_pkt(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- size_t sent;
- CURLcode curlcode;
- struct blocked_pkt *blkpkt;
-
- for(; qs->num_blocked_pkt_sent < qs->num_blocked_pkt;
- ++qs->num_blocked_pkt_sent) {
- blkpkt = &qs->blocked_pkt[qs->num_blocked_pkt_sent];
- curlcode = send_packet(&sent, data, sockfd, qs, blkpkt->pkt,
- blkpkt->pktlen, blkpkt->gsolen);
-
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- blkpkt->pkt += sent;
- blkpkt->pktlen -= sent;
- }
- return curlcode;
- }
- }
-
- qs->num_blocked_pkt = 0;
- qs->num_blocked_pkt_sent = 0;
-
- return CURLE_OK;
-}
-
-static CURLcode ng_flush_egress(struct Curl_easy *data,
- int sockfd,
- struct quicsocket *qs)
-{
- int rv;
- size_t sent;
- ngtcp2_ssize outlen;
- uint8_t *outpos = qs->pktbuf;
- size_t max_udp_payload_size =
- ngtcp2_conn_get_max_tx_udp_payload_size(qs->qconn);
- size_t path_max_udp_payload_size =
- ngtcp2_conn_get_path_max_tx_udp_payload_size(qs->qconn);
- size_t max_pktcnt =
- CURLMIN(MAX_PKT_BURST, qs->pktbuflen / max_udp_payload_size);
- size_t pktcnt = 0;
- size_t gsolen;
- ngtcp2_path_storage ps;
- ngtcp2_tstamp ts = timestamp();
- ngtcp2_tstamp expiry;
- ngtcp2_duration timeout;
- int64_t stream_id;
- nghttp3_ssize veccnt;
- int fin;
- nghttp3_vec vec[16];
- ngtcp2_ssize ndatalen;
- uint32_t flags;
- CURLcode curlcode;
-
- rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
- if(rv) {
- failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
- ngtcp2_strerror(rv));
- ngtcp2_connection_close_error_set_transport_error_liberr(&qs->last_error,
- rv, NULL, 0);
- return CURLE_SEND_ERROR;
- }
-
- if(qs->num_blocked_pkt) {
- curlcode = send_blocked_pkt(data, sockfd, qs);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- }
-
- ngtcp2_path_storage_zero(&ps);
-
- for(;;) {
- veccnt = 0;
- stream_id = -1;
- fin = 0;
-
- if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
- veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
- sizeof(vec) / sizeof(vec[0]));
- if(veccnt < 0) {
- failf(data, "nghttp3_conn_writev_stream returned error: %s",
- nghttp3_strerror((int)veccnt));
- ngtcp2_connection_close_error_set_application_error(
- &qs->last_error,
- nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
- return CURLE_SEND_ERROR;
- }
- }
-
- flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
- (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
- outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, outpos,
- max_udp_payload_size,
- &ndatalen, flags, stream_id,
- (const ngtcp2_vec *)vec, veccnt, ts);
- if(outlen == 0) {
- if(outpos != qs->pktbuf) {
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
- gsolen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- }
-
- break;
- }
- if(outlen < 0) {
- switch(outlen) {
- case NGTCP2_ERR_STREAM_DATA_BLOCKED:
- assert(ndatalen == -1);
- nghttp3_conn_block_stream(qs->h3conn, stream_id);
- continue;
- case NGTCP2_ERR_STREAM_SHUT_WR:
- assert(ndatalen == -1);
- nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
- continue;
- case NGTCP2_ERR_WRITE_MORE:
- assert(ndatalen >= 0);
- rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
- if(rv) {
- failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
- nghttp3_strerror(rv));
- return CURLE_SEND_ERROR;
- }
- continue;
- default:
- assert(ndatalen == -1);
- failf(data, "ngtcp2_conn_writev_stream returned error: %s",
- ngtcp2_strerror((int)outlen));
- ngtcp2_connection_close_error_set_transport_error_liberr(
- &qs->last_error, (int)outlen, NULL, 0);
- return CURLE_SEND_ERROR;
- }
- }
- else if(ndatalen >= 0) {
- rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
- if(rv) {
- failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
- nghttp3_strerror(rv));
- return CURLE_SEND_ERROR;
- }
- }
-
- outpos += outlen;
-
- if(pktcnt == 0) {
- gsolen = outlen;
- }
- else if((size_t)outlen > gsolen ||
- (gsolen > path_max_udp_payload_size &&
- (size_t)outlen != gsolen)) {
- /* Packet larger than path_max_udp_payload_size is PMTUD probe
- packet and it might not be sent because of EMSGSIZE. Send
- them separately to minimize the loss. */
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - outlen - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent,
- outpos - outlen - qs->pktbuf - sent, gsolen);
- push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
- curlcode = send_packet(&sent, data, sockfd, qs, outpos - outlen, outlen,
- outlen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- assert(0 == sent);
- push_blocked_pkt(qs, outpos - outlen, outlen, outlen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
-
- pktcnt = 0;
- outpos = qs->pktbuf;
- continue;
- }
-
- if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
- curlcode = send_packet(&sent, data, sockfd, qs, qs->pktbuf,
- outpos - qs->pktbuf, gsolen);
- if(curlcode) {
- if(curlcode == CURLE_AGAIN) {
- push_blocked_pkt(qs, qs->pktbuf + sent, outpos - qs->pktbuf - sent,
- gsolen);
- Curl_expire(data, 1, EXPIRE_QUIC);
- return CURLE_OK;
- }
- return curlcode;
- }
-
- pktcnt = 0;
- outpos = qs->pktbuf;
- }
- }
-
- expiry = ngtcp2_conn_get_expiry(qs->qconn);
- if(expiry != UINT64_MAX) {
- if(expiry <= ts) {
- timeout = 0;
- }
- else {
- timeout = expiry - ts;
- if(timeout % NGTCP2_MILLISECONDS) {
- timeout += NGTCP2_MILLISECONDS;
- }
- }
- Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
- */
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- DEBUGASSERT(conn);
- if(conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- stream->upload_done = TRUE;
- (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from http.c:Curl_http_done when a request completes.
- */
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- (void)premature;
- if(data->conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- struct HTTP *stream = data->req.p.http;
- Curl_dyn_free(&stream->overflow);
- free(stream->h3out);
- }
-}
-
-/*
- * Called from transfer.c:data_pending to know if we should keep looping
- * to receive more data from the connection.
- */
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- /* We may have received more data than we're able to hold in the receive
- buffer and allocated an overflow buffer. Since it's possible that
- there's no more data coming on the socket, we need to keep reading
- until the overflow buffer is empty. */
- const struct HTTP *stream = data->req.p.http;
- return Curl_dyn_len(&stream->overflow) > 0;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
- struct quicsocket *qs = conn->quic;
-
- if(ngtcp2_conn_get_expiry(qs->qconn) > timestamp()) {
- return CURLE_OK;
- }
-
- if(ng_flush_egress(data, sockfd, qs)) {
- return CURLE_SEND_ERROR;
- }
-
- return CURLE_OK;
-}
-
-#endif
diff --git a/Utilities/cmcurl/lib/vquic/quiche.c b/Utilities/cmcurl/lib/vquic/quiche.c
deleted file mode 100644
index 2b9a0410f1..0000000000
--- a/Utilities/cmcurl/lib/vquic/quiche.c
+++ /dev/null
@@ -1,892 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_QUICHE
-#include <quiche.h>
-#include <openssl/err.h>
-#include <openssl/ssl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "quic.h"
-#include "strcase.h"
-#include "multiif.h"
-#include "connect.h"
-#include "strerror.h"
-#include "vquic.h"
-#include "transfer.h"
-#include "h2h3.h"
-#include "vtls/openssl.h"
-#include "vtls/keylog.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define DEBUG_HTTP3
-/* #define DEBUG_QUICHE */
-#ifdef DEBUG_HTTP3
-#define H3BUGF(x) x
-#else
-#define H3BUGF(x) do { } while(0)
-#endif
-
-#define QUIC_MAX_STREAMS (256*1024)
-#define QUIC_MAX_DATA (1*1024*1024)
-#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
-
-static CURLcode process_ingress(struct Curl_easy *data,
- curl_socket_t sockfd,
- struct quicsocket *qs);
-
-static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
- struct quicsocket *qs);
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len);
-static Curl_recv h3_stream_recv;
-static Curl_send h3_stream_send;
-
-static int quiche_getsock(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t *socks)
-{
- struct SingleRequest *k = &data->req;
- int bitmap = GETSOCK_BLANK;
-
- socks[0] = conn->sock[FIRSTSOCKET];
-
- /* in an HTTP/2 connection we can basically always get a frame so we should
- always be ready for one */
- bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
-
- /* we're still uploading or the HTTP/2 layer wants to send data */
- if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
- bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
-
- return bitmap;
-}
-
-static CURLcode qs_disconnect(struct Curl_easy *data,
- struct quicsocket *qs)
-{
- DEBUGASSERT(qs);
- if(qs->conn) {
- (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0);
- /* flushing the egress is not a failsafe way to deliver all the
- outstanding packets, but we also don't want to get stuck here... */
- (void)flush_egress(data, qs->sockfd, qs);
- quiche_conn_free(qs->conn);
- qs->conn = NULL;
- }
- if(qs->h3config)
- quiche_h3_config_free(qs->h3config);
- if(qs->h3c)
- quiche_h3_conn_free(qs->h3c);
- if(qs->cfg) {
- quiche_config_free(qs->cfg);
- qs->cfg = NULL;
- }
- return CURLE_OK;
-}
-
-static CURLcode quiche_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- struct quicsocket *qs = conn->quic;
- (void)dead_connection;
- return qs_disconnect(data, qs);
-}
-
-void Curl_quic_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- int tempindex)
-{
- if(conn->transport == TRNSPRT_QUIC)
- qs_disconnect(data, &conn->hequic[tempindex]);
-}
-
-static unsigned int quiche_conncheck(struct Curl_easy *data,
- struct connectdata *conn,
- unsigned int checks_to_perform)
-{
- (void)data;
- (void)conn;
- (void)checks_to_perform;
- return CONNRESULT_NONE;
-}
-
-static CURLcode quiche_do(struct Curl_easy *data, bool *done)
-{
- struct HTTP *stream = data->req.p.http;
- stream->h3req = FALSE; /* not sent */
- return Curl_http(data, done);
-}
-
-static const struct Curl_handler Curl_handler_http3 = {
- "HTTPS", /* scheme */
- ZERO_NULL, /* setup_connection */
- quiche_do, /* do_it */
- Curl_http_done, /* done */
- ZERO_NULL, /* do_more */
- ZERO_NULL, /* connect_it */
- ZERO_NULL, /* connecting */
- ZERO_NULL, /* doing */
- quiche_getsock, /* proto_getsock */
- quiche_getsock, /* doing_getsock */
- ZERO_NULL, /* domore_getsock */
- quiche_getsock, /* perform_getsock */
- quiche_disconnect, /* disconnect */
- ZERO_NULL, /* readwrite */
- quiche_conncheck, /* connection_check */
- ZERO_NULL, /* attach connection */
- PORT_HTTP, /* defport */
- CURLPROTO_HTTPS, /* protocol */
- CURLPROTO_HTTP, /* family */
- PROTOPT_SSL | PROTOPT_STREAM /* flags */
-};
-
-#ifdef DEBUG_QUICHE
-static void quiche_debug_log(const char *line, void *argp)
-{
- (void)argp;
- fprintf(stderr, "%s\n", line);
-}
-#endif
-
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
- SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
- SSL_CTX_set_alpn_protos(ssl_ctx,
- (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
-
- SSL_CTX_set_default_verify_paths(ssl_ctx);
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
- }
-
- {
- struct connectdata *conn = data->conn;
- if(conn->ssl_config.verifypeer) {
- const char * const ssl_cafile = conn->ssl_config.CAfile;
- const char * const ssl_capath = conn->ssl_config.CApath;
- if(ssl_cafile || ssl_capath) {
- SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- /* tell OpenSSL where to find CA certificates that are used to verify
- the server's certificate. */
- if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return NULL;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates won't work so
- use openssl's built-in default as fallback */
- SSL_CTX_set_default_verify_paths(ssl_ctx);
- }
-#endif
- }
- }
- return ssl_ctx;
-}
-
-static int quic_init_ssl(struct quicsocket *qs, struct connectdata *conn)
-{
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- const char * const hostname = conn->host.name;
-
- DEBUGASSERT(!qs->ssl);
- qs->ssl = SSL_new(qs->sslctx);
-
- SSL_set_app_data(qs->ssl, qs);
-
- /* set SNI */
- SSL_set_tlsext_host_name(qs->ssl, hostname);
- return 0;
-}
-
-
-CURLcode Curl_quic_connect(struct Curl_easy *data,
- struct connectdata *conn, curl_socket_t sockfd,
- int sockindex,
- const struct sockaddr *addr, socklen_t addrlen)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- char ipbuf[40];
- int port;
- int rv;
-
-#ifdef DEBUG_QUICHE
- /* initialize debug log callback only once */
- static int debug_log_init = 0;
- if(!debug_log_init) {
- quiche_enable_debug_logging(quiche_debug_log, NULL);
- debug_log_init = 1;
- }
-#endif
-
- (void)addr;
- (void)addrlen;
-
- qs->sockfd = sockfd;
- qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
- if(!qs->cfg) {
- failf(data, "can't create quiche config");
- return CURLE_FAILED_INIT;
- }
-
- quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
- quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
- QUIC_MAX_DATA);
- quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
- quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
- quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
- quiche_config_set_application_protos(qs->cfg,
- (uint8_t *)
- QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- - 1);
-
- qs->sslctx = quic_ssl_ctx(data);
- if(!qs->sslctx)
- return CURLE_QUIC_CONNECT_ERROR;
-
- if(quic_init_ssl(qs, conn))
- return CURLE_QUIC_CONNECT_ERROR;
-
- result = Curl_rand(data, qs->scid, sizeof(qs->scid));
- if(result)
- return result;
-
- qs->local_addrlen = sizeof(qs->local_addr);
- rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
- &qs->local_addrlen);
- if(rv == -1)
- return CURLE_QUIC_CONNECT_ERROR;
-
- qs->conn = quiche_conn_new_with_tls((const uint8_t *) qs->scid,
- sizeof(qs->scid), NULL, 0,
- (struct sockaddr *)&qs->local_addr,
- qs->local_addrlen, addr, addrlen,
- qs->cfg, qs->ssl, false);
- if(!qs->conn) {
- failf(data, "can't create quiche connection");
- return CURLE_OUT_OF_MEMORY;
- }
-
- /* Known to not work on Windows */
-#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
- {
- int qfd;
- (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd);
- if(qfd != -1)
- quiche_conn_set_qlog_fd(qs->conn, qfd,
- "qlog title", "curl qlog");
- }
-#endif
-
- result = flush_egress(data, sockfd, qs);
- if(result)
- return result;
-
- /* extract the used address as a string */
- if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
- char buffer[STRERROR_LEN];
- failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- infof(data, "Connect socket %d over QUIC to %s:%ld",
- sockfd, ipbuf, port);
-
- Curl_persistconninfo(data, conn, NULL, -1);
-
- {
- unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
- unsigned alpn_len, offset = 0;
-
- /* Replace each ALPN length prefix by a comma. */
- while(offset < sizeof(alpn_protocols) - 1) {
- alpn_len = alpn_protocols[offset];
- alpn_protocols[offset] = ',';
- offset += 1 + alpn_len;
- }
-
- infof(data, "Sent QUIC client Initial, ALPN: %s",
- alpn_protocols + 1);
- }
-
- return CURLE_OK;
-}
-
-static CURLcode quiche_has_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- int tempindex)
-{
- CURLcode result;
- struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
-
- conn->recv[sockindex] = h3_stream_recv;
- conn->send[sockindex] = h3_stream_send;
- conn->handler = &Curl_handler_http3;
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- conn->httpversion = 30;
- conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-
- if(conn->ssl_config.verifyhost) {
- X509 *server_cert;
- server_cert = SSL_get_peer_certificate(qs->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, conn, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
- infof(data, "Verified certificate just fine");
- }
- else
- infof(data, "Skipped certificate verification");
-
- qs->h3config = quiche_h3_config_new();
- if(!qs->h3config)
- return CURLE_OUT_OF_MEMORY;
-
- /* Create a new HTTP/3 connection on the QUIC connection. */
- qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
- if(!qs->h3c) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- if(conn->hequic[1-tempindex].cfg) {
- qs = &conn->hequic[1-tempindex];
- quiche_config_free(qs->cfg);
- quiche_conn_free(qs->conn);
- qs->cfg = NULL;
- qs->conn = NULL;
- }
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, qs->ssl);
-
- return CURLE_OK;
- fail:
- quiche_h3_config_free(qs->h3config);
- quiche_h3_conn_free(qs->h3c);
- return result;
-}
-
-/*
- * This function gets polled to check if this QUIC connection has connected.
- */
-CURLcode Curl_quic_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *done)
-{
- CURLcode result;
- struct quicsocket *qs = &conn->hequic[sockindex];
- curl_socket_t sockfd = conn->tempsock[sockindex];
-
- result = process_ingress(data, sockfd, qs);
- if(result)
- goto error;
-
- result = flush_egress(data, sockfd, qs);
- if(result)
- goto error;
-
- if(quiche_conn_is_established(qs->conn)) {
- *done = TRUE;
- result = quiche_has_connected(data, conn, 0, sockindex);
- DEBUGF(infof(data, "quiche established connection"));
- }
-
- return result;
- error:
- qs_disconnect(data, qs);
- return result;
-}
-
-static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- ssize_t recvd;
- uint8_t *buf = (uint8_t *)data->state.buffer;
- size_t bufsize = data->set.buffer_size;
- struct sockaddr_storage from;
- socklen_t from_len;
- quiche_recv_info recv_info;
-
- DEBUGASSERT(qs->conn);
-
- /* in case the timeout expired */
- quiche_conn_on_timeout(qs->conn);
-
- do {
- from_len = sizeof(from);
-
- recvd = recvfrom(sockfd, buf, bufsize, 0,
- (struct sockaddr *)&from, &from_len);
-
- if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)))
- break;
-
- if(recvd < 0) {
- failf(data, "quiche: recvfrom() unexpectedly returned %zd "
- "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
- return CURLE_RECV_ERROR;
- }
-
- recv_info.from = (struct sockaddr *) &from;
- recv_info.from_len = from_len;
- recv_info.to = (struct sockaddr *) &qs->local_addr;
- recv_info.to_len = qs->local_addrlen;
-
- recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info);
- if(recvd == QUICHE_ERR_DONE)
- break;
-
- if(recvd < 0) {
- if(QUICHE_ERR_TLS_FAIL == recvd) {
- long verify_ok = SSL_get_verify_result(qs->ssl);
- if(verify_ok != X509_V_OK) {
- failf(data, "SSL certificate problem: %s",
- X509_verify_cert_error_string(verify_ok));
-
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- }
-
- failf(data, "quiche_conn_recv() == %zd", recvd);
-
- return CURLE_RECV_ERROR;
- }
- } while(1);
-
- return CURLE_OK;
-}
-
-/*
- * flush_egress drains the buffers and sends off data.
- * Calls failf() on errors.
- */
-static CURLcode flush_egress(struct Curl_easy *data, int sockfd,
- struct quicsocket *qs)
-{
- ssize_t sent;
- uint8_t out[1200];
- int64_t timeout_ns;
- quiche_send_info send_info;
-
- do {
- sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info);
- if(sent == QUICHE_ERR_DONE)
- break;
-
- if(sent < 0) {
- failf(data, "quiche_conn_send returned %zd", sent);
- return CURLE_SEND_ERROR;
- }
-
- sent = send(sockfd, out, sent, 0);
- if(sent < 0) {
- failf(data, "send() returned %zd", sent);
- return CURLE_SEND_ERROR;
- }
- } while(1);
-
- /* time until the next timeout event, as nanoseconds. */
- timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
- if(timeout_ns)
- /* expire uses milliseconds */
- Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
-
- return CURLE_OK;
-}
-
-struct h3h1header {
- char *dest;
- size_t destlen; /* left to use */
- size_t nlen; /* used */
-};
-
-static int cb_each_header(uint8_t *name, size_t name_len,
- uint8_t *value, size_t value_len,
- void *argp)
-{
- struct h3h1header *headers = (struct h3h1header *)argp;
- size_t olen = 0;
-
- if((name_len == 7) && !strncmp(H2H3_PSEUDO_STATUS, (char *)name, 7)) {
- msnprintf(headers->dest,
- headers->destlen, "HTTP/3 %.*s\n",
- (int) value_len, value);
- }
- else if(!headers->nlen) {
- return CURLE_HTTP3;
- }
- else {
- msnprintf(headers->dest,
- headers->destlen, "%.*s: %.*s\n",
- (int)name_len, name, (int) value_len, value);
- }
- olen = strlen(headers->dest);
- headers->destlen -= olen;
- headers->nlen += olen;
- headers->dest += olen;
- return 0;
-}
-
-static ssize_t h3_stream_recv(struct Curl_easy *data,
- int sockindex,
- char *buf,
- size_t buffersize,
- CURLcode *curlcode)
-{
- ssize_t recvd = -1;
- ssize_t rcode;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- quiche_h3_event *ev;
- int rc;
- struct h3h1header headers;
- struct HTTP *stream = data->req.p.http;
- headers.dest = buf;
- headers.destlen = buffersize;
- headers.nlen = 0;
-
- if(process_ingress(data, sockfd, qs)) {
- infof(data, "h3_stream_recv returns on ingress");
- *curlcode = CURLE_RECV_ERROR;
- return -1;
- }
-
- if(qs->h3_recving) {
- /* body receiving state */
- rcode = quiche_h3_recv_body(qs->h3c, qs->conn, stream->stream3_id,
- (unsigned char *)buf, buffersize);
- if(rcode <= 0) {
- recvd = -1;
- qs->h3_recving = FALSE;
- /* fall through into the while loop below */
- }
- else
- recvd = rcode;
- }
-
- while(recvd < 0) {
- int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
- if(s < 0)
- /* nothing more to do */
- break;
-
- if(s != stream->stream3_id) {
- /* another transfer, ignore for now */
- infof(data, "Got h3 for stream %u, expects %u",
- s, stream->stream3_id);
- continue;
- }
-
- switch(quiche_h3_event_type(ev)) {
- case QUICHE_H3_EVENT_HEADERS:
- rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
- if(rc) {
- *curlcode = rc;
- failf(data, "Error in HTTP/3 response header");
- break;
- }
- recvd = headers.nlen;
- break;
- case QUICHE_H3_EVENT_DATA:
- if(!stream->firstbody) {
- /* add a header-body separator CRLF */
- buf[0] = '\r';
- buf[1] = '\n';
- buf += 2;
- buffersize -= 2;
- stream->firstbody = TRUE;
- recvd = 2; /* two bytes already */
- }
- else
- recvd = 0;
- rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
- buffersize);
- if(rcode <= 0) {
- recvd = -1;
- break;
- }
- qs->h3_recving = TRUE;
- recvd += rcode;
- break;
-
- case QUICHE_H3_EVENT_RESET:
- streamclose(conn, "Stream reset");
- *curlcode = CURLE_PARTIAL_FILE;
- return -1;
-
- case QUICHE_H3_EVENT_FINISHED:
- streamclose(conn, "End of stream");
- recvd = 0; /* end of stream */
- break;
- default:
- break;
- }
-
- quiche_h3_event_free(ev);
- }
- if(flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
- if(recvd >= 0)
- /* Get this called again to drain the event queue */
- Curl_expire(data, 0, EXPIRE_QUIC);
-
- data->state.drain = (recvd >= 0) ? 1 : 0;
- return recvd;
-}
-
-static ssize_t h3_stream_send(struct Curl_easy *data,
- int sockindex,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
-{
- ssize_t sent;
- struct connectdata *conn = data->conn;
- struct quicsocket *qs = conn->quic;
- curl_socket_t sockfd = conn->sock[sockindex];
- struct HTTP *stream = data->req.p.http;
-
- if(!stream->h3req) {
- CURLcode result = http_request(data, mem, len);
- if(result) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- sent = len;
- }
- else {
- sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
- (uint8_t *)mem, len, FALSE);
- if(sent == QUICHE_H3_ERR_DONE) {
- sent = 0;
- }
- else if(sent < 0) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
- }
-
- if(flush_egress(data, sockfd, qs)) {
- *curlcode = CURLE_SEND_ERROR;
- return -1;
- }
-
- *curlcode = CURLE_OK;
- return sent;
-}
-
-/*
- * Store quiche version info in this buffer.
- */
-void Curl_quic_ver(char *p, size_t len)
-{
- (void)msnprintf(p, len, "quiche/%s", quiche_version());
-}
-
-/* Index where :authority header field will appear in request header
- field list. */
-#define AUTHORITY_DST_IDX 3
-
-static CURLcode http_request(struct Curl_easy *data, const void *mem,
- size_t len)
-{
- struct connectdata *conn = data->conn;
- struct HTTP *stream = data->req.p.http;
- size_t nheader;
- int64_t stream3_id;
- quiche_h3_header *nva = NULL;
- struct quicsocket *qs = conn->quic;
- CURLcode result = CURLE_OK;
- struct h2h3req *hreq = NULL;
-
- stream->h3req = TRUE; /* senf off! */
-
- result = Curl_pseudo_headers(data, mem, len, &hreq);
- if(result)
- goto fail;
- nheader = hreq->entries;
-
- nva = malloc(sizeof(quiche_h3_header) * nheader);
- if(!nva) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
- else {
- unsigned int i;
- for(i = 0; i < nheader; i++) {
- nva[i].name = (unsigned char *)hreq->header[i].name;
- nva[i].name_len = hreq->header[i].namelen;
- nva[i].value = (unsigned char *)hreq->header[i].value;
- nva[i].value_len = hreq->header[i].valuelen;
- }
- }
-
- switch(data->state.httpreq) {
- case HTTPREQ_POST:
- case HTTPREQ_POST_FORM:
- case HTTPREQ_POST_MIME:
- case HTTPREQ_PUT:
- if(data->state.infilesize != -1)
- stream->upload_left = data->state.infilesize;
- else
- /* data sending without specifying the data amount up front */
- stream->upload_left = -1; /* unknown, but not zero */
-
- stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
- stream->upload_left ? FALSE: TRUE);
- if((stream3_id >= 0) && data->set.postfields) {
- ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
- (uint8_t *)data->set.postfields,
- stream->upload_left, TRUE);
- if(sent <= 0) {
- failf(data, "quiche_h3_send_body failed");
- result = CURLE_SEND_ERROR;
- }
- stream->upload_left = 0; /* nothing left to send */
- }
- break;
- default:
- stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
- TRUE);
- break;
- }
-
- Curl_safefree(nva);
-
- if(stream3_id < 0) {
- H3BUGF(infof(data, "quiche_h3_send_request returned %d",
- stream3_id));
- result = CURLE_SEND_ERROR;
- goto fail;
- }
-
- infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
- stream3_id, (void *)data);
- stream->stream3_id = stream3_id;
-
- Curl_pseudo_free(hreq);
- return CURLE_OK;
-
-fail:
- free(nva);
- Curl_pseudo_free(hreq);
- return result;
-}
-
-/*
- * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
- */
-CURLcode Curl_quic_done_sending(struct Curl_easy *data)
-{
- struct connectdata *conn = data->conn;
- DEBUGASSERT(conn);
- if(conn->handler == &Curl_handler_http3) {
- /* only for HTTP/3 transfers */
- ssize_t sent;
- struct HTTP *stream = data->req.p.http;
- struct quicsocket *qs = conn->quic;
- stream->upload_done = TRUE;
- sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
- NULL, 0, TRUE);
- if(sent < 0)
- return CURLE_SEND_ERROR;
- }
-
- return CURLE_OK;
-}
-
-/*
- * Called from http.c:Curl_http_done when a request completes.
- */
-void Curl_quic_done(struct Curl_easy *data, bool premature)
-{
- (void)data;
- (void)premature;
-}
-
-/*
- * Called from transfer.c:data_pending to know if we should keep looping
- * to receive more data from the connection.
- */
-bool Curl_quic_data_pending(const struct Curl_easy *data)
-{
- (void)data;
- return FALSE;
-}
-
-/*
- * Called from transfer.c:Curl_readwrite when neither HTTP level read
- * nor write is performed. It is a good place to handle timer expiry
- * for QUIC transport.
- */
-CURLcode Curl_quic_idle(struct Curl_easy *data)
-{
- (void)data;
- return CURLE_OK;
-}
-
-#endif
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index e52a4f301d..bbdeabd695 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,15 +24,26 @@
#include "curl_setup.h"
-#ifdef ENABLE_QUIC
-
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "urldata.h"
#include "dynbuf.h"
-#include "curl_printf.h"
+#include "cfilters.h"
+#include "curl_log.h"
+#include "curl_msh3.h"
+#include "curl_ngtcp2.h"
+#include "curl_quiche.h"
#include "vquic.h"
+#include "vquic_int.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#ifdef ENABLE_QUIC
#ifdef O_BINARY
#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
@@ -40,6 +51,232 @@
#define QLOGMODE O_WRONLY|O_CREAT
#endif
+void Curl_quic_ver(char *p, size_t len)
+{
+#ifdef USE_NGTCP2
+ Curl_ngtcp2_ver(p, len);
+#elif defined(USE_QUICHE)
+ Curl_quiche_ver(p, len);
+#elif defined(USE_MSH3)
+ Curl_msh3_ver(p, len);
+#endif
+}
+
+CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen)
+{
+ qctx->num_blocked_pkt = 0;
+ qctx->num_blocked_pkt_sent = 0;
+ memset(&qctx->blocked_pkt, 0, sizeof(qctx->blocked_pkt));
+
+ qctx->pktbuflen = pktbuflen;
+ qctx->pktbuf = malloc(qctx->pktbuflen);
+ if(!qctx->pktbuf)
+ return CURLE_OUT_OF_MEMORY;
+
+#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
+ qctx->no_gso = FALSE;
+#else
+ qctx->no_gso = TRUE;
+#endif
+
+ return CURLE_OK;
+}
+
+void vquic_ctx_free(struct cf_quic_ctx *qctx)
+{
+ free(qctx->pktbuf);
+ qctx->pktbuf = NULL;
+}
+
+static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen,
+ size_t gsolen, size_t *psent);
+
+static CURLcode do_sendmsg(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent)
+{
+#ifdef HAVE_SENDMSG
+ struct iovec msg_iov;
+ struct msghdr msg = {0};
+ ssize_t sent;
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ uint8_t msg_ctrl[32];
+ struct cmsghdr *cm;
+#endif
+
+ *psent = 0;
+ msg_iov.iov_base = (uint8_t *)pkt;
+ msg_iov.iov_len = pktlen;
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+
+#if defined(__linux__) && defined(UDP_SEGMENT)
+ if(pktlen > gsolen) {
+ /* Only set this, when we need it. macOS, for example,
+ * does not seem to like a msg_control of length 0. */
+ msg.msg_control = msg_ctrl;
+ assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
+ msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
+ cm = CMSG_FIRSTHDR(&msg);
+ cm->cmsg_level = SOL_UDP;
+ cm->cmsg_type = UDP_SEGMENT;
+ cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
+ *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
+ }
+#endif
+
+
+ while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ switch(SOCKERRNO) {
+ case EAGAIN:
+#if EAGAIN != EWOULDBLOCK
+ case EWOULDBLOCK:
+#endif
+ return CURLE_AGAIN;
+ case EMSGSIZE:
+ /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
+ break;
+ case EIO:
+ if(pktlen > gsolen) {
+ /* GSO failure */
+ failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
+ SOCKERRNO);
+ qctx->no_gso = TRUE;
+ return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
+ }
+ /* FALLTHROUGH */
+ default:
+ failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else {
+ assert(pktlen == (size_t)sent);
+ }
+#else
+ ssize_t sent;
+ (void)gsolen;
+
+ *psent = 0;
+
+ while((sent = send(qctx->sockfd,
+ (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ return CURLE_AGAIN;
+ }
+ else {
+ failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
+ if(SOCKERRNO != EMSGSIZE) {
+ return CURLE_SEND_ERROR;
+ }
+ /* UDP datagram is too large; caused by PMTUD. Just let it be
+ lost. */
+ }
+ }
+#endif
+ (void)cf;
+ *psent = pktlen;
+
+ return CURLE_OK;
+}
+
+static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen,
+ size_t gsolen, size_t *psent)
+{
+ const uint8_t *p, *end = pkt + pktlen;
+ size_t sent;
+
+ *psent = 0;
+
+ for(p = pkt; p < end; p += gsolen) {
+ size_t len = CURLMIN(gsolen, (size_t)(end - p));
+ CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
+ if(curlcode != CURLE_OK) {
+ return curlcode;
+ }
+ *psent += sent;
+ }
+
+ return CURLE_OK;
+}
+
+CURLcode vquic_send_packet(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent)
+{
+ if(qctx->no_gso && pktlen > gsolen) {
+ return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
+ }
+
+ return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
+}
+
+
+
+void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen)
+{
+ struct vquic_blocked_pkt *blkpkt;
+
+ (void)cf;
+ assert(qctx->num_blocked_pkt <
+ sizeof(qctx->blocked_pkt) / sizeof(qctx->blocked_pkt[0]));
+
+ blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt++];
+
+ blkpkt->pkt = pkt;
+ blkpkt->pktlen = pktlen;
+ blkpkt->gsolen = gsolen;
+}
+
+CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx)
+{
+ size_t sent;
+ CURLcode curlcode;
+ struct vquic_blocked_pkt *blkpkt;
+
+ (void)cf;
+ for(; qctx->num_blocked_pkt_sent < qctx->num_blocked_pkt;
+ ++qctx->num_blocked_pkt_sent) {
+ blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt_sent];
+ curlcode = vquic_send_packet(cf, data, qctx, blkpkt->pkt,
+ blkpkt->pktlen, blkpkt->gsolen, &sent);
+
+ if(curlcode) {
+ if(curlcode == CURLE_AGAIN) {
+ blkpkt->pkt += sent;
+ blkpkt->pktlen -= sent;
+ }
+ return curlcode;
+ }
+ }
+
+ qctx->num_blocked_pkt = 0;
+ qctx->num_blocked_pkt_sent = 0;
+
+ return CURLE_OK;
+}
+
/*
* If the QLOGDIR environment variable is set, open and return a file
* descriptor to write the log to.
@@ -84,4 +321,80 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
return CURLE_OK;
}
+
+CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport)
+{
+ (void)transport;
+ DEBUGASSERT(transport == TRNSPRT_QUIC);
+#ifdef USE_NGTCP2
+ return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
+#elif defined(USE_QUICHE)
+ return Curl_cf_quiche_create(pcf, data, conn, ai);
+#elif defined(USE_MSH3)
+ return Curl_cf_msh3_create(pcf, data, conn, ai);
+#else
+ *pcf = NULL;
+ (void)data;
+ (void)conn;
+ (void)ai;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+bool Curl_conn_is_http3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex)
+{
+#ifdef USE_NGTCP2
+ return Curl_conn_is_ngtcp2(data, conn, sockindex);
+#elif defined(USE_QUICHE)
+ return Curl_conn_is_quiche(data, conn, sockindex);
+#elif defined(USE_MSH3)
+ return Curl_conn_is_msh3(data, conn, sockindex);
+#else
+ return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (conn->httpversion == 30));
+#endif
+}
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+ if(conn->transport == TRNSPRT_UNIX) {
+ /* cannot do QUIC over a unix domain socket */
+ return CURLE_QUIC_CONNECT_ERROR;
+ }
+ if(!(conn->handler->flags & PROTOPT_SSL)) {
+ failf(data, "HTTP/3 requested for non-HTTPS URL");
+ return CURLE_URL_MALFORMAT;
+ }
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.socksproxy) {
+ failf(data, "HTTP/3 is not supported over a SOCKS proxy");
+ return CURLE_URL_MALFORMAT;
+ }
+ if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
+ failf(data, "HTTP/3 is not supported over a HTTP proxy");
+ return CURLE_URL_MALFORMAT;
+ }
#endif
+
+ return CURLE_OK;
+}
+
+#else /* ENABLE_QUIC */
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn)
+{
+ (void)conn;
+ (void)data;
+ DEBUGF(infof(data, "QUIC is not supported in this build"));
+ return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* !ENABLE_QUIC */
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h
index 8f599a8f49..dc73957aaf 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.h
+++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,10 +27,38 @@
#include "curl_setup.h"
#ifdef ENABLE_QUIC
+struct Curl_cfilter;
+struct Curl_easy;
+struct connectdata;
+struct Curl_addrinfo;
+
+void Curl_quic_ver(char *p, size_t len);
+
CURLcode Curl_qlogdir(struct Curl_easy *data,
unsigned char *scid,
size_t scidlen,
int *qlogfdp);
-#endif
+
+
+CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ const struct Curl_addrinfo *ai,
+ int transport);
+
+bool Curl_conn_is_http3(const struct Curl_easy *data,
+ const struct connectdata *conn,
+ int sockindex);
+
+extern struct Curl_cftype Curl_cft_http3;
+
+#else /* ENABLE_QUIC */
+
+#define Curl_conn_is_http3(a,b,c) FALSE
+
+#endif /* !ENABLE_QUIC */
+
+CURLcode Curl_conn_may_http3(struct Curl_easy *data,
+ const struct connectdata *conn);
#endif /* HEADER_CURL_VQUIC_QUIC_H */
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.h b/Utilities/cmcurl/lib/vquic/vquic_int.h
index 2265999e22..42aba39b06 100644
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.h
+++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_VQUIC_NGTCP2_H
-#define HEADER_CURL_VQUIC_NGTCP2_H
+#ifndef HEADER_CURL_VQUIC_QUIC_INT_H
+#define HEADER_CURL_VQUIC_QUIC_INT_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,68 +26,47 @@
#include "curl_setup.h"
-#ifdef USE_NGTCP2
+#ifdef ENABLE_QUIC
-#ifdef HAVE_NETINET_UDP_H
-#include <netinet/udp.h>
-#endif
-
-#include <ngtcp2/ngtcp2_crypto.h>
-#include <nghttp3/nghttp3.h>
-#ifdef USE_OPENSSL
-#include <openssl/ssl.h>
-#elif defined(USE_WOLFSSL)
-#include <wolfssl/options.h>
-#include <wolfssl/ssl.h>
-#include <wolfssl/quic.h>
-#endif
-
-struct gtls_instance;
-
-struct blocked_pkt {
+struct vquic_blocked_pkt {
const uint8_t *pkt;
size_t pktlen;
size_t gsolen;
};
-struct quicsocket {
- struct connectdata *conn; /* point back to the connection */
- ngtcp2_conn *qconn;
- ngtcp2_cid dcid;
- ngtcp2_cid scid;
- uint32_t version;
- ngtcp2_settings settings;
- ngtcp2_transport_params transport_params;
- ngtcp2_connection_close_error last_error;
- ngtcp2_crypto_conn_ref conn_ref;
-#ifdef USE_OPENSSL
- SSL_CTX *sslctx;
- SSL *ssl;
-#elif defined(USE_GNUTLS)
- struct gtls_instance *gtls;
-#elif defined(USE_WOLFSSL)
- WOLFSSL_CTX *sslctx;
- WOLFSSL *ssl;
-#endif
+struct cf_quic_ctx {
+ curl_socket_t sockfd;
struct sockaddr_storage local_addr;
socklen_t local_addrlen;
- bool no_gso;
+ struct vquic_blocked_pkt blocked_pkt[2];
uint8_t *pktbuf;
- size_t pktbuflen;
/* the number of entries in blocked_pkt */
size_t num_blocked_pkt;
- /* the number of processed entries in blocked_pkt */
size_t num_blocked_pkt_sent;
/* the packets blocked by sendmsg (EAGAIN or EWOULDBLOCK) */
- struct blocked_pkt blocked_pkt[2];
-
- nghttp3_conn *h3conn;
- nghttp3_settings h3settings;
- int qlogfd;
+ size_t pktbuflen;
+ /* the number of processed entries in blocked_pkt */
+ bool no_gso;
};
-#include "urldata.h"
+CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen);
+void vquic_ctx_free(struct cf_quic_ctx *qctx);
+
+CURLcode vquic_send_packet(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen,
+ size_t *psent);
+
+void vquic_push_blocked_pkt(struct Curl_cfilter *cf,
+ struct cf_quic_ctx *qctx,
+ const uint8_t *pkt, size_t pktlen, size_t gsolen);
+
+CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct cf_quic_ctx *qctx);
+
-#endif
+#endif /* !ENABLE_QUIC */
-#endif /* HEADER_CURL_VQUIC_NGTCP2_H */
+#endif /* HEADER_CURL_VQUIC_QUIC_INT_H */
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index d9fa58acf8..b31f741ba9 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2017 - 2022 Red Hat, Inc.
+ * Copyright (C) Red Hat, Inc.
*
* Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
* Robert Kolcun, Andreas Schneider
@@ -685,7 +685,6 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
struct ssh_conn *sshc = &conn->proto.sshc;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc = SSH_NO_ERROR, err;
- char *new_readdir_line;
int seekerr = CURL_SEEKFUNC_OK;
const char *err_msg;
*block = 0; /* we're not blocking by default */
@@ -1432,7 +1431,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
case SSH_SFTP_READDIR:
-
+ Curl_dyn_reset(&sshc->readdir_buf);
if(sshc->readdir_attrs)
sftp_attributes_free(sshc->readdir_attrs);
@@ -1468,17 +1467,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->readdir_len);
}
else {
- sshc->readdir_currLen = strlen(sshc->readdir_longentry);
- sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
- sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
- if(!sshc->readdir_line) {
- state(data, SSH_SFTP_CLOSE);
+ if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ state(data, SSH_STOP);
break;
}
- memcpy(sshc->readdir_line, sshc->readdir_longentry,
- sshc->readdir_currLen);
if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
SSH_S_IFLNK)) {
@@ -1541,24 +1535,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->readdir_linkPath);
- /* get room for the filename and extra output */
- sshc->readdir_totalLen += 4 + sshc->readdir_len;
- new_readdir_line = Curl_saferealloc(sshc->readdir_line,
- sshc->readdir_totalLen);
- if(!new_readdir_line) {
- sshc->readdir_line = NULL;
- state(data, SSH_SFTP_CLOSE);
+ if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
+ sshc->readdir_filename)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
- sshc->readdir_line = new_readdir_line;
-
- sshc->readdir_currLen += msnprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen,
- " -> %s",
- sshc->readdir_filename);
sftp_attributes_free(sshc->readdir_link_attrs);
sshc->readdir_link_attrs = NULL;
@@ -1568,21 +1549,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
state(data, SSH_SFTP_READDIR_BOTTOM);
/* FALLTHROUGH */
case SSH_SFTP_READDIR_BOTTOM:
- sshc->readdir_currLen += msnprintf(sshc->readdir_line +
- sshc->readdir_currLen,
- sshc->readdir_totalLen -
- sshc->readdir_currLen, "\n");
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- sshc->readdir_line,
- sshc->readdir_currLen);
+ if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
+ result = CURLE_OUT_OF_MEMORY;
+ else
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&sshc->readdir_buf),
+ Curl_dyn_len(&sshc->readdir_buf));
if(!result) {
/* output debug output if that is requested */
- Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
- sshc->readdir_currLen);
- data->req.bytecount += sshc->readdir_currLen;
+ Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf),
+ Curl_dyn_len(&sshc->readdir_buf));
+ data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf);
}
- Curl_safefree(sshc->readdir_line);
ssh_string_free_char(sshc->readdir_tmp);
sshc->readdir_tmp = NULL;
@@ -2021,7 +2000,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->rsa);
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
- Curl_safefree(sshc->readdir_line);
+ Curl_dyn_free(&sshc->readdir_buf);
Curl_safefree(sshc->readdir_linkPath);
SSH_STRING_FREE_CHAR(sshc->homedir);
@@ -2166,11 +2145,12 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
struct SSHPROTO *ssh;
- (void)conn;
+ struct ssh_conn *sshc = &conn->proto.sshc;
data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
if(!ssh)
return CURLE_OUT_OF_MEMORY;
+ Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2);
return CURLE_OK;
}
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index ce9229f8fa..f1154dc47a 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -100,10 +100,11 @@
/* Local functions: */
static const char *sftp_libssh2_strerror(unsigned long err);
+#ifdef CURL_LIBSSH2_DEBUG
static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
static LIBSSH2_FREE_FUNC(my_libssh2_free);
-
+#endif
static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -144,7 +145,7 @@ const struct Curl_handler Curl_handler_scp = {
scp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
- ssh_attach,
+ ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -173,7 +174,7 @@ const struct Curl_handler Curl_handler_sftp = {
sftp_disconnect, /* disconnect */
ZERO_NULL, /* readwrite */
ZERO_NULL, /* connection_check */
- ssh_attach,
+ ssh_attach, /* attach */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -283,6 +284,8 @@ static CURLcode libssh2_session_error_to_CURLE(int err)
return CURLE_SSH;
}
+#ifdef CURL_LIBSSH2_DEBUG
+
static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
{
(void)abstract; /* arg not used */
@@ -302,6 +305,8 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free)
free(ptr);
}
+#endif
+
/*
* SSH State machine related code
*/
@@ -840,6 +845,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
#endif
static const char * const hostkey_method_ssh_rsa
= "ssh-rsa";
+ static const char * const hostkey_method_ssh_rsa_all
+ = "rsa-sha2-256,rsa-sha2-512,ssh-rsa";
static const char * const hostkey_method_ssh_dss
= "ssh-dss";
@@ -914,7 +921,16 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
break;
#endif
case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
- hostkey_method = hostkey_method_ssh_rsa;
+#ifdef HAVE_LIBSSH2_VERSION
+ if(libssh2_version(0x010900))
+ /* since 1.9.0 libssh2_session_method_pref() works as expected */
+ hostkey_method = hostkey_method_ssh_rsa_all;
+ else
+#endif
+ /* old libssh2 which cannot correctly remove unsupported methods due
+ * to bug in src/kex.c or does not support the new methods anyways.
+ */
+ hostkey_method = hostkey_method_ssh_rsa;
break;
case LIBSSH2_KNOWNHOST_KEY_SSHDSS:
hostkey_method = hostkey_method_ssh_dss;
@@ -2389,7 +2405,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
if(result) {
- sshc->readdir_line = NULL;
Curl_safefree(sshp->readdir_filename);
Curl_safefree(sshp->readdir_longentry);
state(data, SSH_SFTP_CLOSE);
@@ -2993,12 +3008,9 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->rsa_pub);
Curl_safefree(sshc->rsa);
-
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
-
Curl_safefree(sshc->homedir);
- Curl_safefree(sshc->readdir_line);
/* the code we are about to return */
result = sshc->actualcode;
@@ -3257,9 +3269,13 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
sock = conn->sock[FIRSTSOCKET];
#endif /* CURL_LIBSSH2_DEBUG */
+#ifdef CURL_LIBSSH2_DEBUG
sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
my_libssh2_free,
my_libssh2_realloc, data);
+#else
+ sshc->ssh_session = libssh2_session_init();
+#endif
if(!sshc->ssh_session) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h
index 13bb8aa2d6..1e1b1379c2 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -147,7 +147,6 @@ struct ssh_conn {
char *homedir; /* when doing SFTP we figure out home dir in the
connect phase */
- char *readdir_line;
/* end of READDIR stuff */
int secondCreateDirs; /* counter use by the code to see if the
@@ -158,7 +157,8 @@ struct ssh_conn {
#if defined(USE_LIBSSH)
char *readdir_linkPath;
- size_t readdir_len, readdir_totalLen, readdir_currLen;
+ size_t readdir_len;
+ struct dynbuf readdir_buf;
/* our variables */
unsigned kbd_state; /* 0 or 1 */
ssh_key privkey;
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c
index 6a8fb560d6..17d59ecd23 100644
--- a/Utilities/cmcurl/lib/vssh/wolfssh.c
+++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c
index d9c0ce0eed..7e3eb79ce8 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.c
+++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -58,7 +58,7 @@ struct ssl_backend_data {
unsigned char buf[BR_SSL_BUFSIZE_BIDI];
br_x509_trust_anchor *anchors;
size_t anchors_len;
- const char *protocols[2];
+ const char *protocols[ALPN_ENTRIES_MAX];
/* SSL client context is active */
bool active;
/* size of pending write, yet to be flushed */
@@ -691,29 +691,17 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
Curl_ssl_sessionid_unlock(data);
}
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
-
- /* NOTE: when adding more protocols here, increase the size of the
- * protocols array in `struct ssl_backend_data`.
- */
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ size_t i;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- backend->protocols[cur++] = ALPN_H2;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ backend->protocols[i] = connssl->alpn->entries[i];
}
-#endif
-
- backend->protocols[cur++] = ALPN_HTTP_1_1;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- br_ssl_engine_set_protocol_names(&backend->ctx.eng,
- backend->protocols, cur);
+ br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
@@ -862,26 +850,11 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(cf->conn->bits.tls_enable_alpn) {
- const char *protocol;
-
- protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
- if(protocol) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, protocol);
+ const char *proto;
-#ifdef USE_HTTP2
- if(!strcmp(protocol, ALPN_H2))
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- else
-#endif
- if(!strcmp(protocol, ALPN_HTTP_1_1))
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- else
- infof(data, "ALPN, unrecognized protocol %s", protocol);
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
- }
- else
- infof(data, VTLS_INFOF_NO_ALPN);
+ proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto,
+ proto? strlen(proto) : 0);
}
if(ssl_config->primary.sessionid) {
@@ -977,7 +950,7 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
{
CURLcode ret;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.h b/Utilities/cmcurl/lib/vtls/bearssl.h
index 5125359961..b3651b092c 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.h
+++ b/Utilities/cmcurl/lib/vtls/bearssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2019 - 2022, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) Michael Forney, <mforney@mforney.org>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c
index 2074dcaa48..59fd27ce4e 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.c
+++ b/Utilities/cmcurl/lib/vtls/gskit.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -499,7 +499,7 @@ static void cancel_async_handshake(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(BACKEND);
- if(QsoCancelOperation(cf->conn->sock[cf->sockindex], 0) > 0)
+ if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0)
QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
}
@@ -532,7 +532,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, int directions)
DEBUGASSERT(connssl_next->backend);
n = 1;
fds[0].fd = BACKEND->remotefd;
- fds[1].fd = cf->conn->sock[cf->sockindex];
+ fds[1].fd = Curl_conn_cf_get_socket(cf, data);
if(directions & SOS_READ) {
fds[0].events |= POLLOUT;
@@ -847,7 +847,7 @@ static CURLcode gskit_connect_step1(struct Curl_cfilter *cf,
result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1);
if(!result)
result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0?
- BACKEND->localfd: cf->conn->sock[cf->sockindex]);
+ BACKEND->localfd: Curl_conn_cf_get_socket(cf, data));
if(!result)
result = set_ciphers(cf, data, BACKEND->handle, &protoflags);
if(!protoflags) {
@@ -1208,7 +1208,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
close_one(cf, data);
rc = 0;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
while(loop--) {
@@ -1230,7 +1230,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
notify alert from the server. No way to gsk_secure_soc_read() now, so
use read(). */
- nread = read(cf->conn->sock[cf->sockindex], buf, sizeof(buf));
+ nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf));
if(nread < 0) {
char buffer[STRERROR_LEN];
@@ -1241,7 +1241,7 @@ static int gskit_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
}
return rc;
diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h
index cf923f6b85..c71e6a0117 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.h
+++ b/Utilities/cmcurl/lib/vtls/gskit.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index 104dce6093..07dfaa437c 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -84,7 +84,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
{
struct Curl_cfilter *cf = s;
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
@@ -102,7 +102,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
{
struct Curl_cfilter *cf = s;
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
@@ -214,7 +214,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
gnutls_session_t session;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
DEBUGASSERT(backend);
session = backend->gtls.session;
@@ -434,12 +434,10 @@ CURLcode gtls_client_init(struct Curl_easy *data,
}
#ifdef USE_GNUTLS_SRP
- if((config->authtype == CURL_TLSAUTH_SRP) &&
- Curl_auth_allowed_to_host(data)) {
+ if(config->username && Curl_auth_allowed_to_host(data)) {
infof(data, "Using TLS-SRP username: %s", config->username);
- rc = gnutls_srp_allocate_client_credentials(
- &gtls->srp_client_cred);
+ rc = gnutls_srp_allocate_client_credentials(&gtls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
gnutls_strerror(rc));
@@ -581,7 +579,7 @@ CURLcode gtls_client_init(struct Curl_easy *data,
#ifdef USE_GNUTLS_SRP
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
- if(config->authtype == CURL_TLSAUTH_SRP) {
+ if(config->username) {
size_t len = strlen(prioritylist);
char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
@@ -646,7 +644,7 @@ CURLcode gtls_client_init(struct Curl_easy *data,
#ifdef USE_GNUTLS_SRP
/* put the credentials to the current session */
- if(config->authtype == CURL_TLSAUTH_SRP) {
+ if(config->username) {
rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP,
gtls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
@@ -700,32 +698,22 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(result)
return result;
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- gnutls_datum_t protocols[2];
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ gnutls_datum_t alpn[ALPN_ENTRIES_MAX];
+ size_t i;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur].data = (unsigned char *)ALPN_H2;
- protocols[cur].size = ALPN_H2_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (unsigned char *)connssl->alpn->entries[i];
+ alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]);
}
-#endif
-
- protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
- protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) {
+ if(gnutls_alpn_set_protocols(backend->gtls.session, alpn,
+ (unsigned)connssl->alpn->count, 0)) {
failf(data, "failed setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
/* This might be a reconnect, so we check for a session ID in the cache
@@ -860,10 +848,8 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
config->verifyhost ||
config->issuercert) {
#ifdef USE_GNUTLS_SRP
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
- && ssl_config->primary.username
- && !config->verifypeer
- && gnutls_cipher_get(session)) {
+ if(ssl_config->primary.username && !config->verifypeer &&
+ gnutls_cipher_get(session)) {
/* no peer cert, but auth is ok if we have SRP user and cipher and no
peer verify */
}
@@ -1271,28 +1257,10 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
int rc;
rc = gnutls_alpn_get_selected_protocol(session, &proto);
- if(rc == 0) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
- proto.data);
-
-#ifdef USE_HTTP2
- if(proto.size == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, proto.data,
- ALPN_H2_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(proto.size == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
+ if(rc == 0)
+ Curl_alpn_set_negotiated(cf, data, proto.data, proto.size);
else
- infof(data, VTLS_INFOF_NO_ALPN);
-
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
}
if(ssl_config->primary.sessionid) {
@@ -1516,7 +1484,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
char buf[120];
while(!done) {
- int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
/* Something to read, let's do it and hope that it is the close
@@ -1556,8 +1524,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
gnutls_certificate_free_credentials(backend->gtls.cred);
#ifdef USE_GNUTLS_SRP
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
- && ssl_config->primary.username != NULL)
+ if(ssl_config->primary.username)
gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
#endif
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h
index 49c1c47637..ac141e1c61 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.h
+++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c
index 2a648f20a9..e827dc58f3 100644
--- a/Utilities/cmcurl/lib/vtls/hostcheck.c
+++ b/Utilities/cmcurl/lib/vtls/hostcheck.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.h b/Utilities/cmcurl/lib/vtls/hostcheck.h
index d3c4eab56d..22a1ac2e56 100644
--- a/Utilities/cmcurl/lib/vtls/hostcheck.h
+++ b/Utilities/cmcurl/lib/vtls/hostcheck.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c
index 1952a690ca..d37bb183e7 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.c
+++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h
index 5d3c675b3e..eff5bf38f3 100644
--- a/Utilities/cmcurl/lib/vtls/keylog.h
+++ b/Utilities/cmcurl/lib/vtls/keylog.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 0b81662b93..7f0f4e3668 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -159,15 +159,14 @@ static void mbed_debug(void *context, int level, const char *f_name,
static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
{
struct Curl_cfilter *cf = bio;
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
- blen, (int)nwritten, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d",
+ blen, nwritten, result));
if(nwritten < 0 && CURLE_AGAIN == result) {
nwritten = MBEDTLS_ERR_SSL_WANT_WRITE;
}
@@ -177,8 +176,7 @@ static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
{
struct Curl_cfilter *cf = bio;
- struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
@@ -188,8 +186,8 @@ static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
- blen, (int)nread, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d",
+ blen, nread, result));
if(nread < 0 && CURLE_AGAIN == result) {
nread = MBEDTLS_ERR_SSL_WANT_READ;
}
@@ -648,14 +646,13 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- const char **p = &backend->protocols[0];
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2)
- *p++ = ALPN_H2;
-#endif
- *p++ = ALPN_HTTP_1_1;
- *p = NULL;
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ size_t i;
+
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ backend->protocols[i] = connssl->alpn->entries[i];
+ }
/* this function doesn't clone the protocols array, which is why we need
to keep it around */
if(mbedtls_ssl_conf_alpn_protocols(&backend->config,
@@ -663,8 +660,8 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "Failed setting ALPN protocols");
return CURLE_SSL_CONNECT_ERROR;
}
- for(p = &backend->protocols[0]; *p; ++p)
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -844,28 +841,11 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
+ if(connssl->alpn) {
+ const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
- if(next_protocol) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol);
-#ifdef USE_HTTP2
- if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) &&
- !next_protocol[ALPN_H2_LENGTH]) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) &&
- !next_protocol[ALPN_HTTP_1_1_LENGTH]) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
- else {
- infof(data, VTLS_INFOF_NO_ALPN);
- }
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto,
+ proto? strlen(proto) : 0);
}
#endif
@@ -1081,7 +1061,7 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
{
CURLcode retcode;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.h b/Utilities/cmcurl/lib/vtls/mbedtls.h
index ec3b43bf9c..d8a0a06eb6 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.h
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
index 7d019ede53..bcb7106a63 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
index 22e8725ab0..2b0bd41c8b 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Hoi-Ho Chan, <hoiho.chan@gmail.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c
index 03694d22ba..12c03900d3 100644
--- a/Utilities/cmcurl/lib/vtls/nss.c
+++ b/Utilities/cmcurl/lib/vtls/nss.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -873,11 +873,11 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
#endif
case SSL_NEXT_PROTO_NO_SUPPORT:
case SSL_NEXT_PROTO_NO_OVERLAP:
- infof(data, VTLS_INFOF_NO_ALPN);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
return;
#ifdef SSL_ENABLE_ALPN
case SSL_NEXT_PROTO_SELECTED:
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf);
+ Curl_alpn_set_negotiated(cf, data, buf, buflen);
break;
#endif
default:
@@ -885,25 +885,6 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
break;
}
-#ifdef USE_HTTP2
- if(buflen == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(buflen == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
-
- /* This callback might get called when PR_Recv() is used within
- * close_one() during a connection shutdown. At that point there might not
- * be any "bundle" associated with the connection anymore.
- */
- if(conn->bundle)
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
}
@@ -1555,36 +1536,6 @@ static void nss_cleanup(void)
initialized = 0;
}
-/*
- * This function uses SSL_peek to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-static int nss_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- int rc;
- char buf;
-
- (void)data;
- DEBUGASSERT(backend);
-
- rc =
- PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK,
- PR_SecondsToInterval(1));
- if(rc > 0)
- return 1; /* connection still in place */
-
- if(rc == 0)
- return 0; /* connection has been closed */
-
- return -1; /* connection status unknown */
-}
-
static void close_one(struct ssl_connect_data *connssl)
{
/* before the cleanup, check whether we are using a client certificate */
@@ -1897,7 +1848,7 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
PRFileDesc *nspr_io_stub = NULL;
PRBool ssl_no_cache;
PRBool ssl_cbc_random_iv;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
@@ -2163,27 +2114,17 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
#endif
#if defined(SSL_ENABLE_ALPN)
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- unsigned char protocols[128];
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur++] = ALPN_H2_LENGTH;
- memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- }
-#endif
- protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
- if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess)
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len)
+ != SECSuccess) {
+ failf(data, "Error setting ALPN");
goto error;
+ }
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -2393,6 +2334,19 @@ static ssize_t nss_send(struct Curl_cfilter *cf,
return rc; /* number of bytes */
}
+static bool
+nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ PRFileDesc *fd = connssl->backend->handle->lower;
+ char buf;
+
+ (void) data;
+
+ /* Returns true in case of error to force reading. */
+ return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0;
+}
+
static ssize_t nss_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, /* transfer */
char *buf, /* store read data here */
@@ -2540,10 +2494,10 @@ const struct Curl_ssl Curl_ssl_nss = {
nss_init, /* init */
nss_cleanup, /* cleanup */
nss_version, /* version */
- nss_check_cxn, /* check_cxn */
+ Curl_none_check_cxn, /* check_cxn */
/* NSS has no shutdown function provided and thus always fail */
Curl_none_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
+ nss_data_pending, /* data_pending */
nss_random, /* random */
nss_cert_status_request, /* cert_status_request */
nss_connect, /* connect */
diff --git a/Utilities/cmcurl/lib/vtls/nssg.h b/Utilities/cmcurl/lib/vtls/nssg.h
index 454a38f1fb..ad7eef5801 100644
--- a/Utilities/cmcurl/lib/vtls/nssg.h
+++ b/Utilities/cmcurl/lib/vtls/nssg.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index c5085be3c3..2ba53d9179 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -96,6 +96,7 @@
#include "curl_memory.h"
#include "memdebug.h"
+
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -262,6 +263,12 @@
#define HAVE_OPENSSL_VERSION
#endif
+#ifdef OPENSSL_IS_BORINGSSL
+typedef uint32_t sslerr_t;
+#else
+typedef unsigned long sslerr_t;
+#endif
+
/*
* Whether the OpenSSL version has the API needed to support sharing an
* X509_STORE between connections. The API is:
@@ -285,17 +292,17 @@
#endif /* !LIBRESSL_VERSION_NUMBER */
struct ssl_backend_data {
- struct Curl_easy *logger; /* transfer handle to pass trace logs to, only
- using sockindex 0 */
/* these ones requires specific SSL-types */
SSL_CTX* ctx;
SSL* handle;
X509* server_cert;
+ BIO_METHOD *bio_method;
CURLcode io_result; /* result of last BIO cfilter operation */
#ifndef HAVE_KEYLOG_CALLBACK
/* Set to true once a valid keylog entry has been created to avoid dupes. */
bool keylog_done;
#endif
+ bool x509_store_setup; /* x509 store has been set up */
};
#if defined(HAVE_SSL_X509_STORE_SHARE)
@@ -710,14 +717,14 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result = CURLE_SEND_ERROR;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"),
- blen, (int)nwritten, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d",
+ blen, (int)nwritten, result));
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nwritten < 0) {
@@ -731,7 +738,7 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_RECV_ERROR;
@@ -741,64 +748,75 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
- /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"),
- blen, (int)nread, result)); */
+ DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d",
+ blen, (int)nread, result));
BIO_clear_retry_flags(bio);
connssl->backend->io_result = result;
if(nread < 0) {
if(CURLE_AGAIN == result)
BIO_set_retry_read(bio);
}
+
+ /* Before returning server replies to the SSL instance, we need
+ * to have setup the x509 store or verification will fail. */
+ if(!connssl->backend->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx);
+ if(result) {
+ connssl->backend->io_result = result;
+ return -1;
+ }
+ connssl->backend->x509_store_setup = TRUE;
+ }
+
return (int)nread;
}
-static BIO_METHOD *bio_cf_method = NULL;
-
#if USE_PRE_1_1_API
static BIO_METHOD bio_cf_meth_1_0 = {
- BIO_TYPE_MEM,
- "OpenSSL CF BIO",
- bio_cf_out_write,
- bio_cf_in_read,
- NULL, /* puts is never called */
- NULL, /* gets is never called */
- bio_cf_ctrl,
- bio_cf_create,
- bio_cf_destroy,
- NULL
+ BIO_TYPE_MEM,
+ "OpenSSL CF BIO",
+ bio_cf_out_write,
+ bio_cf_in_read,
+ NULL, /* puts is never called */
+ NULL, /* gets is never called */
+ bio_cf_ctrl,
+ bio_cf_create,
+ bio_cf_destroy,
+ NULL
};
-static void bio_cf_init_methods(void)
+static BIO_METHOD *bio_cf_method_create(void)
{
- bio_cf_method = &bio_cf_meth_1_0;
+ return &bio_cf_meth_1_0;
}
-#define bio_cf_free_methods() Curl_nop_stmt
+#define bio_cf_method_free(m) Curl_nop_stmt
#else
-static void bio_cf_init_methods(void)
+static BIO_METHOD *bio_cf_method_create(void)
{
- bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
- BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
- BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
- BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
- BIO_meth_set_create(bio_cf_method, &bio_cf_create);
- BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+ BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
+ if(m) {
+ BIO_meth_set_write(m, &bio_cf_out_write);
+ BIO_meth_set_read(m, &bio_cf_in_read);
+ BIO_meth_set_ctrl(m, &bio_cf_ctrl);
+ BIO_meth_set_create(m, &bio_cf_create);
+ BIO_meth_set_destroy(m, &bio_cf_destroy);
+ }
+ return m;
}
-static void bio_cf_free_methods(void)
+static void bio_cf_method_free(BIO_METHOD *m)
{
- BIO_meth_free(bio_cf_method);
+ if(m)
+ BIO_meth_free(m);
}
#endif
-static bool ossl_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data);
-
/*
* Number of bytes to read from the random number seed file. This must be
* a finite value (because some entropy "files" like /dev/urandom have
@@ -930,54 +948,6 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
return buf;
}
-/* Return an extra data index for the transfer data.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_data_index(void)
-{
- static int ssl_ex_data_data_index = -1;
- if(ssl_ex_data_data_index < 0) {
- ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return ssl_ex_data_data_index;
-}
-
-/* Return an extra data index for the associated Curl_cfilter instance.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_cf_index(void)
-{
- static int ssl_ex_data_cf_index = -1;
- if(ssl_ex_data_cf_index < 0) {
- ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return ssl_ex_data_cf_index;
-}
-
-/* Return an extra data index for the sockindex.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_ssl_sockindex_index(void)
-{
- static int sockindex_index = -1;
- if(sockindex_index < 0) {
- sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return sockindex_index;
-}
-
-/* Return an extra data index for proxy boolean.
- * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
- */
-static int ossl_get_proxy_index(void)
-{
- static int proxy_index = -1;
- if(proxy_index < 0) {
- proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- }
- return proxy_index;
-}
-
static int passwd_callback(char *buf, int num, int encrypting,
void *global_passwd)
{
@@ -1254,7 +1224,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
if(ret) {
X509 *ca;
- unsigned long err;
+ sslerr_t err;
if(!SSL_CTX_clear_chain_certs(ctx)) {
ret = 0;
@@ -1776,14 +1746,8 @@ static int ossl_init(void)
OpenSSL_add_all_algorithms();
#endif
- bio_cf_init_methods();
Curl_tls_keylog_open();
- /* Initialize the extra data indexes */
- if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 ||
- ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0)
- return 0;
-
return 1;
}
@@ -1822,61 +1786,6 @@ static void ossl_cleanup(void)
#endif
Curl_tls_keylog_close();
- bio_cf_free_methods();
-}
-
-/*
- * This function is used to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- /* SSL_peek takes data out of the raw recv buffer without peeking so we use
- recv MSG_PEEK instead. Bug #795 */
-#ifdef MSG_PEEK
- char buf;
- ssize_t nread;
- nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex],
- (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1,
- (RECV_TYPE_ARG4)MSG_PEEK);
- if(nread == 0)
- return 0; /* connection has been closed */
- if(nread == 1)
- return 1; /* connection still in place */
- else if(nread == -1) {
- int err = SOCKERRNO;
- if(err == EINPROGRESS ||
-#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
- err == EAGAIN ||
-#endif
- err == EWOULDBLOCK)
- return 1; /* connection still in place */
- if(err == ECONNRESET ||
-#ifdef ECONNABORTED
- err == ECONNABORTED ||
-#endif
-#ifdef ENETDOWN
- err == ENETDOWN ||
-#endif
-#ifdef ENETRESET
- err == ENETRESET ||
-#endif
-#ifdef ESHUTDOWN
- err == ESHUTDOWN ||
-#endif
-#ifdef ETIMEDOUT
- err == ETIMEDOUT ||
-#endif
- err == ENOTCONN)
- return 0; /* connection has been closed */
- }
-#endif
- (void)data;
- return -1; /* connection status unknown */
}
/* Selects an OpenSSL crypto engine
@@ -1968,19 +1877,15 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
return list;
}
-#define set_logger(connssl, data) \
- connssl->backend->logger = data
-
static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
+ (void)data;
DEBUGASSERT(backend);
if(backend->handle) {
- set_logger(connssl, data);
-
if(cf->next && cf->next->connected) {
char buf[32];
/* Maybe the server has already sent a close notify alert.
@@ -1997,6 +1902,11 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
if(backend->ctx) {
SSL_CTX_free(backend->ctx);
backend->ctx = NULL;
+ backend->x509_store_setup = FALSE;
+ }
+ if(backend->bio_method) {
+ bio_cf_method_free(backend->bio_method);
+ backend->bio_method = NULL;
}
}
@@ -2034,7 +1944,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
if(backend->handle) {
buffsize = (int)sizeof(buf);
while(!done && loop--) {
- int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex],
+ int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
SSL_SHUTDOWN_TIMEOUT);
if(what > 0) {
ERR_clear_error();
@@ -2163,6 +2073,22 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
return FALSE;
}
+static CURLcode
+ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert, const char *hostname,
+ const char *dispname);
+
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert)
+{
+ const char *hostname, *dispname;
+ int port;
+
+ (void)conn;
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
+ return ossl_verifyhost(data, conn, server_cert, hostname, dispname);
+}
+
/* Quote from RFC2818 section 3.1 "Server Identity"
If a subjectAltName extension of type dNSName is present, that MUST
@@ -2185,8 +2111,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
This function is now used from ngtcp2 (QUIC) as well.
*/
-CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
- X509 *server_cert)
+static CURLcode
+ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+ X509 *server_cert, const char *hostname,
+ const char *dispname)
{
bool matched = FALSE;
int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
@@ -2200,12 +2128,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
CURLcode result = CURLE_OK;
bool dNSName = FALSE; /* if a dNSName field exists in the cert */
bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */
- const char *hostname, *dispname;
- int port;
size_t hostlen;
(void)conn;
- Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port);
hostlen = strlen(hostname);
#ifndef ENABLE_IPV6
@@ -2668,24 +2593,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
const void *buf, size_t len, SSL *ssl,
void *userp)
{
- char unknown[32];
- const char *verstr = NULL;
- struct connectdata *conn = userp;
- int cf_idx = ossl_get_ssl_cf_index();
- struct ssl_connect_data *connssl;
+ const char *verstr = "???";
+ struct Curl_cfilter *cf = userp;
struct Curl_easy *data = NULL;
- struct Curl_cfilter *cf;
-
- DEBUGASSERT(cf_idx >= 0);
- cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
- DEBUGASSERT(cf);
- connssl = cf->ctx;
- DEBUGASSERT(connssl);
- DEBUGASSERT(connssl->backend);
- data = connssl->backend->logger;
+ char unknown[32];
- if(!conn || !data || !data->set.fdebug ||
- (direction != 0 && direction != 1))
+ if(!cf)
+ return;
+ data = CF_DATA_CURRENT(cf);
+ if(!data || !data->set.fdebug || (direction && direction != 1))
return;
switch(ssl_ver) {
@@ -2730,6 +2646,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
* For TLS 1.3, skip notification of the decrypted inner Content-Type.
*/
if(ssl_ver
+#ifdef SSL3_RT_HEADER
+ && content_type != SSL3_RT_HEADER
+#endif
#ifdef SSL3_RT_INNER_CONTENT_TYPE
&& content_type != SSL3_RT_INNER_CONTENT_TYPE
#endif
@@ -2765,7 +2684,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
}
txt_len = msnprintf(ssl_buf, sizeof(ssl_buf),
- CFMSG(cf, "%s (%s), %s, %s (%d):\n"),
+ "%s (%s), %s, %s (%d):\n",
verstr, direction?"OUT":"IN",
tls_rt_name, msg_name, msg_type);
if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
@@ -2983,21 +2902,14 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
struct Curl_easy *data;
struct Curl_cfilter *cf;
const struct ssl_config_data *config;
- curl_socket_t *sockindex_ptr;
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
+ struct ssl_connect_data *connssl;
bool isproxy;
- if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0)
- return 0;
-
- cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx);
- data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx);
+ cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ connssl = cf? cf->ctx : NULL;
+ data = connssl? CF_DATA_CURRENT(cf) : NULL;
/* The sockindex has been stored as a pointer to an array element */
- sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
- if(!cf || !data || !sockindex_ptr)
+ if(!cf || !data)
return 0;
isproxy = Curl_ssl_cf_is_proxy(cf);
@@ -3091,7 +3003,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
BIO_free(cbio);
/* if we didn't end up importing anything, treat that as an error */
- return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE);
+ return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
}
static CURLcode populate_x509_store(struct Curl_cfilter *cf,
@@ -3110,206 +3022,219 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
const char * const ssl_crlfile = ssl_config->primary.CRLfile;
const bool verifypeer = conn_config->verifypeer;
bool imported_native_ca = false;
+ bool imported_ca_info_blob = false;
if(!store)
return CURLE_OUT_OF_MEMORY;
+ if(verifypeer) {
#if defined(USE_WIN32_CRYPTO)
- /* Import certificates from the Windows root certificate store if requested.
- https://stackoverflow.com/questions/9507184/
- https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
- https://datatracker.ietf.org/doc/html/rfc5280 */
- if((conn_config->verifypeer || conn_config->verifyhost) &&
- (ssl_config->native_ca_store)) {
- HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
-
- if(hStore) {
- PCCERT_CONTEXT pContext = NULL;
- /* The array of enhanced key usage OIDs will vary per certificate and is
- declared outside of the loop so that rather than malloc/free each
- iteration we can grow it with realloc, when necessary. */
- CERT_ENHKEY_USAGE *enhkey_usage = NULL;
- DWORD enhkey_usage_size = 0;
-
- /* This loop makes a best effort to import all valid certificates from
- the MS root store. If a certificate cannot be imported it is skipped.
- 'result' is used to store only hard-fail conditions (such as out of
- memory) that cause an early break. */
- result = CURLE_OK;
- for(;;) {
- X509 *x509;
- FILETIME now;
- BYTE key_usage[2];
- DWORD req_size;
- const unsigned char *encoded_cert;
+ /* Import certificates from the Windows root certificate store if
+ requested.
+ https://stackoverflow.com/questions/9507184/
+ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+ https://datatracker.ietf.org/doc/html/rfc5280 */
+ if(ssl_config->native_ca_store) {
+ HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT"));
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and
+ is declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is
+ skipped. 'result' is used to store only hard-fail conditions (such
+ as out of memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char cert_name[256];
+ char cert_name[256];
#endif
- pContext = CertEnumCertificatesInStore(hStore, pContext);
- if(!pContext)
- break;
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
- NULL, cert_name, sizeof(cert_name))) {
- strcpy(cert_name, "Unknown");
- }
- infof(data, "SSL: Checking cert \"%s\"", cert_name);
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"", cert_name);
#endif
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
- encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
- if(!encoded_cert)
- continue;
-
- GetSystemTimeAsFileTime(&now);
- if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
- CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
- continue;
-
- /* If key usage exists check for signing attribute */
- if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
- pContext->pCertInfo,
- key_usage, sizeof(key_usage))) {
- if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
continue;
- }
- else if(GetLastError())
- continue;
-
- /* If enhanced key usage exists check for server auth attribute.
- *
- * Note "In a Microsoft environment, a certificate might also have EKU
- * extended properties that specify valid uses for the certificate."
- * The call below checks both, and behavior varies depending on what is
- * found. For more details see CertGetEnhancedKeyUsage doc.
- */
- if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
- if(req_size && req_size > enhkey_usage_size) {
- void *tmp = realloc(enhkey_usage, req_size);
-
- if(!tmp) {
- failf(data, "SSL: Out of memory allocating for OID list");
- result = CURLE_OUT_OF_MEMORY;
- break;
- }
- enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
- enhkey_usage_size = req_size;
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
}
+ else if(GetLastError())
+ continue;
- if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
- if(!enhkey_usage->cUsageIdentifier) {
- /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
- good for all uses. If it returns zero, the certificate has no
- valid uses." */
- if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
- continue;
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have
+ * EKU extended properties that specify valid uses for the
+ * certificate." The call below checks both, and behavior varies
+ * depending on what is found. For more details see
+ * CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
}
- else {
- DWORD i;
- bool found = false;
-
- for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
- if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
- enhkey_usage->rgpszUsageIdentifier[i])) {
- found = true;
- break;
- }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate
+ is good for all uses. If it returns zero, the certificate
+ has no valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
}
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
- if(!found)
- continue;
+ if(!found)
+ continue;
+ }
}
+ else
+ continue;
}
else
continue;
- }
- else
- continue;
- x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
- if(!x509)
- continue;
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
- /* Try to import the certificate. This may fail for legitimate reasons
- such as duplicate certificate, which is allowed by MS but not
- OpenSSL. */
- if(X509_STORE_add_cert(store, x509) == 1) {
+ /* Try to import the certificate. This may fail for legitimate
+ reasons such as duplicate certificate, which is allowed by MS but
+ not OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- infof(data, "SSL: Imported cert \"%s\"", cert_name);
+ infof(data, "SSL: Imported cert \"%s\"", cert_name);
#endif
- imported_native_ca = true;
+ imported_native_ca = true;
+ }
+ X509_free(x509);
}
- X509_free(x509);
- }
- free(enhkey_usage);
- CertFreeCertificateContext(pContext);
- CertCloseStore(hStore, 0);
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
- if(result)
- return result;
+ if(result)
+ return result;
+ }
+ if(imported_native_ca)
+ infof(data, "successfully imported Windows CA store");
+ else
+ infof(data, "error importing Windows CA store, continuing anyway");
}
- if(imported_native_ca)
- infof(data, "successfully imported Windows CA store");
- else
- infof(data, "error importing Windows CA store, continuing anyway");
- }
#endif
-
- if(ca_info_blob) {
- result = load_cacert_from_memory(store, ca_info_blob);
- if(result) {
- if(result == CURLE_OUT_OF_MEMORY ||
- (verifypeer && !imported_native_ca)) {
+ if(ca_info_blob) {
+ result = load_cacert_from_memory(store, ca_info_blob);
+ if(result) {
failf(data, "error importing CA certificate blob");
return result;
}
- /* Only warn if no certificate verification is required. */
- infof(data, "error importing CA certificate blob, continuing anyway");
+ else {
+ imported_ca_info_blob = true;
+ infof(data, "successfully imported CA certificate blob");
+ }
}
- }
- if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
+ if(ssl_cafile || ssl_capath) {
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
- /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
- if(ssl_cafile &&
- !X509_STORE_load_file(store, ssl_cafile)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate file: %s", ssl_cafile);
- return CURLE_SSL_CACERT_BADFILE;
- }
- if(ssl_capath &&
- !X509_STORE_load_path(store, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate path: %s", ssl_capath);
- return CURLE_SSL_CACERT_BADFILE;
- }
+ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
+ if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "error setting certificate file, continuing anyway");
+ }
+ if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else
+ infof(data, "error setting certificate path, continuing anyway");
+ }
#else
- /* tell OpenSSL where to find CA certificates that are used to verify the
- server's certificate. */
- if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- return CURLE_SSL_CACERT_BADFILE;
- }
+ /* tell OpenSSL where to find CA certificates that are used to verify the
+ server's certificate. */
+ if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) {
+ if(!imported_native_ca && !imported_ca_info_blob) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway");
+ }
+ }
#endif
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
+ infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+ }
#ifdef CURL_CA_FALLBACK
- if(verifypeer &&
- !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
- /* verifying the peer without any CA certificates won't
- work so use openssl's built-in default as fallback */
- X509_STORE_set_default_paths(store);
- }
+ if(!ssl_cafile && !ssl_capath &&
+ !imported_native_ca && !imported_ca_info_blob) {
+ /* verifying the peer without any CA certificates won't
+ work so use openssl's built-in default as fallback */
+ X509_STORE_set_default_paths(store);
+ }
#endif
+ }
if(ssl_crlfile) {
/* tell OpenSSL where to find CRL file that is used to check certificate
@@ -3440,9 +3365,9 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
}
}
-static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_backend_data *backend)
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -3462,10 +3387,10 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
cached_store = get_cached_x509_store(cf, data);
if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
- SSL_CTX_set_cert_store(backend->ctx, cached_store);
+ SSL_CTX_set_cert_store(ssl_ctx, cached_store);
}
else {
- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
result = populate_x509_store(cf, data, store);
if(result == CURLE_OK && cache_criteria_met) {
@@ -3476,11 +3401,11 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
return result;
}
#else /* HAVE_SSL_X509_STORE_SHARE */
-static CURLcode set_up_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_backend_data *backend)
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx)
{
- X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
return populate_x509_store(cf, data, store);
}
@@ -3510,9 +3435,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
#endif
#endif
const long int ssl_version = conn_config->version;
-#ifdef USE_OPENSSL_SRP
- const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype;
-#endif
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
const char * const ssl_cert_type = ssl_config->cert_type;
@@ -3580,8 +3502,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
if(data->set.fdebug && data->set.verbose) {
/* the SSL trace callback is only used for verbose logging */
SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
- SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn);
- set_logger(connssl, data);
+ SSL_CTX_set_msg_callback_arg(backend->ctx, cf);
}
#endif
@@ -3677,36 +3598,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
SSL_CTX_set_options(backend->ctx, ctx_options);
#ifdef HAS_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- unsigned char protocols[128];
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur++] = ALPN_H2_LENGTH;
-
- memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- }
-#endif
-
- protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- /* expects length prefixed preference ordered list of protocols in wire
- * format
- */
- if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) {
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result ||
+ SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) {
failf(data, "Error setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
@@ -3764,8 +3666,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
#endif
#ifdef USE_OPENSSL_SRP
- if((ssl_authtype == CURL_TLSAUTH_SRP) &&
- Curl_auth_allowed_to_host(data)) {
+ if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) {
char * const ssl_username = ssl_config->primary.username;
char * const ssl_password = ssl_config->primary.password;
infof(data, "Using TLS-SRP username: %s", ssl_username);
@@ -3789,10 +3690,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
#endif
- result = set_up_x509_store(cf, data, backend);
- if(result)
- return result;
-
/* OpenSSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
@@ -3836,6 +3733,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OUT_OF_MEMORY;
}
+ SSL_set_app_data(backend->handle, cf);
+
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus)
@@ -3863,13 +3762,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
#endif
- if(!ossl_attach_data(cf, data)) {
- /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */
- failf(data, "SSL: ossl_attach_data failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
+ SSL_set_app_data(backend->handle, cf);
if(ssl_config->primary.sessionid) {
Curl_ssl_sessionid_lock(data);
@@ -3888,13 +3781,26 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
Curl_ssl_sessionid_unlock(data);
}
- bio = BIO_new(bio_cf_method);
+ backend->bio_method = bio_cf_method_create();
+ if(!backend->bio_method)
+ return CURLE_OUT_OF_MEMORY;
+ bio = BIO_new(backend->bio_method);
if(!bio)
return CURLE_OUT_OF_MEMORY;
BIO_set_data(bio, cf);
+#ifdef HAVE_SSL_SET0_WBIO
+ /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works
+ * without backward compat quirks. Every call takes one reference, so we
+ * up it and pass. SSL* then owns it and will free.
+ * We check on the function in configure, since libressl and friends
+ * each have their own versions to add support for this. */
+ BIO_up_ref(bio);
+ SSL_set0_rbio(backend->handle, bio);
+ SSL_set0_wbio(backend->handle, bio);
+#else
SSL_set_bio(backend->handle, bio, bio);
-
+#endif
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
@@ -3915,6 +3821,16 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
ERR_clear_error();
err = SSL_connect(backend->handle);
+
+ if(!backend->x509_store_setup) {
+ /* After having send off the ClientHello, we prepare the x509
+ * store to verify the coming certificate from the server */
+ CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+ if(result)
+ return result;
+ backend->x509_store_setup = TRUE;
+ }
+
#ifndef HAVE_KEYLOG_CALLBACK
if(Curl_tls_keylog_enabled()) {
/* If key logging is enabled, wait for the handshake to complete and then
@@ -3949,7 +3865,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
}
else {
/* untreated error */
- unsigned long errdetail;
+ sslerr_t errdetail;
char error_buffer[256]="";
CURLcode result;
long lerr;
@@ -4043,26 +3959,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
const unsigned char *neg_protocol;
unsigned int len;
SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
- if(len) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol);
-#ifdef USE_HTTP2
- if(len == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, neg_protocol, len)) {
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(len == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- }
- else
- infof(data, VTLS_INFOF_NO_ALPN);
-
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ return Curl_alpn_set_negotiated(cf, data, neg_protocol, len);
}
#endif
@@ -4205,7 +4103,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
BIO_free(mem);
if(conn_config->verifyhost) {
- result = Curl_ossl_verifyhost(data, conn, backend->server_cert);
+ result = ossl_verifyhost(data, conn, backend->server_cert,
+ connssl->hostname, connssl->dispname);
if(result) {
X509_free(backend->server_cert);
backend->server_cert = NULL;
@@ -4379,7 +4278,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
{
CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
@@ -4418,8 +4317,9 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
}
/* if ssl is expecting something, check if it's available. */
- if(connssl->connecting_state == ssl_connect_2_reading ||
- connssl->connecting_state == ssl_connect_2_writing) {
+ if(!nonblocking &&
+ (connssl->connecting_state == ssl_connect_2_reading ||
+ connssl->connecting_state == ssl_connect_2_writing)) {
curl_socket_t writefd = ssl_connect_2_writing ==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
@@ -4427,7 +4327,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking?0:timeout_ms);
+ timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -4435,11 +4335,6 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
goto out;
}
if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- result = CURLE_OK;
- goto out;
- }
/* timeout */
failf(data, "SSL connection timeout");
result = CURLE_OPERATION_TIMEDOUT;
@@ -4527,7 +4422,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
'size_t' */
int err;
char error_buffer[256];
- unsigned long sslerror;
+ sslerr_t sslerror;
int memlen;
int rc;
struct ssl_connect_data *connssl = cf->ctx;
@@ -4539,7 +4434,6 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- set_logger(connssl, data);
rc = SSL_write(backend->handle, mem, memlen);
if(rc <= 0) {
@@ -4636,7 +4530,6 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- set_logger(connssl, data);
nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
if(nread <= 0) {
@@ -4854,89 +4747,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
(void *)backend->ctx : (void *)backend->handle;
}
-static bool ossl_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- const struct ssl_config_data *config;
-
- DEBUGASSERT(backend);
-
- /* If we don't have SSL context, do nothing. */
- if(!backend->handle)
- return FALSE;
-
- config = Curl_ssl_cf_get_config(cf, data);
- if(config->primary.sessionid) {
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
-
- if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
- proxy_idx >= 0) {
- int data_status, cf_status, sockindex_status, proxy_status;
-
- /* Store the data needed for the "new session" callback.
- * The sockindex is stored as a pointer to an array element. */
- data_status = SSL_set_ex_data(backend->handle, data_idx, data);
- cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf);
- sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx,
- cf->conn->sock + cf->sockindex);
-#ifndef CURL_DISABLE_PROXY
- proxy_status = SSL_set_ex_data(backend->handle, proxy_idx,
- Curl_ssl_cf_is_proxy(cf)?
- (void *) 1 : NULL);
-#else
- proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL);
-#endif
- if(data_status && cf_status && sockindex_status && proxy_status)
- return TRUE;
- }
- return FALSE;
- }
- return TRUE;
-}
-
-/*
- * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after
- * the handshake. If the transfer that sets up the callback gets killed before
- * this callback arrives, we must make sure to properly clear the data to
- * avoid UAF problems. A future optimization could be to instead store another
- * transfer that might still be using the same connection.
- */
-
-static void ossl_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- DEBUGASSERT(backend);
-
- /* If we don't have SSL context, do nothing. */
- if(!backend->handle)
- return;
-
- if(ssl_config->primary.sessionid) {
- int data_idx = ossl_get_ssl_data_index();
- int cf_idx = ossl_get_ssl_cf_index();
- int sockindex_idx = ossl_get_ssl_sockindex_index();
- int proxy_idx = ossl_get_proxy_index();
-
- if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 &&
- proxy_idx >= 0) {
- /* Disable references to data in "new session" callback to avoid
- * accessing a stale pointer. */
- SSL_set_ex_data(backend->handle, data_idx, NULL);
- SSL_set_ex_data(backend->handle, cf_idx, NULL);
- SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
- SSL_set_ex_data(backend->handle, proxy_idx, NULL);
- }
- }
-}
-
static void ossl_free_multi_ssl_backend_data(
struct multi_ssl_backend_data *mbackend)
{
@@ -4969,7 +4779,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_init, /* init */
ossl_cleanup, /* cleanup */
ossl_version, /* version */
- ossl_check_cxn, /* check_cxn */
+ Curl_none_check_cxn, /* check_cxn */
ossl_shutdown, /* shutdown */
ossl_data_pending, /* data_pending */
ossl_random, /* random */
@@ -4990,8 +4800,8 @@ const struct Curl_ssl Curl_ssl_openssl = {
#else
NULL, /* sha256sum */
#endif
- ossl_attach_data, /* use of data in this connection */
- ossl_detach_data, /* remote of data from this connection */
+ NULL, /* use of data in this connection */
+ NULL, /* remote of data from this connection */
ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h
index 9df4ecddba..950faab889 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.h
+++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -56,5 +56,14 @@ CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
+/**
+ * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and
+ * easy handle `data`. Will allow reuse of a shared cache if suitable
+ * and configured.
+ */
+CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ SSL_CTX *ssl_ctx);
+
#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index 27f4ec8d8c..003533dbb9 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
@@ -150,6 +150,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t plain_bytes_copied = 0;
rustls_result rresult = 0;
char errorbuf[255];
+ size_t errorlen;
rustls_io_result io_error;
DEBUGASSERT(backend);
@@ -161,7 +162,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
io_error = rustls_connection_read_tls(rconn, read_cb, &io_ctx,
&tls_bytes_read);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
- infof(data, CFMSG(cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
+ DEBUGF(LOG_CF(data, cf, "cr_recv: EAGAIN or EWOULDBLOCK"));
}
else if(io_error) {
char buffer[STRERROR_LEN];
@@ -171,12 +172,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return -1;
}
- infof(data, CFMSG(cf, "cr_recv: read %ld TLS bytes"), tls_bytes_read);
+ DEBUGF(LOG_CF(data, cf, "cr_recv: read %ld TLS bytes", tls_bytes_read));
rresult = rustls_connection_process_new_packets(rconn);
if(rresult != RUSTLS_RESULT_OK) {
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
- failf(data, "%.*s", n, errorbuf);
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_process_new_packets: %.*s",
+ errorlen, errorbuf);
*err = map_error(rresult);
return -1;
}
@@ -189,14 +191,21 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
plainlen - plain_bytes_copied,
&n);
if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
- infof(data, CFMSG(cf, "cr_recv: got PLAINTEXT_EMPTY. "
- "will try again later."));
+ DEBUGF(LOG_CF(data, cf, "cr_recv: got PLAINTEXT_EMPTY. "
+ "will try again later."));
backend->data_pending = FALSE;
break;
}
+ else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) {
+ failf(data, "rustls: peer closed TCP connection "
+ "without first closing TLS connection");
+ *err = CURLE_READ_ERROR;
+ return -1;
+ }
else if(rresult != RUSTLS_RESULT_OK) {
/* n always equals 0 in this case, don't need to check it */
- failf(data, "error in rustls_connection_read: %d", rresult);
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_read: %.*s", errorlen, errorbuf);
*err = CURLE_READ_ERROR;
return -1;
}
@@ -207,7 +216,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
break;
}
else {
- infof(data, CFMSG(cf, "cr_recv: got %ld plain bytes"), n);
+ DEBUGF(LOG_CF(data, cf, "cr_recv: got %ld plain bytes", n));
plain_bytes_copied += n;
}
}
@@ -254,22 +263,25 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t tlswritten_total = 0;
rustls_result rresult;
rustls_io_result io_error;
+ char errorbuf[256];
+ size_t errorlen;
DEBUGASSERT(backend);
rconn = backend->conn;
- infof(data, CFMSG(cf, "cr_send: %ld plain bytes"), plainlen);
+ DEBUGF(LOG_CF(data, cf, "cr_send: %ld plain bytes", plainlen));
if(plainlen > 0) {
rresult = rustls_connection_write(rconn, plainbuf, plainlen,
&plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
- failf(data, "error in rustls_connection_write");
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "rustls_connection_write: %.*s", errorlen, errorbuf);
*err = CURLE_WRITE_ERROR;
return -1;
}
else if(plainwritten == 0) {
- failf(data, "EOF in rustls_connection_write");
+ failf(data, "rustls_connection_write: EOF");
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -282,8 +294,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
&tlswritten);
if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
- infof(data, CFMSG(cf, "cr_send: EAGAIN after %ld bytes"),
- tlswritten_total);
+ DEBUGF(LOG_CF(data, cf, "cr_send: EAGAIN after %zu bytes",
+ tlswritten_total));
*err = CURLE_AGAIN;
return -1;
}
@@ -299,7 +311,7 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
*err = CURLE_WRITE_ERROR;
return -1;
}
- infof(data, CFMSG(cf, "cr_send: wrote %ld TLS bytes"), tlswritten);
+ DEBUGF(LOG_CF(data, cf, "cr_send: wrote %zu TLS bytes", tlswritten));
tlswritten_total += tlswritten;
}
@@ -349,22 +361,25 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
char errorbuf[256];
size_t errorlen;
int result;
- rustls_slice_bytes alpn[2] = {
- { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH },
- { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
- };
DEBUGASSERT(backend);
rconn = backend->conn;
config_builder = rustls_client_config_builder_new();
-#ifdef USE_HTTP2
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
-#else
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
-#endif
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+ size_t i;
+
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+ alpn[i].len = strlen(connssl->alpn->entries[i]);
+ }
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ }
if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none);
@@ -384,7 +399,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
ca_info_blob->len, verifypeer);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to parse trusted certificates from blob");
+ failf(data, "rustls: failed to parse trusted certificates from blob");
rustls_root_cert_store_free(roots);
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
@@ -394,7 +409,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_client_config_builder_use_roots(config_builder, roots);
rustls_root_cert_store_free(roots);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to load trusted certificates");
+ failf(data, "rustls: failed to load trusted certificates");
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
return CURLE_SSL_CACERT_BADFILE;
@@ -404,7 +419,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
result = rustls_client_config_builder_load_roots_from_file(
config_builder, ssl_cafile);
if(result != RUSTLS_RESULT_OK) {
- failf(data, "failed to load trusted certificates");
+ failf(data, "rustls: failed to load trusted certificates");
rustls_client_config_free(
rustls_client_config_builder_build(config_builder));
return CURLE_SSL_CACERT_BADFILE;
@@ -416,7 +431,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
{
char *snihost = Curl_ssl_snihost(data, hostname, NULL);
if(!snihost) {
- failf(data, "Failed to set SNI");
+ failf(data, "rustls: failed to get SNI");
return CURLE_SSL_CONNECT_ERROR;
}
result = rustls_client_connection_new(backend->config, snihost, &rconn);
@@ -439,29 +454,7 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
size_t len = 0;
rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
- if(!protocol) {
- infof(data, VTLS_INFOF_NO_ALPN);
- return;
- }
-
-#ifdef USE_HTTP2
- if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
- cf->conn->alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(len == ALPN_HTTP_1_1_LENGTH &&
- 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1);
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
- }
- else {
- infof(data, "ALPN, negotiated an unrecognized protocol");
- }
-
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, protocol, len);
}
static CURLcode
@@ -469,7 +462,7 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_backend_data *const backend = connssl->backend;
struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK;
@@ -573,7 +566,7 @@ cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
curl_socket_t *socks)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
struct ssl_backend_data *const backend = connssl->backend;
struct rustls_connection *rconn = NULL;
@@ -616,7 +609,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
rustls_connection_send_close_notify(backend->conn);
n = cr_send(cf, data, NULL, 0, &tmperr);
if(n < 0) {
- failf(data, "error sending close notify: %d", tmperr);
+ failf(data, "rustls: error sending close_notify: %d", tmperr);
}
rustls_connection_free(backend->conn);
diff --git a/Utilities/cmcurl/lib/vtls/rustls.h b/Utilities/cmcurl/lib/vtls/rustls.h
index 6b393dd639..bfbe23de3e 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.h
+++ b/Utilities/cmcurl/lib/vtls/rustls.h
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
+ * Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
*
* This software is licensed as described in the file COPYING, which
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 7eab9542af..6f94c7e349 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -5,9 +5,9 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
- * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -264,128 +264,133 @@ set_ssl_version_min_max(DWORD *enabled_protocols,
/* longest is 26, buffer is slightly bigger */
#define LONGEST_ALG_ID 32
-#define CIPHEROPTION(X) \
- if(strcmp(#X, tmp) == 0) \
- return X
+#define CIPHEROPTION(x) {#x, x}
-static int
-get_alg_id_by_name(char *name)
-{
- char tmp[LONGEST_ALG_ID] = { 0 };
- char *nameEnd = strchr(name, ':');
- size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
+struct algo {
+ const char *name;
+ int id;
+};
- /* reject too-long alg names */
- if(n > (LONGEST_ALG_ID - 1))
- return 0;
-
- strncpy(tmp, name, n);
- tmp[n] = 0;
- CIPHEROPTION(CALG_MD2);
- CIPHEROPTION(CALG_MD4);
- CIPHEROPTION(CALG_MD5);
- CIPHEROPTION(CALG_SHA);
- CIPHEROPTION(CALG_SHA1);
- CIPHEROPTION(CALG_MAC);
- CIPHEROPTION(CALG_RSA_SIGN);
- CIPHEROPTION(CALG_DSS_SIGN);
+static const struct algo algs[]= {
+ CIPHEROPTION(CALG_MD2),
+ CIPHEROPTION(CALG_MD4),
+ CIPHEROPTION(CALG_MD5),
+ CIPHEROPTION(CALG_SHA),
+ CIPHEROPTION(CALG_SHA1),
+ CIPHEROPTION(CALG_MAC),
+ CIPHEROPTION(CALG_RSA_SIGN),
+ CIPHEROPTION(CALG_DSS_SIGN),
/* ifdefs for the options that are defined conditionally in wincrypt.h */
#ifdef CALG_NO_SIGN
- CIPHEROPTION(CALG_NO_SIGN);
+ CIPHEROPTION(CALG_NO_SIGN),
#endif
- CIPHEROPTION(CALG_RSA_KEYX);
- CIPHEROPTION(CALG_DES);
+ CIPHEROPTION(CALG_RSA_KEYX),
+ CIPHEROPTION(CALG_DES),
#ifdef CALG_3DES_112
- CIPHEROPTION(CALG_3DES_112);
+ CIPHEROPTION(CALG_3DES_112),
#endif
- CIPHEROPTION(CALG_3DES);
- CIPHEROPTION(CALG_DESX);
- CIPHEROPTION(CALG_RC2);
- CIPHEROPTION(CALG_RC4);
- CIPHEROPTION(CALG_SEAL);
+ CIPHEROPTION(CALG_3DES),
+ CIPHEROPTION(CALG_DESX),
+ CIPHEROPTION(CALG_RC2),
+ CIPHEROPTION(CALG_RC4),
+ CIPHEROPTION(CALG_SEAL),
#ifdef CALG_DH_SF
- CIPHEROPTION(CALG_DH_SF);
+ CIPHEROPTION(CALG_DH_SF),
#endif
- CIPHEROPTION(CALG_DH_EPHEM);
+ CIPHEROPTION(CALG_DH_EPHEM),
#ifdef CALG_AGREEDKEY_ANY
- CIPHEROPTION(CALG_AGREEDKEY_ANY);
+ CIPHEROPTION(CALG_AGREEDKEY_ANY),
#endif
#ifdef CALG_HUGHES_MD5
- CIPHEROPTION(CALG_HUGHES_MD5);
+ CIPHEROPTION(CALG_HUGHES_MD5),
#endif
- CIPHEROPTION(CALG_SKIPJACK);
+ CIPHEROPTION(CALG_SKIPJACK),
#ifdef CALG_TEK
- CIPHEROPTION(CALG_TEK);
+ CIPHEROPTION(CALG_TEK),
#endif
- CIPHEROPTION(CALG_CYLINK_MEK);
- CIPHEROPTION(CALG_SSL3_SHAMD5);
+ CIPHEROPTION(CALG_CYLINK_MEK),
+ CIPHEROPTION(CALG_SSL3_SHAMD5),
#ifdef CALG_SSL3_MASTER
- CIPHEROPTION(CALG_SSL3_MASTER);
+ CIPHEROPTION(CALG_SSL3_MASTER),
#endif
#ifdef CALG_SCHANNEL_MASTER_HASH
- CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH);
+ CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH),
#endif
#ifdef CALG_SCHANNEL_MAC_KEY
- CIPHEROPTION(CALG_SCHANNEL_MAC_KEY);
+ CIPHEROPTION(CALG_SCHANNEL_MAC_KEY),
#endif
#ifdef CALG_SCHANNEL_ENC_KEY
- CIPHEROPTION(CALG_SCHANNEL_ENC_KEY);
+ CIPHEROPTION(CALG_SCHANNEL_ENC_KEY),
#endif
#ifdef CALG_PCT1_MASTER
- CIPHEROPTION(CALG_PCT1_MASTER);
+ CIPHEROPTION(CALG_PCT1_MASTER),
#endif
#ifdef CALG_SSL2_MASTER
- CIPHEROPTION(CALG_SSL2_MASTER);
+ CIPHEROPTION(CALG_SSL2_MASTER),
#endif
#ifdef CALG_TLS1_MASTER
- CIPHEROPTION(CALG_TLS1_MASTER);
+ CIPHEROPTION(CALG_TLS1_MASTER),
#endif
#ifdef CALG_RC5
- CIPHEROPTION(CALG_RC5);
+ CIPHEROPTION(CALG_RC5),
#endif
#ifdef CALG_HMAC
- CIPHEROPTION(CALG_HMAC);
+ CIPHEROPTION(CALG_HMAC),
#endif
#ifdef CALG_TLS1PRF
- CIPHEROPTION(CALG_TLS1PRF);
+ CIPHEROPTION(CALG_TLS1PRF),
#endif
#ifdef CALG_HASH_REPLACE_OWF
- CIPHEROPTION(CALG_HASH_REPLACE_OWF);
+ CIPHEROPTION(CALG_HASH_REPLACE_OWF),
#endif
#ifdef CALG_AES_128
- CIPHEROPTION(CALG_AES_128);
+ CIPHEROPTION(CALG_AES_128),
#endif
#ifdef CALG_AES_192
- CIPHEROPTION(CALG_AES_192);
+ CIPHEROPTION(CALG_AES_192),
#endif
#ifdef CALG_AES_256
- CIPHEROPTION(CALG_AES_256);
+ CIPHEROPTION(CALG_AES_256),
#endif
#ifdef CALG_AES
- CIPHEROPTION(CALG_AES);
+ CIPHEROPTION(CALG_AES),
#endif
#ifdef CALG_SHA_256
- CIPHEROPTION(CALG_SHA_256);
+ CIPHEROPTION(CALG_SHA_256),
#endif
#ifdef CALG_SHA_384
- CIPHEROPTION(CALG_SHA_384);
+ CIPHEROPTION(CALG_SHA_384),
#endif
#ifdef CALG_SHA_512
- CIPHEROPTION(CALG_SHA_512);
+ CIPHEROPTION(CALG_SHA_512),
#endif
#ifdef CALG_ECDH
- CIPHEROPTION(CALG_ECDH);
+ CIPHEROPTION(CALG_ECDH),
#endif
#ifdef CALG_ECMQV
- CIPHEROPTION(CALG_ECMQV);
+ CIPHEROPTION(CALG_ECMQV),
#endif
#ifdef CALG_ECDSA
- CIPHEROPTION(CALG_ECDSA);
+ CIPHEROPTION(CALG_ECDSA),
#endif
#ifdef CALG_ECDH_EPHEM
- CIPHEROPTION(CALG_ECDH_EPHEM);
+ CIPHEROPTION(CALG_ECDH_EPHEM),
#endif
- return 0;
+ {NULL, 0},
+};
+
+static int
+get_alg_id_by_name(char *name)
+{
+ char *nameEnd = strchr(name, ':');
+ size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
+ int i;
+
+ for(i = 0; algs[i].name; i++) {
+ if((n == strlen(algs[i].name) && !strncmp(algs[i].name, name, n)))
+ return algs[i].id;
+ }
+ return 0; /* not found */
}
#define NUM_CIPHERS 47 /* There are 47 options listed above */
@@ -1105,7 +1110,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#ifdef HAS_ALPN
/* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
Also it doesn't seem to be supported for Wine, see curl bug #983. */
- backend->use_alpn = cf->conn->bits.tls_enable_alpn &&
+ backend->use_alpn = connssl->alpn &&
!GetProcAddress(GetModuleHandle(TEXT("ntdll")),
"wine_get_version") &&
curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
@@ -1196,44 +1201,44 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
int list_start_index = 0;
unsigned int *extension_len = NULL;
unsigned short* list_len = NULL;
+ struct alpn_proto_buf proto;
/* The first four bytes will be an unsigned int indicating number
of bytes of data in the rest of the buffer. */
extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]);
- cur += sizeof(unsigned int);
+ cur += (int)sizeof(unsigned int);
/* The next four bytes are an indicator that this buffer will contain
ALPN data, as opposed to NPN, for example. */
*(unsigned int *)(void *)&alpn_buffer[cur] =
SecApplicationProtocolNegotiationExt_ALPN;
- cur += sizeof(unsigned int);
+ cur += (int)sizeof(unsigned int);
/* The next two bytes will be an unsigned short indicating the number
of bytes used to list the preferred protocols. */
list_len = (unsigned short*)(void *)(&alpn_buffer[cur]);
- cur += sizeof(unsigned short);
+ cur += (int)sizeof(unsigned short);
list_start_index = cur;
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
- alpn_buffer[cur++] = ALPN_H2_LENGTH;
- memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH);
- cur += ALPN_H2_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result) {
+ failf(data, "Error setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
}
-#endif
-
- alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
- memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
- cur += ALPN_HTTP_1_1_LENGTH;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ memcpy(&alpn_buffer[cur], proto.data, proto.len);
+ cur += proto.len;
*list_len = curlx_uitous(cur - list_start_index);
- *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
+ *extension_len = *list_len +
+ (unsigned short)sizeof(unsigned int) +
+ (unsigned short)sizeof(unsigned short);
InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
+
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
else {
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
@@ -1727,40 +1732,23 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
if(alpn_result.ProtoNegoStatus ==
SecApplicationProtocolNegotiationStatus_Success) {
- unsigned char alpn = 0;
-
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
- alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
+ unsigned char prev_alpn = cf->conn->alpn;
-#ifdef USE_HTTP2
- if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
- !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
- alpn = CURL_HTTP_VERSION_2;
- }
- else
-#endif
- if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
- ALPN_HTTP_1_1_LENGTH)) {
- alpn = CURL_HTTP_VERSION_1_1;
- }
+ Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId,
+ alpn_result.ProtocolIdSize);
if(backend->recv_renegotiating) {
- if(alpn != cf->conn->alpn) {
+ if(prev_alpn != cf->conn->alpn &&
+ prev_alpn != CURL_HTTP_VERSION_NONE) {
+ /* Renegotiation selected a different protocol now, we cannot
+ * deal with this */
failf(data, "schannel: server selected an ALPN protocol too late");
return CURLE_SSL_CONNECT_ERROR;
}
}
- else
- cf->conn->alpn = alpn;
}
else {
if(!backend->recv_renegotiating)
- infof(data, VTLS_INFOF_NO_ALPN);
- }
-
- if(!backend->recv_renegotiating) {
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
}
}
#endif
@@ -1841,7 +1829,7 @@ schannel_connect_common(struct Curl_cfilter *cf,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
timediff_t timeout_ms;
int what;
@@ -2056,7 +2044,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else if(!timeout_ms)
timeout_ms = TIMEDIFF_T_MAX;
- what = SOCKET_WRITABLE(cf->conn->sock[cf->sockindex], timeout_ms);
+ what = SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h
index 6d4235a96c..7fae39fa0a 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.h
+++ b/Utilities/cmcurl/lib/vtls/schannel.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -179,8 +179,8 @@ struct ssl_backend_data {
size_t encdata_offset, decdata_offset;
unsigned char *encdata_buffer, *decdata_buffer;
/* encdata_is_incomplete: if encdata contains only a partial record that
- can't be decrypted without another Curl_read_plain (that is, status is
- SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+ can't be decrypted without another recv() (that is, status is
+ SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
more bytes into encdata then set this back to false. */
bool encdata_is_incomplete;
unsigned long req_flags, ret_flags;
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index e4992162e6..d75ee8dfe7 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -5,9 +5,9 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
- * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index d903c53d10..7f55fb5be7 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -137,14 +137,6 @@
#include "memdebug.h"
-#define DEBUG_CF 0
-
-#if DEBUG_CF
-#define CF_DEBUGF(x) x
-#else
-#define CF_DEBUGF(x) do { } while(0)
-#endif
-
/* From MacTypes.h (which we can't include because it isn't present in iOS: */
#define ioErr -36
#define paramErr -50
@@ -840,15 +832,15 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection,
struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result;
OSStatus rtn = noErr;
DEBUGASSERT(data);
nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result);
- CF_DEBUGF(infof(data, CFMSG(cf, "bio_read(len=%zu) -> %zd, result=%d"),
- *dataLength, nread, result));
+ DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d",
+ *dataLength, nread, result));
if(nread < 0) {
switch(result) {
case CURLE_OK:
@@ -876,15 +868,15 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection,
struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result;
OSStatus rtn = noErr;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
- CF_DEBUGF(infof(data, CFMSG(cf, "bio_send(len=%zu) -> %zd, result=%d"),
- *dataLength, nwritten, result));
+ DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d",
+ *dataLength, nwritten, result));
if(nwritten <= 0) {
if(result == CURLE_AGAIN) {
rtn = errSSLWouldBlock;
@@ -1644,7 +1636,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
const bool verifypeer = conn_config->verifypeer;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
- bool isproxy = Curl_ssl_cf_is_proxy(cf);
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
@@ -1657,7 +1648,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step1")));
+ DEBUGF(LOG_CF(data, cf, "connect_step1"));
GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
#endif /* CURL_BUILD_MAC */
@@ -1805,33 +1796,28 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
- if(cf->conn->bits.tls_enable_alpn) {
+ if(connssl->alpn) {
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ struct alpn_proto_buf proto;
+ size_t i;
+ CFStringRef cstr;
CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0,
&kCFTypeArrayCallBacks);
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!isproxy || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2));
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i],
+ kCFStringEncodingUTF8);
+ if(!cstr)
+ return CURLE_OUT_OF_MEMORY;
+ CFArrayAppendValue(alpnArr, cstr);
+ CFRelease(cstr);
}
-#endif
-
- CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- /* expects length prefixed preference ordered list of protocols in wire
- * format
- */
err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr);
if(err != noErr)
infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d",
err);
CFRelease(alpnArr);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
}
#endif
@@ -2164,50 +2150,39 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
return sep_end - in;
}
+#define MAX_CERTS_SIZE (50*1024*1024) /* arbitrary - to catch mistakes */
+
static int read_cert(const char *file, unsigned char **out, size_t *outlen)
{
int fd;
- ssize_t n, len = 0, cap = 512;
- unsigned char buf[512], *data;
+ ssize_t n;
+ unsigned char buf[512];
+ struct dynbuf certs;
+
+ Curl_dyn_init(&certs, MAX_CERTS_SIZE);
fd = open(file, 0);
if(fd < 0)
return -1;
- data = malloc(cap);
- if(!data) {
- close(fd);
- return -1;
- }
-
for(;;) {
n = read(fd, buf, sizeof(buf));
+ if(!n)
+ break;
if(n < 0) {
close(fd);
- free(data);
+ Curl_dyn_free(&certs);
return -1;
}
- else if(n == 0) {
+ if(Curl_dyn_addn(&certs, buf, n)) {
close(fd);
- break;
- }
-
- if(len + n >= cap) {
- cap *= 2;
- data = Curl_saferealloc(data, cap);
- if(!data) {
- close(fd);
- return -1;
- }
+ return -1;
}
-
- memcpy(data + len, buf, n);
- len += n;
}
- data[len] = '\0';
+ close(fd);
- *out = data;
- *outlen = len;
+ *out = Curl_dyn_uptr(&certs);
+ *outlen = Curl_dyn_len(&certs);
return 0;
}
@@ -2216,16 +2191,18 @@ static int append_cert_to_array(struct Curl_easy *data,
const unsigned char *buf, size_t buflen,
CFMutableArrayRef array)
{
- CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
char *certp;
CURLcode result;
+ SecCertificateRef cacert;
+ CFDataRef certdata;
+
+ certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
if(!certdata) {
failf(data, "SSL: failed to allocate array for CA certificate");
return CURLE_OUT_OF_MEMORY;
}
- SecCertificateRef cacert =
- SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
+ cacert = SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
CFRelease(certdata);
if(!cacert) {
failf(data, "SSL: failed to create SecCertificate from CA certificate");
@@ -2302,7 +2279,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
/* This is not a PEM file, probably a certificate in DER format. */
rc = append_cert_to_array(data, certbuf, buflen, array);
if(rc != CURLE_OK) {
- CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed")));
+ DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
result = rc;
goto out;
}
@@ -2316,7 +2293,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
rc = append_cert_to_array(data, der, derlen, array);
free(der);
if(rc != CURLE_OK) {
- CF_DEBUGF(infof(data, CFMSG(cf, "append_cert for CA failed")));
+ DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
result = rc;
goto out;
}
@@ -2332,7 +2309,7 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
goto out;
}
- CF_DEBUGF(infof(data, CFMSG(cf, "setting %d trust anchors"), n));
+ DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n));
ret = SecTrustSetAnchorCertificates(trust, array);
if(ret != noErr) {
failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret);
@@ -2354,11 +2331,11 @@ static CURLcode verify_cert_buf(struct Curl_cfilter *cf,
switch(trust_eval) {
case kSecTrustResultUnspecified:
/* what does this really mean? */
- CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Unspecified")));
+ DEBUGF(LOG_CF(data, cf, "trust result: Unspecified"));
result = CURLE_OK;
goto out;
case kSecTrustResultProceed:
- CF_DEBUGF(infof(data, CFMSG(cf, "trust result: Proceed")));
+ DEBUGF(LOG_CF(data, cf, "trust result: Proceed"));
result = CURLE_OK;
goto out;
@@ -2391,7 +2368,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
size_t buflen;
if(ca_info_blob) {
- CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from config blob")));
+ DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob"));
certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
if(!certbuf) {
return CURLE_OUT_OF_MEMORY;
@@ -2401,8 +2378,7 @@ static CURLcode verify_cert(struct Curl_cfilter *cf,
certbuf[ca_info_blob->len]='\0';
}
else if(cafile) {
- CF_DEBUGF(infof(data, CFMSG(cf, "verify_peer, CA from file '%s'"),
- cafile));
+ DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile));
if(read_cert(cafile, &certbuf, &buflen) < 0) {
failf(data, "SSL: failed to read or invalid CA certificate");
return CURLE_SSL_CACERT_BADFILE;
@@ -2440,11 +2416,15 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
do {
SecTrustRef trust;
- OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
+ OSStatus ret;
+ SecKeyRef keyRef;
+ OSStatus success;
+
+ ret = SSLCopyPeerTrust(ctx, &trust);
if(ret != noErr || !trust)
break;
- SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
+ keyRef = SecTrustCopyPublicKey(trust);
CFRelease(trust);
if(!keyRef)
break;
@@ -2458,8 +2438,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
#elif SECTRANSP_PINNEDPUBKEY_V2
- OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
- &publicKeyBits);
+ success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
+ &publicKeyBits);
CFRelease(keyRef);
if(success != errSecSuccess || !publicKeyBits)
break;
@@ -2538,7 +2518,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
DEBUGASSERT(backend);
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step2")));
+ DEBUGF(LOG_CF(data, cf, "connect_step2"));
/* Here goes nothing: */
check_handshake:
@@ -3002,12 +2982,13 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result;
- CF_DEBUGF(infof(data, CFMSG(cf, "connect_step3")));
+ DEBUGF(LOG_CF(data, cf, "connect_step3"));
/* There is no step 3!
* Well, okay, let's collect server certificates, and if verbose mode is on,
* let's print the details of the server certificates. */
- const CURLcode result = collect_server_cert(cf, data);
+ result = collect_server_cert(cf, data);
if(result)
return result;
@@ -3022,7 +3003,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
@@ -3112,7 +3093,7 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(ssl_connect_done == connssl->connecting_state) {
- CF_DEBUGF(infof(data, CFMSG(cf, "connected")));
+ DEBUGF(LOG_CF(data, cf, "connected"));
connssl->state = ssl_connection_complete;
*done = TRUE;
}
@@ -3158,7 +3139,7 @@ static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGASSERT(backend);
if(backend->ssl_ctx) {
- CF_DEBUGF(infof(data, CFMSG(cf, "close")));
+ DEBUGF(LOG_CF(data, cf, "close"));
(void)SSLClose(backend->ssl_ctx);
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
if(SSLCreateContext)
@@ -3200,9 +3181,10 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
rc = 0;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
+ SSL_SHUTDOWN_TIMEOUT);
- CF_DEBUGF(infof(data, CFMSG(cf, "shutdown")));
+ DEBUGF(LOG_CF(data, cf, "shutdown"));
while(loop--) {
if(what < 0) {
/* anything that gets here is fatally bad */
@@ -3229,7 +3211,7 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
if(nread <= 0)
break;
- what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
}
return rc;
@@ -3251,35 +3233,6 @@ static size_t sectransp_version(char *buffer, size_t size)
return msnprintf(buffer, size, "SecureTransport");
}
-/*
- * This function uses SSLGetSessionState to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-static int sectransp_check_cxn(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- OSStatus err;
- SSLSessionState state;
-
- (void)data;
- DEBUGASSERT(backend);
-
- if(backend->ssl_ctx) {
- CF_DEBUGF(infof(data, CFMSG(cf, "check connection")));
- err = SSLGetSessionState(backend->ssl_ctx, &state);
- if(err == noErr)
- return state == kSSLConnected || state == kSSLHandshake;
- return -1;
- }
- return 0;
-}
-
static bool sectransp_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
@@ -3292,7 +3245,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ssl_ctx) { /* SSL is in use */
- CF_DEBUGF(infof(data, CFMSG(cf, "data_pending")));
+ DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
if(err == noErr)
return buffer > 0UL;
@@ -3424,13 +3377,15 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
again:
+ *curlcode = CURLE_OK;
err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed);
if(err != noErr) {
switch(err) {
case errSSLWouldBlock: /* return how much we read (if anything) */
- if(processed)
+ if(processed) {
return (ssize_t)processed;
+ }
*curlcode = CURLE_AGAIN;
return -1L;
break;
@@ -3442,7 +3397,7 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf,
case errSSLClosedGraceful:
case errSSLClosedNoNotify:
*curlcode = CURLE_OK;
- return -1L;
+ return 0;
break;
/* The below is errSSLPeerAuthCompleted; it's not defined in
@@ -3453,8 +3408,10 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf,
CURLcode result = verify_cert(cf, data, conn_config->CAfile,
conn_config->ca_info_blob,
backend->ssl_ctx);
- if(result)
- return result;
+ if(result) {
+ *curlcode = result;
+ return -1;
+ }
}
goto again;
default:
@@ -3491,7 +3448,7 @@ const struct Curl_ssl Curl_ssl_sectransp = {
Curl_none_init, /* init */
Curl_none_cleanup, /* cleanup */
sectransp_version, /* version */
- sectransp_check_cxn, /* check_cxn */
+ Curl_none_check_cxn, /* check_cxn */
sectransp_shutdown, /* shutdown */
sectransp_data_pending, /* data_pending */
sectransp_random, /* random */
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.h b/Utilities/cmcurl/lib/vtls/sectransp.h
index 2d53b7c480..0f1085ad91 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.h
+++ b/Utilities/cmcurl/lib/vtls/sectransp.h
@@ -7,8 +7,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 873ee6bac7..144e6ee5b8 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -73,6 +73,7 @@
#include "curl_memory.h"
#include "memdebug.h"
+
/* convenience macro to check if this handle is using a shared SSL session */
#define SSLSESSION_SHARED(data) (data->share && \
(data->share->specifier & \
@@ -150,7 +151,6 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
#ifdef USE_TLS_SRP
!Curl_timestrcmp(data->username, needle->username) &&
!Curl_timestrcmp(data->password, needle->password) &&
- (data->authtype == needle->authtype) &&
#endif
strcasecompare(data->cipher_list, needle->cipher_list) &&
strcasecompare(data->cipher_list13, needle->cipher_list13) &&
@@ -173,9 +173,6 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
dest->verifystatus = source->verifystatus;
dest->sessionid = source->sessionid;
dest->ssl_options = source->ssl_options;
-#ifdef USE_TLS_SRP
- dest->authtype = source->authtype;
-#endif
CLONE_BLOB(cert_blob);
CLONE_BLOB(ca_info_blob);
@@ -272,8 +269,8 @@ void Curl_ssl_cleanup(void)
static bool ssl_prefs_check(struct Curl_easy *data)
{
/* check for CURLOPT_SSLVERSION invalid parameter value */
- const long sslver = data->set.ssl.primary.version;
- if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) {
+ const unsigned char sslver = data->set.ssl.primary.version;
+ if(sslver >= CURL_SSLVERSION_LAST) {
failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
return FALSE;
}
@@ -293,7 +290,8 @@ static bool ssl_prefs_check(struct Curl_easy *data)
return TRUE;
}
-static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
+static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
+ const struct alpn_spec *alpn)
{
struct ssl_connect_data *ctx;
@@ -302,6 +300,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data)
if(!ctx)
return NULL;
+ ctx->alpn = alpn;
ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
@@ -318,13 +317,6 @@ static void cf_ctx_free(struct ssl_connect_data *ctx)
}
}
-static void cf_ctx_set_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- if(cf->ctx)
- ((struct ssl_connect_data *)cf->ctx)->call_data = data;
-}
-
static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
@@ -339,7 +331,6 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
result = Curl_ssl->connect_blocking(cf, data);
if(!result) {
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
DEBUGASSERT(connssl->state == ssl_connection_complete);
}
@@ -615,19 +606,20 @@ int Curl_ssl_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
curl_socket_t *socks)
{
struct ssl_connect_data *connssl = cf->ctx;
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
- (void)data;
- if(connssl->connecting_state == ssl_connect_2_writing) {
- /* write mode */
- socks[0] = cf->conn->sock[FIRSTSOCKET];
- return GETSOCK_WRITESOCK(0);
- }
- if(connssl->connecting_state == ssl_connect_2_reading) {
- /* read mode */
- socks[0] = cf->conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0);
+ if(sock != CURL_SOCKET_BAD) {
+ if(connssl->connecting_state == ssl_connect_2_writing) {
+ /* write mode */
+ socks[0] = sock;
+ return GETSOCK_WRITESOCK(0);
+ }
+ if(connssl->connecting_state == ssl_connect_2_reading) {
+ /* read mode */
+ socks[0] = sock;
+ return GETSOCK_READSOCK(0);
+ }
}
-
return GETSOCK_BLANK;
}
@@ -685,20 +677,6 @@ void Curl_ssl_version(char *buffer, size_t size)
#endif
}
-/*
- * This function tries to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
- */
-int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn)
-{
- struct Curl_cfilter *cf = Curl_ssl_cf_get_ssl(conn->cfilter[FIRSTSOCKET]);
- return cf? Curl_ssl->check_cxn(cf, data) : -1;
-}
-
void Curl_ssl_free_certinfo(struct Curl_easy *data)
{
struct curl_certinfo *ci = &data->info.certs;
@@ -1430,53 +1408,80 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
#ifdef USE_SSL
+static void free_hostname(struct ssl_connect_data *connssl)
+{
+ if(connssl->dispname != connssl->hostname)
+ free(connssl->dispname);
+ free(connssl->hostname);
+ connssl->hostname = connssl->dispname = NULL;
+}
+
static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- /* TODO: close_one closes BOTH conn->ssl AND conn->proxy_ssl for this
- * sockindex (if in use). Gladly, it is safe to call more than once. */
if(connssl) {
Curl_ssl->close(cf, data);
connssl->state = ssl_connection_none;
+ free_hostname(connssl);
}
cf->connected = FALSE;
}
-static void reinit_hostname(struct Curl_cfilter *cf)
+static CURLcode reinit_hostname(struct Curl_cfilter *cf)
{
struct ssl_connect_data *connssl = cf->ctx;
+ const char *ehostname, *edispname;
+ int eport;
+ /* We need the hostname for SNI negotiation. Once handshaked, this
+ * remains the SNI hostname for the TLS connection. But when the
+ * connection is reused, the settings in cf->conn might change.
+ * So we keep a copy of the hostname we use for SNI.
+ */
#ifndef CURL_DISABLE_PROXY
if(Curl_ssl_cf_is_proxy(cf)) {
- /* TODO: there is not definition for a proxy setup on a secondary conn */
- connssl->hostname = cf->conn->http_proxy.host.name;
- connssl->dispname = cf->conn->http_proxy.host.dispname;
- connssl->port = cf->conn->http_proxy.port;
+ ehostname = cf->conn->http_proxy.host.name;
+ edispname = cf->conn->http_proxy.host.dispname;
+ eport = cf->conn->http_proxy.port;
}
else
#endif
{
- /* TODO: secondaryhostname is set to the IP address we connect to
- * in the FTP handler, it is assumed that host verification uses the
- * hostname from FIRSTSOCKET */
- if(cf->sockindex == SECONDARYSOCKET && 0) {
- connssl->hostname = cf->conn->secondaryhostname;
- connssl->dispname = connssl->hostname;
- connssl->port = cf->conn->secondary_port;
+ ehostname = cf->conn->host.name;
+ edispname = cf->conn->host.dispname;
+ eport = cf->conn->remote_port;
+ }
+
+ /* change if ehostname changed */
+ if(ehostname && (!connssl->hostname
+ || strcmp(ehostname, connssl->hostname))) {
+ free_hostname(connssl);
+ connssl->hostname = strdup(ehostname);
+ if(!connssl->hostname) {
+ free_hostname(connssl);
+ return CURLE_OUT_OF_MEMORY;
}
+ if(!edispname || !strcmp(ehostname, edispname))
+ connssl->dispname = connssl->hostname;
else {
- connssl->hostname = cf->conn->host.name;
- connssl->dispname = cf->conn->host.dispname;
- connssl->port = cf->conn->remote_port;
+ connssl->dispname = strdup(edispname);
+ if(!connssl->dispname) {
+ free_hostname(connssl);
+ return CURLE_OUT_OF_MEMORY;
+ }
}
}
- DEBUGASSERT(connssl->hostname);
+ connssl->port = eport;
+ return CURLE_OK;
}
static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
cf_close(cf, data);
+ CF_DATA_RESTORE(cf, save);
cf_ctx_free(cf->ctx);
cf->ctx = NULL;
}
@@ -1484,10 +1489,12 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
static void ssl_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+
+ CF_DATA_SAVE(save, cf, data);
cf_close(cf, data);
cf->next->cft->close(cf->next, data);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
}
static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
@@ -1495,6 +1502,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
bool blocking, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
+ struct cf_call_data save;
CURLcode result;
if(cf->connected) {
@@ -1502,7 +1510,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- cf_ctx_set_data(cf, data);
+ CF_DATA_SAVE(save, cf, data);
(void)connssl;
DEBUGASSERT(data->conn);
DEBUGASSERT(data->conn == cf->conn);
@@ -1513,10 +1521,10 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
if(result || !*done)
goto out;
- /* TODO: right now we do not fully control when hostname is set,
- * assign it on each connect call. */
- reinit_hostname(cf);
*done = FALSE;
+ result = reinit_hostname(cf);
+ if(result)
+ goto out;
if(blocking) {
result = ssl_connect(cf, data);
@@ -1528,26 +1536,26 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
if(!result && *done) {
cf->connected = TRUE;
- if(cf->sockindex == FIRSTSOCKET && !Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
+ connssl->handshake_done = Curl_now();
DEBUGASSERT(connssl->state == ssl_connection_complete);
}
out:
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
+ struct cf_call_data save;
bool result;
- cf_ctx_set_data(cf, (struct Curl_easy *)data);
- if(cf->ctx && Curl_ssl->data_pending(cf, data))
+ CF_DATA_SAVE(save, cf, data);
+ if(Curl_ssl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
@@ -1555,12 +1563,13 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
struct Curl_easy *data, const void *buf, size_t len,
CURLcode *err)
{
+ struct cf_call_data save;
ssize_t nwritten;
+ CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- cf_ctx_set_data(cf, data);
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return nwritten;
}
@@ -1568,12 +1577,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
+ struct cf_call_data save;
ssize_t nread;
+ CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- cf_ctx_set_data(cf, data);
nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return nread;
}
@@ -1581,39 +1591,100 @@ static int ssl_cf_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
+ struct cf_call_data save;
int result;
- cf_ctx_set_data(cf, data);
+ CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->get_select_socks(cf, data, socks);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
return result;
}
-static void ssl_cf_attach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2)
{
- if(Curl_ssl->attach_data) {
- cf_ctx_set_data(cf, data);
- Curl_ssl->attach_data(cf, data);
- cf_ctx_set_data(cf, NULL);
+ struct cf_call_data save;
+
+ (void)arg1;
+ (void)arg2;
+ switch(event) {
+ case CF_CTRL_DATA_ATTACH:
+ if(Curl_ssl->attach_data) {
+ CF_DATA_SAVE(save, cf, data);
+ Curl_ssl->attach_data(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ }
+ break;
+ case CF_CTRL_DATA_DETACH:
+ if(Curl_ssl->detach_data) {
+ CF_DATA_SAVE(save, cf, data);
+ Curl_ssl->detach_data(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ }
+ break;
+ default:
+ break;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ int query, int *pres1, void *pres2)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+
+ switch(query) {
+ case CF_QUERY_TIMER_APPCONNECT: {
+ struct curltime *when = pres2;
+ if(cf->connected && !Curl_ssl_cf_is_proxy(cf))
+ *when = connssl->handshake_done;
+ return CURLE_OK;
}
+ default:
+ break;
+ }
+ return cf->next?
+ cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+ CURLE_UNKNOWN_OPTION;
}
-static void ssl_cf_detach_data(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool *input_pending)
{
- if(Curl_ssl->detach_data) {
- cf_ctx_set_data(cf, data);
- Curl_ssl->detach_data(cf, data);
- cf_ctx_set_data(cf, NULL);
+ struct cf_call_data save;
+ int result;
+ /*
+ * This function tries to determine connection status.
+ *
+ * Return codes:
+ * 1 means the connection is still in place
+ * 0 means the connection has been closed
+ * -1 means the connection status is unknown
+ */
+ CF_DATA_SAVE(save, cf, data);
+ result = Curl_ssl->check_cxn(cf, data);
+ CF_DATA_RESTORE(cf, save);
+ if(result > 0) {
+ *input_pending = TRUE;
+ return TRUE;
+ }
+ if(result == 0) {
+ *input_pending = FALSE;
+ return FALSE;
}
+ /* ssl backend does not know */
+ return cf->next?
+ cf->next->cft->is_alive(cf->next, data, input_pending) :
+ FALSE; /* pessimistic in absence of data */
}
-static const struct Curl_cftype cft_ssl = {
+struct Curl_cftype Curl_cft_ssl = {
"SSL",
CF_TYPE_SSL,
+ CURL_LOG_DEFAULT,
ssl_cf_destroy,
- Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@@ -1621,15 +1692,17 @@ static const struct Curl_cftype cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_attach_data,
- ssl_cf_detach_data,
+ ssl_cf_cntrl,
+ cf_ssl_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ ssl_cf_query,
};
-static const struct Curl_cftype cft_ssl_proxy = {
+struct Curl_cftype Curl_cft_ssl_proxy = {
"SSL-PROXY",
CF_TYPE_SSL,
+ CURL_LOG_DEFAULT,
ssl_cf_destroy,
- Curl_cf_def_setup,
ssl_cf_connect,
ssl_cf_close,
Curl_cf_def_get_host,
@@ -1637,65 +1710,107 @@ static const struct Curl_cftype cft_ssl_proxy = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_attach_data,
- ssl_cf_detach_data,
+ ssl_cf_cntrl,
+ cf_ssl_is_alive,
+ Curl_cf_def_conn_keep_alive,
+ Curl_cf_def_query,
};
-CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
- struct Curl_cfilter *cf;
+ struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
DEBUGASSERT(data->conn);
- ctx = cf_ctx_new(data);
+
+ ctx = cf_ctx_new(data, Curl_alpn_get_spec(data, conn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- result = Curl_cf_create(&cf, &cft_ssl, ctx);
- if(result)
- goto out;
-
- Curl_conn_cf_add(data, conn, sockindex, cf);
-
- result = CURLE_OK;
+ result = Curl_cf_create(&cf, &Curl_cft_ssl, ctx);
out:
if(result)
cf_ctx_free(ctx);
+ *pcf = result? NULL : cf;
return result;
}
-#ifndef CURL_DISABLE_PROXY
-CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
+CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_create(&cf, data, conn);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
+
+CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
{
struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_create(&cf, data, cf_at->conn);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
+ return result;
+}
+
+#ifndef CURL_DISABLE_PROXY
+static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct Curl_cfilter *cf = NULL;
struct ssl_connect_data *ctx;
CURLcode result;
- ctx = cf_ctx_new(data);
+ ctx = cf_ctx_new(data, Curl_alpn_get_proxy_spec(data, conn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
+ result = Curl_cf_create(&cf, &Curl_cft_ssl_proxy, ctx);
- result = Curl_cf_create(&cf, &cft_ssl_proxy, ctx);
+out:
if(result)
- goto out;
+ cf_ctx_free(ctx);
+ *pcf = result? NULL : cf;
+ return result;
+}
- Curl_conn_cf_add(data, conn, sockindex, cf);
+CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
- result = CURLE_OK;
+ result = cf_ssl_proxy_create(&cf, data, conn);
+ if(!result)
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ return result;
+}
-out:
- if(result)
- cf_ctx_free(ctx);
+CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result;
+
+ result = cf_ssl_proxy_create(&cf, data, cf_at->conn);
+ if(!result)
+ Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
@@ -1717,9 +1832,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
/* get first filter in chain, if any is present */
cf = Curl_ssl_cf_get_ssl(data->conn->cfilter[sockindex]);
if(cf) {
- cf_ctx_set_data(cf, data);
+ struct cf_call_data save;
+ CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->get_internals(cf->ctx, info);
- cf_ctx_set_data(cf, NULL);
+ CF_DATA_RESTORE(cf, save);
}
}
return result;
@@ -1733,7 +1849,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
(void)data;
for(; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl) {
+ if(cf->cft == &Curl_cft_ssl) {
if(Curl_ssl->shut_down(cf, data))
result = CURLE_SSL_SHUTDOWN_FAILED;
Curl_conn_cf_discard(cf, data);
@@ -1749,7 +1865,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
struct Curl_cfilter *cf, *lowest_ssl_cf = NULL;
for(cf = conn->cfilter[sockindex]; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy) {
+ if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy) {
lowest_ssl_cf = cf;
if(cf->connected || (cf->next && cf->next->connected)) {
/* connected or about to start */
@@ -1762,7 +1878,7 @@ static struct Curl_cfilter *get_ssl_cf_engaged(struct connectdata *conn,
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
{
- return (cf->cft == &cft_ssl_proxy);
+ return (cf->cft == &Curl_cft_ssl_proxy);
}
struct ssl_config_data *
@@ -1814,10 +1930,142 @@ Curl_ssl_get_primary_config(struct Curl_easy *data,
struct Curl_cfilter *Curl_ssl_cf_get_ssl(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
- if(cf->cft == &cft_ssl || cf->cft == &cft_ssl_proxy)
+ if(cf->cft == &Curl_cft_ssl || cf->cft == &Curl_cft_ssl_proxy)
return cf;
}
return NULL;
}
+static const struct alpn_spec ALPN_SPEC_H10 = {
+ { ALPN_HTTP_1_0 }, 1
+};
+static const struct alpn_spec ALPN_SPEC_H11 = {
+ { ALPN_HTTP_1_1 }, 1
+};
+#ifdef USE_HTTP2
+static const struct alpn_spec ALPN_SPEC_H2_H11 = {
+ { ALPN_H2, ALPN_HTTP_1_1 }, 2
+};
+#endif
+
+const struct alpn_spec *
+Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(!conn->bits.tls_enable_alpn)
+ return NULL;
+ if(data->state.httpwant == CURL_HTTP_VERSION_1_0)
+ return &ALPN_SPEC_H10;
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2)
+ return &ALPN_SPEC_H2_H11;
+#endif
+ return &ALPN_SPEC_H11;
+}
+
+const struct alpn_spec *
+Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn)
+{
+ if(!conn->bits.tls_enable_alpn)
+ return NULL;
+ if(data->state.httpwant == CURL_HTTP_VERSION_1_0)
+ return &ALPN_SPEC_H10;
+ return &ALPN_SPEC_H11;
+}
+
+CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec)
+{
+ size_t i, len;
+ int off = 0;
+ unsigned char blen;
+
+ memset(buf, 0, sizeof(*buf));
+ for(i = 0; spec && i < spec->count; ++i) {
+ len = strlen(spec->entries[i]);
+ if(len >= ALPN_NAME_MAX)
+ return CURLE_FAILED_INIT;
+ blen = (unsigned char)len;
+ if(off + blen + 1 >= (int)sizeof(buf->data))
+ return CURLE_FAILED_INIT;
+ buf->data[off++] = blen;
+ memcpy(buf->data + off, spec->entries[i], blen);
+ off += blen;
+ }
+ buf->len = off;
+ return CURLE_OK;
+}
+
+CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec)
+{
+ size_t i, len;
+ size_t off = 0;
+
+ memset(buf, 0, sizeof(*buf));
+ for(i = 0; spec && i < spec->count; ++i) {
+ len = strlen(spec->entries[i]);
+ if(len >= ALPN_NAME_MAX)
+ return CURLE_FAILED_INIT;
+ if(off + len + 2 >= (int)sizeof(buf->data))
+ return CURLE_FAILED_INIT;
+ if(off)
+ buf->data[off++] = ',';
+ memcpy(buf->data + off, spec->entries[i], len);
+ off += len;
+ }
+ buf->data[off] = '\0';
+ buf->len = (int)off;
+ return CURLE_OK;
+}
+
+CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const unsigned char *proto,
+ size_t proto_len)
+{
+ int can_multi = 0;
+
+ if(proto && proto_len) {
+ if(proto_len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_1;
+ }
+ else if(proto_len == ALPN_HTTP_1_0_LENGTH &&
+ !memcmp(ALPN_HTTP_1_0, proto, ALPN_HTTP_1_0_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_1_0;
+ }
+#ifdef USE_HTTP2
+ else if(proto_len == ALPN_H2_LENGTH &&
+ !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_2;
+ can_multi = 1;
+ }
+#endif
+#ifdef USE_HTTP3
+ else if(proto_len == ALPN_H3_LENGTH &&
+ !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) {
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ can_multi = 1;
+ }
+#endif
+ else {
+ cf->conn->alpn = CURL_HTTP_VERSION_NONE;
+ failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
+ /* TODO: do we want to fail this? Previous code just ignored it and
+ * some vtls backends even ignore the return code of this function. */
+ /* return CURLE_NOT_BUILT_IN; */
+ goto out;
+ }
+ infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto);
+ }
+ else {
+ cf->conn->alpn = CURL_HTTP_VERSION_NONE;
+ infof(data, VTLS_INFOF_NO_ALPN);
+ }
+
+out:
+ Curl_multiuse_state(data, can_multi? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ return CURLE_OK;
+}
+
#endif /* USE_SSL */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 5ad64fcf60..0d9e74a699 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -27,7 +27,6 @@
struct connectdata;
struct ssl_config_data;
-struct ssl_connect_data;
struct ssl_primary_config;
struct Curl_ssl_session;
@@ -53,6 +52,7 @@ struct Curl_ssl_session;
/* Curl_multi SSL backend-specific data; declared differently by each SSL
backend */
struct multi_ssl_backend_data;
+struct Curl_cfilter;
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@@ -68,8 +68,53 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
#define ALPN_HTTP_1_1_LENGTH 8
#define ALPN_HTTP_1_1 "http/1.1"
+#define ALPN_HTTP_1_0_LENGTH 8
+#define ALPN_HTTP_1_0 "http/1.0"
#define ALPN_H2_LENGTH 2
#define ALPN_H2 "h2"
+#define ALPN_H3_LENGTH 2
+#define ALPN_H3 "h3"
+
+/* conservative sizes on the ALPN entries and count we are handling,
+ * we can increase these if we ever feel the need or have to accommodate
+ * ALPN strings from the "outside". */
+#define ALPN_NAME_MAX 10
+#define ALPN_ENTRIES_MAX 3
+#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1))
+
+struct alpn_spec {
+ const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
+ size_t count; /* number of entries */
+};
+
+struct alpn_proto_buf {
+ unsigned char data[ALPN_PROTO_BUF_MAX];
+ int len;
+};
+
+CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec);
+CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
+ const struct alpn_spec *spec);
+
+CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const unsigned char *proto,
+ size_t proto_len);
+
+/**
+ * Get the ALPN specification to use for talking to remote host.
+ * May return NULL if ALPN is disabled on the connection.
+ */
+const struct alpn_spec *
+Curl_alpn_get_spec(struct Curl_easy *data, struct connectdata *conn);
+
+/**
+ * Get the ALPN specification to use for talking to the proxy.
+ * May return NULL if ALPN is disabled on the connection.
+ */
+const struct alpn_spec *
+Curl_alpn_get_proxy_spec(struct Curl_easy *data, struct connectdata *conn);
char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen);
@@ -95,7 +140,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
/* init the SSL session ID cache */
CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
-int Curl_ssl_check_cxn(struct Curl_easy *data, struct connectdata *conn);
/* Certificate information list handling. */
@@ -156,6 +200,9 @@ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
+
CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
int sockindex);
@@ -163,6 +210,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
CURLcode Curl_ssl_cfilter_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
+CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
+ struct Curl_easy *data);
#endif /* !CURL_DISABLE_PROXY */
/**
@@ -208,6 +257,9 @@ bool Curl_ssl_supports(struct Curl_easy *data, int ssl_option);
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n);
+extern struct Curl_cftype Curl_cft_ssl;
+extern struct Curl_cftype Curl_cft_ssl_proxy;
+
#else /* if not USE_SSL */
/* When SSL support is not present, just define away these function calls */
@@ -218,7 +270,6 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
#define Curl_ssl_initsessions(x,y) CURLE_OK
-#define Curl_ssl_check_cxn(d,x) 0
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h
index 6710a2b559..a20ca7db7f 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_int.h
+++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,18 +33,20 @@
struct ssl_connect_data {
ssl_connection_state state;
ssl_connect_state connecting_state;
- const char *hostname; /* hostnaem for verification */
- const char *dispname; /* display version of hostname */
+ char *hostname; /* hostname for verification */
+ char *dispname; /* display version of hostname */
int port; /* remote port at origin */
+ const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
struct ssl_backend_data *backend; /* vtls backend specific props */
- struct Curl_easy *call_data; /* data handle used in current call,
- * same as parameter passed, but available
- * here for backend internal callbacks
- * that need it. NULLed after at the
- * end of each vtls filter invcocation. */
+ struct cf_call_data call_data; /* data handle used in current call */
+ struct curltime handshake_done; /* time when handshake finished */
};
+#define CF_CTX_CALL_DATA(cf) \
+ ((struct ssl_connect_data *)(cf)->ctx)->call_data
+
+
/* Definitions for SSL Implementations */
struct Curl_ssl {
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index 7cc4774e83..ac68eabab6 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -94,6 +94,7 @@
struct ssl_backend_data {
SSL_CTX* ctx;
SSL* handle;
+ CURLcode io_result; /* result of last BIO cfilter operation */
};
#ifdef OPENSSL_EXTRA
@@ -218,29 +219,9 @@ static const struct group_name_map gnm[] = {
{ WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
{ WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
{ WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
- { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" },
- { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" },
- { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" },
- { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" },
- { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" },
- { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" },
- { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" },
- { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" },
- { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" },
- { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" },
- { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" },
- { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" },
- { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" },
- { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" },
- { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" },
- { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" },
- { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" },
{ WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
{ WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
{ WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
- { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" },
- { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" },
- { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" },
{ 0, NULL }
};
#endif
@@ -300,12 +281,15 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
+ connssl->backend->io_result = result;
+ DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
+ blen, nwritten, result));
wolfSSL_BIO_clear_retry_flags(bio);
if(nwritten < 0 && CURLE_AGAIN == result)
BIO_set_retry_read(bio);
@@ -316,7 +300,7 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct Curl_easy *data = connssl->call_data;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_OK;
@@ -326,6 +310,9 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
return 0;
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ connssl->backend->io_result = result;
+ DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d",
+ blen, nread, result));
wolfSSL_BIO_clear_retry_flags(bio);
if(nread < 0 && CURLE_AGAIN == result)
BIO_set_retry_read(bio);
@@ -633,29 +620,18 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
#ifdef HAVE_ALPN
- if(cf->conn->bits.tls_enable_alpn) {
- char protocols[128];
- *protocols = '\0';
-
- /* wolfSSL's ALPN protocol name list format is a comma separated string of
- protocols in descending order of preference, eg: "h2,http/1.1" */
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
- strcpy(protocols + strlen(protocols), ALPN_H2 ",");
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- }
-#endif
-
- strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+ if(connssl->alpn) {
+ struct alpn_proto_buf proto;
+ CURLcode result;
- if(wolfSSL_UseALPN(backend->handle, protocols,
- (unsigned)strlen(protocols),
+ result = Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ if(result ||
+ wolfSSL_UseALPN(backend->handle, (char *)proto.data, proto.len,
WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) {
failf(data, "SSL: failed setting ALPN protocols");
return CURLE_SSL_CONNECT_ERROR;
}
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif /* HAVE_ALPN */
@@ -707,7 +683,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
#else /* USE_BIO_CHAIN */
/* pass the raw socket into the SSL layer */
- if(!SSL_set_fd(backend->handle, (int)cf->conn->sock[cf->sockindex])) {
+ if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) {
failf(data, "SSL: SSL_set_fd failed");
return CURLE_SSL_CONNECT_ERROR;
}
@@ -822,6 +798,9 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
#endif
+ else if(backend->io_result == CURLE_AGAIN) {
+ return CURLE_OK;
+ }
else {
failf(data, "SSL_connect failed with error %d: %s", detail,
ERR_error_string(detail, error_buffer));
@@ -883,25 +862,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
if(rc == SSL_SUCCESS) {
- infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, protocol_len, protocol);
-
- if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
- !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
- cf->conn->alpn = CURL_HTTP_VERSION_1_1;
-#ifdef USE_HTTP2
- else if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
- protocol_len == ALPN_H2_LENGTH &&
- !memcmp(protocol, ALPN_H2, ALPN_H2_LENGTH))
- cf->conn->alpn = CURL_HTTP_VERSION_2;
-#endif
- else
- infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
- protocol);
- Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ?
- BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol,
+ protocol_len);
}
else if(rc == SSL_ALPN_NOT_FOUND)
- infof(data, VTLS_INFOF_NO_ALPN);
+ Curl_alpn_set_negotiated(cf, data, NULL, 0);
else {
failf(data, "ALPN, failure getting protocol, error %d", rc);
return CURLE_SSL_CONNECT_ERROR;
@@ -995,7 +960,6 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
ERR_clear_error();
rc = SSL_write(backend->handle, mem, memlen);
-
if(rc <= 0) {
int err = SSL_get_error(backend->handle, rc);
@@ -1003,9 +967,17 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_write() */
+ DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len));
*curlcode = CURLE_AGAIN;
return -1;
default:
+ if(backend->io_result == CURLE_AGAIN) {
+ DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len));
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
+ DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d",
+ len, rc, err));
failf(data, "SSL write: %s, errno %d",
ERR_error_string(err, error_buffer),
SOCKERRNO);
@@ -1013,6 +985,7 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
return -1;
}
}
+ DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc));
return rc;
}
@@ -1042,19 +1015,19 @@ static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
struct Curl_easy *data,
- char *buf,
- size_t buffersize,
+ char *buf, size_t blen,
CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
struct ssl_backend_data *backend = connssl->backend;
char error_buffer[WOLFSSL_MAX_ERROR_SZ];
- int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
int nread;
DEBUGASSERT(backend);
ERR_clear_error();
+ *curlcode = CURLE_OK;
nread = SSL_read(backend->handle, buf, buffsize);
@@ -1063,22 +1036,31 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
switch(err) {
case SSL_ERROR_ZERO_RETURN: /* no more data */
- break;
+ DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen));
+ *curlcode = CURLE_OK;
+ return 0;
case SSL_ERROR_NONE:
/* FALLTHROUGH */
case SSL_ERROR_WANT_READ:
/* FALLTHROUGH */
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
+ DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen));
*curlcode = CURLE_AGAIN;
return -1;
default:
+ if(backend->io_result == CURLE_AGAIN) {
+ DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen));
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
failf(data, "SSL read: %s, errno %d",
ERR_error_string(err, error_buffer), SOCKERRNO);
*curlcode = CURLE_RECV_ERROR;
return -1;
}
}
+ DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread));
return nread;
}
@@ -1166,7 +1148,7 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
int what;
/* check if the connection has already been established */
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h
index b2e7c3fde2..a5ed848099 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.h
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c
index 4c1c9a8b79..c298200452 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.c
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -48,6 +48,7 @@
#include "curl_ctype.h"
#include "hostcheck.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_int.h"
#include "sendf.h"
#include "inet_pton.h"
#include "curl_base64.h"
@@ -1117,7 +1118,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
version = (version << 8) | *(const unsigned char *) ccp;
if(data->set.ssl.certinfo) {
- ccp = curl_maprintf("%lx", version);
+ ccp = curl_maprintf("%x", version);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
@@ -1126,7 +1127,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
return result;
}
if(!certnum)
- infof(data, " Version: %lu (0x%lx)", version + 1, version);
+ infof(data, " Version: %u (0x%x)", version + 1, version);
/* Serial number. */
ccp = ASN1tostr(&cert.serialNumber, 0);
@@ -1313,7 +1314,8 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
/* Get the server IP address. */
#ifdef ENABLE_IPV6
- if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
+ if(cf->conn->bits.ipv6_ip &&
+ Curl_inet_pton(AF_INET6, connssl->hostname, &addr))
addrlen = sizeof(struct in6_addr);
else
#endif
@@ -1348,19 +1350,18 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
break;
switch(name.tag) {
case 2: /* DNS name. */
- matched = 0;
len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING,
name.beg, name.end);
- if(len > 0) {
- if(size_t)len == strlen(dnsname)
- matched = Curl_cert_hostcheck(dnsname, (size_t)len,
- connssl->hostname, hostlen);
- free(dnsname);
- }
+ if(len > 0 && (size_t)len == strlen(dnsname))
+ matched = Curl_cert_hostcheck(dnsname, (size_t)len,
+ connssl->hostname, hostlen);
+ else
+ matched = 0;
+ free(dnsname);
break;
case 7: /* IP address. */
- matched = (name.end - name.beg) == addrlen &&
+ matched = (size_t)(name.end - name.beg) == addrlen &&
!memcmp(&addr, name.beg, addrlen);
break;
}
@@ -1406,8 +1407,10 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf,
failf(data, "SSL: unable to obtain common name from peer certificate");
else {
len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end);
- if(len < 0)
+ if(len < 0) {
+ free(dnsname);
return CURLE_OUT_OF_MEMORY;
+ }
if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */
failf(data, "SSL: illegal cert name field");
else if(Curl_cert_hostcheck((const char *) dnsname,
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h
index eb8e9597cb..5496de40e4 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.h
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -8,7 +8,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index b00d7a5a26..10c91fb28f 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h
index 4367099d92..99b243379e 100644
--- a/Utilities/cmcurl/lib/warnless.h
+++ b/Utilities/cmcurl/lib/warnless.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/Utilities/cmcurl/lib/wildcard.h b/Utilities/cmcurl/lib/wildcard.h
deleted file mode 100644
index 21e933b9a4..0000000000
--- a/Utilities/cmcurl/lib/wildcard.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef HEADER_CURL_WILDCARD_H
-#define HEADER_CURL_WILDCARD_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 2010 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-#include <curl/curl.h>
-#include "llist.h"
-
-/* list of wildcard process states */
-typedef enum {
- CURLWC_CLEAR = 0,
- CURLWC_INIT = 1,
- CURLWC_MATCHING, /* library is trying to get list of addresses for
- downloading */
- CURLWC_DOWNLOADING,
- CURLWC_CLEAN, /* deallocate resources and reset settings */
- CURLWC_SKIP, /* skip over concrete file */
- CURLWC_ERROR, /* error cases */
- CURLWC_DONE /* if is wildcard->state == CURLWC_DONE wildcard loop
- will end */
-} wildcard_states;
-
-typedef void (*wildcard_dtor)(void *ptr);
-
-/* struct keeping information about wildcard download process */
-struct WildcardData {
- wildcard_states state;
- char *path; /* path to the directory, where we trying wildcard-match */
- char *pattern; /* wildcard pattern */
- struct Curl_llist filelist; /* llist with struct Curl_fileinfo */
- void *protdata; /* pointer to protocol specific temporary data */
- wildcard_dtor dtor;
- void *customptr; /* for CURLOPT_CHUNK_DATA pointer */
-};
-
-CURLcode Curl_wildcard_init(struct WildcardData *wc);
-void Curl_wildcard_dtor(struct WildcardData *wc);
-
-struct Curl_easy;
-
-#else
-/* FTP is disabled */
-#define Curl_wildcard_dtor(x)
-#endif
-
-#endif /* HEADER_CURL_WILDCARD_H */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
index c1b2622a71..e8495dcfc0 100644
--- a/Utilities/cmcurl/lib/ws.c
+++ b/Utilities/cmcurl/lib/ws.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -115,12 +115,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
return result;
}
-CURLcode Curl_ws_accept(struct Curl_easy *data)
+/*
+ * 'nread' is number of bytes of websocket data already in the buffer at
+ * 'mem'.
+ */
+CURLcode Curl_ws_accept(struct Curl_easy *data,
+ const char *mem, size_t nread)
{
struct SingleRequest *k = &data->req;
struct HTTP *ws = data->req.p.http;
struct connectdata *conn = data->conn;
struct websocket *wsp = &data->req.p.http->ws;
+ struct ws_conn *wsc = &conn->proto.ws;
CURLcode result;
/* Verify the Sec-WebSocket-Accept response.
@@ -149,13 +155,17 @@ CURLcode Curl_ws_accept(struct Curl_easy *data)
infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
+ Curl_dyn_init(&wsc->early, data->set.buffer_size);
+ if(nread) {
+ result = Curl_dyn_addn(&wsc->early, mem, nread);
+ if(result)
+ return result;
+ infof(data, "%zu bytes websocket payload", nread);
+ wsp->stillb = Curl_dyn_ptr(&wsc->early);
+ wsp->stillblen = Curl_dyn_len(&wsc->early);
+ }
k->upgr101 = UPGR101_RECEIVED;
- if(data->set.connect_only)
- /* switch off non-blocking sockets */
- (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE);
-
- wsp->oleft = 0;
return result;
}
@@ -183,12 +193,12 @@ static void ws_decode_shift(struct Curl_easy *data, size_t spent)
/* ws_decode() decodes a binary frame into structured WebSocket data,
- wpkt - the incoming raw data. If NULL, work on the already buffered data.
- ilen - the size of the provided data, perhaps too little, perhaps too much
- out - stored pointed to extracted data
+ data - the transfer
+ inbuf - incoming raw data. If NULL, work on the already buffered data.
+ inlen - size of the provided data, perhaps too little, perhaps too much
+ headlen - stored length of the frame header
olen - stored length of the extracted data
oleft - number of unread bytes pending to that belongs to this frame
- more - if there is more data in there
flags - stored bitmask about the frame
Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
@@ -246,6 +256,9 @@ static CURLcode ws_decode(struct Curl_easy *data,
infof(data, "WS: received OPCODE PONG");
*flags |= CURLWS_PONG;
break;
+ default:
+ failf(data, "WS: unknown opcode: %x", opcode);
+ return CURLE_RECV_ERROR;
}
if(inbuf[1] & WSBIT_MASK) {
@@ -296,7 +309,7 @@ static CURLcode ws_decode(struct Curl_easy *data,
*oleft = 0; /* bytes yet to come (for this frame) */
}
- infof(data, "WS: received %zu bytes payload (%zu left, buflen was %zu)",
+ infof(data, "WS: received %Ou bytes payload (%Ou left, buflen was %zu)",
payloadsize, *oleft, inlen);
return CURLE_OK;
}
@@ -339,9 +352,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
result = ws_decode(data, wsbuf, buflen,
&headlen, &write_len, &fb_left, &recvflags);
- consumed += headlen;
- wsbuf += headlen;
- buflen -= headlen;
if(result == CURLE_AGAIN)
/* insufficient amount of data, keep it for later.
* we pretend to have written all since we have a copy */
@@ -350,6 +360,10 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
infof(data, "WS: decode error %d", (int)result);
return nitems - 1;
}
+ consumed += headlen;
+ wsbuf += headlen;
+ buflen -= headlen;
+
/* New frame. store details about the frame to be reachable with
curl_ws_meta() from within the write callback */
ws->ws.frame.age = 0;
@@ -392,7 +406,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
return nitems;
}
-
CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
size_t buflen, size_t *nread,
struct curl_ws_frame **metap)
@@ -409,7 +422,7 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
return result;
while(!done) {
- size_t write_len;
+ size_t datalen;
unsigned int recvflags;
if(!wsp->stillblen) {
@@ -419,26 +432,32 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
data->set.buffer_size, &n);
if(result)
return result;
- if(!n)
+ if(!n) {
/* connection closed */
+ infof(data, "connection expectedly closed?");
return CURLE_GOT_NOTHING;
+ }
wsp->stillb = data->state.buffer;
wsp->stillblen = n;
}
- infof(data, "WS: got %u websocket bytes to decode",
- (int)wsp->stillblen);
+ infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen);
if(!wsp->frame.bytesleft) {
size_t headlen;
curl_off_t oleft;
/* detect new frame */
result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
- &headlen, &write_len, &oleft, &recvflags);
+ &headlen, &datalen, &oleft, &recvflags);
if(result == CURLE_AGAIN)
/* a packet fragment only */
break;
else if(result)
return result;
+ if(datalen > buflen) {
+ size_t diff = datalen - buflen;
+ datalen = buflen;
+ oleft += diff;
+ }
wsp->stillb += headlen;
wsp->stillblen -= headlen;
wsp->frame.offset = 0;
@@ -447,40 +466,45 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
}
else {
/* existing frame, remaining payload handling */
- write_len = wsp->frame.bytesleft;
- if(write_len > wsp->stillblen)
- write_len = wsp->stillblen;
+ datalen = wsp->frame.bytesleft;
+ if(datalen > wsp->stillblen)
+ datalen = wsp->stillblen;
+ if(datalen > buflen)
+ datalen = buflen;
+
+ wsp->frame.offset += wsp->frame.len;
+ wsp->frame.bytesleft -= datalen;
}
+ wsp->frame.len = datalen;
/* auto-respond to PINGs */
if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
- infof(data, "WS: auto-respond to PING with a PONG");
+ size_t nsent = 0;
+ infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload",
+ datalen);
/* send back the exact same content as a PONG */
- result = curl_ws_send(data, wsp->stillb, write_len,
- &write_len, 0, CURLWS_PONG);
+ result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0,
+ CURLWS_PONG);
if(result)
return result;
+ infof(data, "WS: bytesleft %zu datalen %zu",
+ wsp->frame.bytesleft, datalen);
+ /* we handled the data part of the PING, advance over that */
+ wsp->stillb += nsent;
+ wsp->stillblen -= nsent;
}
- else if(write_len || !wsp->frame.bytesleft) {
- if(write_len > buflen)
- write_len = buflen;
+ else if(datalen) {
/* copy the payload to the user buffer */
- memcpy(buffer, wsp->stillb, write_len);
- *nread = write_len;
+ memcpy(buffer, wsp->stillb, datalen);
+ *nread = datalen;
done = TRUE;
- }
- if(write_len) {
- /* update buffer and frame info */
- wsp->frame.offset += write_len;
- DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len);
- if(wsp->frame.bytesleft)
- wsp->frame.bytesleft -= write_len;
- DEBUGASSERT(write_len <= wsp->stillblen);
- wsp->stillblen -= write_len;
+
+ wsp->stillblen -= datalen;
if(wsp->stillblen)
- wsp->stillb += write_len;
- else
+ wsp->stillb += datalen;
+ else {
wsp->stillb = NULL;
+ }
}
}
*metap = &wsp->frame;
@@ -555,17 +579,27 @@ static size_t ws_packethead(struct Curl_easy *data,
}
if(!(flags & CURLWS_CONT)) {
- /* if not marked as continuing, assume this is the final fragment */
- firstbyte |= WSBIT_FIN | opcode;
+ if(!ws->ws.contfragment)
+ /* not marked as continuing, this is the final fragment */
+ firstbyte |= WSBIT_FIN | opcode;
+ else
+ /* marked as continuing, this is the final fragment; set CONT
+ opcode and FIN bit */
+ firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
+
ws->ws.contfragment = FALSE;
+ infof(data, "WS: set FIN");
}
else if(ws->ws.contfragment) {
/* the previous fragment was not a final one and this isn't either, keep a
CONT opcode and no FIN bit */
firstbyte |= WSBIT_OPCODE_CONT;
+ infof(data, "WS: keep CONT, no FIN");
}
else {
+ firstbyte = opcode;
ws->ws.contfragment = TRUE;
+ infof(data, "WS: set CONT, no FIN");
}
out[0] = firstbyte;
if(len > 65535) {
@@ -686,8 +720,14 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
headlen + buflen, written);
- *sent = written;
+ if(!result) {
+ /* the *sent number only counts "payload", excluding the header */
+ if((size_t)written > headlen)
+ *sent = written - headlen;
+ else
+ *sent = 0;
+ }
return result;
}
@@ -698,6 +738,17 @@ void Curl_ws_done(struct Curl_easy *data)
Curl_dyn_free(&wsp->buf);
}
+CURLcode Curl_ws_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection)
+{
+ struct ws_conn *wsc = &conn->proto.ws;
+ (void)data;
+ (void)dead_connection;
+ Curl_dyn_free(&wsc->early);
+ return CURLE_OK;
+}
+
CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
{
/* we only return something for websocket, called from within the callback
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h
index 2f3ed2d1e6..176dda470b 100644
--- a/Utilities/cmcurl/lib/ws.h
+++ b/Utilities/cmcurl/lib/ws.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -46,24 +46,28 @@ struct websocket {
size_t usedbuf; /* number of leading bytes in 'buf' the most recent complete
websocket frame uses */
struct curl_ws_frame frame; /* the struct used for frame state */
- curl_off_t oleft; /* outstanding number of payload bytes left from the
- server */
size_t stillblen; /* number of bytes left in the buffer to deliver in
the next curl_ws_recv() call */
- char *stillb; /* the stillblen pending bytes are here */
+ const char *stillb; /* the stillblen pending bytes are here */
curl_off_t sleft; /* outstanding number of payload bytes left to send */
unsigned int xori; /* xor index */
};
-CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
-CURLcode Curl_ws_accept(struct Curl_easy *data);
+struct ws_conn {
+ struct dynbuf early; /* data already read when switching to ws */
+};
+CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
+CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
void Curl_ws_done(struct Curl_easy *data);
-
+CURLcode Curl_ws_disconnect(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool dead_connection);
#else
#define Curl_ws_request(x,y) CURLE_OK
#define Curl_ws_done(x) Curl_nop_stmt
+#define Curl_ws_free(x) Curl_nop_stmt
#endif
#endif /* HEADER_CURL_WS_H */
diff --git a/bootstrap b/bootstrap
index ea9816cf1d..a006aeee87 100755
--- a/bootstrap
+++ b/bootstrap
@@ -307,6 +307,7 @@ CMAKE_CXX_SOURCES="\
cmBuildCommand \
cmCMakeLanguageCommand \
cmCMakeMinimumRequired \
+ cmList \
cmCMakePath \
cmCMakePathCommand \
cmCMakePolicyCommand \
@@ -337,6 +338,7 @@ CMAKE_CXX_SOURCES="\
cmELF \
cmEnableLanguageCommand \
cmEnableTestingCommand \
+ cmEvaluatedTargetProperty \
cmExecProgramCommand \
cmExecuteProcessCommand \
cmExpandedCommandArgument \
@@ -569,7 +571,6 @@ KWSYS_FILES="\
RegularExpression.hxx \
Status.hxx \
String.h \
- String.hxx \
System.h \
SystemTools.hxx \
Terminal.h"
diff --git a/doxygen.config b/doxygen.config
deleted file mode 100644
index 82add731e6..0000000000
--- a/doxygen.config
+++ /dev/null
@@ -1,697 +0,0 @@
-# Doxyfile 1.1.4-20000625
-
-# This file describes the settings to be used by doxygen for a project
-#
-# All text after a hash (#) is considered a comment and will be ignored
-# The format is:
-# TAG = value [value, ...]
-# Values that contain spaces should be placed between quotes (" ")
-
-#---------------------------------------------------------------------------
-# General configuration options
-#---------------------------------------------------------------------------
-
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
-# by quotes) that should identify the project.
-
-PROJECT_NAME = CMAKE
-
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
-
-PROJECT_NUMBER = 0.0.1
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
-
-OUTPUT_DIRECTORY = ./Doxygen
-
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all
-# documentation generated by doxygen is written. Doxygen will use this
-# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# Dutch, French, Italian, Czech, Swedish, German, Finnish, Japanese,
-# Spanish and Russian
-
-OUTPUT_LANGUAGE = English
-
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
-# top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it.
-
-DISABLE_INDEX = NO
-
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
-# documentation are documented, even if no documentation was available.
-# Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
-EXTRACT_ALL = YES
-
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
-
-EXTRACT_PRIVATE = NO
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
-
-EXTRACT_STATIC = YES
-
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_MEMBERS = NO
-
-# If the HIDE_UNDOC_CLASSESS tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these class will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
-
-HIDE_UNDOC_CLASSES = NO
-
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
-
-BRIEF_MEMBER_DESC = YES
-
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
-# brief descriptions will be completely suppressed.
-
-REPEAT_BRIEF = YES
-
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
-# description.
-
-ALWAYS_DETAILED_SEC = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
-
-FULL_PATH_NAMES = NO
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user defined part of the path. Stripping is
-# only done if one of the specified strings matches the left-hand part of
-# the path.
-
-STRIP_FROM_PATH =
-
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
-
-INTERNAL_DOCS = NO
-
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a class diagram (in Html and LaTeX) for classes with base or
-# super classes. Setting the tag to NO turns the diagrams off.
-
-CLASS_DIAGRAMS = YES
-
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-
-SOURCE_BROWSER = YES
-
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
-
-INLINE_SOURCES = NO
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
-STRIP_CODE_COMMENTS = YES
-
-# If the CASE_SENSE_NAMES tag is set to NO (the default) then Doxygen
-# will only generate file names in lower case letters. If set to
-# YES upper case letters are also allowed. This is useful if you have
-# classes or files whose names only differ in case and if your file system
-# supports case sensitive file names.
-
-CASE_SENSE_NAMES = YES
-
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
-
-HIDE_SCOPE_NAMES = NO
-
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
-
-VERBATIM_HEADERS = YES
-
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put list of the files that are included by a file in the documentation
-# of that file.
-
-SHOW_INCLUDE_FILES = YES
-
-# If the JAVADOC_AUTOBRIEF tag is set to YES (the default) then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the Javadoc-style will
-# behave just like the Qt-style comments.
-
-JAVADOC_AUTOBRIEF = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# reimplements.
-
-INHERIT_DOCS = YES
-
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
-
-INLINE_INFO = YES
-
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
-
-SORT_MEMBER_DOCS = YES
-
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
-
-TAB_SIZE = 8
-
-# The ENABLE_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
-
-ENABLED_SECTIONS =
-
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
-
-QUIET = NO
-
-# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
-
-WARNINGS = YES
-
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
-
-WARN_IF_UNDOCUMENTED = YES
-
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text.
-
-WARN_FORMAT = "$file:$line: $text"
-
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
-
-INPUT = "Source"
-
-# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-FILE_PATTERNS = *.h *.txx *.cxx
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
-
-RECURSIVE = YES
-
-# The EXCLUDE tag can be used to specify files and/or directories that should
-# excluded from the INPUT source files. This way you can easily exclude a
-# subdirectory from a directory tree whose root is specified with the INPUT tag.
-
-EXCLUDE =
-
-# If the value of the INPUT tag contains directories, you can use the
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
-
-EXCLUDE_PATTERNS =
-
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
-
-EXAMPLE_PATH =
-
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
-
-EXAMPLE_PATTERNS =
-
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
-
-IMAGE_PATH =
-
-# The INPUT_FILTER tag can be used to specify a program that doxygen should
-# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
-# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
-
-INPUT_FILTER =
-
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
-
-ALPHABETICAL_INDEX = YES
-
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
-
-COLS_IN_ALPHA_INDEX = 3
-
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
-
-IGNORE_PREFIX =
-
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
-
-GENERATE_HTML = YES
-
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
-
-HTML_OUTPUT = html
-
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header.
-
-HTML_HEADER =
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
-
-HTML_FOOTER =
-
-# The HTML_STYLESHEET tag can be used to specify a user defined cascading
-# style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet
-
-HTML_STYLESHEET =
-
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS = YES
-
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
-# of the generated HTML documentation.
-
-GENERATE_HTMLHELP = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
-
-GENERATE_LATEX = YES
-
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
-
-LATEX_OUTPUT = latex
-
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_LATEX = NO
-
-# The PAPER_TYPE tag can be used to set the paper type that is used
-# by the printer. Possible values are: a4, a4wide, letter, legal and
-# executive. If left blank a4wide will be used.
-
-PAPER_TYPE = a4wide
-
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
-
-EXTRA_PACKAGES =
-
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
-
-LATEX_HEADER =
-
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will
-# contain links (just like the HTML output) instead of page references
-# This makes the output suitable for online browsing using a pdf viewer.
-
-PDF_HYPERLINKS = NO
-
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
-
-LATEX_BATCHMODE = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# For now this is experimental and is disabled by default. The RTF output
-# is optimised for Word 97 and may not look too pretty with other readers
-# or editors.
-
-GENERATE_RTF = YES
-
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
-
-RTF_OUTPUT = rtf
-
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
-
-COMPACT_RTF = NO
-
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using a WORD or other.
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
-
-RTF_HYPERLINKS = NO
-
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
-
-GENERATE_MAN = YES
-
-# The MAN_OUTPUT tag is used to specify where the man pages will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `man' will be used as the default path.
-
-MAN_OUTPUT = man
-
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
-
-MAN_EXTENSION = .3
-
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
-#---------------------------------------------------------------------------
-
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
-
-ENABLE_PREPROCESSING = YES
-
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed.
-
-MACRO_EXPANSION = YES
-
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# in the INCLUDE_PATH (see below) will be search if a #include is found.
-
-SEARCH_INCLUDES = NO
-
-# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
-
-INCLUDE_PATH =
-
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed.
-
-PREDEFINED = "itkNotUsed(x)="\
- "itkSetMacro(name,type)= \
- virtual void Set##name (type _arg);" \
- "itkGetMacro(name,type)= \
- virtual type Get##name ();" \
- "itkGetConstMacro(name,type)= \
- virtual type Get##name () const;" \
- "itkSetStringMacro(name)= \
- virtual void Set##name (const char* _arg);" \
- "itkGetStringMacro(name)= \
- virtual const char* Get##name () const;" \
- "itkSetClampMacro(name,type,min,max)= \
- virtual void Set##name (type _arg);" \
- "itkSetObjectMacro(name,type)= \
- virtual void Set##name (type* _arg);" \
- "itkGetObjectMacro(name,type)= \
- virtual type* Get##name ();" \
- "itkBooleanMacro(name)= \
- virtual void name##On (); \
- virtual void name##Off ();" \
- "itkSetVector2Macro(name,type)= \
- virtual void Set##name (type _arg1, type _arg2) \
- virtual void Set##name (type _arg[2]);" \
- "itkGetVector2Macro(name,type)= \
- virtual type* Get##name () const; \
- virtual void Get##name (type& _arg1, type& _arg2) const; \
- virtual void Get##name (type _arg[2]) const;" \
- "itkSetVector3Macro(name,type)= \
- virtual void Set##name (type _arg1, type _arg2, type _arg3) \
- virtual void Set##name (type _arg[3]);" \
- "itkGetVector3Macro(name,type)= \
- virtual type* Get##name () const; \
- virtual void Get##name (type& _arg1, type& _arg2, type& _arg3) const; \
- virtual void Get##name (type _arg[3]) const;" \
- "itkSetVector4Macro(name,type)= \
- virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4) \
- virtual void Set##name (type _arg[4]);" \
- "itkGetVector4Macro(name,type)= \
- virtual type* Get##name () const; \
- virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4) const; \
- virtual void Get##name (type _arg[4]) const;" \
- "itkSetVector6Macro(name,type)= \
- virtual void Set##name (type _arg1, type _arg2, type _arg3, type _arg4, type _arg5, type _arg6) \
- virtual void Set##name (type _arg[6]);" \
- "itkGetVector6Macro(name,type)= \
- virtual type* Get##name () const; \
- virtual void Get##name (type& _arg1, type& _arg2, type& _arg3, type& _arg4, type& _arg5, type& _arg6) const; \
- virtual void Get##name (type _arg[6]) const;" \
- "itkSetVectorMacro(name,type,count)= \
- virtual void Set##name(type data[]);" \
- "itkGetVectorMacro(name,type,count)= \
- virtual type* Get##name () const;" \
- "itkNewMacro(type)= \
- static Pointer New();" \
- "itkTypeMacro(thisClass,superclass)= \
- virtual const char *GetClassName() const;" \
- "ITK_NUMERIC_LIMITS= \
- std::numeric_limits"
-
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED tag.
-
-EXPAND_ONLY_PREDEF = YES
-
-#---------------------------------------------------------------------------
-# Configuration::addtions related to external references
-#---------------------------------------------------------------------------
-
-# The TAGFILES tag can be used to specify one or more tagfiles.
-
-TAGFILES =
-
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
-
-GENERATE_TAGFILE =
-
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
-
-ALLEXTERNALS = NO
-
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of `which perl').
-
-PERL_PATH = /usr/bin/perl
-
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool
-#---------------------------------------------------------------------------
-
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
-
-HAVE_DOT = YES
-
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# the CLASS_DIAGRAMS tag to NO.
-
-CLASS_GRAPH = YES
-
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
-
-COLLABORATION_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, INCLUDE_GRAPH, and HAVE_DOT tags are set to
-# YES then doxygen will generate a graph for each documented file showing
-# the direct and indirect include dependencies of the file with other
-# documented files.
-
-INCLUDE_GRAPH = YES
-
-# If the ENABLE_PREPROCESSING, INCLUDED_BY_GRAPH, and HAVE_DOT tags are set to
-# YES then doxygen will generate a graph for each documented header file showing
-# the documented files that directly or indirectly include this file
-
-INCLUDED_BY_GRAPH = YES
-
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will graphical hierarchy of all classes instead of a textual one.
-
-GRAPHICAL_HIERARCHY = YES
-
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found on the path.
-
-DOT_PATH =
-
-# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than
-# this value, doxygen will try to truncate the graph, so that it fits within
-# the specified constraint. Beware that most browsers cannot cope with very
-# large images.
-
-MAX_DOT_GRAPH_WIDTH = 1024
-
-# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than
-# this value, doxygen will try to truncate the graph, so that it fits within
-# the specified constraint. Beware that most browsers cannot cope with very
-# large images.
-
-MAX_DOT_GRAPH_HEIGHT = 1024
-
-#---------------------------------------------------------------------------
-# Configuration::addtions related to the search engine
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE = NO
-
-# The CGI_NAME tag should be the name of the CGI script that
-# starts the search engine (doxysearch) with the correct parameters.
-# A script with this name will be generated by doxygen.
-
-CGI_NAME = search.cgi
-
-# The CGI_URL tag should be the absolute URL to the directory where the
-# cgi binaries are located. See the documentation of your http daemon for
-# details.
-
-CGI_URL =
-
-# The DOC_URL tag should be the absolute URL to the directory where the
-# documentation is located. If left blank the absolute path to the
-# documentation, with file:// prepended to it, will be used.
-
-DOC_URL =
-
-# The DOC_ABSPATH tag should be the absolute path to the directory where the
-# documentation is located. If left blank the directory on the local machine
-# will be used.
-
-DOC_ABSPATH =
-
-# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
-# is installed.
-
-BIN_ABSPATH = /usr/local/bin/
-
-# The EXT_DOC_PATHS tag can be used to specify one or more paths to
-# documentation generated for other projects. This allows doxysearch to search
-# the documentation for these projects as well.
-
-EXT_DOC_PATHS =