Imported Upstream version 3.25.0 upstream/3.25.0
authorJinWang An <jinwang.an@samsung.com>
Tue, 27 Dec 2022 08:20:24 +0000 (17:20 +0900)
committerJinWang An <jinwang.an@samsung.com>
Tue, 27 Dec 2022 08:20:24 +0000 (17:20 +0900)
2039 files changed:
Auxiliary/cmake-mode.el
Auxiliary/vim/indent/cmake.vim
Auxiliary/vim/syntax/cmake.vim
CMakeLists.txt
CTestCustom.cmake.in
CompileFlags.cmake
Copyright.txt
Help/command/FIND_XXX.txt
Help/command/add_custom_command.rst
Help/command/add_custom_target.rst
Help/command/add_subdirectory.rst
Help/command/block.rst [new file with mode: 0644]
Help/command/build_command.rst
Help/command/cmake_language.rst
Help/command/cmake_policy.rst
Help/command/continue.rst
Help/command/ctest_build.rst
Help/command/ctest_run_script.rst
Help/command/ctest_start.rst
Help/command/ctest_test.rst
Help/command/enable_language.rst
Help/command/endblock.rst [new file with mode: 0644]
Help/command/export.rst
Help/command/file.rst
Help/command/find_package.rst
Help/command/foreach.rst
Help/command/function.rst
Help/command/get_test_property.rst
Help/command/if.rst
Help/command/install.rst
Help/command/message.rst
Help/command/return.rst
Help/command/set.rst
Help/command/target_compile_definitions.rst
Help/command/target_compile_options.rst
Help/command/target_include_directories.rst
Help/command/target_link_directories.rst
Help/command/target_link_libraries.rst
Help/command/target_link_options.rst
Help/command/target_precompile_headers.rst
Help/command/target_sources.rst
Help/command/try_compile.rst
Help/command/try_run.rst
Help/command/while.rst
Help/cpack_gen/archive.rst
Help/cpack_gen/deb.rst
Help/cpack_gen/external.rst
Help/cpack_gen/freebsd.rst
Help/cpack_gen/ifw.rst
Help/cpack_gen/nsis.rst
Help/cpack_gen/nuget.rst
Help/cpack_gen/rpm.rst
Help/cpack_gen/wix.rst
Help/dev/experimental.rst
Help/envvar/ASM_DIALECTFLAGS.rst
Help/envvar/CFLAGS.rst
Help/envvar/CMAKE_GENERATOR.rst
Help/envvar/CMAKE_GENERATOR_PLATFORM.rst
Help/envvar/CMAKE_GENERATOR_TOOLSET.rst
Help/envvar/CSFLAGS.rst
Help/envvar/CTEST_PROGRESS_OUTPUT.rst
Help/envvar/CUDAFLAGS.rst
Help/envvar/CXXFLAGS.rst
Help/envvar/DESTDIR.rst
Help/envvar/FFLAGS.rst
Help/envvar/HIPFLAGS.rst
Help/envvar/ISPCFLAGS.rst
Help/envvar/LANG_FLAGS.txt [new file with mode: 0644]
Help/envvar/RCFLAGS.rst
Help/envvar/SSL_CERT_DIR.rst [new file with mode: 0644]
Help/envvar/SSL_CERT_FILE.rst [new file with mode: 0644]
Help/generator/Green Hills MULTI.rst
Help/generator/Ninja Multi-Config.rst
Help/generator/Visual Studio 10 2010.rst
Help/generator/Visual Studio 11 2012.rst
Help/generator/Visual Studio 12 2013.rst
Help/generator/Visual Studio 14 2015.rst
Help/generator/Visual Studio 15 2017.rst
Help/generator/Visual Studio 16 2019.rst
Help/generator/Visual Studio 17 2022.rst
Help/generator/Visual Studio 9 2008.rst
Help/generator/Xcode.rst
Help/guide/ide-integration/index.rst
Help/guide/tutorial/A Basic Starting Point.rst
Help/guide/tutorial/Adding Export Configuration.rst
Help/guide/tutorial/Adding Generator Expressions.rst
Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
Help/guide/tutorial/Adding System Introspection.rst
Help/guide/tutorial/Adding Usage Requirements for a Library.rst
Help/guide/tutorial/Adding a Custom Command and Generated File.rst
Help/guide/tutorial/Adding a Library.rst
Help/guide/tutorial/Complete/CMakeLists.txt
Help/guide/tutorial/Complete/License.txt
Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Installing and Testing.rst
Help/guide/tutorial/Packaging Debug and Release.rst
Help/guide/tutorial/Packaging an Installer.rst
Help/guide/tutorial/Selecting Static or Shared Libraries.rst
Help/guide/tutorial/Step1/CMakeLists.txt [new file with mode: 0644]
Help/guide/tutorial/Step1/TutorialConfig.h.in [new file with mode: 0644]
Help/guide/tutorial/Step1/tutorial.cxx
Help/guide/tutorial/Step10/CMakeLists.txt
Help/guide/tutorial/Step10/License.txt
Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step10/MathFunctions/MathFunctions.h
Help/guide/tutorial/Step10/MathFunctions/mysqrt.cxx
Help/guide/tutorial/Step10/TutorialConfig.h.in
Help/guide/tutorial/Step10/tutorial.cxx
Help/guide/tutorial/Step11/CMakeLists.txt
Help/guide/tutorial/Step11/License.txt
Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step11/tutorial.cxx
Help/guide/tutorial/Step12/CMakeLists.txt
Help/guide/tutorial/Step12/License.txt
Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step2/CMakeLists.txt
Help/guide/tutorial/Step2/MathFunctions/CMakeLists.txt [new file with mode: 0644]
Help/guide/tutorial/Step2/TutorialConfig.h.in
Help/guide/tutorial/Step2/tutorial.cxx
Help/guide/tutorial/Step3/CMakeLists.txt
Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step4/CMakeLists.txt
Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step5/CMakeLists.txt
Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step6/CMakeLists.txt
Help/guide/tutorial/Step6/CTestConfig.cmake [new file with mode: 0644]
Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx [deleted file]
Help/guide/tutorial/Step6/MathFunctions/mysqrt.cxx
Help/guide/tutorial/Step7/CMakeLists.txt
Help/guide/tutorial/Step7/CTestConfig.cmake [new file with mode: 0644]
Help/guide/tutorial/Step7/License.txt [deleted file]
Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step7/MathFunctions/MakeTable.cxx [deleted file]
Help/guide/tutorial/Step7/MathFunctions/mysqrt.cxx
Help/guide/tutorial/Step8/CMakeLists.txt
Help/guide/tutorial/Step8/License.txt [deleted file]
Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step8/MathFunctions/mysqrt.cxx
Help/guide/tutorial/Step9/CMakeLists.txt
Help/guide/tutorial/Step9/License.txt
Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx [deleted file]
Help/guide/tutorial/Step9/MathFunctions/mysqrt.h [deleted file]
Help/guide/tutorial/index.rst
Help/guide/user-interaction/index.rst
Help/manual/OPTIONS_BUILD.txt
Help/manual/OPTIONS_HELP.txt
Help/manual/ccmake.1.rst
Help/manual/cmake-buildsystem.7.rst
Help/manual/cmake-commands.7.rst
Help/manual/cmake-env-variables.7.rst
Help/manual/cmake-generator-expressions.7.rst
Help/manual/cmake-generators.7.rst
Help/manual/cmake-gui.1.rst
Help/manual/cmake-language.7.rst
Help/manual/cmake-modules.7.rst
Help/manual/cmake-policies.7.rst
Help/manual/cmake-presets.7.rst
Help/manual/cmake-properties.7.rst
Help/manual/cmake-qt.7.rst
Help/manual/cmake-toolchains.7.rst
Help/manual/cmake-variables.7.rst
Help/manual/cmake.1.rst
Help/manual/cpack.1.rst
Help/manual/ctest.1.rst
Help/manual/presets/example.json
Help/manual/presets/schema.json
Help/module/FindOpenSP.rst [new file with mode: 0644]
Help/module/FindSDL_gfx.rst [new file with mode: 0644]
Help/policy/CMP0058.rst
Help/policy/CMP0140.rst [new file with mode: 0644]
Help/policy/CMP0141.rst [new file with mode: 0644]
Help/policy/CMP0142.rst [new file with mode: 0644]
Help/prop_dir/SYSTEM.rst [new file with mode: 0644]
Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst
Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst
Help/prop_inst/CPACK_START_MENU_SHORTCUTS.rst
Help/prop_test/FIXTURES_REQUIRED.rst
Help/prop_test/LABELS.rst
Help/prop_tgt/COMPILE_WARNING_AS_ERROR.rst
Help/prop_tgt/CONFIG_POSTFIX.rst
Help/prop_tgt/CUDA_STANDARD.rst
Help/prop_tgt/CXX_MODULE_DIRS.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_SET.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_SETS.rst [new file with mode: 0644]
Help/prop_tgt/CXX_MODULE_SET_NAME.rst [new file with mode: 0644]
Help/prop_tgt/CXX_STANDARD.rst
Help/prop_tgt/EXPORT_NO_SYSTEM.rst [new file with mode: 0644]
Help/prop_tgt/HIP_STANDARD.rst
Help/prop_tgt/IMPORTED_NO_SYSTEM.rst
Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst [new file with mode: 0644]
Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst [new file with mode: 0644]
Help/prop_tgt/LANG_CLANG_TIDY.rst
Help/prop_tgt/LANG_COMPILER_LAUNCHER.rst
Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt [new file with mode: 0644]
Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst [new file with mode: 0644]
Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst
Help/prop_tgt/OBJCXX_STANDARD.rst
Help/prop_tgt/SYSTEM.rst [new file with mode: 0644]
Help/prop_tgt/VS_DEBUGGER_COMMAND.rst
Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst
Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst
Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst
Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst
Help/prop_tgt/VS_KEYWORD.rst
Help/prop_tgt/XCODE_GENERATE_SCHEME.rst
Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst [new file with mode: 0644]
Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst [new file with mode: 0644]
Help/prop_tgt/XCODE_SCHEME_LAUNCH_CONFIGURATION.rst [new file with mode: 0644]
Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst [new file with mode: 0644]
Help/release/3.0.rst
Help/release/3.11.rst
Help/release/3.12.rst
Help/release/3.20.rst
Help/release/3.21.rst
Help/release/3.23.rst
Help/release/3.24.rst
Help/release/3.25.rst [new file with mode: 0644]
Help/release/3.3.rst
Help/release/3.4.rst
Help/release/3.7.rst
Help/release/index.rst
Help/variable/BSD.rst [new file with mode: 0644]
Help/variable/CMAKE_ANDROID_API.rst
Help/variable/CMAKE_BINARY_DIR.rst
Help/variable/CMAKE_CFG_INTDIR.rst
Help/variable/CMAKE_COMMAND.rst
Help/variable/CMAKE_CPACK_COMMAND.rst
Help/variable/CMAKE_CTEST_COMMAND.rst
Help/variable/CMAKE_CURRENT_BINARY_DIR.rst
Help/variable/CMAKE_CURRENT_SOURCE_DIR.rst
Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
Help/variable/CMAKE_FIND_DEBUG_MODE.rst
Help/variable/CMAKE_FIND_PACKAGE_REDIRECTS_DIR.rst
Help/variable/CMAKE_GENERATOR.rst
Help/variable/CMAKE_GENERATOR_PLATFORM.rst
Help/variable/CMAKE_GENERATOR_TOOLSET.rst
Help/variable/CMAKE_HOST_BSD.rst [new file with mode: 0644]
Help/variable/CMAKE_HOST_LINUX.rst [new file with mode: 0644]
Help/variable/CMAKE_INSTALL_PREFIX.rst
Help/variable/CMAKE_LANG_COMPILER.rst
Help/variable/CMAKE_LANG_COMPILER_ID.rst
Help/variable/CMAKE_LANG_FLAGS.rst
Help/variable/CMAKE_MAKE_PROGRAM.rst
Help/variable/CMAKE_MESSAGE_CONTEXT.rst
Help/variable/CMAKE_MESSAGE_LOG_LEVEL.rst
Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst [new file with mode: 0644]
Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
Help/variable/CMAKE_SCRIPT_MODE_FILE.rst
Help/variable/CMAKE_SOURCE_DIR.rst
Help/variable/CMAKE_TASKING_TOOLSET.rst [new file with mode: 0644]
Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst
Help/variable/CMAKE_WARN_DEPRECATED.rst
Help/variable/CMAKE_XCODE_PLATFORM_TOOLSET.rst
Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst [new file with mode: 0644]
Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst [new file with mode: 0644]
Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION.rst [new file with mode: 0644]
Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst [new file with mode: 0644]
Help/variable/CPACK_CUSTOM_INSTALL_VARIABLES.rst
Help/variable/CTEST_CONFIGURATION_TYPE.rst
Help/variable/CTEST_RUN_CURRENT_SCRIPT.rst
Help/variable/LINK_LIBRARY_PREDEFINED_FEATURES.txt
Help/variable/LINUX.rst [new file with mode: 0644]
Modules/CMakeCXXCompilerId.cpp.in
Modules/CMakeCompilerIdDetection.cmake
Modules/CMakeDependentOption.cmake
Modules/CMakeDetermineASMCompiler.cmake
Modules/CMakeDetermineCSharpCompiler.cmake
Modules/CMakeDetermineCUDACompiler.cmake
Modules/CMakeDetermineCompileFeatures.cmake
Modules/CMakeDetermineCompilerABI.cmake
Modules/CMakeDetermineCompilerId.cmake
Modules/CMakeDetermineFortranCompiler.cmake
Modules/CMakeDetermineHIPCompiler.cmake
Modules/CMakeDetermineVSServicePack.cmake
Modules/CMakeFindBinUtils.cmake
Modules/CMakeFindFrameworks.cmake
Modules/CMakeHIPInformation.cmake
Modules/CMakeParseImplicitLinkInfo.cmake
Modules/CMakePlatformId.h.in
Modules/CMakeSwiftInformation.cmake
Modules/CMakeSystemSpecificInformation.cmake
Modules/CMakeTestCCompiler.cmake
Modules/CMakeTestCSharpCompiler.cmake
Modules/CMakeTestCUDACompiler.cmake
Modules/CMakeTestCXXCompiler.cmake
Modules/CMakeTestFortranCompiler.cmake
Modules/CMakeTestHIPCompiler.cmake
Modules/CMakeTestOBJCCompiler.cmake
Modules/CMakeTestOBJCXXCompiler.cmake
Modules/CMakeTestSwiftCompiler.cmake
Modules/CPack.cmake
Modules/CPackComponent.cmake
Modules/CPackIFW.cmake
Modules/CTest.cmake
Modules/CTestTargets.cmake
Modules/CTestUseLaunchers.cmake
Modules/CheckCXXSymbolExists.cmake
Modules/CheckFortranFunctionExists.cmake
Modules/CheckFunctionExists.cmake
Modules/CheckIPOSupported.cmake
Modules/CheckIncludeFile.cmake
Modules/CheckIncludeFileCXX.cmake
Modules/CheckIncludeFiles.cmake
Modules/CheckLibraryExists.cmake
Modules/CheckPrototypeDefinition.cmake
Modules/CheckSymbolExists.cmake
Modules/CheckTypeSize.cmake
Modules/CheckVariableExists.cmake
Modules/Compiler/Clang-CUDA.cmake
Modules/Compiler/Clang.cmake
Modules/Compiler/IAR-CXX.cmake
Modules/Compiler/IntelLLVM-FindBinUtils.cmake [new file with mode: 0644]
Modules/Compiler/IntelLLVM.cmake
Modules/Compiler/MSVC-CXX.cmake
Modules/Compiler/NVIDIA-CUDA.cmake
Modules/Compiler/Tasking-ASM.cmake [new file with mode: 0644]
Modules/Compiler/Tasking-C.cmake [new file with mode: 0644]
Modules/Compiler/Tasking-CXX.cmake [new file with mode: 0644]
Modules/Compiler/Tasking-DetermineCompiler.cmake [new file with mode: 0644]
Modules/Compiler/Tasking-FindBinUtils.cmake [new file with mode: 0644]
Modules/Compiler/Tasking.cmake [new file with mode: 0644]
Modules/ExternalProject.cmake
Modules/ExternalProject/gitupdate.cmake.in
Modules/FetchContent.cmake
Modules/FindAVIFile.cmake
Modules/FindBLAS.cmake
Modules/FindBacktrace.cmake
Modules/FindCUDA.cmake
Modules/FindCUDA/select_compute_arch.cmake
Modules/FindCUDAToolkit.cmake
Modules/FindCURL.cmake
Modules/FindCoin3D.cmake
Modules/FindCxxTest.cmake
Modules/FindDCMTK.cmake
Modules/FindDevIL.cmake
Modules/FindDoxygen.cmake
Modules/FindGDAL.cmake
Modules/FindGLEW.cmake
Modules/FindGLUT.cmake
Modules/FindGTK2.cmake
Modules/FindHDF5.cmake
Modules/FindICU.cmake
Modules/FindLAPACK.cmake
Modules/FindLTTngUST.cmake
Modules/FindLibArchive.cmake
Modules/FindMFC.cmake
Modules/FindMPI.cmake
Modules/FindMatlab.cmake
Modules/FindOpenACC.cmake
Modules/FindOpenAL.cmake
Modules/FindOpenGL.cmake
Modules/FindOpenMP.cmake
Modules/FindOpenSP.cmake [new file with mode: 0644]
Modules/FindOpenSSL.cmake
Modules/FindPackageHandleStandardArgs.cmake
Modules/FindPython.cmake
Modules/FindPython/Support.cmake
Modules/FindPython2.cmake
Modules/FindPython3.cmake
Modules/FindRuby.cmake
Modules/FindSDL_gfx.cmake [new file with mode: 0644]
Modules/FindSDL_image.cmake
Modules/FindSDL_mixer.cmake
Modules/FindSDL_net.cmake
Modules/FindSDL_sound.cmake
Modules/FindSDL_ttf.cmake
Modules/FindSWIG.cmake
Modules/FindThreads.cmake
Modules/FindVulkan.cmake
Modules/FindXCTest.cmake
Modules/FindwxWindows.cmake
Modules/FortranCInterface.cmake
Modules/FortranCInterface/Detect.cmake
Modules/GoogleTest.cmake
Modules/Internal/CPack/CPackDeb.cmake
Modules/Internal/CPack/CPackRPM.cmake
Modules/Internal/CheckSourceCompiles.cmake
Modules/Internal/CheckSourceRuns.cmake
Modules/Internal/FeatureTesting.cmake
Modules/Platform/AIX-GNU.cmake
Modules/Platform/AIX-XL.cmake
Modules/Platform/AIX/ExportImportList
Modules/Platform/DragonFly.cmake
Modules/Platform/FreeBSD.cmake
Modules/Platform/Linux-IntelLLVM.cmake
Modules/Platform/Linux-NVHPC.cmake
Modules/Platform/Linux.cmake
Modules/Platform/NetBSD.cmake
Modules/Platform/OpenBSD.cmake
Modules/Platform/SerenityOS-Clang-ASM.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-Clang-C.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-Clang-CXX.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-GNU-ASM.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-GNU-C.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-GNU-CXX.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS-GNU.cmake [new file with mode: 0644]
Modules/Platform/SerenityOS.cmake [new file with mode: 0644]
Modules/Platform/Windows-Clang-HIP.cmake [new file with mode: 0644]
Modules/Platform/Windows-Clang.cmake
Modules/Platform/Windows-Intel-C.cmake
Modules/Platform/Windows-Intel-CXX.cmake
Modules/Platform/Windows-Intel-Fortran.cmake
Modules/Platform/Windows-IntelLLVM-Fortran.cmake
Modules/Platform/Windows-IntelLLVM.cmake
Modules/Platform/Windows-MSVC.cmake
Modules/Platform/Windows-NVIDIA-CUDA.cmake
Modules/Platform/Windows.cmake
Modules/Platform/kFreeBSD.cmake
Modules/TestBigEndian.cmake
Modules/TestCXXAcceptsFlag.cmake
Modules/TestForANSIForScope.cmake
Modules/TestForSSTREAM.cmake
Modules/TestForSTDNamespace.cmake
Modules/UseJava.cmake
Modules/UseSWIG.cmake
Source/CMakeLists.txt
Source/CMakeVersion.cmake
Source/CPack/cmCPackArchiveGenerator.cxx
Source/CPack/cmCPackConfigure.h.in
Source/CPack/cmCPackDragNDropGenerator.cxx
Source/CPack/cmCPackFreeBSDGenerator.cxx
Source/CPack/cmCPackGeneratorFactory.cxx
Source/CPack/cmCPackGeneratorFactory.h
Source/CPack/cmCPackNSISGenerator.cxx
Source/CPack/cpack.cxx
Source/CTest/cmCTestCoverageCommand.cxx
Source/CTest/cmCTestCoverageCommand.h
Source/CTest/cmCTestHandlerCommand.cxx
Source/CTest/cmCTestHandlerCommand.h
Source/CTest/cmCTestMemCheckHandler.cxx
Source/CTest/cmCTestRunTest.cxx
Source/CTest/cmCTestSubmitCommand.cxx
Source/CTest/cmCTestSubmitCommand.h
Source/CTest/cmCTestUploadCommand.cxx
Source/CTest/cmCTestUploadCommand.h
Source/CursesDialog/cmCursesLongMessageForm.cxx
Source/CursesDialog/form/CMakeLists.txt
Source/CursesDialog/form/fty_int.c
Source/CursesDialog/form/fty_num.c
Source/LexerParser/cmFortranLexer.cxx
Source/LexerParser/cmFortranLexer.in.l
Source/LexerParser/cmFortranParser.cxx
Source/LexerParser/cmFortranParser.y
Source/Modules/CMakeBuildUtilities.cmake [new file with mode: 0644]
Source/QtDialog/CMakeLists.txt
Source/QtDialog/FirstConfigure.cxx
Source/cmAddSubDirectoryCommand.cxx
Source/cmArgumentParser.cxx
Source/cmArgumentParser.h
Source/cmArgumentParserTypes.h [new file with mode: 0644]
Source/cmBlockCommand.cxx [new file with mode: 0644]
Source/cmBlockCommand.h [new file with mode: 0644]
Source/cmCMakeHostSystemInformationCommand.cxx
Source/cmCMakeLanguageCommand.cxx
Source/cmCMakePathCommand.cxx
Source/cmCMakePresetsGraph.cxx
Source/cmCMakePresetsGraph.h
Source/cmCMakePresetsGraphInternal.h
Source/cmCMakePresetsGraphReadJSON.cxx
Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx [new file with mode: 0644]
Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx [new file with mode: 0644]
Source/cmCTest.cxx
Source/cmCTest.h
Source/cmCommands.cxx
Source/cmCommonTargetGenerator.cxx
Source/cmComputeLinkInformation.cxx
Source/cmConfigure.cmake.h.in
Source/cmCoreTryCompile.cxx
Source/cmCoreTryCompile.h
Source/cmCreateTestSourceList.cxx
Source/cmCurl.cxx
Source/cmCxxModuleMapper.cxx [new file with mode: 0644]
Source/cmCxxModuleMapper.h [new file with mode: 0644]
Source/cmDefinePropertyCommand.cxx
Source/cmDocumentation.cxx
Source/cmExecuteProcessCommand.cxx
Source/cmExecutionStatus.h
Source/cmExperimental.cxx [new file with mode: 0644]
Source/cmExperimental.h [new file with mode: 0644]
Source/cmExportBuildFileGenerator.cxx
Source/cmExportBuildFileGenerator.h
Source/cmExportCommand.cxx
Source/cmExportFileGenerator.cxx
Source/cmExportFileGenerator.h
Source/cmExportInstallAndroidMKGenerator.cxx
Source/cmExportInstallFileGenerator.cxx
Source/cmExportInstallFileGenerator.h
Source/cmExportTryCompileFileGenerator.h
Source/cmFileAPICodemodel.cxx
Source/cmFileCommand.cxx
Source/cmFileCopier.cxx
Source/cmFindBase.cxx
Source/cmFindBase.h
Source/cmFindLibraryCommand.cxx
Source/cmFindPackageCommand.cxx
Source/cmFindPackageCommand.h
Source/cmFindPathCommand.cxx
Source/cmFindProgramCommand.cxx
Source/cmForEachCommand.cxx
Source/cmFunctionBlocker.cxx
Source/cmFunctionBlocker.h
Source/cmFunctionCommand.cxx
Source/cmGeneratedFileStream.cxx
Source/cmGeneratedFileStream.h
Source/cmGeneratorTarget.cxx
Source/cmGeneratorTarget.h
Source/cmGhsMultiTargetGenerator.cxx
Source/cmGlobalGenerator.cxx
Source/cmGlobalGenerator.h
Source/cmGlobalNinjaGenerator.cxx
Source/cmGlobalNinjaGenerator.h
Source/cmGlobalVisualStudio10Generator.cxx
Source/cmGlobalVisualStudio10Generator.h
Source/cmGlobalVisualStudio11Generator.cxx
Source/cmGlobalVisualStudio7Generator.cxx
Source/cmGlobalVisualStudio7Generator.h
Source/cmGlobalVisualStudioGenerator.cxx
Source/cmGlobalVisualStudioGenerator.h
Source/cmGlobalVisualStudioVersionedGenerator.cxx
Source/cmGlobalXCodeGenerator.cxx
Source/cmGlobalXCodeGenerator.h
Source/cmIfCommand.cxx
Source/cmInstallCommand.cxx
Source/cmInstallCommandArguments.h
Source/cmInstallCxxModuleBmiGenerator.cxx [new file with mode: 0644]
Source/cmInstallCxxModuleBmiGenerator.h [new file with mode: 0644]
Source/cmInstallExportGenerator.cxx
Source/cmInstallExportGenerator.h
Source/cmLinkLineDeviceComputer.cxx
Source/cmLinkLineDeviceComputer.h
Source/cmListFileCache.cxx
Source/cmLocalGenerator.cxx
Source/cmLocalGenerator.h
Source/cmLocalNinjaGenerator.cxx
Source/cmLocalUnixMakefileGenerator3.cxx
Source/cmLocalVisualStudio10Generator.h
Source/cmLocalVisualStudio7Generator.cxx
Source/cmMacroCommand.cxx
Source/cmMakefile.cxx
Source/cmMakefile.h
Source/cmMakefileExecutableTargetGenerator.cxx
Source/cmMakefileLibraryTargetGenerator.cxx
Source/cmMakefileTargetGenerator.cxx
Source/cmMakefileTargetGenerator.h
Source/cmMessageCommand.cxx
Source/cmMessageType.h
Source/cmNinjaNormalTargetGenerator.cxx
Source/cmNinjaTargetGenerator.cxx
Source/cmOutputConverter.cxx
Source/cmParseArgumentsCommand.cxx
Source/cmPolicies.h
Source/cmQtAutoGenInitializer.cxx
Source/cmQtAutoGenInitializer.h
Source/cmReturnCommand.cxx
Source/cmScanDepFormat.cxx
Source/cmScanDepFormat.h
Source/cmScriptGenerator.cxx
Source/cmSetPropertyCommand.cxx
Source/cmStandardLevelResolver.cxx
Source/cmState.cxx
Source/cmStatePrivate.h
Source/cmStateSnapshot.cxx
Source/cmStringCommand.cxx
Source/cmSubdirCommand.cxx
Source/cmSystemTools.cxx
Source/cmSystemTools.h
Source/cmTarget.cxx
Source/cmTarget.h
Source/cmTargetCompileDefinitionsCommand.cxx
Source/cmTargetExport.h
Source/cmTargetIncludeDirectoriesCommand.cxx
Source/cmTargetLinkLibrariesCommand.cxx
Source/cmTargetPrecompileHeadersCommand.cxx
Source/cmTargetSourcesCommand.cxx
Source/cmTryCompileCommand.cxx
Source/cmTryCompileCommand.h
Source/cmTryRunCommand.cxx
Source/cmTryRunCommand.h
Source/cmVisualStudio10TargetGenerator.cxx
Source/cmVisualStudio10TargetGenerator.h
Source/cmVisualStudioGeneratorOptions.cxx
Source/cmWhileCommand.cxx
Source/cmWindowsRegistry.h
Source/cmXCodeScheme.cxx
Source/cm_codecvt.cxx
Source/cm_codecvt.hxx
Source/cmake.cxx
Source/cmake.h
Source/cmake.version.manifest
Source/cmakemain.cxx
Source/cmcldeps.cxx
Source/cmcmd.cxx
Source/kwsys/Directory.cxx
Source/kwsys/SystemTools.cxx
Templates/MSBuild/FlagTables/v10_CSharp.json
Templates/MSBuild/FlagTables/v10_Cuda.json
Templates/MSBuild/FlagTables/v11_CSharp.json
Templates/MSBuild/FlagTables/v12_CSharp.json
Templates/MSBuild/FlagTables/v140_CSharp.json
Templates/MSBuild/FlagTables/v141_CSharp.json
Templates/MSBuild/FlagTables/v142_CSharp.json
Templates/MSBuild/FlagTables/v143_CSharp.json
Tests/Assembler/CMakeLists.txt
Tests/CMakeLib/CMakeLists.txt
Tests/CMakeLib/PseudoMemcheck/CMakeLists.txt
Tests/CMakeLib/testArgumentParser.cxx
Tests/CMakeLib/testCMExtEnumSet.cxx
Tests/CMakeLists.txt
Tests/CMakeOnly/AllFindModules/CMakeLists.txt
Tests/CMakeTests/CMakeLists.txt
Tests/CMakeTests/CheckSourceTreeTest.cmake.in [deleted file]
Tests/CMakeTests/ImplicitLinkInfoTest.cmake.in
Tests/CTestCoverageCollectGCOV/test.cmake.in
Tests/CTestTestFdSetSize/sleep.c
Tests/CTestUpdateCommon.cmake
Tests/CTestUpdateP4.cmake.in
Tests/CheckSourceTree/CMakeLists.txt [new file with mode: 0644]
Tests/CheckSourceTree/check.cmake [new file with mode: 0644]
Tests/CheckSwift.cmake
Tests/CompileFeatures/CMakeLists.txt
Tests/CompileFeatures/genex_test.cpp
Tests/Complex/CMakeLists.txt
Tests/Complex/Executable/complex.cxx
Tests/ComplexOneConfig/CMakeLists.txt
Tests/ComplexOneConfig/Executable/complex.cxx
Tests/Cuda/SeparableCompCXXOnly/CMakeLists.txt
Tests/CudaOnly/CMakeLists.txt
Tests/CudaOnly/DeviceLTO/CMakeLists.txt [new file with mode: 0644]
Tests/CudaOnly/DeviceLTO/file1.cu [new file with mode: 0644]
Tests/CudaOnly/DeviceLTO/file2.cu [new file with mode: 0644]
Tests/CudaOnly/DeviceLTO/file3.cu [new file with mode: 0644]
Tests/CudaOnly/DeviceLTO/main.cu [new file with mode: 0644]
Tests/CudaOnly/ToolkitMultipleDirs/CMakeLists.txt [new file with mode: 0644]
Tests/CudaOnly/ToolkitMultipleDirs/main.cu [new file with mode: 0644]
Tests/CudaOnly/ToolkitMultipleDirs/subdir/CMakeLists.txt [new file with mode: 0644]
Tests/EnforceConfig.cmake.in
Tests/ExportImport/Export/CMakeLists.txt
Tests/ExportImport/Import/A/CMakeLists.txt
Tests/ExternalProject/CMakeLists.txt
Tests/FindOpenAL/CMakeLists.txt [new file with mode: 0644]
Tests/FindOpenAL/Test/CMakeLists.txt [new file with mode: 0644]
Tests/FindOpenAL/Test/main.cxx [new file with mode: 0644]
Tests/FindOpenSP/CMakeLists.txt [new file with mode: 0644]
Tests/FindOpenSP/Test/CMakeLists.txt [new file with mode: 0644]
Tests/FindOpenSP/Test/main.cxx [new file with mode: 0644]
Tests/FindOpenSP/Test/test.sgml [new file with mode: 0644]
Tests/FindPackageTest/CMakeLists.txt
Tests/FindVulkan/Test/CMakeLists.txt
Tests/FindVulkan/Test/Run-dxc_exe.cmake [new file with mode: 0644]
Tests/FindVulkan/Test/main-dxc_lib.cxx [new file with mode: 0644]
Tests/FindVulkan/Test/main-volk.cxx [new file with mode: 0644]
Tests/FortranModules/Submodules/CMakeLists.txt
Tests/FortranModules/Submodules/main.f90
Tests/FortranModules/Submodules/obfuscated_parent.f90 [new file with mode: 0644]
Tests/FortranModules/test_module_main.f90
Tests/FunctionTest/CMakeLists.txt
Tests/IncludeDirectories/CMakeLists.txt
Tests/LoadCommand/CMakeCommands/cmTestCommand.c
Tests/LoadCommandOneConfig/CMakeCommands/cmTestCommand.c
Tests/MSVCDebugInformationFormat/CMakeLists.txt [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/override-C.cmake [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/override-CUDA.cmake [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/override-CXX.cmake [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/override-Fortran.cmake [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/verify.F90 [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/verify.c [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/verify.cu [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/verify.cxx [new file with mode: 0644]
Tests/MSVCDebugInformationFormat/verify.h [new file with mode: 0644]
Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt [new file with mode: 0644]
Tests/Module/CheckIPOSupported-CUDA/bar.cu [new file with mode: 0644]
Tests/Module/CheckIPOSupported-CUDA/foo.cu [new file with mode: 0644]
Tests/Module/CheckIPOSupported-CUDA/main.cu [new file with mode: 0644]
Tests/ModuleDefinition/CMakeLists.txt
Tests/Preprocess/CMakeLists.txt
Tests/Preprocess/preprocess.c
Tests/Preprocess/preprocess.cxx
Tests/RunCMake/AutoExportDll/AutoExport.cmake
Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake
Tests/RunCMake/CMakeDependentOption/UseDotSymbol-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakeDependentOption/UseDotSymbol.cmake [new file with mode: 0644]
Tests/RunCMake/CMakeLists.txt
Tests/RunCMake/CMakePresets/Comment-stderr.txt
Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresets/EmptyPresetName-stderr.txt
Tests/RunCMake/CMakePresets/GoodNoSCache.cmake
Tests/RunCMake/CMakePresets/IncludeNotFound-stderr.txt
Tests/RunCMake/CMakePresets/JSONParseError-stderr.txt
Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresets/ListAllPresetsNoTest.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresets/ListPresetsInvalidType-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-write-only-result.txt with 100% similarity]
Tests/RunCMake/CMakePresets/ListPresetsInvalidType-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-config-file-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-configurations-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-debug-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-generators-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-no-environment-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-package-directory-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-package-name-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-package-version-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-variables-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-verbose-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good-package-with-environment-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/Good.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/ListPresets-package-x-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/ListPresets.cmake [moved from Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake with 100% similarity]
Tests/RunCMake/CMakePresetsPackage/ListPresets.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-no-perms-result.txt with 100% similarity]
Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsPackage/check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/Good-test-outputJUnit-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/Good-test-outputLog-check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/Good.json.in
Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-result.txt with 100% similarity]
Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-result.txt with 100% similarity]
Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/BadExitCode.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/BadExitCodeTest.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-result.txt with 100% similarity]
Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-result.txt [moved from Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake with 100% similarity]
Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/Fresh.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/Fresh.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/Good-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/Good.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/Good.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/GoodUser-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/GoodUser.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/GoodUser.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-result.txt [moved from Tests/RunCMake/file-CHMOD/CHMOD-all-perms-result.txt with 100% similarity]
Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/ListPresets-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/ListPresets.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnreachableStepUser.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-result.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro.json.in [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/check.cmake [new file with mode: 0644]
Tests/RunCMake/CMakePresetsWorkflow/cpack_staging.cmake.in [new file with mode: 0644]
Tests/RunCMake/CPack/DEB/Helpers.cmake
Tests/RunCMake/CPack/DEB/Prerequirements.cmake
Tests/RunCMake/CPack/RPM/Prerequirements.cmake
Tests/RunCMake/CPack/tests/GENERATE_SHLIBS/DEB-Prerequirements.cmake
Tests/RunCMake/CPack/tests/GENERATE_SHLIBS_LDCONFIG/DEB-Prerequirements.cmake
Tests/RunCMake/CPack/tests/INSTALL_SCRIPTS/RPM-Prerequirements.cmake
Tests/RunCMake/CTestTimeout/TestTimeout.c
Tests/RunCMake/CUDA_architectures/architectures-suffix-stderr.txt
Tests/RunCMake/CXXModules/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMI-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMI-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMI.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NoDyndepSupport.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/check-json.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/compiler_introspection.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/generated-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/generated/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/library-static-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/library/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/library/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/partitions-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/partitions/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/partitions/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/partitions/partition.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/simple-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/simple/importable.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/examples/simple/main.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/c-anchor.c [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-header.h [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-impl.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-internal-part.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-part-impl.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-part.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module-use.cxx [new file with mode: 0644]
Tests/RunCMake/CXXModules/sources/module.cxx [new file with mode: 0644]
Tests/RunCMake/CheckCompilerFlag/CheckCCompilerFlag.cmake
Tests/RunCMake/CheckCompilerFlag/CheckCXXCompilerFlag.cmake
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagC.cmake [new file with mode: 0644]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagCUDA.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckCUDACompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagFortran.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckFortranCompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagHIP.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckHIPCompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagISPC.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckISPCCompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagOBJC.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckOBJCCompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagOBJCXX.cmake [moved from Tests/RunCMake/CheckCompilerFlag/CheckOBJCXXCompilerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckCompilerFlag/RunCMakeTest.cmake
Tests/RunCMake/CheckIPOSupported/default-lang-none-stderr.txt
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagC.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckCLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagCUDA.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckCUDALinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagCXX.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckCXXLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagFortran.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckFortranLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagHIP.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckHIPLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagOBJC.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckOBJCLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/CheckLinkerFlagOBJCXX.cmake [moved from Tests/RunCMake/CheckLinkerFlag/CheckOBJCXXLinkerFlag.cmake with 100% similarity]
Tests/RunCMake/CheckLinkerFlag/RunCMakeTest.cmake
Tests/RunCMake/CheckSourceCompiles/CheckCSourceCompiles.cmake
Tests/RunCMake/CheckSourceCompiles/CheckCXXSourceCompiles.cmake
Tests/RunCMake/CheckSourceCompiles/CheckOBJCSourceCompiles.cmake
Tests/RunCMake/CheckSourceCompiles/CheckOBJCXXSourceCompiles.cmake
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesC.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesCUDA.cmake [moved from Tests/RunCMake/CheckSourceCompiles/CheckCUDASourceCompiles.cmake with 100% similarity]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesFortran.cmake [moved from Tests/RunCMake/CheckSourceCompiles/CheckFortranSourceCompiles.cmake with 100% similarity]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesHIP.cmake [moved from Tests/RunCMake/CheckSourceCompiles/CheckHIPSourceCompiles.cmake with 100% similarity]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesISPC.cmake [moved from Tests/RunCMake/CheckSourceCompiles/CheckISPCSourceCompiles.cmake with 100% similarity]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJC.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceCompiles/RunCMakeTest.cmake
Tests/RunCMake/CheckSourceRuns/CheckCSourceRuns.cmake
Tests/RunCMake/CheckSourceRuns/CheckCXXSourceRuns.cmake
Tests/RunCMake/CheckSourceRuns/CheckOBJCSourceRuns.cmake
Tests/RunCMake/CheckSourceRuns/CheckOBJCXXSourceRuns.cmake
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsC.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsCUDA.cmake [moved from Tests/RunCMake/CheckSourceRuns/CheckCUDASourceRuns.cmake with 100% similarity]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsFortran.cmake [moved from Tests/RunCMake/CheckSourceRuns/CheckFortranSourceRuns.cmake with 100% similarity]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsHIP.cmake [moved from Tests/RunCMake/CheckSourceRuns/CheckHIPSourceRuns.cmake with 100% similarity]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJC.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CheckSourceRuns/RunCMakeTest.cmake
Tests/RunCMake/ClangTidy/RunCMakeTest.cmake
Tests/RunCMake/ClangTidy/compdb.cmake [new file with mode: 0644]
Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt [deleted file]
Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake [new file with mode: 0644]
Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_capabilities-stdout.txt
Tests/RunCMake/CommandLine/E_env-equal.cmake [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-bad-operation-result.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-bad-operation-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-cmake_list-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-path_list-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-reset-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-reset-to-unset-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-set-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-string-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-unset-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-with-double-dash-result.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-result.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/P_working-dir.cmake
Tests/RunCMake/CommandLine/RunCMakeTest.cmake
Tests/RunCMake/CommandLine/cmake_depends-stdout.txt
Tests/RunCMake/CommandLine/debug-trycompile-stderr.txt [new file with mode: 0644]
Tests/RunCMake/CommandLine/debug-trycompile.cmake
Tests/RunCMake/CommandLine/trycompile-clean.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerLauncher/RunCMakeTest.cmake
Tests/RunCMake/CompilerTest/C-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/C.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/CUDA-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/CUDA.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/CXX-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/CXX.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/Fortran-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/Fortran.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/HIP-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/HIP.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/ISPC-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/ISPC.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/OBJC-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/OBJC.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/OBJCXX-stdout.txt [new file with mode: 0644]
Tests/RunCMake/CompilerTest/OBJCXX.cmake [new file with mode: 0644]
Tests/RunCMake/CompilerTest/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/Configure/CopyFileABI-override.cmake [moved from Tests/RunCMake/Configure/FailCopyFileABI-override.cmake with 64% similarity]
Tests/RunCMake/Configure/CopyFileABI-stdout.txt [moved from Tests/RunCMake/Configure/FailCopyFileABI-stdout.txt with 62% similarity]
Tests/RunCMake/Configure/CopyFileABI.cmake [moved from Tests/RunCMake/Configure/FailCopyFileABI.cmake with 71% similarity]
Tests/RunCMake/Configure/FailCopyFileABI-check.cmake [deleted file]
Tests/RunCMake/Configure/RunCMakeTest.cmake
Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build1-stdout.txt [new file with mode: 0644]
Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build2-stdout.txt [new file with mode: 0644]
Tests/RunCMake/ExternalProject/BUILD_ALWAYS.cmake [new file with mode: 0644]
Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
Tests/RunCMake/FPHSA/RunCMakeTest.cmake
Tests/RunCMake/FPHSA/empty_version-result.txt [new file with mode: 0644]
Tests/RunCMake/FPHSA/empty_version-stderr.txt [new file with mode: 0644]
Tests/RunCMake/FPHSA/empty_version.cmake [new file with mode: 0644]
Tests/RunCMake/FPHSA/exact_1_no_version_var-stdout.txt [new file with mode: 0644]
Tests/RunCMake/FPHSA/exact_1_no_version_var.cmake [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesNonSystem/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesNonSystem/bar.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesNonSystem/foo.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/bar.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/foo.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/zot.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/bar.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/foo.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/zot.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/bar.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/foo.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/IncludesSystem/zot.cpp [new file with mode: 0644]
Tests/RunCMake/FetchContent/RunCMakeTest.cmake
Tests/RunCMake/FetchContent/System.cmake [new file with mode: 0644]
Tests/RunCMake/FileAPI/CMakeLists.txt
Tests/RunCMake/FileAPI/ClientStateful-check.py
Tests/RunCMake/FileAPI/RunCMakeTest.cmake
Tests/RunCMake/FileAPI/codemodel-v2-check.py
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/alias.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/custom.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/dir_dir.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/external.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/interface.json
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/subdir.json [new file with mode: 0644]
Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json
Tests/RunCMake/FileAPI/codemodel-v2-data/projects/codemodel-v2.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/all_build_top.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_exe.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_object_lib.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json [new file with mode: 0644]
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_exe.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_object_lib.json
Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json
Tests/RunCMake/FileAPI/codemodel-v2.cmake
Tests/RunCMake/FileAPI/subdir/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/FileAPI/subdir/empty.c [new file with mode: 0644]
Tests/RunCMake/File_Archive/RunCMakeTest.cmake
Tests/RunCMake/File_Archive/create-missing-args-result.txt [new file with mode: 0644]
Tests/RunCMake/File_Archive/create-missing-args-stderr.txt [new file with mode: 0644]
Tests/RunCMake/File_Archive/create-missing-args.cmake [new file with mode: 0644]
Tests/RunCMake/File_Archive/extract-missing-args-result.txt [new file with mode: 0644]
Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt [new file with mode: 0644]
Tests/RunCMake/File_Archive/extract-missing-args.cmake [new file with mode: 0644]
Tests/RunCMake/File_Configure/BadArgContent-stderr.txt
Tests/RunCMake/File_Configure/BadArgOutput-stderr.txt
Tests/RunCMake/File_Configure/NoArgContent-result.txt [new file with mode: 0644]
Tests/RunCMake/File_Configure/NoArgContent-stderr.txt [new file with mode: 0644]
Tests/RunCMake/File_Configure/NoArgContent.cmake [new file with mode: 0644]
Tests/RunCMake/File_Configure/NoArgOutput-result.txt [new file with mode: 0644]
Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt [new file with mode: 0644]
Tests/RunCMake/File_Configure/NoArgOutput.cmake [new file with mode: 0644]
Tests/RunCMake/File_Configure/RunCMakeTest.cmake
Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt
Tests/RunCMake/File_Generate/InputAndContent-check.cmake [new file with mode: 0644]
Tests/RunCMake/File_Generate/InputAndContent-input.txt [new file with mode: 0644]
Tests/RunCMake/File_Generate/InputAndContent.cmake [new file with mode: 0644]
Tests/RunCMake/File_Generate/NewLineStyle-NoArg-stderr.txt
Tests/RunCMake/File_Generate/RunCMakeTest.cmake
Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
Tests/RunCMake/Framework/FrameworkConsumption.cmake
Tests/RunCMake/Framework/main2.c [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-result.txt [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-stderr.txt [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NoEffect.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-WARN.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-common.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/MSVCDebugInformationFormat/empty.cxx [new file with mode: 0644]
Tests/RunCMake/Ninja/QtAutoMocSkipPch.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/RunCMakeTest.cmake
Tests/RunCMake/Ninja/ShowIncludes-54936-check.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-54936-stdout.txt [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-54936.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-65001-check.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-65001-stdout.txt [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-65001.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-check.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes-cmake.cmake [new file with mode: 0644]
Tests/RunCMake/Ninja/ShowIncludes.cmake [new file with mode: 0644]
Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake [new file with mode: 0644]
Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake [new file with mode: 0644]
Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
Tests/RunCMake/PrecompileHeaders/DisabledPch.cmake
Tests/RunCMake/PrecompileHeaders/PchDebugGenex.cmake
Tests/RunCMake/PrecompileHeaders/PchIncludedAllLanguages.cmake
Tests/RunCMake/PrecompileHeaders/PchIncludedOneLanguage.cmake
Tests/RunCMake/PrecompileHeaders/PchInterface.cmake
Tests/RunCMake/PrecompileHeaders/PchLibObjLibExe.cmake
Tests/RunCMake/PrecompileHeaders/PchMultilanguage.cmake
Tests/RunCMake/PrecompileHeaders/PchPrologueEpilogue.cmake
Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW-empty.cmake [new file with mode: 0644]
Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW.cmake [new file with mode: 0644]
Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-OLD.cmake [new file with mode: 0644]
Tests/RunCMake/PrecompileHeaders/PchReuseFrom-common.cmake [moved from Tests/RunCMake/PrecompileHeaders/PchReuseFrom.cmake with 92% similarity]
Tests/RunCMake/PrecompileHeaders/PchReuseFromObjLib.cmake
Tests/RunCMake/PrecompileHeaders/PchReuseFromPrefixed.cmake
Tests/RunCMake/PrecompileHeaders/PchReuseFromSubdir.cmake
Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
Tests/RunCMake/PrecompileHeaders/SkipPrecompileHeaders.cmake
Tests/RunCMake/RunCMake.cmake
Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
Tests/RunCMake/VS10Project/DebugInformationFormat-check.cmake [new file with mode: 0644]
Tests/RunCMake/VS10Project/DebugInformationFormat.cmake [new file with mode: 0644]
Tests/RunCMake/VS10Project/InterfaceLibSources-check.cmake
Tests/RunCMake/VS10Project/RunCMakeTest.cmake
Tests/RunCMake/VerifyHeaderSets/VerifyHeaderSets-check.cmake
Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake
Tests/RunCMake/VsDotnetSdk/VsDotnetSdk.cmake
Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake [new file with mode: 0644]
Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake [new file with mode: 0644]
Tests/RunCMake/XcodeProject/Clean-build-check.cmake [new file with mode: 0644]
Tests/RunCMake/XcodeProject/Clean-clean-check.cmake [new file with mode: 0644]
Tests/RunCMake/XcodeProject/Clean.cmake [new file with mode: 0644]
Tests/RunCMake/XcodeProject/Clean/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/XcodeProject/Clean/empty.c [new file with mode: 0644]
Tests/RunCMake/XcodeProject/EffectivePlatformNameOFF.cmake [new file with mode: 0644]
Tests/RunCMake/XcodeProject/RunCMakeTest.cmake
Tests/RunCMake/XcodeProject/SearchPaths-check.cmake
Tests/RunCMake/XcodeProject/SearchPaths.cmake
Tests/RunCMake/XcodeProject/XcodeSchemaProperty-check.cmake
Tests/RunCMake/XcodeProject/XcodeSchemaProperty.cmake
Tests/RunCMake/add_subdirectory/RunCMakeTest.cmake
Tests/RunCMake/add_subdirectory/System.cmake [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub1/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub1/bar.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub1/foo.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub1/zot.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub2/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub2/bar.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub2/foo.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/SubSub2/zot.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/bar.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/foo.cpp [new file with mode: 0644]
Tests/RunCMake/add_subdirectory/System/zot.cpp [new file with mode: 0644]
Tests/RunCMake/block/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/block/EndAlone-result.txt [new file with mode: 0644]
Tests/RunCMake/block/EndAlone-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/EndAlone.cmake [new file with mode: 0644]
Tests/RunCMake/block/EndAloneWithArgument-result.txt [new file with mode: 0644]
Tests/RunCMake/block/EndAloneWithArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/EndAloneWithArgument.cmake [new file with mode: 0644]
Tests/RunCMake/block/EndMissing-result.txt [new file with mode: 0644]
Tests/RunCMake/block/EndMissing-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/EndMissing.cmake [new file with mode: 0644]
Tests/RunCMake/block/EndWithArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/EndWithArgument.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidArgument-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidArgument.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting1-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting1-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting1.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting2-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting2-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting2.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting3-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting3-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting3.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting4-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting4-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting4.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting5-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting5-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting5.cmake [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting6-result.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting6-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/InvalidNesting6.cmake [new file with mode: 0644]
Tests/RunCMake/block/MissingArgument-result.txt [new file with mode: 0644]
Tests/RunCMake/block/MissingArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/MissingArgument.cmake [new file with mode: 0644]
Tests/RunCMake/block/RunCMakeTest.cmake [new file with mode: 0644]
Tests/RunCMake/block/Scope-POLICIES.cmake [new file with mode: 0644]
Tests/RunCMake/block/Scope-VARIABLES.cmake [new file with mode: 0644]
Tests/RunCMake/block/Scope.cmake [new file with mode: 0644]
Tests/RunCMake/block/Scope/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/block/Workflows.cmake [new file with mode: 0644]
Tests/RunCMake/block/WrongArgument-result.txt [new file with mode: 0644]
Tests/RunCMake/block/WrongArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/WrongArgument.cmake [new file with mode: 0644]
Tests/RunCMake/block/WrongScope-result.txt [new file with mode: 0644]
Tests/RunCMake/block/WrongScope-stderr.txt [new file with mode: 0644]
Tests/RunCMake/block/WrongScope.cmake [new file with mode: 0644]
Tests/RunCMake/cmake_host_system_information/Registry_BadQuery2-stderr.txt
Tests/RunCMake/cmake_host_system_information/Registry_BadView1-stderr.txt
Tests/RunCMake/cmake_language/RunCMakeTest.cmake
Tests/RunCMake/cmake_language/get_message_log_level.cmake [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt [new file with mode: 0644]
Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-no-arg-stderr.txt
Tests/RunCMake/cmake_path/RunCMakeTest.cmake
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-result.txt [new file with mode: 0644]
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-stdout.txt [new file with mode: 0644]
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop.cmake [new file with mode: 0644]
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-result.txt [new file with mode: 0644]
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-stdout.txt [new file with mode: 0644]
Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system.cmake [new file with mode: 0644]
Tests/RunCMake/ctest_environment/RunCMakeTest.cmake
Tests/RunCMake/ctest_memcheck/testCudaSanitizer.cmake
Tests/RunCMake/execute_process/EchoCommand3-stderr.txt
Tests/RunCMake/execute_process/EncodingMissing-stderr.txt
Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-override.cmake [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt [deleted file]
Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake [deleted file]
Tests/RunCMake/file-CHMOD/RunCMakeTest.cmake
Tests/RunCMake/file-CHMOD/all-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/all-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/all-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-path-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-path.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/invalid-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-file-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/missing-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/no-perms-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/no-perms-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/no-perms.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/ok.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/override.cmake [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/write-only-result.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/write-only-stderr.txt [new file with mode: 0644]
Tests/RunCMake/file-CHMOD/write-only.cmake [new file with mode: 0644]
Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/badargs2-stderr.txt
Tests/RunCMake/file/REAL_PATH-no-base-dir-stderr.txt
Tests/RunCMake/file/RunCMakeTest.cmake
Tests/RunCMake/file/TIMESTAMP-stdout.txt [new file with mode: 0644]
Tests/RunCMake/file/TIMESTAMP.cmake [new file with mode: 0644]
Tests/RunCMake/find_file/RunCMakeTest.cmake
Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-no-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-stdout.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_file/VALIDATOR.cmake [new file with mode: 0644]
Tests/RunCMake/find_library/RunCMakeTest.cmake
Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-no-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-stdout.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_library/VALIDATOR.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/RunCMakeTest.cmake
Tests/RunCMake/find_package/SearchPaths.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1.2.3/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake [new file with mode: 0644]
Tests/RunCMake/find_path/RunCMakeTest.cmake
Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-no-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-stdout.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_path/VALIDATOR.cmake [new file with mode: 0644]
Tests/RunCMake/find_program/RunCMakeTest.cmake
Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-no-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-stdout.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake [new file with mode: 0644]
Tests/RunCMake/find_program/VALIDATOR.cmake [new file with mode: 0644]
Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-result.txt [new file with mode: 0644]
Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stderr.txt [new file with mode: 0644]
Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stdout.txt [new file with mode: 0644]
Tests/RunCMake/install/DIRECTORY-symlink-clobber.cmake [new file with mode: 0644]
Tests/RunCMake/install/RunCMakeTest.cmake
Tests/RunCMake/project/LanguagesDuplicate-check.cmake [new file with mode: 0644]
Tests/RunCMake/project/LanguagesDuplicate.cmake [new file with mode: 0644]
Tests/RunCMake/project/RunCMakeTest.cmake
Tests/RunCMake/pseudo_tidy.c
Tests/RunCMake/return/CMP0140-NEW.cmake [new file with mode: 0644]
Tests/RunCMake/return/CMP0140-OLD.cmake [new file with mode: 0644]
Tests/RunCMake/return/CMP0140-WARN-result.txt [new file with mode: 0644]
Tests/RunCMake/return/CMP0140-WARN-stderr.txt [new file with mode: 0644]
Tests/RunCMake/return/CMP0140-WARN.cmake [new file with mode: 0644]
Tests/RunCMake/return/CMakeLists.txt
Tests/RunCMake/return/PropagateFromDirectory.cmake [new file with mode: 0644]
Tests/RunCMake/return/PropagateFromFunction.cmake [new file with mode: 0644]
Tests/RunCMake/return/PropagateNothing.cmake [new file with mode: 0644]
Tests/RunCMake/return/RunCMakeTest.cmake
Tests/RunCMake/return/WrongArgument-result.txt [new file with mode: 0644]
Tests/RunCMake/return/WrongArgument-stderr.txt [new file with mode: 0644]
Tests/RunCMake/return/WrongArgument.cmake [new file with mode: 0644]
Tests/RunCMake/return/subdir/CMakeLists.txt [new file with mode: 0644]
Tests/RunCMake/showIncludes.c [new file with mode: 0644]
Tests/RunCMake/string/JSON.cmake
Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt [new file with mode: 0644]
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
Tests/RunCMake/target_link_libraries-LINK_LIBRARY/apple_framework.cmake
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt [new file with mode: 0644]
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt [new file with mode: 0644]
Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake [new file with mode: 0644]
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt [new file with mode: 0644]
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt [new file with mode: 0644]
Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake [new file with mode: 0644]
Tests/RunCMake/target_sources/RunCMakeTest.cmake
Tests/RunCMake/try_compile/BadLinkLibraries-stderr.txt
Tests/RunCMake/try_compile/BadLinkLibraries.cmake
Tests/RunCMake/try_compile/BadSources1-stderr.txt
Tests/RunCMake/try_compile/BadSources1.cmake
Tests/RunCMake/try_compile/BadSources2-stderr.txt
Tests/RunCMake/try_compile/BadSources2.cmake
Tests/RunCMake/try_compile/BinDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/BinDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/BinDirRelative-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/BinDirRelative-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/BinDirRelative.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/BuildType.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/BuildTypeAsFlag.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/CStandard-stderr.txt
Tests/RunCMake/try_compile/CStandard.cmake
Tests/RunCMake/try_compile/CStandardGNU.cmake
Tests/RunCMake/try_compile/CStandardNoDefault.cmake
Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile-stderr.txt
Tests/RunCMake/try_compile/CopyFileErrorNoCopyFile.cmake
Tests/RunCMake/try_compile/CudaStandard-stderr.txt
Tests/RunCMake/try_compile/CudaStandard.cmake
Tests/RunCMake/try_compile/CxxStandard-stderr.txt
Tests/RunCMake/try_compile/CxxStandard.cmake
Tests/RunCMake/try_compile/CxxStandardGNU.cmake
Tests/RunCMake/try_compile/CxxStandardNoDefault.cmake
Tests/RunCMake/try_compile/EmptyListArgs.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/EmptyValueArgs-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/EmptyValueArgs.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/EnvConfig.cmake
Tests/RunCMake/try_compile/ISPCDuplicateTarget.cmake
Tests/RunCMake/try_compile/ISPCDuplicateTargetNinja.cmake
Tests/RunCMake/try_compile/ISPCInvalidTarget.cmake
Tests/RunCMake/try_compile/ISPCTargets.cmake
Tests/RunCMake/try_compile/NoArgs-stderr.txt
Tests/RunCMake/try_compile/NoCStandard-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/NoCStandard-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/NoCStandard.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/NoCopyFile-stderr.txt
Tests/RunCMake/try_compile/NoCopyFile.cmake
Tests/RunCMake/try_compile/NoCopyFile2-stderr.txt
Tests/RunCMake/try_compile/NoCopyFile2.cmake
Tests/RunCMake/try_compile/NoCopyFileError-stderr.txt
Tests/RunCMake/try_compile/NoCopyFileError.cmake
Tests/RunCMake/try_compile/NoOutputVariable-stderr.txt
Tests/RunCMake/try_compile/NoOutputVariable.cmake
Tests/RunCMake/try_compile/NoOutputVariable2-stderr.txt
Tests/RunCMake/try_compile/NoOutputVariable2.cmake
Tests/RunCMake/try_compile/NoSources-stderr.txt
Tests/RunCMake/try_compile/NonSourceCompileDefinitions-stderr.txt
Tests/RunCMake/try_compile/NonSourceCopyFile-stderr.txt
Tests/RunCMake/try_compile/ObjCStandard-stderr.txt
Tests/RunCMake/try_compile/ObjCStandard.cmake
Tests/RunCMake/try_compile/ObjCxxStandard-stderr.txt
Tests/RunCMake/try_compile/ObjCxxStandard.cmake
Tests/RunCMake/try_compile/OldProjectBinDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/OldProjectBinDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/OldProjectBinDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/OldProjectSrcDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/OneArg-stderr.txt
Tests/RunCMake/try_compile/OutputDirAsFlag.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/PlatformVariables-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectBinDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectBinDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectBinDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectCopyFile-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectCopyFile.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirMissing-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/ProjectSrcDirMissing.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/RunCMakeTest.cmake
Tests/RunCMake/try_compile/SourceFromBadFile-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromBadFile.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromBadName-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromBadName.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromOneArg-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromOneArg.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/TargetTypeExe.cmake
Tests/RunCMake/try_compile/TargetTypeInvalid-stderr.txt
Tests/RunCMake/try_compile/TargetTypeInvalid.cmake
Tests/RunCMake/try_compile/TargetTypeStatic.cmake
Tests/RunCMake/try_compile/TryRunArgs-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/TryRunArgs.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/TwoArgs-stderr.txt
Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_compile/new_signature.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/old_and_new_signature_tests.cmake [new file with mode: 0644]
Tests/RunCMake/try_compile/old_signature.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/BadLinkLibraries-stderr.txt
Tests/RunCMake/try_run/BadLinkLibraries.cmake
Tests/RunCMake/try_run/BinDirEmpty-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/BinDirEmpty-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/BinDirEmpty.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/BinDirRelative-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/BinDirRelative-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/BinDirRelative.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoCompileOutputVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoCompileOutputVariable-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoCompileOutputVariable.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoOutputCompileVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoOutputVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoOutputVariable-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoOutputVariable.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunOutputVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunOutputVariable-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunOutputVariable.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdErrVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdErrVariable-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdErrVariable.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdOutVariable-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdOutVariable-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoRunStdOutVariable.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/NoWorkingDirectory-result.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoWorkingDirectory-stderr.txt [new file with mode: 0644]
Tests/RunCMake/try_run/NoWorkingDirectory.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/RunCMakeTest.cmake
Tests/RunCMake/try_run/WorkingDirArg.cmake
Tests/RunCMake/try_run/new_signature.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/old_and_new_signature_tests.cmake [new file with mode: 0644]
Tests/RunCMake/try_run/old_signature.cmake [new file with mode: 0644]
Tests/SwiftMix/CMain.c
Tests/SwiftMix/CMakeLists.txt
Tests/SwiftMix/SwiftMain.swift
Tests/SwiftMixLib/CMakeLists.txt [new file with mode: 0644]
Tests/SwiftMixLib/lib.c [new file with mode: 0644]
Tests/SwiftMixLib/lib.cpp [new file with mode: 0644]
Tests/SwiftMixLib/lib.swift [new file with mode: 0644]
Tests/SwiftMixLib/main.swift [new file with mode: 0644]
Tests/SwiftOnly/CMakeLists.txt
Tests/SwiftOnly/SubA/CMakeLists.txt [new file with mode: 0644]
Tests/SwiftOnly/SubA/SubA.swift [new file with mode: 0644]
Tests/SwiftOnly/SubB/CMakeLists.txt [new file with mode: 0644]
Tests/SwiftOnly/SubB/SubB.swift [new file with mode: 0644]
Tests/SwiftOnly/main.swift
Tests/TryCompile/CMakeLists.txt
Tests/TryCompile/exit_with_error.c
Tests/TryCompile/old_and_new_signature_tests.cmake [new file with mode: 0644]
Tests/TryCompile/stdout_and_stderr.c [new file with mode: 0644]
Tests/VSGNUFortran/CMakeLists.txt
Tests/VSWinStorePhone/VerifyAppPackage.cmake
Utilities/Doxygen/CMakeLists.txt
Utilities/IWYU/mapping.imp
Utilities/Release/WiX/CustomAction/CMakeLists.txt
Utilities/Release/linux/aarch64/Dockerfile
Utilities/Release/linux/x86_64/Dockerfile
Utilities/Scripts/update-curl.bash
Utilities/Scripts/update-libuv.bash
Utilities/Sphinx/CMakeLists.txt
Utilities/Sphinx/cmake.py
Utilities/Sphinx/static/cmake.css
Utilities/cmThirdPartyChecks.cmake
Utilities/cmcurl/CMake/CMakeConfigurableFile.in
Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
Utilities/cmcurl/CMake/CurlTests.c
Utilities/cmcurl/CMake/FindBearSSL.cmake
Utilities/cmcurl/CMake/FindBrotli.cmake
Utilities/cmcurl/CMake/FindCARES.cmake
Utilities/cmcurl/CMake/FindGSS.cmake
Utilities/cmcurl/CMake/FindLibPSL.cmake [new file with mode: 0644]
Utilities/cmcurl/CMake/FindLibSSH2.cmake
Utilities/cmcurl/CMake/FindMSH3.cmake
Utilities/cmcurl/CMake/FindMbedTLS.cmake
Utilities/cmcurl/CMake/FindNGHTTP2.cmake
Utilities/cmcurl/CMake/FindNGHTTP3.cmake
Utilities/cmcurl/CMake/FindNGTCP2.cmake
Utilities/cmcurl/CMake/FindNSS.cmake
Utilities/cmcurl/CMake/FindQUICHE.cmake
Utilities/cmcurl/CMake/FindWolfSSL.cmake
Utilities/cmcurl/CMake/FindZstd.cmake
Utilities/cmcurl/CMake/Macros.cmake
Utilities/cmcurl/CMake/OtherTests.cmake
Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
Utilities/cmcurl/CMake/Utilities.cmake
Utilities/cmcurl/CMake/cmake_uninstall.cmake.in
Utilities/cmcurl/CMake/curl-config.cmake.in
Utilities/cmcurl/CMakeLists.txt
Utilities/cmcurl/include/curl/curl.h
Utilities/cmcurl/include/curl/curlver.h
Utilities/cmcurl/include/curl/easy.h
Utilities/cmcurl/include/curl/header.h
Utilities/cmcurl/include/curl/mprintf.h
Utilities/cmcurl/include/curl/multi.h
Utilities/cmcurl/include/curl/options.h
Utilities/cmcurl/include/curl/stdcheaders.h
Utilities/cmcurl/include/curl/system.h
Utilities/cmcurl/include/curl/typecheck-gcc.h
Utilities/cmcurl/include/curl/urlapi.h
Utilities/cmcurl/include/curl/websockets.h [new file with mode: 0644]
Utilities/cmcurl/lib/CMakeLists.txt
Utilities/cmcurl/lib/Makefile.inc
Utilities/cmcurl/lib/altsvc.c
Utilities/cmcurl/lib/altsvc.h
Utilities/cmcurl/lib/amigaos.c
Utilities/cmcurl/lib/amigaos.h
Utilities/cmcurl/lib/arpa_telnet.h
Utilities/cmcurl/lib/asyn-ares.c
Utilities/cmcurl/lib/asyn-thread.c
Utilities/cmcurl/lib/asyn.h
Utilities/cmcurl/lib/base64.c
Utilities/cmcurl/lib/bufref.c
Utilities/cmcurl/lib/bufref.h
Utilities/cmcurl/lib/c-hyper.c
Utilities/cmcurl/lib/c-hyper.h
Utilities/cmcurl/lib/conncache.c
Utilities/cmcurl/lib/conncache.h
Utilities/cmcurl/lib/connect.c
Utilities/cmcurl/lib/connect.h
Utilities/cmcurl/lib/content_encoding.c
Utilities/cmcurl/lib/content_encoding.h
Utilities/cmcurl/lib/cookie.c
Utilities/cmcurl/lib/cookie.h
Utilities/cmcurl/lib/curl_addrinfo.c
Utilities/cmcurl/lib/curl_addrinfo.h
Utilities/cmcurl/lib/curl_base64.h
Utilities/cmcurl/lib/curl_config.h.cmake
Utilities/cmcurl/lib/curl_ctype.c [deleted file]
Utilities/cmcurl/lib/curl_ctype.h
Utilities/cmcurl/lib/curl_des.c
Utilities/cmcurl/lib/curl_des.h
Utilities/cmcurl/lib/curl_endian.c
Utilities/cmcurl/lib/curl_endian.h
Utilities/cmcurl/lib/curl_fnmatch.c
Utilities/cmcurl/lib/curl_fnmatch.h
Utilities/cmcurl/lib/curl_get_line.c
Utilities/cmcurl/lib/curl_get_line.h
Utilities/cmcurl/lib/curl_gethostname.c
Utilities/cmcurl/lib/curl_gethostname.h
Utilities/cmcurl/lib/curl_gssapi.c
Utilities/cmcurl/lib/curl_gssapi.h
Utilities/cmcurl/lib/curl_hmac.h
Utilities/cmcurl/lib/curl_krb5.h
Utilities/cmcurl/lib/curl_ldap.h
Utilities/cmcurl/lib/curl_md4.h
Utilities/cmcurl/lib/curl_md5.h
Utilities/cmcurl/lib/curl_memory.h
Utilities/cmcurl/lib/curl_memrchr.c
Utilities/cmcurl/lib/curl_memrchr.h
Utilities/cmcurl/lib/curl_multibyte.c
Utilities/cmcurl/lib/curl_multibyte.h
Utilities/cmcurl/lib/curl_ntlm_core.c
Utilities/cmcurl/lib/curl_ntlm_core.h
Utilities/cmcurl/lib/curl_ntlm_wb.c
Utilities/cmcurl/lib/curl_ntlm_wb.h
Utilities/cmcurl/lib/curl_path.c
Utilities/cmcurl/lib/curl_path.h
Utilities/cmcurl/lib/curl_printf.h
Utilities/cmcurl/lib/curl_range.c
Utilities/cmcurl/lib/curl_range.h
Utilities/cmcurl/lib/curl_rtmp.c
Utilities/cmcurl/lib/curl_rtmp.h
Utilities/cmcurl/lib/curl_sasl.c
Utilities/cmcurl/lib/curl_sasl.h
Utilities/cmcurl/lib/curl_setup.h
Utilities/cmcurl/lib/curl_setup_once.h
Utilities/cmcurl/lib/curl_sha256.h
Utilities/cmcurl/lib/curl_sspi.c
Utilities/cmcurl/lib/curl_sspi.h
Utilities/cmcurl/lib/curl_threads.c
Utilities/cmcurl/lib/curl_threads.h
Utilities/cmcurl/lib/curlx.h
Utilities/cmcurl/lib/dict.c
Utilities/cmcurl/lib/dict.h
Utilities/cmcurl/lib/doh.c
Utilities/cmcurl/lib/doh.h
Utilities/cmcurl/lib/dotdot.c [deleted file]
Utilities/cmcurl/lib/dynbuf.c
Utilities/cmcurl/lib/dynbuf.h
Utilities/cmcurl/lib/easy.c
Utilities/cmcurl/lib/easy_lock.h [new file with mode: 0644]
Utilities/cmcurl/lib/easygetopt.c
Utilities/cmcurl/lib/easyif.h
Utilities/cmcurl/lib/easyoptions.c
Utilities/cmcurl/lib/easyoptions.h
Utilities/cmcurl/lib/escape.c
Utilities/cmcurl/lib/escape.h
Utilities/cmcurl/lib/file.c
Utilities/cmcurl/lib/file.h
Utilities/cmcurl/lib/fileinfo.c
Utilities/cmcurl/lib/fileinfo.h
Utilities/cmcurl/lib/fopen.c [new file with mode: 0644]
Utilities/cmcurl/lib/fopen.h [moved from Utilities/cmcurl/lib/vssh/wolfssh.h with 77% similarity]
Utilities/cmcurl/lib/formdata.c
Utilities/cmcurl/lib/formdata.h
Utilities/cmcurl/lib/ftp.c
Utilities/cmcurl/lib/ftp.h
Utilities/cmcurl/lib/ftplistparser.c
Utilities/cmcurl/lib/ftplistparser.h
Utilities/cmcurl/lib/functypes.h [new file with mode: 0644]
Utilities/cmcurl/lib/getenv.c
Utilities/cmcurl/lib/getinfo.c
Utilities/cmcurl/lib/getinfo.h
Utilities/cmcurl/lib/gopher.c
Utilities/cmcurl/lib/gopher.h
Utilities/cmcurl/lib/h2h3.c
Utilities/cmcurl/lib/h2h3.h
Utilities/cmcurl/lib/hash.c
Utilities/cmcurl/lib/hash.h
Utilities/cmcurl/lib/headers.c
Utilities/cmcurl/lib/headers.h
Utilities/cmcurl/lib/hmac.c
Utilities/cmcurl/lib/hostasyn.c
Utilities/cmcurl/lib/hostip.c
Utilities/cmcurl/lib/hostip.h
Utilities/cmcurl/lib/hostip4.c
Utilities/cmcurl/lib/hostip6.c
Utilities/cmcurl/lib/hostsyn.c
Utilities/cmcurl/lib/hsts.c
Utilities/cmcurl/lib/hsts.h
Utilities/cmcurl/lib/http.c
Utilities/cmcurl/lib/http.h
Utilities/cmcurl/lib/http2.c
Utilities/cmcurl/lib/http2.h
Utilities/cmcurl/lib/http_aws_sigv4.c
Utilities/cmcurl/lib/http_aws_sigv4.h
Utilities/cmcurl/lib/http_chunks.c
Utilities/cmcurl/lib/http_chunks.h
Utilities/cmcurl/lib/http_digest.c
Utilities/cmcurl/lib/http_digest.h
Utilities/cmcurl/lib/http_negotiate.c
Utilities/cmcurl/lib/http_negotiate.h
Utilities/cmcurl/lib/http_ntlm.c
Utilities/cmcurl/lib/http_ntlm.h
Utilities/cmcurl/lib/http_proxy.c
Utilities/cmcurl/lib/http_proxy.h
Utilities/cmcurl/lib/idn_win32.c
Utilities/cmcurl/lib/if2ip.c
Utilities/cmcurl/lib/if2ip.h
Utilities/cmcurl/lib/imap.c
Utilities/cmcurl/lib/imap.h
Utilities/cmcurl/lib/inet_ntop.c
Utilities/cmcurl/lib/inet_ntop.h
Utilities/cmcurl/lib/inet_pton.c
Utilities/cmcurl/lib/inet_pton.h
Utilities/cmcurl/lib/krb5.c
Utilities/cmcurl/lib/ldap.c
Utilities/cmcurl/lib/libcurl.rc
Utilities/cmcurl/lib/llist.c
Utilities/cmcurl/lib/llist.h
Utilities/cmcurl/lib/md4.c
Utilities/cmcurl/lib/md5.c
Utilities/cmcurl/lib/memdebug.c
Utilities/cmcurl/lib/memdebug.h
Utilities/cmcurl/lib/mime.c
Utilities/cmcurl/lib/mime.h
Utilities/cmcurl/lib/mprintf.c
Utilities/cmcurl/lib/mqtt.c
Utilities/cmcurl/lib/mqtt.h
Utilities/cmcurl/lib/multi.c
Utilities/cmcurl/lib/multihandle.h
Utilities/cmcurl/lib/multiif.h
Utilities/cmcurl/lib/netrc.c
Utilities/cmcurl/lib/netrc.h
Utilities/cmcurl/lib/nonblock.c
Utilities/cmcurl/lib/nonblock.h
Utilities/cmcurl/lib/noproxy.c [new file with mode: 0644]
Utilities/cmcurl/lib/noproxy.h [moved from Utilities/cmcurl/lib/dotdot.h with 57% similarity]
Utilities/cmcurl/lib/openldap.c
Utilities/cmcurl/lib/parsedate.c
Utilities/cmcurl/lib/parsedate.h
Utilities/cmcurl/lib/pingpong.c
Utilities/cmcurl/lib/pingpong.h
Utilities/cmcurl/lib/pop3.c
Utilities/cmcurl/lib/pop3.h
Utilities/cmcurl/lib/progress.c
Utilities/cmcurl/lib/progress.h
Utilities/cmcurl/lib/psl.c
Utilities/cmcurl/lib/psl.h
Utilities/cmcurl/lib/quic.h
Utilities/cmcurl/lib/rand.c
Utilities/cmcurl/lib/rand.h
Utilities/cmcurl/lib/rename.c
Utilities/cmcurl/lib/rename.h
Utilities/cmcurl/lib/rtsp.c
Utilities/cmcurl/lib/rtsp.h
Utilities/cmcurl/lib/select.c
Utilities/cmcurl/lib/select.h
Utilities/cmcurl/lib/sendf.c
Utilities/cmcurl/lib/sendf.h
Utilities/cmcurl/lib/setopt.c
Utilities/cmcurl/lib/setopt.h
Utilities/cmcurl/lib/setup-os400.h
Utilities/cmcurl/lib/setup-vms.h
Utilities/cmcurl/lib/setup-win32.h
Utilities/cmcurl/lib/sha256.c
Utilities/cmcurl/lib/share.c
Utilities/cmcurl/lib/share.h
Utilities/cmcurl/lib/sigpipe.h
Utilities/cmcurl/lib/slist.c
Utilities/cmcurl/lib/slist.h
Utilities/cmcurl/lib/smb.c
Utilities/cmcurl/lib/smb.h
Utilities/cmcurl/lib/smtp.c
Utilities/cmcurl/lib/smtp.h
Utilities/cmcurl/lib/sockaddr.h
Utilities/cmcurl/lib/socketpair.c
Utilities/cmcurl/lib/socketpair.h
Utilities/cmcurl/lib/socks.c
Utilities/cmcurl/lib/socks.h
Utilities/cmcurl/lib/socks_gssapi.c
Utilities/cmcurl/lib/socks_sspi.c
Utilities/cmcurl/lib/speedcheck.c
Utilities/cmcurl/lib/speedcheck.h
Utilities/cmcurl/lib/splay.c
Utilities/cmcurl/lib/splay.h
Utilities/cmcurl/lib/strcase.c
Utilities/cmcurl/lib/strcase.h
Utilities/cmcurl/lib/strdup.c
Utilities/cmcurl/lib/strdup.h
Utilities/cmcurl/lib/strerror.c
Utilities/cmcurl/lib/strerror.h
Utilities/cmcurl/lib/strtok.c
Utilities/cmcurl/lib/strtok.h
Utilities/cmcurl/lib/strtoofft.c
Utilities/cmcurl/lib/strtoofft.h
Utilities/cmcurl/lib/system_win32.c
Utilities/cmcurl/lib/system_win32.h
Utilities/cmcurl/lib/telnet.c
Utilities/cmcurl/lib/telnet.h
Utilities/cmcurl/lib/tftp.c
Utilities/cmcurl/lib/tftp.h
Utilities/cmcurl/lib/timediff.c
Utilities/cmcurl/lib/timediff.h
Utilities/cmcurl/lib/timeval.c
Utilities/cmcurl/lib/timeval.h
Utilities/cmcurl/lib/transfer.c
Utilities/cmcurl/lib/transfer.h
Utilities/cmcurl/lib/url.c
Utilities/cmcurl/lib/url.h
Utilities/cmcurl/lib/urlapi-int.h
Utilities/cmcurl/lib/urlapi.c
Utilities/cmcurl/lib/urldata.h
Utilities/cmcurl/lib/vauth/cleartext.c
Utilities/cmcurl/lib/vauth/cram.c
Utilities/cmcurl/lib/vauth/digest.c
Utilities/cmcurl/lib/vauth/digest.h
Utilities/cmcurl/lib/vauth/digest_sspi.c
Utilities/cmcurl/lib/vauth/gsasl.c
Utilities/cmcurl/lib/vauth/krb5_gssapi.c
Utilities/cmcurl/lib/vauth/krb5_sspi.c
Utilities/cmcurl/lib/vauth/ntlm.c
Utilities/cmcurl/lib/vauth/ntlm.h
Utilities/cmcurl/lib/vauth/ntlm_sspi.c
Utilities/cmcurl/lib/vauth/oauth2.c
Utilities/cmcurl/lib/vauth/spnego_gssapi.c
Utilities/cmcurl/lib/vauth/spnego_sspi.c
Utilities/cmcurl/lib/vauth/vauth.c
Utilities/cmcurl/lib/vauth/vauth.h
Utilities/cmcurl/lib/version.c
Utilities/cmcurl/lib/version_win32.c
Utilities/cmcurl/lib/version_win32.h
Utilities/cmcurl/lib/vquic/msh3.c
Utilities/cmcurl/lib/vquic/msh3.h
Utilities/cmcurl/lib/vquic/ngtcp2.c
Utilities/cmcurl/lib/vquic/ngtcp2.h
Utilities/cmcurl/lib/vquic/quiche.c
Utilities/cmcurl/lib/vquic/quiche.h
Utilities/cmcurl/lib/vquic/vquic.c
Utilities/cmcurl/lib/vquic/vquic.h
Utilities/cmcurl/lib/vssh/libssh.c
Utilities/cmcurl/lib/vssh/libssh2.c
Utilities/cmcurl/lib/vssh/ssh.h
Utilities/cmcurl/lib/vssh/wolfssh.c
Utilities/cmcurl/lib/vtls/bearssl.c
Utilities/cmcurl/lib/vtls/bearssl.h
Utilities/cmcurl/lib/vtls/gskit.c
Utilities/cmcurl/lib/vtls/gskit.h
Utilities/cmcurl/lib/vtls/gtls.c
Utilities/cmcurl/lib/vtls/gtls.h
Utilities/cmcurl/lib/vtls/hostcheck.c
Utilities/cmcurl/lib/vtls/hostcheck.h
Utilities/cmcurl/lib/vtls/keylog.c
Utilities/cmcurl/lib/vtls/keylog.h
Utilities/cmcurl/lib/vtls/mbedtls.c
Utilities/cmcurl/lib/vtls/mbedtls.h
Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
Utilities/cmcurl/lib/vtls/mbedtls_threadlock.h
Utilities/cmcurl/lib/vtls/nss.c
Utilities/cmcurl/lib/vtls/nssg.h
Utilities/cmcurl/lib/vtls/openssl.c
Utilities/cmcurl/lib/vtls/openssl.h
Utilities/cmcurl/lib/vtls/rustls.c
Utilities/cmcurl/lib/vtls/rustls.h
Utilities/cmcurl/lib/vtls/schannel.c
Utilities/cmcurl/lib/vtls/schannel.h
Utilities/cmcurl/lib/vtls/schannel_verify.c
Utilities/cmcurl/lib/vtls/sectransp.c
Utilities/cmcurl/lib/vtls/sectransp.h
Utilities/cmcurl/lib/vtls/vtls.c
Utilities/cmcurl/lib/vtls/vtls.h
Utilities/cmcurl/lib/vtls/wolfssl.c
Utilities/cmcurl/lib/vtls/wolfssl.h
Utilities/cmcurl/lib/vtls/x509asn1.c
Utilities/cmcurl/lib/vtls/x509asn1.h
Utilities/cmcurl/lib/warnless.c
Utilities/cmcurl/lib/warnless.h
Utilities/cmcurl/lib/wildcard.c
Utilities/cmcurl/lib/wildcard.h
Utilities/cmcurl/lib/ws.c [new file with mode: 0644]
Utilities/cmcurl/lib/ws.h [new file with mode: 0644]
Utilities/cmexpat/CMakeLists.txt
Utilities/cmjsoncpp/CMakeLists.txt
Utilities/cmlibarchive/CMakeLists.txt
Utilities/cmlibuv/CMakeLists.txt
Utilities/cmlibuv/include/uv.h
Utilities/cmlibuv/include/uv/version.h
Utilities/cmlibuv/include/uv/win.h
Utilities/cmlibuv/src/fs-poll.c
Utilities/cmlibuv/src/idna.c
Utilities/cmlibuv/src/strscpy.h
Utilities/cmlibuv/src/strtok.c [new file with mode: 0644]
Utilities/cmlibuv/src/strtok.h [new file with mode: 0644]
Utilities/cmlibuv/src/unix/atomic-ops.h
Utilities/cmlibuv/src/unix/bsd-ifaddrs.c
Utilities/cmlibuv/src/unix/bsd-proctitle.c
Utilities/cmlibuv/src/unix/cmake-bootstrap.c
Utilities/cmlibuv/src/unix/core.c
Utilities/cmlibuv/src/unix/freebsd.c
Utilities/cmlibuv/src/unix/fs.c
Utilities/cmlibuv/src/unix/hurd.c [new file with mode: 0644]
Utilities/cmlibuv/src/unix/internal.h
Utilities/cmlibuv/src/unix/kqueue.c
Utilities/cmlibuv/src/unix/linux-core.c
Utilities/cmlibuv/src/unix/os390-syscalls.c
Utilities/cmlibuv/src/unix/os390.c
Utilities/cmlibuv/src/unix/pipe.c
Utilities/cmlibuv/src/unix/process.c
Utilities/cmlibuv/src/unix/stream.c
Utilities/cmlibuv/src/unix/sunos.c
Utilities/cmlibuv/src/unix/tcp.c
Utilities/cmlibuv/src/unix/thread.c
Utilities/cmlibuv/src/unix/tty.c
Utilities/cmlibuv/src/unix/udp.c
Utilities/cmlibuv/src/uv-common.c
Utilities/cmlibuv/src/uv-common.h
Utilities/cmlibuv/src/win/async.c
Utilities/cmlibuv/src/win/core.c
Utilities/cmlibuv/src/win/fs-event.c
Utilities/cmlibuv/src/win/fs.c
Utilities/cmlibuv/src/win/handle-inl.h
Utilities/cmlibuv/src/win/handle.c
Utilities/cmlibuv/src/win/internal.h
Utilities/cmlibuv/src/win/loop-watcher.c
Utilities/cmlibuv/src/win/pipe.c
Utilities/cmlibuv/src/win/poll.c
Utilities/cmlibuv/src/win/process.c
Utilities/cmlibuv/src/win/req-inl.h
Utilities/cmlibuv/src/win/signal.c
Utilities/cmlibuv/src/win/stream-inl.h
Utilities/cmlibuv/src/win/stream.c
Utilities/cmlibuv/src/win/tcp.c
Utilities/cmlibuv/src/win/thread.c
Utilities/cmlibuv/src/win/tty.c
Utilities/cmlibuv/src/win/udp.c
Utilities/cmlibuv/src/win/util.c
Utilities/cmlibuv/src/win/winapi.c
Utilities/cmlibuv/src/win/winsock.c
Utilities/std/cmext/enum_set
bootstrap

index 8224d9e..a11becb 100644 (file)
@@ -41,8 +41,8 @@ set the path with these commands:
   :group 'cmake)
 
 ;; Keywords
-(defconst cmake-keywords-block-open '("IF" "MACRO" "FOREACH" "ELSE" "ELSEIF" "WHILE" "FUNCTION"))
-(defconst cmake-keywords-block-close '("ENDIF" "ENDFOREACH" "ENDMACRO" "ELSE" "ELSEIF" "ENDWHILE" "ENDFUNCTION"))
+(defconst cmake-keywords-block-open '("BLOCK" "IF" "MACRO" "FOREACH" "ELSE" "ELSEIF" "WHILE" "FUNCTION"))
+(defconst cmake-keywords-block-close '("ENDBLOCK" "ENDIF" "ENDFOREACH" "ENDMACRO" "ELSE" "ELSEIF" "ENDWHILE" "ENDFUNCTION"))
 (defconst cmake-keywords
   (let ((kwds (append cmake-keywords-block-open cmake-keywords-block-close nil)))
     (delete-dups kwds)))
@@ -288,6 +288,39 @@ This puts the mark at the end, and point at the beginning."
 
 ;------------------------------------------------------------------------------
 
+(defun cmake--syntax-propertize-until-bracket-close (syntax)
+  ;; This function assumes that a previous search has matched the
+  ;; beginning of a bracket_comment or bracket_argument and that the
+  ;; second capture group has matched the equal signs between the two
+  ;; opening brackets
+  (let* ((mb (match-beginning 2))
+         (me (match-end 2))
+         (cb (format "]%s]" (buffer-substring mb me))))
+    (save-match-data
+      (if (search-forward cb end 'move)
+          (progn
+            (setq me (match-end 0))
+            (put-text-property
+             (1- me)
+             me
+             'syntax-table
+             (string-to-syntax syntax)))
+        (setq me end)))
+    (put-text-property
+     (match-beginning 1)
+     me
+     'syntax-multiline
+     t)))
+
+(defconst cmake--syntax-propertize-function
+  (syntax-propertize-rules
+   ("\\(#\\)\\[\\(=*\\)\\["
+    (1
+     (prog1 "!" (cmake--syntax-propertize-until-bracket-close "!"))))
+   ("\\(\\[\\)\\(=*\\)\\["
+    (1
+     (prog1 "|" (cmake--syntax-propertize-until-bracket-close "|"))))))
+
 ;; Syntax table for this mode.
 (defvar cmake-mode-syntax-table nil
   "Syntax table for CMake mode.")
@@ -318,7 +351,10 @@ This puts the mark at the end, and point at the beginning."
   ; Setup indentation function.
   (set (make-local-variable 'indent-line-function) 'cmake-indent)
   ; Setup comment syntax.
-  (set (make-local-variable 'comment-start) "#"))
+  (set (make-local-variable 'comment-start) "#")
+  ;; Setup syntax propertization
+  (set (make-local-variable 'syntax-propertize-function) cmake--syntax-propertize-function)
+  (add-hook 'syntax-propertize-extend-region-functions #'syntax-propertize-multiline nil t))
 
 ;; Default cmake-mode key bindings
 (define-key cmake-mode-map "\e\C-a" #'cmake-beginning-of-defun)
index 672bdcc..0c662fa 100644 (file)
@@ -59,8 +59,8 @@ fun! CMakeGetIndent(lnum)
 
   let cmake_closing_parens_line = '^\s*\()\+\)\s*$'
 
-  let cmake_indent_begin_regex = '^\s*\(IF\|MACRO\|FOREACH\|ELSE\|ELSEIF\|WHILE\|FUNCTION\)\s*('
-  let cmake_indent_end_regex = '^\s*\(ENDIF\|ENDFOREACH\|ENDMACRO\|ELSE\|ELSEIF\|ENDWHILE\|ENDFUNCTION\)\s*('
+  let cmake_indent_begin_regex = '^\s*\(BLOCK\|IF\|MACRO\|FOREACH\|ELSE\|ELSEIF\|WHILE\|FUNCTION\)\s*('
+  let cmake_indent_end_regex = '^\s*\(ENDBLOCK\|ENDIF\|ENDFOREACH\|ENDMACRO\|ELSE\|ELSEIF\|ENDWHILE\|ENDFUNCTION\)\s*('
 
   if this_line =~? cmake_closing_parens_line
     if previous_line !~? cmake_indent_open_regex
index e1a2885..9eb993a 100644 (file)
@@ -436,6 +436,7 @@ syn keyword cmakeProperty contained
             \ XCODE_SCHEME_ENVIRONMENT
             \ XCODE_SCHEME_EXECUTABLE
             \ XCODE_SCHEME_GUARD_MALLOC
+            \ XCODE_SCHEME_LAUNCH_MODE
             \ XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
             \ XCODE_SCHEME_MALLOC_GUARD_EDGES
             \ XCODE_SCHEME_MALLOC_SCRIBBLE
@@ -444,6 +445,9 @@ syn keyword cmakeProperty contained
             \ XCODE_SCHEME_THREAD_SANITIZER_STOP
             \ XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER
             \ XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP
+            \ XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+            \ XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
+            \ XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ XCODE_SCHEME_WORKING_DIRECTORY
             \ XCODE_SCHEME_ZOMBIE_OBJECTS
             \ XCTEST
@@ -1537,6 +1541,7 @@ syn keyword cmakeVariable contained
             \ CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
             \ CMAKE_XCODE_SCHEME_ENVIRONMENT
             \ CMAKE_XCODE_SCHEME_GUARD_MALLOC
+            \ CMAKE_XCODE_SCHEME_LAUNCH_MODE
             \ CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
             \ CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES
             \ CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE
@@ -1545,6 +1550,9 @@ syn keyword cmakeVariable contained
             \ CMAKE_XCODE_SCHEME_THREAD_SANITIZER_STOP
             \ CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER
             \ CMAKE_XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP
+            \ CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+            \ CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
+            \ CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ CMAKE_XCODE_SCHEME_WORKING_DIRECTORY
             \ CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS
             \ CPACK_ABSOLUTE_DESTINATION_FILES
@@ -3830,6 +3838,7 @@ syn keyword cmakeCommand
             \ add_subdirectory
             \ add_test
             \ aux_source_directory
+            \ block
             \ break
             \ build_command
             \ cmake_host_system_information
@@ -3857,6 +3866,7 @@ syn keyword cmakeCommand
             \ define_property
             \ enable_language
             \ enable_testing
+            \ endblock
             \ endfunction
             \ endmacro
             \ execute_process
index 9de5338..2b9eb2d 100644 (file)
@@ -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.22 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.23 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)
 
@@ -17,8 +17,8 @@ unset(CMAKE_USER_MAKE_RULES_OVERRIDE_C)
 if(MSVC AND NOT CMAKE_VERSION VERSION_LESS 3.15)
   # Filter out MSVC runtime library flags that may have come from
   # the cache of an existing build tree or from scripts.
-  foreach(l C CXX)
-    foreach(c DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
+  foreach(l IN ITEMS C CXX)
+    foreach(c IN ITEMS DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
       string(REGEX REPLACE "[-/]M[DT]d?( |$)" "" "CMAKE_${l}_FLAGS_${c}" "${CMAKE_${l}_FLAGS_${c}}")
     endforeach()
   endforeach()
@@ -60,17 +60,6 @@ else()
   set(USE_LGPL "")
 endif()
 
-if("${CMake_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
-  # Disallow architecture-specific try_run.  It may not run on the host.
-  macro(TRY_RUN)
-    if(CMAKE_TRY_COMPILE_OSX_ARCHITECTURES)
-      message(FATAL_ERROR "TRY_RUN not allowed with CMAKE_TRY_COMPILE_OSX_ARCHITECTURES=[${CMAKE_TRY_COMPILE_OSX_ARCHITECTURES}]")
-    else()
-      _TRY_RUN(${ARGV})
-    endif()
-  endmacro()
-endif()
-
 # Use most-recent available language dialects with GNU and Clang
 if(NOT DEFINED CMAKE_C_STANDARD AND NOT CMake_NO_C_STANDARD)
   include(${CMake_SOURCE_DIR}/Source/Checks/cm_c11_thread_local.cmake)
@@ -81,7 +70,7 @@ if(NOT DEFINED CMAKE_C_STANDARD AND NOT CMake_NO_C_STANDARD)
   endif()
 endif()
 if(NOT DEFINED CMAKE_CXX_STANDARD AND NOT CMake_NO_CXX_STANDARD)
-  if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.14)
+  if(CMAKE_CXX_COMPILER_ID STREQUAL SunPro AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.14)
     set(CMAKE_CXX_STANDARD 98)
   else()
     if(NOT CMAKE_VERSION VERSION_LESS 3.8)
@@ -114,9 +103,7 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
 endif()
 
 # Inform STL library header wrappers whether to use system versions.
-configure_file(${CMake_SOURCE_DIR}/Utilities/std/cmSTL.hxx.in
-  ${CMake_BINARY_DIR}/Utilities/cmSTL.hxx
-  @ONLY)
+configure_file(Utilities/std/cmSTL.hxx.in Utilities/cmSTL.hxx @ONLY)
 
 # set the internal encoding of CMake to UTF-8
 set(KWSYS_ENCODING_DEFAULT_CODEPAGE CP_UTF8)
@@ -143,7 +130,7 @@ option(CMake_BUILD_DEVELOPER_REFERENCE
 mark_as_advanced(CMake_BUILD_DEVELOPER_REFERENCE)
 
 # option to build using interprocedural optimizations (IPO/LTO)
-if (NOT CMAKE_VERSION VERSION_LESS 3.12.2)
+if(NOT CMAKE_VERSION VERSION_LESS 3.12.2)
   option(CMake_BUILD_LTO "Compile CMake with link-time optimization if supported" OFF)
   if(CMake_BUILD_LTO)
     include(CheckIPOSupported)
@@ -165,7 +152,7 @@ macro(CMAKE_HANDLE_SYSTEM_LIBRARIES)
   # Allow the user to enable/disable all system utility library options by
   # defining CMAKE_USE_SYSTEM_LIBRARIES or CMAKE_USE_SYSTEM_LIBRARY_${util}.
   set(UTILITIES BZIP2 CURL EXPAT FORM JSONCPP LIBARCHIVE LIBLZMA LIBRHASH LIBUV NGHTTP2 ZLIB ZSTD)
-  foreach(util ${UTILITIES})
+  foreach(util IN LISTS UTILITIES)
     if(NOT DEFINED CMAKE_USE_SYSTEM_LIBRARY_${util}
         AND DEFINED CMAKE_USE_SYSTEM_LIBRARIES)
       set(CMAKE_USE_SYSTEM_LIBRARY_${util} "${CMAKE_USE_SYSTEM_LIBRARIES}")
@@ -215,16 +202,14 @@ macro(CMAKE_HANDLE_SYSTEM_LIBRARIES)
   mark_as_advanced(CMAKE_USE_SYSTEM_KWIML)
 
   # Mention to the user what system libraries are being used.
-  foreach(util ${UTILITIES} KWIML)
+  foreach(util IN LISTS UTILITIES ITEMS KWIML)
     if(CMAKE_USE_SYSTEM_${util})
       message(STATUS "Using system-installed ${util}")
     endif()
   endforeach()
 
   # Inform utility library header wrappers whether to use system versions.
-  configure_file(${CMake_SOURCE_DIR}/Utilities/cmThirdParty.h.in
-    ${CMake_BINARY_DIR}/Utilities/cmThirdParty.h
-    @ONLY)
+  configure_file(Utilities/cmThirdParty.h.in Utilities/cmThirdParty.h @ONLY)
 
 endmacro()
 
@@ -235,7 +220,7 @@ endmacro()
 macro(CMAKE_SETUP_TESTING)
   if(BUILD_TESTING)
     set(CMAKE_TEST_SYSTEM_LIBRARIES 0)
-    foreach(util CURL EXPAT ZLIB)
+    foreach(util IN ITEMS CURL EXPAT ZLIB)
       if(CMAKE_USE_SYSTEM_${util})
         set(CMAKE_TEST_SYSTEM_LIBRARIES 1)
       endif()
@@ -250,7 +235,7 @@ macro(CMAKE_SETUP_TESTING)
       set(CMAKE_CTEST_COMMAND "${CMake_TEST_EXTERNAL_CMAKE}/ctest")
       set(CMAKE_CMAKE_COMMAND "${CMake_TEST_EXTERNAL_CMAKE}/cmake")
       set(CMAKE_CPACK_COMMAND "${CMake_TEST_EXTERNAL_CMAKE}/cpack")
-      foreach(exe cmake ctest cpack)
+      foreach(exe IN ITEMS cmake ctest cpack)
         add_executable(${exe} IMPORTED)
         set_property(TARGET ${exe} PROPERTY IMPORTED_LOCATION ${CMake_TEST_EXTERNAL_CMAKE}/${exe})
       endforeach()
@@ -262,18 +247,12 @@ macro(CMAKE_SETUP_TESTING)
   endif()
 
   # configure some files for testing
-  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Templates/CTestScript.cmake.in"
-    "${CMAKE_CURRENT_BINARY_DIR}/CTestScript.cmake"
-    @ONLY)
-  configure_file(${CMake_SOURCE_DIR}/Tests/.NoDartCoverage
-    ${CMake_BINARY_DIR}/Tests/.NoDartCoverage)
-  configure_file(${CMake_SOURCE_DIR}/Tests/.NoDartCoverage
-    ${CMake_BINARY_DIR}/Modules/.NoDartCoverage)
-  configure_file(${CMake_SOURCE_DIR}/CTestCustom.cmake.in
-    ${CMake_BINARY_DIR}/CTestCustom.cmake @ONLY)
+  configure_file(Templates/CTestScript.cmake.in CTestScript.cmake @ONLY)
+  configure_file(Tests/.NoDartCoverage Tests/.NoDartCoverage)
+  configure_file(Tests/.NoDartCoverage Modules/.NoDartCoverage)
+  configure_file(CTestCustom.cmake.in CTestCustom.cmake @ONLY)
   if(BUILD_TESTING AND DART_ROOT)
-    configure_file(${CMake_SOURCE_DIR}/CMakeLogo.gif
-      ${CMake_BINARY_DIR}/Testing/HTML/TestingResults/Icons/Logo.gif COPYONLY)
+    configure_file(CMakeLogo.gif Testing/HTML/TestingResults/Icons/Logo.gif COPYONLY)
   endif()
   mark_as_advanced(DART_ROOT)
 endmacro()
@@ -339,390 +318,6 @@ macro(CMAKE_SET_TARGET_FOLDER tgt folder)
   endif()
 endmacro()
 
-
-#-----------------------------------------------------------------------
-# a macro to build the utilities used by CMake
-# Simply to improve readability of the main script.
-#-----------------------------------------------------------------------
-macro (CMAKE_BUILD_UTILITIES)
-  find_package(Threads)
-
-  # Suppress unnecessary checks in third-party code.
-  include(Utilities/cmThirdPartyChecks.cmake)
-
-  #---------------------------------------------------------------------
-  # Create the kwsys library for CMake.
-  set(KWSYS_NAMESPACE cmsys)
-  set(KWSYS_USE_SystemTools 1)
-  set(KWSYS_USE_Directory 1)
-  set(KWSYS_USE_RegularExpression 1)
-  set(KWSYS_USE_Base64 1)
-  set(KWSYS_USE_MD5 1)
-  set(KWSYS_USE_Process 1)
-  set(KWSYS_USE_CommandLineArguments 1)
-  set(KWSYS_USE_ConsoleBuf 1)
-  set(KWSYS_HEADER_ROOT ${CMake_BINARY_DIR}/Source)
-  set(KWSYS_INSTALL_DOC_DIR "${CMAKE_DOC_DIR}")
-  if(CMake_NO_CXX_STANDARD)
-    set(KWSYS_CXX_STANDARD "")
-  endif()
-  if(CMake_NO_SELF_BACKTRACE)
-    set(KWSYS_NO_EXECINFO 1)
-  endif()
-  if(WIN32)
-    # FIXME: Teach KWSys to hard-code these checks on Windows.
-    set(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC_COMPILED 0)
-    set(KWSYS_C_HAS_PTRDIFF_T_COMPILED 1)
-    set(KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H_COMPILED 1)
-    set(KWSYS_CXX_HAS_RLIMIT64_COMPILED 0)
-    set(KWSYS_CXX_HAS_SETENV_COMPILED 0)
-    set(KWSYS_CXX_HAS_UNSETENV_COMPILED 0)
-    set(KWSYS_CXX_HAS_UTIMENSAT_COMPILED 0)
-    set(KWSYS_CXX_HAS_UTIMES_COMPILED 0)
-    set(KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED 0)
-    set(KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED 0)
-    set(KWSYS_STL_HAS_WSTRING_COMPILED 1)
-    set(KWSYS_SYS_HAS_IFADDRS_H 0)
-  endif()
-  add_subdirectory(Source/kwsys)
-  set(kwsys_folder "Utilities/KWSys")
-  CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE} "${kwsys_folder}")
-  CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}_c "${kwsys_folder}")
-  if(BUILD_TESTING)
-    CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestDynload "${kwsys_folder}")
-    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()
-
-  #---------------------------------------------------------------------
-  # Setup third-party libraries.
-  # Everything in the tree should be able to include files from the
-  # Utilities directory.
-  if ((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-    # using -isystem option generate error "template with C linkage"
-    include_directories("${CMake_SOURCE_DIR}/Utilities/std")
-  else()
-    include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities/std")
-  endif()
-
-  include_directories("${CMake_BINARY_DIR}/Utilities")
-  if ((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-    # using -isystem option generate error "template with C linkage"
-    include_directories("${CMake_SOURCE_DIR}/Utilities")
-  else()
-    include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build CMake std library for CMake and CTest.
-  set(CMAKE_STD_LIBRARY cmstd)
-  add_subdirectory(Utilities/std)
-  CMAKE_SET_TARGET_FOLDER(cmstd "Utilities/std")
-
-  # check for the use of system libraries versus builtin ones
-  # (a macro defined in this file)
-  CMAKE_HANDLE_SYSTEM_LIBRARIES()
-
-  if(CMAKE_USE_SYSTEM_KWIML)
-    find_package(KWIML 1.0)
-    if(NOT KWIML_FOUND)
-      message(FATAL_ERROR "CMAKE_USE_SYSTEM_KWIML is ON but KWIML is not found!")
-    endif()
-    set(CMake_KWIML_LIBRARIES kwiml::kwiml)
-  else()
-    set(CMake_KWIML_LIBRARIES "")
-    if(BUILD_TESTING)
-      set(KWIML_TEST_ENABLE 1)
-    endif()
-    add_subdirectory(Utilities/KWIML)
-  endif()
-
-  if(CMAKE_USE_SYSTEM_LIBRHASH)
-    find_package(LibRHash)
-    if(NOT LibRHash_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_LIBRHASH is ON but LibRHash is not found!")
-    endif()
-    set(CMAKE_LIBRHASH_LIBRARIES LibRHash::LibRHash)
-  else()
-    set(CMAKE_LIBRHASH_LIBRARIES cmlibrhash)
-    add_subdirectory(Utilities/cmlibrhash)
-    CMAKE_SET_TARGET_FOLDER(cmlibrhash "Utilities/3rdParty")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build zlib library for Curl, CMake, and CTest.
-  set(CMAKE_ZLIB_HEADER "cm_zlib.h")
-  if(CMAKE_USE_SYSTEM_ZLIB)
-    find_package(ZLIB)
-    if(NOT ZLIB_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_ZLIB is ON but a zlib is not found!")
-    endif()
-    set(CMAKE_ZLIB_INCLUDES ${ZLIB_INCLUDE_DIR})
-    set(CMAKE_ZLIB_LIBRARIES ${ZLIB_LIBRARIES})
-  else()
-    set(CMAKE_ZLIB_INCLUDES ${CMake_SOURCE_DIR}/Utilities)
-    set(CMAKE_ZLIB_LIBRARIES cmzlib)
-    set(WITHOUT_ZLIB_DLL "")
-    set(WITHOUT_ZLIB_DLL_WITH_LIB cmzlib)
-    set(ZLIB_DLL "")
-    set(ZLIB_DLL_WITH_LIB cmzlib)
-    set(ZLIB_WINAPI "")
-    set(ZLIB_WINAPI_COMPILED 0)
-    set(ZLIB_WINAPI_WITH_LIB cmzlib)
-    add_subdirectory(Utilities/cmzlib)
-    CMAKE_SET_TARGET_FOLDER(cmzlib "Utilities/3rdParty")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build Curl library for CTest.
-  if(CMAKE_USE_SYSTEM_CURL)
-    find_package(CURL)
-    if(NOT CURL_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_CURL is ON but a curl is not found!")
-    endif()
-    set(CMAKE_CURL_INCLUDES ${CURL_INCLUDE_DIRS})
-    set(CMAKE_CURL_LIBRARIES ${CURL_LIBRARIES})
-  else()
-    set(CURL_SPECIAL_ZLIB_H ${CMAKE_ZLIB_HEADER})
-    set(CURL_SPECIAL_LIBZ_INCLUDES ${CMAKE_ZLIB_INCLUDES})
-    set(CURL_SPECIAL_LIBZ ${CMAKE_ZLIB_LIBRARIES})
-    set(CMAKE_CURL_INCLUDES)
-    set(CMAKE_CURL_LIBRARIES cmcurl)
-    if(CMAKE_TESTS_CDASH_SERVER)
-      set(CMAKE_CURL_TEST_URL "${CMAKE_TESTS_CDASH_SERVER}/user.php")
-    endif()
-    set(_CMAKE_USE_OPENSSL_DEFAULT OFF)
-    if(NOT DEFINED CMAKE_USE_OPENSSL AND NOT WIN32 AND NOT APPLE
-        AND CMAKE_SYSTEM_NAME MATCHES "(Linux|FreeBSD)")
-      set(_CMAKE_USE_OPENSSL_DEFAULT ON)
-    endif()
-    option(CMAKE_USE_OPENSSL "Use OpenSSL." ${_CMAKE_USE_OPENSSL_DEFAULT})
-    mark_as_advanced(CMAKE_USE_OPENSSL)
-    if(CMAKE_USE_OPENSSL)
-      set(CURL_CA_BUNDLE "" CACHE FILEPATH "Path to SSL CA Certificate Bundle")
-      set(CURL_CA_PATH "" CACHE PATH "Path to SSL CA Certificate Directory")
-      mark_as_advanced(CURL_CA_BUNDLE CURL_CA_PATH)
-    endif()
-    if(NOT CMAKE_USE_SYSTEM_NGHTTP2)
-      # Tell curl's FindNGHTTP2 module to use our library.
-      set(NGHTTP2_LIBRARY cmnghttp2)
-      set(NGHTTP2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmnghttp2/lib/includes)
-    endif()
-    add_subdirectory(Utilities/cmcurl)
-    CMAKE_SET_TARGET_FOLDER(cmcurl "Utilities/3rdParty")
-    CMAKE_SET_TARGET_FOLDER(LIBCURL "Utilities/3rdParty")
-    if(NOT CMAKE_USE_SYSTEM_NGHTTP2)
-      # Configure after curl to re-use some check results.
-      add_subdirectory(Utilities/cmnghttp2)
-      CMAKE_SET_TARGET_FOLDER(cmnghttp2 "Utilities/3rdParty")
-    endif()
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build expat library for CMake, CTest, and libarchive.
-  if(CMAKE_USE_SYSTEM_EXPAT)
-    find_package(EXPAT)
-    if(NOT EXPAT_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_EXPAT is ON but a expat is not found!")
-    endif()
-    set(CMAKE_EXPAT_INCLUDES ${EXPAT_INCLUDE_DIRS})
-    set(CMAKE_EXPAT_LIBRARIES ${EXPAT_LIBRARIES})
-  else()
-    set(CMAKE_EXPAT_INCLUDES)
-    set(CMAKE_EXPAT_LIBRARIES cmexpat)
-    add_subdirectory(Utilities/cmexpat)
-    CMAKE_SET_TARGET_FOLDER(cmexpat "Utilities/3rdParty")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build or use system libbz2 for libarchive.
-  if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
-    if(CMAKE_USE_SYSTEM_BZIP2)
-      find_package(BZip2)
-    else()
-      set(BZIP2_INCLUDE_DIR
-        "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmbzip2")
-      set(BZIP2_LIBRARIES cmbzip2)
-      set(BZIP2_NEED_PREFIX "")
-      set(USE_BZIP2_DLL "")
-      set(USE_BZIP2_DLL_WITH_LIB cmbzip2)
-      set(USE_BZIP2_STATIC "")
-      set(USE_BZIP2_STATIC_WITH_LIB cmbzip2)
-      add_subdirectory(Utilities/cmbzip2)
-      CMAKE_SET_TARGET_FOLDER(cmbzip2 "Utilities/3rdParty")
-    endif()
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build or use system zstd for libarchive.
-  if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
-    if(NOT CMAKE_USE_SYSTEM_ZSTD)
-      set(ZSTD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmzstd")
-      set(ZSTD_LIBRARY cmzstd)
-      add_subdirectory(Utilities/cmzstd)
-      CMAKE_SET_TARGET_FOLDER(cmzstd "Utilities/3rdParty")
-    endif()
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build or use system liblzma for libarchive.
-  if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
-    if(CMAKE_USE_SYSTEM_LIBLZMA)
-      find_package(LibLZMA)
-      if(NOT LIBLZMA_FOUND)
-        message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBLZMA is ON but LibLZMA is not found!")
-      endif()
-    else()
-      add_subdirectory(Utilities/cmliblzma)
-      CMAKE_SET_TARGET_FOLDER(cmliblzma "Utilities/3rdParty")
-      set(LIBLZMA_HAS_AUTO_DECODER 1)
-      set(LIBLZMA_HAS_EASY_ENCODER 1)
-      set(LIBLZMA_HAS_LZMA_PRESET 1)
-      set(LIBLZMA_INCLUDE_DIR
-        "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmliblzma/liblzma/api")
-      set(LIBLZMA_LIBRARY cmliblzma)
-      set(HAVE_LZMA_STREAM_ENCODER_MT 1)
-    endif()
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build or use system libarchive for CMake and CTest.
-  if(CMAKE_USE_SYSTEM_LIBARCHIVE)
-    find_package(LibArchive 3.3.3)
-    if(NOT LibArchive_FOUND)
-      message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBARCHIVE is ON but LibArchive is not found!")
-    endif()
-    set(CMAKE_TAR_INCLUDES ${LibArchive_INCLUDE_DIRS})
-    set(CMAKE_TAR_LIBRARIES ${LibArchive_LIBRARIES})
-  else()
-    set(EXPAT_INCLUDE_DIR ${CMAKE_EXPAT_INCLUDES})
-    set(EXPAT_LIBRARY ${CMAKE_EXPAT_LIBRARIES})
-    set(ZLIB_INCLUDE_DIR ${CMAKE_ZLIB_INCLUDES})
-    set(ZLIB_LIBRARY ${CMAKE_ZLIB_LIBRARIES})
-    add_definitions(-DLIBARCHIVE_STATIC)
-    set(ENABLE_MBEDTLS OFF)
-    set(ENABLE_NETTLE OFF)
-    if(DEFINED CMAKE_USE_OPENSSL)
-      set(ENABLE_OPENSSL "${CMAKE_USE_OPENSSL}")
-    else()
-      set(ENABLE_OPENSSL OFF)
-    endif()
-    set(ENABLE_LIBB2 OFF)
-    set(ENABLE_LZ4 OFF)
-    set(ENABLE_LZO OFF)
-    set(ENABLE_LZMA ON)
-    set(ENABLE_ZSTD ON)
-    set(ENABLE_ZLIB ON)
-    set(ENABLE_BZip2 ON)
-    set(ENABLE_LIBXML2 OFF)
-    set(ENABLE_EXPAT OFF)
-    set(ENABLE_PCREPOSIX OFF)
-    set(ENABLE_LibGCC OFF)
-    set(ENABLE_CNG OFF)
-    set(ENABLE_TAR OFF)
-    set(ENABLE_TAR_SHARED OFF)
-    set(ENABLE_CPIO OFF)
-    set(ENABLE_CPIO_SHARED OFF)
-    set(ENABLE_CAT OFF)
-    set(ENABLE_CAT_SHARED OFF)
-    set(ENABLE_XATTR OFF)
-    set(ENABLE_ACL OFF)
-    set(ENABLE_ICONV OFF)
-    set(ENABLE_TEST OFF)
-    set(ENABLE_COVERAGE OFF)
-    set(ENABLE_INSTALL OFF)
-    set(POSIX_REGEX_LIB "" CACHE INTERNAL "libarchive: No POSIX regular expression support")
-    set(ENABLE_SAFESEH "" CACHE INTERNAL "libarchive: No /SAFESEH linker flag")
-    set(WINDOWS_VERSION "WIN7" CACHE INTERNAL "libarchive: Set Windows version to use (Windows only)")
-    add_subdirectory(Utilities/cmlibarchive)
-    CMAKE_SET_TARGET_FOLDER(cmlibarchive "Utilities/3rdParty")
-    set(CMAKE_TAR_LIBRARIES cmlibarchive ${BZIP2_LIBRARIES})
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build jsoncpp library.
-  if(CMAKE_USE_SYSTEM_JSONCPP)
-    find_package(JsonCpp 1.4.1)
-    if(NOT JsonCpp_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!")
-    endif()
-    if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC|Clang")
-      set_property(TARGET JsonCpp::JsonCpp APPEND PROPERTY
-        INTERFACE_COMPILE_OPTIONS -Wno-deprecated-declarations)
-    endif()
-    set(CMAKE_JSONCPP_LIBRARIES JsonCpp::JsonCpp)
-  else()
-    set(CMAKE_JSONCPP_LIBRARIES cmjsoncpp)
-    add_subdirectory(Utilities/cmjsoncpp)
-    CMAKE_SET_TARGET_FOLDER(cmjsoncpp "Utilities/3rdParty")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Build libuv library.
-  if(CMAKE_USE_SYSTEM_LIBUV)
-    if(WIN32)
-      find_package(LibUV 1.38.0)
-    else()
-      find_package(LibUV 1.28.0)
-    endif()
-    if(NOT LIBUV_FOUND)
-      message(FATAL_ERROR
-        "CMAKE_USE_SYSTEM_LIBUV is ON but a libuv is not found!")
-    endif()
-    set(CMAKE_LIBUV_LIBRARIES LibUV::LibUV)
-  else()
-    set(CMAKE_LIBUV_LIBRARIES cmlibuv)
-    add_subdirectory(Utilities/cmlibuv)
-    CMAKE_SET_TARGET_FOLDER(cmlibuv "Utilities/3rdParty")
-  endif()
-
-  #---------------------------------------------------------------------
-  # Use curses?
-  if(NOT DEFINED BUILD_CursesDialog)
-    if (UNIX)
-      include(${CMake_SOURCE_DIR}/Source/Checks/Curses.cmake)
-      set(BUILD_CursesDialog_DEFAULT "${CMakeCheckCurses_COMPILED}")
-    elseif(WIN32)
-      set(BUILD_CursesDialog_DEFAULT "OFF")
-    endif()
-    option(BUILD_CursesDialog "Build the CMake Curses Dialog ccmake" "${BUILD_CursesDialog_DEFAULT}")
-  endif ()
-  if(BUILD_CursesDialog)
-    if(UNIX)
-      set(CURSES_NEED_NCURSES TRUE)
-      find_package(Curses)
-      if(NOT CURSES_FOUND)
-        message(WARNING
-          "'ccmake' will not be built because Curses was not found.\n"
-          "Turn off BUILD_CursesDialog to suppress this message."
-          )
-        set(BUILD_CursesDialog 0)
-      endif()
-    elseif(WIN32)
-      # FIXME: Add support for system-provided pdcurses.
-      add_subdirectory(Utilities/cmpdcurses)
-      set(CURSES_LIBRARY cmpdcurses)
-      set(CURSES_INCLUDE_PATH "") # cmpdcurses has usage requirements
-      set(CMAKE_USE_SYSTEM_FORM 0)
-      set(HAVE_CURSES_USE_DEFAULT_COLORS 1)
-    endif()
-  endif()
-  if(BUILD_CursesDialog)
-    if(NOT CMAKE_USE_SYSTEM_FORM)
-      add_subdirectory(Source/CursesDialog/form)
-    elseif(NOT CURSES_FORM_LIBRARY)
-      message( FATAL_ERROR "CMAKE_USE_SYSTEM_FORM in ON but CURSES_FORM_LIBRARY is not set!" )
-    endif()
-  endif()
-endmacro ()
-
 #-----------------------------------------------------------------------
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(CMAKE_CXX_PLATFORM_ID MATCHES "OpenBSD")
@@ -749,7 +344,7 @@ include(Source/CMakeVersion.cmake)
 
 # Include the standard Dart testing module
 enable_testing()
-include (${CMAKE_ROOT}/Modules/Dart.cmake)
+include(${CMAKE_ROOT}/Modules/Dart.cmake)
 
 # Set up test-time configuration.
 set_directory_properties(PROPERTIES
@@ -803,8 +398,9 @@ if(CMake_TEST_EXTERNAL_CMAKE)
 endif()
 
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
-  # build the utilities (a macro defined in this file)
-  CMAKE_BUILD_UTILITIES()
+  find_package(Threads)
+  # build the utilities
+  include(CMakeBuildUtilities)
 
   if(BUILD_QtDialog)
     if(APPLE)
@@ -813,10 +409,9 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
       set(CMAKE_BUNDLE_LOCATION "${CMAKE_INSTALL_PREFIX}")
       # make sure CMAKE_INSTALL_PREFIX ends in /
       if(NOT CMAKE_INSTALL_PREFIX MATCHES "/$")
-        set(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/")
+        string(APPEND CMAKE_INSTALL_PREFIX "/")
       endif()
-      set(CMAKE_INSTALL_PREFIX
-        "${CMAKE_INSTALL_PREFIX}CMake.app/Contents")
+      string(APPEND CMAKE_INSTALL_PREFIX "CMake.app/Contents")
     endif()
   endif()
 
@@ -829,14 +424,11 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
   endif()
 
   # add the uninstall support
-  configure_file(
-    "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
-    "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
-    @ONLY)
+  configure_file(cmake_uninstall.cmake.in cmake_uninstall.cmake @ONLY)
   add_custom_target(uninstall
     "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 
-  include (CMakeCPack.cmake)
+  include(CMakeCPack.cmake)
 
 endif()
 
@@ -860,10 +452,10 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
                          -Wshadow -Wpointer-arith -Wformat-security -Wundef
       )
 
-      foreach(FLAG_LANG  C CXX)
-        foreach(FLAG ${${FLAG_LANG}_FLAGS_LIST})
+      foreach(FLAG_LANG IN ITEMS C CXX)
+        foreach(FLAG IN LISTS ${FLAG_LANG}_FLAGS_LIST)
           if(NOT " ${CMAKE_${FLAG_LANG}_FLAGS} " MATCHES " ${FLAG} ")
-            set(CMAKE_${FLAG_LANG}_FLAGS "${CMAKE_${FLAG_LANG}_FLAGS} ${FLAG}")
+            string(APPEND CMAKE_${FLAG_LANG}_FLAGS " ${FLAG}")
           endif()
         endforeach()
       endforeach()
@@ -883,9 +475,6 @@ add_subdirectory(Tests)
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(BUILD_TESTING)
     CMAKE_SET_TARGET_FOLDER(CMakeLibTests "Tests")
-    IF(TARGET CMakeServerLibTests)
-      CMAKE_SET_TARGET_FOLDER(CMakeServerLibTests "Tests")
-    ENDIF()
   endif()
   if(TARGET documentation)
     CMAKE_SET_TARGET_FOLDER(documentation "Documentation")
@@ -894,7 +483,8 @@ endif()
 
 if(BUILD_TESTING)
   add_test(SystemInformationNew "${CMAKE_CMAKE_COMMAND}"
-    --system-information  -G "${CMAKE_GENERATOR}" )
+    --system-information  -G "${CMAKE_GENERATOR}"
+  )
 endif()
 
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
index 49026a3..85af8ed 100644 (file)
@@ -74,6 +74,7 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION
   "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*may return deterministic values"
   "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*rand.*isn.*t random" # we do not do crypto
   "cm(StringCommand|CTestTestHandler)\\.cxx.*warning.*srand.*seed choices are.*poor" # we do not do crypto
+  "cmFindPackageCommand.cxx.*: warning #177-D: parameter .* was declared but never referenced"
   "IPA warning: function.*multiply defined in"
   "LICENSE WARNING" # PGI license expiry.  Not useful in nightly testing.
 
@@ -83,6 +84,11 @@ list(APPEND CTEST_CUSTOM_WARNING_EXCEPTION
   "compilation completed with warnings" # PGI
   "[0-9]+ Warning\\(s\\) detected" # SunPro
 
+  # Ignore false positive on `cm::optional` usage from GCC
+  "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '.*cm::optional<CxxModuleMapFormat>::_mem\\)\\)' may be used uninitialized \\[-Wmaybe-uninitialized\\]"
+  "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: note: '.*cm::optional<CxxModuleMapFormat>::_mem\\)\\)' was declared here"
+  "cmGlobalNinjaGenerator.cxx:[0-9]*:[0-9]*: warning: '\\*\\(\\(void\\*\\)& modmap_fmt \\+4\\)' may be used uninitialized in this function \\[-Wmaybe-uninitialized\\]"
+
   # clang-analyzer exceptions
   "cmListFileLexer.c:[0-9]+:[0-9]+: warning: Array subscript is undefined"
   "jsoncpp/src/.*:[0-9]+:[0-9]+: warning: Value stored to .* is never read"
index e6fb20b..bf8a082 100644 (file)
@@ -18,12 +18,12 @@ endif()
 # not hurt other versions, and this will work into the
 # future
 if(MSVC OR _INTEL_WINDOWS OR _CLANG_MSVC_WINDOWS)
-  add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
+  add_compile_definitions(_CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE)
 else()
 endif()
 
 if(MSVC)
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stack:10000000")
+  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.
@@ -62,7 +62,7 @@ endif()
 # Use 64-bit off_t on 32-bit Linux
 if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SIZEOF_VOID_P EQUAL 4)
   # ensure 64bit offsets are used for filesystem accesses for 32bit compilation
-  add_definitions(-D_FILE_OFFSET_BITS=64)
+  add_compile_definitions(_FILE_OFFSET_BITS=64)
 endif()
 
 # Workaround for TOC Overflow on ppc64
@@ -98,7 +98,7 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro AND
   endif()
 endif()
 
-foreach(lang C CXX)
+foreach(lang IN ITEMS C CXX)
   # Suppress warnings from PGI compiler.
   if (CMAKE_${lang}_COMPILER_ID STREQUAL "PGI")
     set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -w")
@@ -136,3 +136,12 @@ OFF to disable /MP completely." )
     endif()
   endif()
 endif()
+
+# Get rid of excess -Wunused-but-set-variable on release builds with LCC >= 1.26
+foreach(l IN ITEMS C CXX)
+  if(CMAKE_${l}_COMPILER_ID STREQUAL "LCC" AND NOT CMAKE_${l}_COMPILER_VERSION VERSION_LESS 1.26)
+    foreach(c IN ITEMS MINSIZEREL RELEASE RELWITHDEBINFO)
+      string(APPEND "CMAKE_${l}_FLAGS_${c}" " -Wno-unused-but-set-variable")
+    endforeach()
+  endif()
+endforeach()
index 2cf1769..bd45dd1 100644 (file)
@@ -53,6 +53,7 @@ The following individuals and institutions are among the Contributors:
 * Clement Creusot <creusot@cs.york.ac.uk>
 * Daniel Blezek <blezek@gmail.com>
 * Daniel Pfeifer <daniel@pfeifer-mail.de>
+* Dawid Wróbel <me@dawidwrobel.com>
 * Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
 * Eran Ifrah <eran.ifrah@gmail.com>
 * Esben Mose Hansen, Ange Optimization ApS
index cd3c78b..bd55e24 100644 (file)
@@ -15,6 +15,7 @@ The general signature is:
              [PATHS [path | ENV var]... ]
              [REGISTRY_VIEW (64|32|64_32|32_64|HOST|TARGET|BOTH)]
              [PATH_SUFFIXES suffix1 [suffix2 ...]]
+             [VALIDATOR function]
              [DOC "cache documentation string"]
              [NO_CACHE]
              [REQUIRED]
@@ -66,6 +67,31 @@ Options include:
   Specify additional subdirectories to check below each directory
   location otherwise considered.
 
+``VALIDATOR``
+  .. versionadded:: 3.25
+
+  Specify a :command:`function` to be called for each candidate item found
+  (a :command:`macro` cannot be provided, that will result in an error).
+  Two arguments will be passed to the validator function: the name of a
+  result variable, and the absolute path to the candidate item.  The item
+  will be accepted and the search will end unless the function sets the
+  value in the result variable to false in the calling scope.  The result
+  variable will hold a true value when the validator function is entered.
+
+  .. parsed-literal::
+
+     function(my_check validator_result_var item)
+       if(NOT item MATCHES ...)
+         set(${validator_result_var} FALSE PARENT_SCOPE)
+       endif()
+     endfunction()
+
+     |FIND_XXX| (result NAMES ... VALIDATOR my_check)
+
+  Note that if a cached result is used, the search is skipped and any
+  ``VALIDATOR`` is ignored.  The cached result is not required to pass the
+  validation function.
+
 ``DOC``
   Specify the documentation string for the ``<VAR>`` cache entry.
 
index 9e60d2d..99adc85 100644 (file)
@@ -67,6 +67,8 @@ The options are:
   Each byproduct file will be marked with the :prop_sf:`GENERATED`
   source file property automatically.
 
+  *See policy* :policy:`CMP0058` *for the motivation behind this feature.*
+
   Explicit specification of byproducts is supported by the
   :generator:`Ninja` generator to tell the ``ninja`` build tool
   how to regenerate byproducts when they are missing.  It is
@@ -434,7 +436,7 @@ one of the keywords to make clear the behavior they expect.
   Because generator expressions can be used in custom commands,
   it is possible to define ``COMMAND`` lines or whole custom commands
   which evaluate to empty strings for certain configurations.
-  For **Visual Studio 2010 (and newer)** generators these command
+  For **Visual Studio 11 2012 (and newer)** generators these command
   lines or custom commands will be omitted for the specific
   configuration and no "empty-string-command" will be added.
 
index def23fa..d8882ca 100644 (file)
@@ -42,6 +42,8 @@ The options are:
   Each byproduct file will be marked with the :prop_sf:`GENERATED`
   source file property automatically.
 
+  *See policy* :policy:`CMP0058` *for the motivation behind this feature.*
+
   Explicit specification of byproducts is supported by the
   :generator:`Ninja` generator to tell the ``ninja`` build tool
   how to regenerate byproducts when they are missing.  It is
index 8dba986..13cae10 100644 (file)
@@ -5,7 +5,7 @@ Add a subdirectory to the build.
 
 .. code-block:: cmake
 
-  add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
+  add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])
 
 Adds a subdirectory to the build.  The source_dir specifies the
 directory in which the source CMakeLists.txt and code files are
@@ -33,3 +33,10 @@ dependencies supersede this exclusion.  If a target built by the
 parent project depends on a target in the subdirectory, the dependee
 target will be included in the parent project build system to satisfy
 the dependency.
+
+If the ``SYSTEM`` argument is provided, the :prop_dir:`SYSTEM` directory
+property of the subdirectory will be set to true.  This property is
+used to initialize the :prop_tgt:`SYSTEM` property of each target
+created in that subdirectory. The include directories of targets with
+:prop_tgt:`SYSTEM` set to true will be treated as ``SYSTEM`` when
+compiling consumers.
diff --git a/Help/command/block.rst b/Help/command/block.rst
new file mode 100644 (file)
index 0000000..dfd60d4
--- /dev/null
@@ -0,0 +1,76 @@
+block
+-----
+
+.. versionadded:: 3.25
+
+Evaluate a group of commands with a dedicated variable and/or policy scope.
+
+.. code-block:: cmake
+
+  block([SCOPE_FOR [POLICIES] [VARIABLES] ] [PROPAGATE <var-name>...])
+    <commands>
+  endblock()
+
+All commands between ``block()`` and the matching :command:`endblock` are
+recorded without being invoked.  Once the :command:`endblock` is evaluated, the
+recorded list of commands is invoked inside the requested scopes, then the
+scopes created by the ``block()`` command are removed.
+
+``SCOPE_FOR``
+  Specify which scopes must be created.
+
+  ``POLICIES``
+    Create a new policy scope. This is equivalent to
+    :command:`cmake_policy(PUSH)`.
+
+  ``VARIABLES``
+    Create a new variable scope.
+
+  If ``SCOPE_FOR`` is not specified, this is equivalent to:
+
+  .. code-block:: cmake
+
+    block(SCOPE_FOR VARIABLES POLICIES)
+
+``PROPAGATE``
+  When a variable scope is created by the :command:`block` command, this
+  option sets or unsets the specified variables in the parent scope. This is
+  equivalent to :command:`set(PARENT_SCOPE)` or :command:`unset(PARENT_SCOPE)`
+  commands.
+
+  .. code-block:: cmake
+
+    set(var1 "INIT1")
+    set(var2 "INIT2")
+
+    block(PROPAGATE var1 var2)
+      set(var1 "VALUE1")
+      unset(var2)
+    endblock()
+
+    # Now var1 holds VALUE1, and var2 is unset
+
+  This option is only allowed when a variable scope is created. An error will
+  be raised in the other cases.
+
+When the ``block()`` is inside a :command:`foreach` or :command:`while`
+command, the :command:`break` and :command:`continue` commands can be used
+inside the block.
+
+.. code-block:: cmake
+
+  while(TRUE)
+    block()
+       ...
+       # the break() command will terminate the while() command
+       break()
+    endblock()
+  endwhile()
+
+
+See Also
+^^^^^^^^
+
+  * :command:`endblock`
+  * :command:`return`
+  * :command:`cmake_policy`
index a03979d..3d86a2e 100644 (file)
@@ -24,12 +24,12 @@ options, if any.  The trailing ``-- -i`` option is added for
 :ref:`Makefile Generators` if policy :policy:`CMP0061` is not set to
 ``NEW``.
 
-When invoked, this ``cmake --build`` command line will launch the
+When invoked, this :option:`cmake --build` command line will launch the
 underlying build system tool.
 
 .. versionadded:: 3.21
-  The ``PARALLEL_LEVEL`` argument can be used to set the ``--parallel``
-  flag.
+  The ``PARALLEL_LEVEL`` argument can be used to set the
+  :option:`--parallel <cmake--build --parallel>` flag.
 
 .. code-block:: cmake
 
@@ -39,7 +39,7 @@ This second signature is deprecated, but still available for backwards
 compatibility.  Use the first signature instead.
 
 It sets the given ``<cachevariable>`` to a command-line string as
-above but without the ``--target`` option.
+above but without the :option:`--target <cmake--build --target>` option.
 The ``<makecommand>`` is ignored but should be the full path to
 devenv, nmake, make or one of the end user build tools
 for legacy invocations.
index cb8d60b..8801a9f 100644 (file)
@@ -14,6 +14,7 @@ Synopsis
   cmake_language(`EVAL`_ CODE <code>...)
   cmake_language(`DEFER`_ <options>... CALL <command> [<arg>...])
   cmake_language(`SET_DEPENDENCY_PROVIDER`_ <command> SUPPORTED_METHODS <methods>...)
+  cmake_language(`GET_MESSAGE_LOG_LEVEL`_ <out-var>)
 
 Introduction
 ^^^^^^^^^^^^
@@ -50,6 +51,7 @@ is equivalent to
   To ensure consistency of the code, the following commands are not allowed:
 
   * ``if`` / ``elseif`` / ``else`` / ``endif``
+  * ``block`` / ``endblock``
   * ``while`` / ``endwhile``
   * ``foreach`` / ``endforeach``
   * ``function`` / ``endfunction``
@@ -491,3 +493,29 @@ calling the provider command recursively for the same dependency.
     SET_DEPENDENCY_PROVIDER mycomp_provide_dependency
     SUPPORTED_METHODS FIND_PACKAGE
   )
+
+Getting current message log level
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.25
+
+.. _GET_MESSAGE_LOG_LEVEL:
+.. _query_message_log_level:
+
+.. code-block:: cmake
+
+  cmake_language(GET_MESSAGE_LOG_LEVEL <output_variable>)
+
+Writes the current :command:`message` logging level
+into the given ``<output_variable>``.
+
+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.
+
+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.
index 94060d9..54fc548 100644 (file)
@@ -103,6 +103,47 @@ Calls to the :command:`cmake_minimum_required(VERSION)`,
 ``cmake_policy(VERSION)``, or ``cmake_policy(SET)`` commands
 influence only the current top of the policy stack.
 
+.. versionadded:: 3.25
+  The :command:`block` and :command:`endblock` commands offer a more flexible
+  and more secure way to manage the policy stack. The pop action is done
+  automatically when the :command:`endblock` command is executed, so it avoid
+  to call the :command:`cmake_policy(POP)` command before each
+  :command:`return` command.
+
+  .. code-block:: cmake
+
+    # stack management with cmake_policy()
+    function(my_func)
+      cmake_policy(PUSH)
+      cmake_policy(SET ...)
+      if (<cond1>)
+        ...
+        cmake_policy(POP)
+        return()
+      elseif(<cond2>)
+        ...
+        cmake_policy(POP)
+        return()
+      endif()
+      ...
+      cmake_policy(POP)
+    endfunction()
+
+    # stack management with block()/endblock()
+    function(my_func)
+      block(SCOPE_FOR POLICIES)
+        cmake_policy(SET ...)
+        if (<cond1>)
+          ...
+          return()
+        elseif(<cond2>)
+          ...
+          return()
+        endif()
+        ...
+      endblock()
+    endfunction()
+
 Commands created by the :command:`function` and :command:`macro`
 commands record policy settings when they are created and
 use the pre-record policies when they are invoked.  If the function or
index f62802e..e8012ee 100644 (file)
@@ -9,8 +9,8 @@ Continue to the top of enclosing foreach or while loop.
 
   continue()
 
-The ``continue`` command allows a cmake script to abort the rest of a block
-in a :command:`foreach` or :command:`while` loop, and start at the top of
-the next iteration.
+The ``continue()`` command allows a cmake script to abort the rest of the
+current iteration of a :command:`foreach` or :command:`while` loop, and start
+at the top of the next iteration.
 
 See also the :command:`break` command.
index e05df1a..8c81f2d 100644 (file)
@@ -40,8 +40,8 @@ The options are:
 ``CONFIGURATION <config>``
   Specify the build configuration (e.g. ``Debug``).  If not
   specified the ``CTEST_BUILD_CONFIGURATION`` variable will be checked.
-  Otherwise the ``-C <cfg>`` option given to the :manual:`ctest(1)`
-  command will be used, if any.
+  Otherwise the :option:`-C \<cfg\> <ctest -C>` option given to the
+  :manual:`ctest(1)` command will be used, if any.
 
 ``PARALLEL_LEVEL <parallel>``
   .. versionadded:: 3.21
@@ -54,7 +54,7 @@ The options are:
   Pass additional arguments to the underlying build command.
   If not specified the ``CTEST_BUILD_FLAGS`` variable will be checked.
   This can, e.g., be used to trigger a parallel build using the
-  ``-j`` option of make. See the :module:`ProcessorCount` module
+  ``-j`` option of ``make``. See the :module:`ProcessorCount` module
   for an example.
 
 ``PROJECT_NAME <project-name>``
index 5ec543e..a2b348f 100644 (file)
@@ -1,15 +1,15 @@
 ctest_run_script
 ----------------
 
-runs a ctest -S script
+runs a :option:`ctest -S` script
 
 ::
 
   ctest_run_script([NEW_PROCESS] script_file_name script_file_name1
               script_file_name2 ... [RETURN_VALUE var])
 
-Runs a script or scripts much like if it was run from ctest -S.  If no
-argument is provided then the current script is run using the current
+Runs a script or scripts much like if it was run from :option:`ctest -S`.
+If no argument is provided then the current script is run using the current
 settings of the variables.  If ``NEW_PROCESS`` is specified then each
 script will be run in a separate process.If ``RETURN_VALUE`` is specified
 the return value of the last script run will be put into ``var``.
index c0f3c6d..921279a 100644 (file)
@@ -45,7 +45,7 @@ The parameters are as follows:
 
     ctest_start(Experimental GROUP GroupExperimental)
 
-  Later, in another ``ctest -S`` script:
+  Later, in another :option:`ctest -S` script:
 
   .. code-block:: cmake
 
index 65f82d7..4f9f891 100644 (file)
@@ -109,8 +109,9 @@ The options are:
   While running tests in parallel, try not to start tests when they
   may cause the CPU load to pass above a given threshold.  If not
   specified the :variable:`CTEST_TEST_LOAD` variable will be checked,
-  and then the ``--test-load`` command-line argument to :manual:`ctest(1)`.
-  See also the ``TestLoad`` setting in the :ref:`CTest Test Step`.
+  and then the :option:`--test-load <ctest --test-load>` command-line
+  argument to :manual:`ctest(1)`. See also the ``TestLoad`` setting
+  in the :ref:`CTest Test Step`.
 
 ``REPEAT <mode>:<n>``
   .. versionadded:: 3.17
@@ -176,8 +177,9 @@ See also the :variable:`CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE`,
 :variable:`CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE` and
 :variable:`CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION` variables, along with their
 corresponding :manual:`ctest(1)` command line options
-``--test-output-size-passed``, ``--test-output-size-failed``, and
-``--test-output-truncation``.
+:option:`--test-output-size-passed <ctest --test-output-size-passed>`,
+:option:`--test-output-size-failed <ctest --test-output-size-failed>`, and
+:option:`--test-output-truncation <ctest --test-output-truncation>`.
 
 .. _`Additional Test Measurements`:
 
index d2acbc8..d9103b8 100644 (file)
@@ -1,13 +1,14 @@
 enable_language
 ---------------
-Enable a language (CXX/C/OBJC/OBJCXX/Fortran/etc)
+
+Enable languages (CXX/C/OBJC/OBJCXX/Fortran/etc)
 
 .. code-block:: cmake
 
-  enable_language(<lang> [OPTIONAL] )
+  enable_language(<lang>... [OPTIONAL])
 
-Enables support for the named language in CMake.  This is
-the same as the :command:`project` command but does not create any of the extra
+Enables support for the named languages in CMake.  This is the same as
+the :command:`project` command but does not create any of the extra
 variables that are created by the project command.  Example languages
 are ``CXX``, ``C``, ``CUDA``, ``OBJC``, ``OBJCXX``, ``Fortran``,
 ``HIP``, ``ISPC``, and ``ASM``.
diff --git a/Help/command/endblock.rst b/Help/command/endblock.rst
new file mode 100644 (file)
index 0000000..3b21c12
--- /dev/null
@@ -0,0 +1,11 @@
+endblock
+--------
+
+.. versionadded:: 3.25
+
+Ends a list of commands in a :command:`block` and removes the scopes
+created by the :command:`block` command.
+
+.. code-block:: cmake
+
+  endblock()
index dc69645..0f79f63 100644 (file)
@@ -25,7 +25,8 @@ Exporting Targets
 .. code-block:: cmake
 
   export(TARGETS <target>... [NAMESPACE <namespace>]
-         [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES])
+         [APPEND] FILE <filename> [EXPORT_LINK_INTERFACE_LIBRARIES]
+         [CXX_MODULES_DIRECTORY <directory>])
 
 Creates a file ``<filename>`` that may be included by outside projects to
 import targets named by ``<target>...`` from the current project's build tree.
@@ -52,6 +53,16 @@ The options are:
   in the export, even when policy :policy:`CMP0022` is NEW.  This is useful
   to support consumers using CMake versions older than 2.8.12.
 
+``CXX_MODULES_DIRECTORY <directory>``
+
+  .. note ::
+
+    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  Export C++ module properties to files under the given directory. Each file
+  will be named according to the target's export name (without any namespace).
+  These files will automatically be included from the export file.
+
 This signature requires all targets to be listed explicitly.  If a library
 target is included in the export, but a target to which it links is not
 included, the behavior is unspecified.  See the `export(EXPORT)`_ signature
@@ -95,7 +106,8 @@ Exporting Targets matching install(EXPORT)
 
 .. code-block:: cmake
 
-  export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>])
+  export(EXPORT <export-name> [NAMESPACE <namespace>] [FILE <filename>]
+         [CXX_MODULES_DIRECTORY <directory>])
 
 Creates a file ``<filename>`` that may be included by outside projects to
 import targets from the current project's build tree.  This is the same
index 3374d2d..fbe2a81 100644 (file)
@@ -1121,8 +1121,11 @@ Additional options to ``DOWNLOAD`` are:
 
   Verify that the downloaded content hash matches the expected value, where
   ``ALGO`` is one of the algorithms supported by ``file(<HASH>)``.
-  If it does not match, the operation fails with an error. It is an error to
-  specify this if ``DOWNLOAD`` is not given a ``<file>``.
+  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>``.
 
 ``EXPECTED_MD5 <value>``
   Historical short-hand for ``EXPECTED_HASH MD5=<value>``. It is an error to
index c96d84e..c99c73d 100644 (file)
@@ -285,29 +285,40 @@ CMake constructs a set of possible installation prefixes for the
 package.  Under each prefix several directories are searched for a
 configuration file.  The tables below show the directories searched.
 Each entry is meant for installation trees following Windows (``W``), UNIX
-(``U``), or Apple (``A``) conventions::
-
-  <prefix>/                                                       (W)
-  <prefix>/(cmake|CMake)/                                         (W)
-  <prefix>/<name>*/                                               (W)
-  <prefix>/<name>*/(cmake|CMake)/                                 (W)
-  <prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/                 (U)
-  <prefix>/(lib/<arch>|lib*|share)/<name>*/                       (U)
-  <prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/         (U)
-  <prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/         (W/U)
-  <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/               (W/U)
-  <prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/ (W/U)
+(``U``), or Apple (``A``) conventions:
+
+==================================================================== ==========
+ Entry                                                               Convention
+==================================================================== ==========
+ ``<prefix>/``                                                          W
+ ``<prefix>/(cmake|CMake)/``                                            W
+ ``<prefix>/<name>*/``                                                  W
+ ``<prefix>/<name>*/(cmake|CMake)/``                                    W
+ ``<prefix>/<name>*/(cmake|CMake)/<name>*/`` [#]_                       W
+ ``<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/``                    U
+ ``<prefix>/(lib/<arch>|lib*|share)/<name>*/``                          U
+ ``<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/``            U
+ ``<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/``            W/U
+ ``<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/``                  W/U
+ ``<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/``    W/U
+==================================================================== ==========
+
+.. [#] .. versionadded:: 3.25
 
 On systems supporting macOS :prop_tgt:`FRAMEWORK` and :prop_tgt:`BUNDLE`, the
 following directories are searched for Frameworks or Application Bundles
-containing a configuration file::
-
-  <prefix>/<name>.framework/Resources/                    (A)
-  <prefix>/<name>.framework/Resources/CMake/              (A)
-  <prefix>/<name>.framework/Versions/*/Resources/         (A)
-  <prefix>/<name>.framework/Versions/*/Resources/CMake/   (A)
-  <prefix>/<name>.app/Contents/Resources/                 (A)
-  <prefix>/<name>.app/Contents/Resources/CMake/           (A)
+containing a configuration file:
+
+=========================================================== ==========
+ Entry                                                      Convention
+=========================================================== ==========
+ ``<prefix>/<name>.framework/Resources/``                      A
+ ``<prefix>/<name>.framework/Resources/CMake/``                A
+ ``<prefix>/<name>.framework/Versions/*/Resources/``           A
+ ``<prefix>/<name>.framework/Versions/*/Resources/CMake/``     A
+ ``<prefix>/<name>.app/Contents/Resources/``                   A
+ ``<prefix>/<name>.app/Contents/Resources/CMake/``             A
+=========================================================== ==========
 
 In all cases the ``<name>`` is treated as case-insensitive and corresponds
 to any of the names specified (``<PackageName>`` or names given by ``NAMES``).
@@ -368,7 +379,7 @@ enabled.
     See policy :policy:`CMP0074`.
 
 2. Search paths specified in cmake-specific cache variables.  These
-   are intended to be used on the command line with a ``-DVAR=value``.
+   are intended to be used on the command line with a :option:`-DVAR=VALUE <cmake -D>`.
    The values are interpreted as :ref:`semicolon-separated lists <CMake Language Lists>`.
    This can be skipped if ``NO_CMAKE_PATH`` is passed or by setting the
    :variable:`CMAKE_FIND_USE_CMAKE_PATH` to ``FALSE``:
index d9f54ca..ddf8dfa 100644 (file)
@@ -130,3 +130,11 @@ yields
   -- en=two, ba=dua
   -- en=three, ba=tiga
   -- en=four, ba=
+
+See Also
+^^^^^^^^
+
+* :command:`break`
+* :command:`continue`
+* :command:`endforeach`
+* :command:`while`
index 3d25aa4..fc55c03 100644 (file)
@@ -73,3 +73,9 @@ argument.  Referencing to ``ARGV#`` arguments beyond ``ARGC`` have
 undefined behavior.  Checking that ``ARGC`` is greater than ``#`` is
 the only way to ensure that ``ARGV#`` was passed to the function as an
 extra argument.
+
+See Also
+^^^^^^^^
+
+* :command:`endfunction`
+* :command:`return`
index e02b9bc..6bcc1ef 100644 (file)
@@ -16,6 +16,7 @@ relevant parent scope as described for the :command:`define_property`
 command and if still unable to find the property, ``VAR`` will be set to
 an empty string.
 
-For a list of standard properties you can type ``cmake --help-property-list``.
+For a list of standard properties you can type
+:option:`cmake --help-property-list`.
 
 See also the more general :command:`get_property` command.
index 301cdce..b72769f 100644 (file)
@@ -424,3 +424,10 @@ There is no automatic evaluation for environment or cache
 :ref:`Variable References`.  Their values must be referenced as
 ``$ENV{<name>}`` or ``$CACHE{<name>}`` wherever the above-documented
 condition syntax accepts ``<variable|string>``.
+
+See also
+^^^^^^^^
+
+  * :command:`else`
+  * :command:`elseif`
+  * :command:`endif`
index 973aa31..feff436 100644 (file)
@@ -132,7 +132,7 @@ Installing Targets
   install(TARGETS targets... [EXPORT <export-name>]
           [RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
           [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
-            PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>]
+            PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI]
            [DESTINATION <dir>]
            [PERMISSIONS permissions...]
            [CONFIGURATIONS [Debug|Release|...]]
@@ -215,11 +215,21 @@ that may be installed:
   ``/blah/include/myproj/here.h`` with a base directory ``/blah/include``
   would be installed to ``myproj/here.h`` below the destination.
 
+``CXX_MODULES_BMI``
+
+  .. note ::
+
+    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  Any module files from C++ modules from ``PUBLIC`` sources in a file set of
+  type ``CXX_MODULES`` will be installed to the given ``DESTINATION``. All
+  modules are placed directly in the destination as no directory structure is
+  derived from the names of the modules. An empty ``DESTINATION`` may be used
+  to suppress installing these files (for use in generic code).
+
 For each of these arguments given, the arguments following them only apply
 to the target or file type specified in the argument. If none is given, the
-installation properties apply to all target types. If only one is given then
-only targets of that type will be installed (which can be used to install
-just a DLL or just an import library.)
+installation properties apply to all target types.
 
 For regular executables, static libraries and shared libraries, the
 ``DESTINATION`` argument is not required.  For these target types, when
@@ -233,6 +243,14 @@ Apple bundles and frameworks.  A destination can be omitted for interface and
 object libraries, but they are handled differently (see the discussion of this
 topic toward the end of this section).
 
+For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE``
+destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are
+installed to their default destinations. If either a ``RUNTIME`` or ``ARCHIVE``
+destination is specified, the component is installed to that destination, and
+the other component is not installed. If both ``RUNTIME`` and ``ARCHIVE``
+destinations are specified, then both components are installed to their
+respective destinations.
+
 The following table shows the target types with their associated variables and
 built-in defaults that apply when no destination is given:
 
@@ -778,9 +796,10 @@ Installing Exports
 .. code-block:: cmake
 
   install(EXPORT <export-name> DESTINATION <dir>
-          [NAMESPACE <namespace>] [[FILE <name>.cmake]|
+          [NAMESPACE <namespace>] [FILE <name>.cmake]
           [PERMISSIONS permissions...]
-          [CONFIGURATIONS [Debug|Release|...]]
+          [CONFIGURATIONS [Debug|Release|...]
+          [CXX_MODULES_DIRECTORY <directory>]
           [EXPORT_LINK_INTERFACE_LIBRARIES]
           [COMPONENT <component>]
           [EXCLUDE_FROM_ALL])
@@ -836,6 +855,18 @@ library is always installed if the headers and CMake export file are present.
   to an ndk build system complete with transitive dependencies, include flags
   and defines required to use the libraries.
 
+``CXX_MODULES_DIRECTORY``
+
+  .. note ::
+
+    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  Specify a subdirectory to store C++ module information for targets in the
+  export set. This directory will be populated with files which add the
+  necessary target property information to the relevant targets. Note that
+  without this information, none of the C++ modules which are part of the
+  targets in the export set will support being imported in consuming targets.
+
 The ``EXPORT`` form is useful to help outside projects use targets built
 and installed by the current project.  For example, the code
 
@@ -932,12 +963,12 @@ Generated Installation Script
 .. note::
 
   Use of this feature is not recommended. Please consider using the
-  ``--install`` argument of :manual:`cmake(1)` instead.
+  :option:`cmake --install` instead.
 
 The ``install()`` command generates a file, ``cmake_install.cmake``, inside
 the build directory, which is used internally by the generated install target
-and by CPack. You can also invoke this script manually with ``cmake -P``. This
-script accepts several variables:
+and by CPack. You can also invoke this script manually with
+:option:`cmake -P`. This script accepts several variables:
 
 ``COMPONENT``
   Set this variable to install only a single CPack component as opposed to all
index ca4f5c1..77d21c8 100644 (file)
@@ -83,8 +83,9 @@ are sent to stderr and are not prefixed with hyphens.  The
 :manual:`CMake GUI <cmake-gui(1)>` displays all messages in its log area.
 The :manual:`curses interface <ccmake(1)>` shows ``STATUS`` to ``TRACE``
 messages one at a time on a status line and other messages in an
-interactive pop-up box.  The ``--log-level`` command-line option to each of
-these tools can be used to control which messages will be shown.
+interactive pop-up box.  The :option:`--log-level <cmake --log-level>`
+command-line option to each of these tools can be used to control which
+messages will be shown.
 
 .. versionadded:: 3.17
   To make a log level persist between CMake runs, the
@@ -104,7 +105,7 @@ these tools can be used to control which messages will be shown.
   list variable to a dot-separated string.  The message context will always
   appear before any indenting content but after any automatically added leading
   hyphens. By default, message context is not shown, it has to be explicitly
-  enabled by giving the :manual:`cmake <cmake(1)>` ``--log-context``
+  enabled by giving the :option:`cmake --log-context`
   command-line option or by setting the :variable:`CMAKE_MESSAGE_CONTEXT_SHOW`
   variable to true.  See the :variable:`CMAKE_MESSAGE_CONTEXT` documentation for
   usage examples.
index ec009d8..3013b52 100644 (file)
@@ -5,16 +5,87 @@ Return from a file, directory or function.
 
 .. code-block:: cmake
 
-  return()
+  return([PROPAGATE <var-name>...])
 
-Returns from a file, directory or function.  When this command is
-encountered in an included file (via :command:`include` or
+When this command is encountered in an included file (via :command:`include` or
 :command:`find_package`), it causes processing of the current file to stop
 and control is returned to the including file.  If it is encountered in a
-file which is not included by another file, e.g.  a ``CMakeLists.txt``,
+file which is not included by another file, e.g. a ``CMakeLists.txt``,
 deferred calls scheduled by :command:`cmake_language(DEFER)` are invoked and
-control is returned to the parent directory if there is one.  If return is
-called in a function, control is returned to the caller of the function.
+control is returned to the parent directory if there is one.
 
-Note that a :command:`macro <macro>`, unlike a :command:`function <function>`,
+If ``return()`` is called in a function, control is returned to the caller
+of that function.  Note that a :command:`macro`, unlike a :command:`function`,
 is expanded in place and therefore cannot handle ``return()``.
+
+Policy :policy:`CMP0140` controls the behavior regarding the arguments of the
+command.  All arguments are ignored unless that policy is set to ``NEW``.
+
+``PROPAGATE``
+  .. versionadded:: 3.25
+
+  This option sets or unsets the specified variables in the parent directory or
+  function caller scope. This is equivalent to :command:`set(PARENT_SCOPE)` or
+  :command:`unset(PARENT_SCOPE)` commands, except for the way it interacts
+  with the :command:`block` command, as described below.
+
+  The ``PROPAGATE`` option can be very useful in conjunction with the
+  :command:`block` command.  A :command:`return` will propagate the
+  specified variables through any enclosing block scopes created by the
+  :command:`block` commands.  Inside a function, this ensures the variables
+  are propagated to the function's caller, regardless of any blocks within
+  the function.  If not inside a function, it ensures the variables are
+  propagated to the parent file or directory scope. For example:
+
+  .. code-block:: cmake
+    :caption: CMakeLists.txt
+
+    cmake_version_required(VERSION 3.25)
+    project(example)
+
+    set(var1 "top-value")
+
+    block(SCOPE_FOR VARIABLES)
+      add_subdirectory(subDir)
+      # var1 has the value "block-nested"
+    endblock()
+
+    # var1 has the value "top-value"
+
+  .. code-block:: cmake
+    :caption: subDir/CMakeLists.txt
+
+    function(multi_scopes result_var1 result_var2)
+      block(SCOPE_FOR VARIABLES)
+        # This would only propagate out of the immediate block, not to
+        # the caller of the function.
+        #set(${result_var1} "new-value" PARENT_SCOPE)
+        #unset(${result_var2} PARENT_SCOPE)
+
+        # This propagates the variables through the enclosing block and
+        # out to the caller of the function.
+        set(${result_var1} "new-value")
+        unset(${result_var2})
+        return(PROPAGATE ${result_var1} ${result_var2})
+      endblock()
+    endfunction()
+
+    set(var1 "some-value")
+    set(var2 "another-value")
+
+    multi_scopes(var1 var2)
+    # Now var1 will hold "new-value" and var2 will be unset
+
+    block(SCOPE_FOR VARIABLES)
+      # This return() will set var1 in the directory scope that included us
+      # via add_subdirectory(). The surrounding block() here does not limit
+      # propagation to the current file, but the block() in the parent
+      # directory scope does prevent propagation going any further.
+      set(var1 "block-nested")
+      return(PROPAGATE var1)
+    endblock()
+
+See Also
+^^^^^^^^
+
+  * :command:`block`
index af862e4..90b57d2 100644 (file)
@@ -22,12 +22,17 @@ Set Normal Variable
 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 function
-creates a new scope.  This command will set the value of a variable
-into the parent directory or calling function (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 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.
 
 Set Cache Entry
 ^^^^^^^^^^^^^^^
@@ -78,7 +83,7 @@ option is given then the cache entry will be set to the given value.
 
 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 ``-D<var>=<value>`` option without
+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,
index 3fb113a..2d292af 100644 (file)
@@ -15,9 +15,9 @@ named ``<target>`` must have been created by a command such as
 :ref:`ALIAS target <Alias Targets>`.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
-items will populate the :prop_tgt:`COMPILE_DEFINITIONS` property of
-``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the
+specify the :ref:`scope <Target Usage Requirements>` of the following arguments.
+``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`COMPILE_DEFINITIONS`
+property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS` property of ``<target>``.
 The following arguments specify compile definitions.  Repeated calls for the
 same ``<target>`` append items in the order called.
index e45b209..0d86c91 100644 (file)
@@ -22,9 +22,9 @@ If ``BEFORE`` is specified, the content will be prepended to the property
 instead of being appended.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
-items will populate the :prop_tgt:`COMPILE_OPTIONS` property of
-``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+specify the :ref:`scope <Target Usage Requirements>` of the following arguments.
+``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`COMPILE_OPTIONS`
+property of ``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_COMPILE_OPTIONS` property of ``<target>``.
 The following arguments specify compile options.  Repeated calls for the same
 ``<target>`` append items in the order called.
index 9a99a7d..f13ff29 100644 (file)
@@ -18,9 +18,9 @@ By using ``AFTER`` or ``BEFORE`` explicitly, you can select between appending
 and prepending, independent of the default.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to specify
-the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC`` items will
-populate the :prop_tgt:`INCLUDE_DIRECTORIES` property of ``<target>``.
-``PUBLIC`` and ``INTERFACE`` items will populate the
+the :ref:`scope <Target Usage Requirements>` of the following arguments.
+``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`INCLUDE_DIRECTORIES`
+property of ``<target>``. ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` property of ``<target>``.
 The following arguments specify include directories.
 
index bb75a3d..b72f746 100644 (file)
@@ -21,11 +21,12 @@ The named ``<target>`` must have been created by a command such as
 :ref:`ALIAS target <Alias Targets>`.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the items that follow them.  ``PRIVATE`` and
-``PUBLIC`` items will populate the :prop_tgt:`LINK_DIRECTORIES` property
-of ``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
-:prop_tgt:`INTERFACE_LINK_DIRECTORIES` property of ``<target>``
-(:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items).
+specify the :ref:`scope <Target Usage Requirements>` of the items that follow
+them. ``PRIVATE`` and ``PUBLIC`` items will populate the
+:prop_tgt:`LINK_DIRECTORIES` property of ``<target>``.  ``PUBLIC`` and
+``INTERFACE`` items will populate the :prop_tgt:`INTERFACE_LINK_DIRECTORIES`
+property of ``<target>`` (:ref:`IMPORTED targets <Imported Targets>` only
+support ``INTERFACE`` items).
 Each item specifies a link directory and will be converted to an absolute
 path if necessary before adding it to the relevant property.  Repeated
 calls for the same ``<target>`` append items in the order called.
index c85094a..bb7b5cc 100644 (file)
@@ -146,8 +146,10 @@ Libraries for a Target and/or its Dependents
                         <PRIVATE|PUBLIC|INTERFACE> <item>...
                        [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
 
-The ``PUBLIC``, ``PRIVATE`` and ``INTERFACE`` keywords can be used to
+The ``PUBLIC``, ``PRIVATE`` and ``INTERFACE``
+:ref:`scope <Target Usage Requirements>` keywords can be used to
 specify both the link dependencies and the link interface in one command.
+
 Libraries and targets following ``PUBLIC`` are linked to, and are made
 part of the link interface.  Libraries and targets following ``PRIVATE``
 are linked to, but are not made part of the link interface.  Libraries
index 87dff39..3cd0e64 100644 (file)
@@ -32,9 +32,9 @@ If ``BEFORE`` is specified, the content will be prepended to the property
 instead of being appended.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
-items will populate the :prop_tgt:`LINK_OPTIONS` property of
-``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+specify the :ref:`scope <Target Usage Requirements>` of the following arguments.
+``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`LINK_OPTIONS`
+property of ``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_LINK_OPTIONS` property of ``<target>``.
 The following arguments specify link options.  Repeated calls for the same
 ``<target>`` append items in the order called.
index 9f7dabb..84f5d12 100644 (file)
@@ -25,9 +25,9 @@ The named ``<target>`` must have been created by a command such as
 :ref:`ALIAS target <Alias Targets>`.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the following arguments.  ``PRIVATE`` and ``PUBLIC``
-items will populate the :prop_tgt:`PRECOMPILE_HEADERS` property of
-``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
+specify the :ref:`scope <Target Usage Requirements>` of the following arguments.
+``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`PRECOMPILE_HEADERS`
+property of ``<target>``.  ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_PRECOMPILE_HEADERS` property of ``<target>``
 (:ref:`IMPORTED targets <Imported Targets>` only support ``INTERFACE`` items).
 Repeated calls for the same ``<target>`` will append items in the order called.
index 72119f6..461175a 100644 (file)
@@ -22,10 +22,10 @@ The named ``<target>`` must have been created by a command such as
   ``<target>`` can be a custom target.
 
 The ``INTERFACE``, ``PUBLIC`` and ``PRIVATE`` keywords are required to
-specify the scope of the source file paths (``<items>``) that follow
-them.  ``PRIVATE`` and ``PUBLIC`` items will populate the :prop_tgt:`SOURCES`
-property of ``<target>``, which are used when building the target itself.
-``PUBLIC`` and ``INTERFACE`` items will populate the
+specify the :ref:`scope <Target Usage Requirements>` of the source file paths
+(``<items>``) that follow them.  ``PRIVATE`` and ``PUBLIC`` items will
+populate the :prop_tgt:`SOURCES` property of ``<target>``, which are used when
+building the target itself. ``PUBLIC`` and ``INTERFACE`` items will populate the
 :prop_tgt:`INTERFACE_SOURCES` property of ``<target>``, which are used
 when building dependents.  A target created by :command:`add_custom_target`
 can only have ``PRIVATE`` scope.
@@ -75,9 +75,33 @@ File Sets
 Adds a file set to a target, or adds files to an existing file set. Targets
 have zero or more named file sets. Each file set has a name, a type, a scope of
 ``INTERFACE``, ``PUBLIC``, or ``PRIVATE``, one or more base directories, and
-files within those directories. The only acceptable type is ``HEADERS``. The
-optional default file sets are named after their type. The target may not be a
-custom target or :prop_tgt:`FRAMEWORK` target.
+files within those directories. The acceptable types include:
+
+``HEADERS``
+
+  Sources intended to be used via a language's ``#include`` mechanism.
+
+``CXX_MODULES``
+
+  .. note ::
+
+    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  Sources which contain C++ interface module or partition units (i.e., those
+  using the ``export`` keyword). This file set type may not have an
+  ``INTERFACE`` scope except on ``IMPORTED`` targets.
+
+``CXX_MODULE_HEADER_UNITS``
+
+  .. note ::
+
+    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+  C++ header sources which may be imported by other C++ source code. This file
+  set type may not have an ``INTERFACE`` scope except on ``IMPORTED`` targets.
+
+The optional default file sets are named after their type. The target may not
+be a custom target or :prop_tgt:`FRAMEWORK` target.
 
 Files in a ``PRIVATE`` or ``PUBLIC`` file set are marked as source files for
 the purposes of IDE integration. Additionally, files in ``HEADERS`` file sets
@@ -93,16 +117,17 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o
 
   The name of the file set to create or add to. It must contain only letters,
   numbers and underscores. Names starting with a capital letter are reserved
-  for built-in file sets predefined by CMake. The only predefined set name is
-  ``HEADERS``. All other set names must not start with a capital letter or
+  for built-in file sets predefined by CMake. The only predefined set names
+  are those matching the acceptable types. All other set names must not start
+  with a capital letter or
   underscore.
 
 ``TYPE <type>``
 
-  Every file set is associated with a particular type of file. ``HEADERS``
-  is currently the only defined type and it is an error to specify anything
-  else. As a special case, if the name of the file set is ``HEADERS``, the
-  type does not need to be specified and the ``TYPE <type>`` arguments can be
+  Every file set is associated with a particular type of file. Only types
+  specified above may be used and it is an error to specify anything else. As
+  a special case, if the name of the file set is one of the types, the type
+  does not need to be specified and the ``TYPE <type>`` arguments can be
   omitted. For all other file set names, ``TYPE`` is required.
 
 ``BASE_DIRS <dirs>...``
@@ -134,6 +159,8 @@ Each ``target_sources(FILE_SET)`` entry starts with ``INTERFACE``, ``PUBLIC``, o
 The following target properties are set by ``target_sources(FILE_SET)``,
 but they should not generally be manipulated directly:
 
+For file sets of type ``HEADERS``:
+
 * :prop_tgt:`HEADER_SETS`
 * :prop_tgt:`INTERFACE_HEADER_SETS`
 * :prop_tgt:`HEADER_SET`
@@ -141,17 +168,37 @@ but they should not generally be manipulated directly:
 * :prop_tgt:`HEADER_DIRS`
 * :prop_tgt:`HEADER_DIRS_<NAME>`
 
+For file sets of type ``CXX_MODULES``:
+
+* :prop_tgt:`CXX_MODULE_SETS`
+* :prop_tgt:`INTERFACE_CXX_MODULE_SETS`
+* :prop_tgt:`CXX_MODULE_SET`
+* :prop_tgt:`CXX_MODULE_SET_<NAME>`
+* :prop_tgt:`CXX_MODULE_DIRS`
+* :prop_tgt:`CXX_MODULE_DIRS_<NAME>`
+
+For file sets of type ``CXX_MODULE_HEADER_UNITS``:
+
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS`
+* :prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS`
+* :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>`
+
 Target properties related to include directories are also modified by
 ``target_sources(FILE_SET)`` as follows:
 
 :prop_tgt:`INCLUDE_DIRECTORIES`
 
-  If the ``TYPE`` is ``HEADERS``, and the scope of the file set is ``PRIVATE``
-  or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are wrapped in
-  :genex:`$<BUILD_INTERFACE>` and appended to this property.
+  If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope
+  of the file set is ``PRIVATE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of
+  the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this
+  property.
 
 :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`
 
-  If the ``TYPE`` is ``HEADERS``, and the scope of the file set is
-  ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of the file set are
-  wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this property.
+  If the ``TYPE`` is ``HEADERS`` or ``CXX_MODULE_HEADER_UNITS``, and the scope
+  of the file set is ``INTERFACE`` or ``PUBLIC``, all of the ``BASE_DIRS`` of
+  the file set are wrapped in :genex:`$<BUILD_INTERFACE>` and appended to this
+  property.
index 806a98d..9e9f39f 100644 (file)
@@ -14,10 +14,16 @@ Try Compiling Whole Projects
 
 .. code-block:: cmake
 
-  try_compile(<resultVar> <bindir> <srcdir>
-              <projectName> [<targetName>] [CMAKE_FLAGS <flags>...]
+  try_compile(<resultVar> PROJECT <projectName>
+              SOURCE_DIR <srcdir>
+              [BINARY_DIR <bindir>]
+              [TARGET <targetName>]
+              [NO_CACHE]
+              [CMAKE_FLAGS <flags>...]
               [OUTPUT_VARIABLE <var>])
 
+.. versionadded:: 3.25
+
 Try building a project.  The success or failure of the ``try_compile``,
 i.e. ``TRUE`` or ``FALSE`` respectively, is returned in ``<resultVar>``.
 
@@ -34,6 +40,17 @@ below for the meaning of other options.
   Previously this was only done by the
   :ref:`source file <Try Compiling Source Files>` signature.
 
+This command also supports an alternate signature
+which was present in older versions of CMake:
+
+.. code-block:: cmake
+
+  try_compile(<resultVar> <bindir> <srcdir>
+              <projectName> [<targetName>]
+              [NO_CACHE]
+              [CMAKE_FLAGS <flags>...]
+              [OUTPUT_VARIABLE <var>])
+
 .. _`Try Compiling Source Files`:
 
 Try Compiling Source Files
@@ -41,7 +58,12 @@ Try Compiling Source Files
 
 .. code-block:: cmake
 
-  try_compile(<resultVar> <bindir> <srcfile|SOURCES srcfile...>
+  try_compile(<resultVar>
+              <SOURCES <srcfile...>                 |
+               SOURCE_FROM_CONTENT <name> <content> |
+               SOURCE_FROM_VAR <name> <var>         |
+               SOURCE_FROM_FILE <name> <path>       >...
+              [NO_CACHE]
               [CMAKE_FLAGS <flags>...]
               [COMPILE_DEFINITIONS <defs>...]
               [LINK_OPTIONS <options>...]
@@ -53,15 +75,19 @@ Try Compiling Source Files
               [<LANG>_EXTENSIONS <bool>]
               )
 
+.. versionadded:: 3.25
+
 Try building an executable or static library from one or more source files
 (which one is determined by the :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE`
 variable).  The success or failure of the ``try_compile``, i.e. ``TRUE`` or
 ``FALSE`` respectively, is returned in ``<resultVar>``.
 
-In this form, one or more source files must be provided.  If
-:variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is unset or is set to ``EXECUTABLE``,
-the sources must include a definition for ``main`` and CMake will create a
-``CMakeLists.txt`` file to build the source(s) as an executable.
+In this form, one or more source files must be provided. Additionally, one of
+``SOURCES`` and/or ``SOURCE_FROM_*`` must precede other keywords.
+
+If :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is unset or is set to
+``EXECUTABLE``, the sources must include a definition for ``main`` and CMake
+will create a ``CMakeLists.txt`` file to build the source(s) as an executable.
 If :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` is set to ``STATIC_LIBRARY``,
 a static library will be built instead and no definition for ``main`` is
 required.  For an executable, the generated ``CMakeLists.txt`` file would
@@ -76,11 +102,45 @@ contain something like the following:
   target_link_options(cmTryCompileExec PRIVATE <LINK_OPTIONS from caller>)
   target_link_libraries(cmTryCompileExec ${LINK_LIBRARIES})
 
+CMake automatically generates, for each ``try_compile`` operation, a
+unique directory under ``${CMAKE_BINARY_DIR}/CMakeFiles/CMakeScratch``
+with an unspecified name.  These directories are cleaned automatically unless
+:option:`--debug-trycompile <cmake --debug-trycompile>` is passed to ``cmake``.
+Such directories from previous runs are also unconditionally cleaned at the
+beginning of any ``cmake`` execution.
+
+This command also supports an alternate signature
+which was present in older versions of CMake:
+
+.. code-block:: cmake
+
+  try_compile(<resultVar> <bindir> <srcfile|SOURCES srcfile...>
+              [NO_CACHE]
+              [CMAKE_FLAGS <flags>...]
+              [COMPILE_DEFINITIONS <defs>...]
+              [LINK_OPTIONS <options>...]
+              [LINK_LIBRARIES <libs>...]
+              [OUTPUT_VARIABLE <var>]
+              [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
+              [<LANG>_STANDARD <std>]
+              [<LANG>_STANDARD_REQUIRED <bool>]
+              [<LANG>_EXTENSIONS <bool>]
+              )
+
+In this version, ``try_compile`` will use ``<bindir>/CMakeFiles/CMakeTmp`` for
+its operation, and all such files will be cleaned automatically.
+For debugging, :option:`--debug-trycompile <cmake --debug-trycompile>` can be
+passed to ``cmake`` to avoid this clean.  However, multiple sequential
+``try_compile`` operations, if given the same ``<bindir>``, will reuse this
+single output directory, such that you can only debug one such ``try_compile``
+call at a time.  Use of the newer signature is recommended to simplify
+debugging of multiple ``try_compile`` operations.
+
 The options are:
 
 ``CMAKE_FLAGS <flags>...``
-  Specify flags of the form ``-DVAR:TYPE=VALUE`` to be passed to
-  the ``cmake`` command-line used to drive the test build.
+  Specify flags of the form :option:`-DVAR:TYPE=VALUE <cmake -D>` to be passed
+  to the :manual:`cmake(1)` command-line used to drive the test build.
   The above example shows how values for variables
   ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``, and ``LINK_LIBRARIES``
   are used.
@@ -111,9 +171,61 @@ The options are:
   set the :prop_tgt:`STATIC_LIBRARY_OPTIONS` target property in the generated
   project, depending on the :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` variable.
 
+``NO_CACHE``
+  .. versionadded:: 3.25
+
+  The result will be stored in a normal variable rather than a cache entry.
+
+  The result variable is normally cached so that a simple pattern can be used
+  to avoid repeating the test on subsequent executions of CMake:
+
+  .. code-block:: cmake
+
+    if(NOT DEFINED RESULTVAR)
+      # ...(check-specific setup code)...
+      try_compile(RESULTVAR ...)
+      # ...(check-specific logging and cleanup code)...
+    endif()
+
+  If the guard variable and result variable are not the same (for example, if
+  the test is part of a larger inspection), ``NO_CACHE`` may be useful to avoid
+  leaking the intermediate result variable into the cache.
+
 ``OUTPUT_VARIABLE <var>``
   Store the output from the build process in the given variable.
 
+``SOURCE_FROM_CONTENT <name> <content>``
+  .. versionadded:: 3.25
+
+  Write ``<content>`` to a file named ``<name>`` in the operation directory.
+  This can be used to bypass the need to separately write a source file when
+  the contents of the file are dynamically specified. The specified ``<name>``
+  is not allowed to contain path components.
+
+  ``SOURCE_FROM_CONTENT`` may be specified multiple times.
+
+``SOURCE_FROM_FILE <name> <path>``
+  .. versionadded:: 3.25
+
+  Copy ``<path>`` to a file named ``<name>`` in the operation directory. This
+  can be used to consolidate files into the operation directory, which may be
+  useful if a source which already exists (i.e. as a stand-alone file in a
+  project's source repository) needs to refer to other file(s) created by
+  ``SOURCE_FROM_*``. (Otherwise, ``SOURCES`` is usually more convenient.) The
+  specified ``<name>`` is not allowed to contain path components.
+
+``SOURCE_FROM_VAR <name> <var>``
+  .. versionadded:: 3.25
+
+  Write the contents of ``<var>`` to a file named ``<name>`` in the operation
+  directory. This is the same as ``SOURCE_FROM_CONTENT``, but takes the
+  contents from the specified CMake variable, rather than directly, which may
+  be useful when passing arguments through a function which wraps
+  ``try_compile``. The specified ``<name>`` is not allowed to contain path
+  components.
+
+  ``SOURCE_FROM_VAR`` may be specified multiple times.
+
 ``<LANG>_STANDARD <std>``
   .. versionadded:: 3.8
 
@@ -136,17 +248,6 @@ The options are:
   :prop_tgt:`OBJC_EXTENSIONS`, :prop_tgt:`OBJCXX_EXTENSIONS`,
   or :prop_tgt:`CUDA_EXTENSIONS` target property of the generated project.
 
-In this version all files in ``<bindir>/CMakeFiles/CMakeTmp`` will be
-cleaned automatically.  For debugging, ``--debug-trycompile`` can be
-passed to ``cmake`` to avoid this clean.  However, multiple sequential
-``try_compile`` operations reuse this single output directory.  If you use
-``--debug-trycompile``, you can only debug one ``try_compile`` call at a time.
-The recommended procedure is to protect all ``try_compile`` calls in your
-project by ``if(NOT DEFINED <resultVar>)`` logic, configure with cmake
-all the way through once, then delete the cache entry associated with
-the try_compile call of interest, and then re-run cmake again with
-``--debug-trycompile``.
-
 Other Behavior Settings
 ^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -214,9 +315,15 @@ a build configuration.
   the generated project (unless overridden by an explicit option).
 
 .. versionchanged:: 3.14
-  For the :generator:`Green Hills MULTI` generator the GHS toolset and target
-  system customization cache variables are also propagated into the test project.
+  For the :generator:`Green Hills MULTI` generator, the GHS toolset and target
+  system customization cache variables are also propagated into the test
+  project.
 
 .. versionadded:: 3.24
   The :variable:`CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES` variable may be
   set to disable passing platform variables into the test project.
+
+.. versionadded:: 3.25
+  If :policy:`CMP0141` is set to ``NEW``, one can use
+  :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` to specify the MSVC debug
+  information format.
index fc41cdd..cd41a4b 100644 (file)
@@ -13,68 +13,99 @@ Try Compiling and Running Source Files
 .. code-block:: cmake
 
   try_run(<runResultVar> <compileResultVar>
-          <bindir> <srcfile> [CMAKE_FLAGS <flags>...]
+          <SOURCES <srcfile...>                 |
+           SOURCE_FROM_CONTENT <name> <content> |
+           SOURCE_FROM_VAR <name> <var>         |
+           SOURCE_FROM_FILE <name> <path>       >...
+          [NO_CACHE]
+          [CMAKE_FLAGS <flags>...]
           [COMPILE_DEFINITIONS <defs>...]
           [LINK_OPTIONS <options>...]
           [LINK_LIBRARIES <libs>...]
           [COMPILE_OUTPUT_VARIABLE <var>]
+          [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
+          [<LANG>_STANDARD <std>]
+          [<LANG>_STANDARD_REQUIRED <bool>]
+          [<LANG>_EXTENSIONS <bool>]
           [RUN_OUTPUT_VARIABLE <var>]
+          [RUN_OUTPUT_STDOUT_VARIABLE <var>]
+          [RUN_OUTPUT_STDERR_VARIABLE <var>]
           [OUTPUT_VARIABLE <var>]
           [WORKING_DIRECTORY <var>]
-          [ARGS <args>...])
+          [ARGS <args>...]
+          )
+
+.. versionadded:: 3.25
 
 Try compiling a ``<srcfile>``.  Returns ``TRUE`` or ``FALSE`` for success
 or failure in ``<compileResultVar>``.  If the compile succeeded, runs the
 executable and returns its exit code in ``<runResultVar>``.  If the
 executable was built, but failed to run, then ``<runResultVar>`` will be
 set to ``FAILED_TO_RUN``.  See the :command:`try_compile` command for
-information on how the test project is constructed to build the source file.
-
-The options are:
-
-``CMAKE_FLAGS <flags>...``
-  Specify flags of the form ``-DVAR:TYPE=VALUE`` to be passed to
-  the ``cmake`` command-line used to drive the test build.
-  The example in :command:`try_compile` shows how values for variables
-  ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``, and ``LINK_LIBRARIES``
-  are used.
+documentation of options common to both commands, and for information on how
+the test project is constructed to build the source file.
 
-``COMPILE_DEFINITIONS <defs>...``
-  Specify ``-Ddefinition`` arguments to pass to :command:`add_definitions`
-  in the generated test project.
-
-``COMPILE_OUTPUT_VARIABLE <var>``
-  Report the compile step build output in a given variable.
+One or more source files must be provided. Additionally, one of ``SOURCES``
+and/or ``SOURCE_FROM_*`` must precede other keywords.
 
-``LINK_LIBRARIES <libs>...``
-  .. versionadded:: 3.2
+This command also supports an alternate signature
+which was present in older versions of CMake:
 
-  Specify libraries to be linked in the generated project.
-  The list of libraries may refer to system libraries and to
-  :ref:`Imported Targets <Imported Targets>` from the calling project.
+.. code-block:: cmake
 
-  If this option is specified, any ``-DLINK_LIBRARIES=...`` value
-  given to the ``CMAKE_FLAGS`` option will be ignored.
+  try_run(<runResultVar> <compileResultVar>
+          <bindir> <srcfile|SOURCES srcfile...>
+          [NO_CACHE]
+          [CMAKE_FLAGS <flags>...]
+          [COMPILE_DEFINITIONS <defs>...]
+          [LINK_OPTIONS <options>...]
+          [LINK_LIBRARIES <libs>...]
+          [COMPILE_OUTPUT_VARIABLE <var>]
+          [COPY_FILE <fileName> [COPY_FILE_ERROR <var>]]
+          [<LANG>_STANDARD <std>]
+          [<LANG>_STANDARD_REQUIRED <bool>]
+          [<LANG>_EXTENSIONS <bool>]
+          [RUN_OUTPUT_VARIABLE <var>]
+          [RUN_OUTPUT_STDOUT_VARIABLE <var>]
+          [RUN_OUTPUT_STDERR_VARIABLE <var>]
+          [OUTPUT_VARIABLE <var>]
+          [WORKING_DIRECTORY <var>]
+          [ARGS <args>...]
+          )
 
-``LINK_OPTIONS <options>...``
-  .. versionadded:: 3.14
+The options specific to ``try_run`` are:
 
-  Specify link step options to pass to :command:`target_link_options` in the
-  generated project.
+``COMPILE_OUTPUT_VARIABLE <var>``
+  Report the compile step build output in a given variable.
 
 ``OUTPUT_VARIABLE <var>``
   Report the compile build output and the output from running the executable
-  in the given variable.  This option exists for legacy reasons.  Prefer
-  ``COMPILE_OUTPUT_VARIABLE`` and ``RUN_OUTPUT_VARIABLE`` instead.
+  in the given variable.  This option exists for legacy reasons and is only
+  supported by the old ``try_run`` signature.
+  Prefer ``COMPILE_OUTPUT_VARIABLE`` and ``RUN_OUTPUT_VARIABLE`` instead.
 
 ``RUN_OUTPUT_VARIABLE <var>``
   Report the output from running the executable in a given variable.
 
+``RUN_OUTPUT_STDOUT_VARIABLE <var>``
+  .. versionadded:: 3.25
+
+  Report the output of stdout from running the executable in a given variable.
+
+``RUN_OUTPUT_STDERR_VARIABLE <var>``
+  .. versionadded:: 3.25
+
+  Report the output of stderr from running the executable in a given variable.
+
 ``WORKING_DIRECTORY <var>``
   .. versionadded:: 3.20
 
   Run the executable in the given directory. If no ``WORKING_DIRECTORY`` is
-  specified, the executable will run in ``<bindir>``.
+  specified, the executable will run in ``<bindir>`` or the current build
+  directory.
+
+``ARGS <args>...``
+  Additional arguments to pass to the executable when running it.
 
 Other Behavior Settings
 ^^^^^^^^^^^^^^^^^^^^^^^
@@ -110,6 +141,7 @@ These cache entries are:
 
 In order to make cross compiling your project easier, use ``try_run``
 only if really required.  If you use ``try_run``, use the
+``RUN_OUTPUT_STDOUT_VARIABLE``, ``RUN_OUTPUT_STDERR_VARIABLE``,
 ``RUN_OUTPUT_VARIABLE`` or ``OUTPUT_VARIABLE`` options only if really
 required.  Using them will require that when cross-compiling, the cache
 variables will have to be set manually to the output of the executable.
index a4957c1..0bafae5 100644 (file)
@@ -23,3 +23,11 @@ Per legacy, the :command:`endwhile` command admits
 an optional ``<condition>`` argument.
 If used, it must be a verbatim repeat of the argument of the opening
 ``while`` command.
+
+See Also
+^^^^^^^^
+
+  * :command:`break`
+  * :command:`continue`
+  * :command:`foreach`
+  * :command:`endwhile`
index a77b615..9df3cc4 100644 (file)
@@ -57,6 +57,12 @@ Variables specific to CPack Archive generator
   .. versionadded:: 3.9
     Per-component ``CPACK_ARCHIVE_<component>_FILE_NAME`` variables.
 
+.. variable:: CPACK_ARCHIVE_FILE_EXTENSION
+
+  .. versionadded:: 3.25
+
+  Package file extension. Default values are given in the list above.
+
 .. variable:: CPACK_ARCHIVE_COMPONENT_INSTALL
 
   Enable component packaging. If enabled (ON), then the archive generator
index f96ca32..1514dbc 100644 (file)
@@ -57,7 +57,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.5
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_NAME`` variables.
 
- See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
+ See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-source
 
 .. variable:: CPACK_DEBIAN_FILE_NAME
               CPACK_DEBIAN_<COMPONENT>_FILE_NAME
@@ -399,7 +399,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.4
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_PREDEPENDS`` variables.
 
- See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
+ See https://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
 
 .. variable:: CPACK_DEBIAN_PACKAGE_ENHANCES
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_ENHANCES
@@ -419,7 +419,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.4
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_ENHANCES`` variables.
 
- See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
+ See https://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
 
 .. variable:: CPACK_DEBIAN_PACKAGE_BREAKS
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_BREAKS
@@ -508,7 +508,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.4
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_REPLACES`` variables.
 
- See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
+ See https://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
 
 .. variable:: CPACK_DEBIAN_PACKAGE_RECOMMENDS
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_RECOMMENDS
@@ -527,7 +527,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.4
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_RECOMMENDS`` variables.
 
- See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
+ See https://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
 
 .. variable:: CPACK_DEBIAN_PACKAGE_SUGGESTS
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_SUGGESTS
@@ -545,7 +545,7 @@ List of CPack DEB generator specific variables:
  .. versionadded:: 3.4
   Per-component ``CPACK_DEBIAN_<COMPONENT>_PACKAGE_SUGGESTS`` variables.
 
- See http://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
+ See https://www.debian.org/doc/debian-policy/ch-relationships.html#s-binarydeps
 
 .. variable:: CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS
 
@@ -639,7 +639,7 @@ List of CPack DEB generator specific variables:
    - :variable:`CPACK_DEBIAN_PACKAGE_SOURCE` for component-based
      installations.
 
- See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Source
+ See https://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-source
 
  .. note::
 
index 4c083f0..b511319 100644 (file)
@@ -207,8 +207,8 @@ following fields in the root:
   set.
 
 ``buildConfig``
-  The build configuration given to CPack with the ``-C`` option. Only present
-  if this option is set.
+  The build configuration given to CPack with the :option:`cpack -C` option.
+  Only present if this option is set.
 
 ``defaultDirectoryPermissions``
   The default directory permissions given in
index f429bc5..faf8c74 100644 (file)
@@ -62,8 +62,6 @@ the RPM information (e.g. package license).
 
     - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` (this is always set
       by CPack itself, if nothing else sets it explicitly).
-    - :variable:`PROJECT_DESCRIPTION` (this can be set with the DESCRIPTION
-      parameter for :command:`project`).
 
 .. variable:: CPACK_FREEBSD_PACKAGE_DESCRIPTION
 
@@ -75,6 +73,10 @@ the RPM information (e.g. package license).
 
     - :variable:`CPACK_DEBIAN_PACKAGE_DESCRIPTION` (this may be set already
       for Debian packaging, so it is used as a fallback).
+    - :variable:`CPACK_PACKAGE_DESCRIPTION_SUMMARY` (this is always set
+      by CPack itself, if nothing else sets it explicitly).
+    - :variable:`PROJECT_DESCRIPTION` (this can be set with the DESCRIPTION
+      parameter for :command:`project`).
 
 .. variable:: CPACK_FREEBSD_PACKAGE_WWW
 
@@ -85,12 +87,12 @@ the RPM information (e.g. package license).
   * Mandatory: YES
   * Default:
 
-   - :variable:`CMAKE_PROJECT_HOMEPAGE_URL`, or if that is not set,
-     :variable:`CPACK_DEBIAN_PACKAGE_HOMEPAGE` (this may be set already
+   - :variable:`CPACK_PACKAGE_HOMEPAGE_URL`, or if that is not set,
+   - :variable:`CPACK_DEBIAN_PACKAGE_HOMEPAGE` (this may be set already
      for Debian packaging, so it is used as a fallback).
 
   .. versionadded:: 3.12
-    The ``CMAKE_PROJECT_HOMEPAGE_URL`` variable.
+    The ``CPACK_PACKAGE_HOMEPAGE_URL`` variable.
 
 .. variable:: CPACK_FREEBSD_PACKAGE_LICENSE
 
index c23bab4..c252095 100644 (file)
@@ -14,7 +14,7 @@ Overview
 
 This :manual:`cpack generator <cpack-generators(7)>` generates
 configuration and meta information for the `Qt Installer Framework
-<http://doc.qt.io/qtinstallerframework/index.html>`_ (QtIFW),
+<https://doc.qt.io/qtinstallerframework/index.html>`_ (QtIFW),
 and runs QtIFW tools to generate a Qt installer.
 
 QtIFW provides tools and utilities to create installers for
index 299cfec..df306c2 100644 (file)
@@ -207,3 +207,34 @@ on Windows Nullsoft Scriptable Install System.
  .. versionadded:: 3.22
 
  If set, do not display the page containing the license during installation.
+
+.. variable:: CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS
+
+ .. versionadded:: 3.25
+
+ This variable is a :ref:`semicolon-separated list <CMake Language Lists>` of
+ arguments to prepend to the nsis script to run.
+ If the arguments do not start with a ``/`` or a ``-``, it will add one
+ automatically to the corresponding arguments.
+ The command that will be run is::
+
+    makensis.exe <preArgs>... "nsisFileName.nsi" <postArgs>...
+
+ where ``<preArgs>...`` is constructed from ``CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS``
+ and ``<postArgs>...``  is constructed from ``CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS``.
+
+
+.. variable:: CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS
+
+ .. versionadded:: 3.25
+
+ This variable is a :ref:`semicolon-separated list <CMake Language Lists>` of
+ arguments to append to the nsis script to run.
+ If the arguments do not start with a ``/`` or a ``-``, it will add one
+ automatically to the corresponding arguments.
+ The command that will be run is::
+
+    makensis.exe <preArgs>... "nsisFileName.nsi" <postArgs>...
+
+ where ``<preArgs>...`` is constructed from ``CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS``
+ and ``<postArgs>...``  is constructed from ``CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS``.
index c980dd6..3bf7f84 100644 (file)
@@ -110,7 +110,7 @@ List of CPack NuGet generator specific variables:
  .. deprecated:: 3.20
   Use a local license file
   (:variable:`CPACK_NUGET_PACKAGE_LICENSE_FILE_NAME`)
-  or a `(SPDX) license identifier`_
+  or a `SPDX license identifier`_
   (:variable:`CPACK_NUGET_PACKAGE_LICENSE_EXPRESSION`) instead.
 
  An URL for the package's license, often shown in UI displays as well
@@ -124,7 +124,7 @@ List of CPack NuGet generator specific variables:
 
  .. versionadded:: 3.20
 
- A Software Package Data Exchange `(SPDX) license identifier`_ such as
+ A Software Package Data Exchange `SPDX license identifier`_ such as
  ``MIT``, ``BSD-3-Clause``, or ``LGPL-3.0-or-later``. In the case of a
  choice of licenses or more complex restrictions, compound license
  expressions may be formed using boolean operators, for example
@@ -162,6 +162,14 @@ List of CPack NuGet generator specific variables:
  * Mandatory : NO
  * Default   : -
 
+.. variable:: CPACK_NUGET_PACKAGE_REQUIRE_LICENSE_ACCEPTANCE
+
+ When set to a true value, the user will be prompted to accept the license
+ before installing the package.
+
+ * Mandatory : NO
+ * Default   : -
+
 .. variable:: CPACK_NUGET_PACKAGE_ICON
               CPACK_NUGET_<compName>_PACKAGE_ICON
 
@@ -247,9 +255,9 @@ List of CPack NuGet generator specific variables:
  * Default   : OFF
 
 
-.. _nuget.org: http://nuget.org
-.. _version specification: https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards
-.. _(SPDX) license identifier: https://spdx.org/licenses/
-.. _SPDX specification: https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
+.. _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
 
 .. NuGet spec docs https://docs.microsoft.com/en-us/nuget/reference/nuspec
index 0d287fc..b1e0077 100644 (file)
@@ -18,7 +18,7 @@ The CPack RPM generator has specific features which are controlled by the specif
 **grouping name** written in upper case. It may be either a component name or
 a component GROUP name. Usually those variables correspond to RPM spec file
 entities. One may find information about spec files here
-http://www.rpm.org/wiki/Docs
+https://rpm.org/documentation
 
 .. versionchanged:: 3.6
 
@@ -972,7 +972,7 @@ For CMake projects SRPM package would be produced by executing::
  Produced SRPM package is expected to be built with :manual:`cmake(1)` executable
  and packaged with :manual:`cpack(1)` executable so CMakeLists.txt has to be
  located in root source directory and must be able to generate binary rpm
- packages by executing ``cpack -G`` command. The two executables as well as
+ packages by executing :option:`cpack -G` command. The two executables as well as
  rpmbuild must also be present when generating binary rpm packages from the
  produced SRPM package.
 
index a3d43fc..c880049 100644 (file)
@@ -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:
- http://wix.sourceforge.net/manual-wix3/WixUI_localization.htm
+ https://wixtoolset.org//documentation/manual/v3/wixui/wixui_localization.html
 
 .. variable:: CPACK_WIX_TEMPLATE
 
index 7638d22..336137f 100644 (file)
@@ -7,6 +7,23 @@ See documentation on `CMake Development`_ for more information.
 
 .. _`CMake Development`: README.rst
 
+Features are gated behind ``CMAKE_EXPERIMENTAL_`` variables which must be set
+to specific values in order to enable their gated behaviors. Note that the
+specific values will change over time to reinforce their experimental nature.
+When used, a warning will be generated to indicate that an experimental
+feature is in use and that the affected behavior in the project is not part of
+CMake's stability guarantees.
+
+C++20 Module APIs
+=================
+
+Variable: ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+Value: ``3c375311-a3c9-4396-a187-3227ef642046``
+
+In order to support C++20 modules, there are a number of behaviors that have
+CMake APIs to provide the required features to build and export them from a
+project.
+
 C++20 Module Dependencies
 =========================
 
@@ -22,6 +39,10 @@ they can use it to develop and test their dependency scanning tool.
 The ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` variable must also be set
 to tell CMake how to invoke the C++20 module dependency scanning tool.
 
+MSVC 19.34 (provided with Visual Studio 17.4) and above contains the support
+that CMake needs and has these variables already set up as required and only
+the UUID variable needs to be set.
+
 For example, add code like the following to a test project:
 
 .. code-block:: cmake
@@ -40,13 +61,8 @@ dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. The
 ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc``
 for scandep rules which use ``msvc``-style dependency reporting.
 
-For tools which need to know the file set the source belongs to, the
-``CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_<FILE_SET_TYPE>`` flag may
-be provided so that different source types can be distinguished prior to
-scanning.
-
 The module dependencies should be written in the format described
-by the `P1689r4`_ paper.
+by the `P1689r5`_ paper.
 
 Compiler writers may try out their scanning functionality using
 the `cxx-modules-sandbox`_ test project, modified to set variables
@@ -73,5 +89,5 @@ the GCC documentation, but the relevant section for the purposes of CMake is:
     -- GCC module mapper documentation
 
 .. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
-.. _`P1689r4`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1689r4.html
+.. _`P1689r5`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
 .. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox
index 2e1c6d2..2af4b58 100644 (file)
@@ -3,13 +3,16 @@ ASM<DIALECT>FLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling a specific dialect of an
-assembly language. ``ASM<DIALECT>FLAGS`` can be ``ASMFLAGS``, ``ASM_NASMFLAGS``,
-``ASM_MASMFLAGS`` or ``ASM-ATTFLAGS``. Will only be used by CMake on the
-first configuration to determine ``ASM_<DIALECT>`` default compilation
-flags, after which the value for ``ASM<DIALECT>FLAGS`` is stored in the cache
-as ``CMAKE_ASM<DIALECT>_FLAGS <CMAKE_<LANG>_FLAGS>``.  For any configuration
-run (including the first), the environment variable will be ignored, if the
-``CMAKE_ASM<DIALECT>_FLAGS <CMAKE_<LANG>_FLAGS>`` variable is defined.
+Add default compilation flags to be used when compiling a specific dialect
+of an assembly language.  ``ASM<DIALECT>FLAGS`` can be one of:
+
+* ``ASMFLAGS``
+* ``ASM_NASMFLAGS``
+* ``ASM_MASMFLAGS``
+* ``ASM-ATTFLAGS``
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_ASM<DIALECT>_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``ASM<DIALECT>``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_ASM<DIALECT>_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 190b4f4..a6b2452 100644 (file)
@@ -3,11 +3,10 @@ CFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``C`` files. Will only be
-used by CMake on the first configuration to determine ``CC`` default compilation
-flags, after which the value for ``CFLAGS`` is stored in the cache
-as :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration run
-(including the first), the environment variable will be ignored if the
-:variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``C`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``C``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_C_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 3488b04..596e8f3 100644 (file)
@@ -6,9 +6,9 @@ CMAKE_GENERATOR
 .. include:: ENV_VAR.txt
 
 Specifies the CMake default generator to use when no generator is supplied
-with ``-G``. If the provided value doesn't name a generator known by CMake,
-the internal default is used.  Either way the resulting generator selection
-is stored in the :variable:`CMAKE_GENERATOR` variable.
+with :option:`-G <cmake -G>`. If the provided value doesn't name a generator
+known by CMake, the internal default is used.  Either way the resulting
+generator selection is stored in the :variable:`CMAKE_GENERATOR` variable.
 
 Some generators may be additionally configured using the environment
 variables:
index b039845..e657904 100644 (file)
@@ -6,5 +6,5 @@ CMAKE_GENERATOR_PLATFORM
 .. include:: ENV_VAR.txt
 
 Default value for :variable:`CMAKE_GENERATOR_PLATFORM` if no Cache entry
-is present and no value is specified by :manual:`cmake(1)` ``-A`` option.
+is present and no value is specified by :option:`cmake -A` option.
 This value is only applied if :envvar:`CMAKE_GENERATOR` is set.
index 394dd88..03208e7 100644 (file)
@@ -6,5 +6,5 @@ CMAKE_GENERATOR_TOOLSET
 .. include:: ENV_VAR.txt
 
 Default value for :variable:`CMAKE_GENERATOR_TOOLSET` if no Cache entry
-is present and no value is specified by :manual:`cmake(1)` ``-T`` option.
+is present and no value is specified by :option:`cmake -T` option.
 This value is only applied if :envvar:`CMAKE_GENERATOR` is set.
index 784328a..6e909fe 100644 (file)
@@ -5,11 +5,10 @@ CSFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``CSharp`` files. Will only be
-used by CMake on the first configuration to determine ``CSharp`` default
-compilation flags, after which the value for ``CSFLAGS`` is stored in the cache
-as :variable:`CMAKE_CSharp_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration
-run (including the first), the environment variable will be ignored if the
-:variable:`CMAKE_CSharp_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``CSharp`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_CSharp_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``CSharp``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_CSharp_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 8c29d7d..348acc6 100644 (file)
@@ -14,5 +14,5 @@ variable is not set or has a value that evaluates to false, output is reported
 normally with each test having its own start and end lines logged to the
 output.
 
-The ``--progress`` option to :manual:`ctest <ctest(1)>` overrides this
-environment variable if both are given.
+The :option:`--progress <ctest --progress>` option to :manual:`ctest <ctest(1)>`
+overrides this environment variable if both are given.
index af577a0..46a91df 100644 (file)
@@ -5,11 +5,10 @@ CUDAFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``CUDA`` files. Will only be
-used by CMake on the first configuration to determine ``CUDA`` default
-compilation flags, after which the value for ``CUDAFLAGS`` is stored in the
-cache as :variable:`CMAKE_CUDA_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration
-run (including the first), the environment variable will be ignored if
-the :variable:`CMAKE_CUDA_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``CUDA`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_CUDA_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``CUDA``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_CUDA_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 460a347..f67431f 100644 (file)
@@ -3,11 +3,10 @@ CXXFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``CXX`` (C++) files. Will
-only be used by CMake on the first configuration to determine ``CXX`` default
-compilation flags, after which the value for ``CXXFLAGS`` is stored in the cache
-as :variable:`CMAKE_CXX_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration run (
-including the first), the environment variable will be ignored if
-the :variable:`CMAKE_CXX_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``CXX`` (C++) files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_CXX_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``CXX``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_CXX_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 94cae4a..dec8430 100644 (file)
@@ -20,8 +20,9 @@ The packaging tool may then construct the package from the content of the
 
 See the :variable:`CMAKE_INSTALL_PREFIX` variable to control the
 installation prefix when configuring a build tree.  Or, when using
-the :manual:`cmake(1)` command-line tool's ``--install`` mode,
-one may specify a different prefix using the ``--prefix`` option.
+the :manual:`cmake(1)` command-line tool's :option:`--install <cmake --install>`
+mode, one may specify a different prefix using the
+:option:`--prefix <cmake--install --prefix>` option.
 
 .. note::
 
index 53bffb6..23bc8d2 100644 (file)
@@ -3,11 +3,10 @@ FFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``Fortran`` files. Will only
-be used by CMake on the first configuration to determine ``Fortran`` default
-compilation flags, after which the value for ``FFLAGS`` is stored in the cache
-as :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration
-run (including the first), the environment variable will be ignored if
-the :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``Fortran`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_Fortran_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``Fortran``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_Fortran_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 0df3416..31e2390 100644 (file)
@@ -5,11 +5,10 @@ HIPFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``HIP`` files. Will only be
-used by CMake on the first configuration to determine ``HIP`` default
-compilation flags, after which the value for ``HIPFLAGS`` is stored in the
-cache as :variable:`CMAKE_HIP_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration
-run (including the first), the environment variable will be ignored if
-the :variable:`CMAKE_HIP_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``HIP`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_HIP_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``HIP``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_HIP_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
index 21df037..b7a2bd5 100644 (file)
@@ -5,11 +5,10 @@ ISPCFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``ISPC`` files. Will only be
-used by CMake on the first configuration to determine ``ISPC`` default
-compilation flags, after which the value for ``ISPCFLAGS`` is stored in the
-cache as :variable:`CMAKE_ISPC_FLAGS <CMAKE_<LANG>_FLAGS>`. For any configuration
-run (including the first), the environment variable will be ignored if
-the :variable:`CMAKE_ISPC_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``ISPC`` files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_ISPC_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``ISPC``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_ISPC_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
diff --git a/Help/envvar/LANG_FLAGS.txt b/Help/envvar/LANG_FLAGS.txt
new file mode 100644 (file)
index 0000000..d01a56d
--- /dev/null
@@ -0,0 +1,6 @@
+CMake uses this environment variable value, in combination with its own
+builtin default flags for the toolchain, to initialize and store the
+|CMAKE_LANG_FLAGS| cache entry.
+This occurs the first time a build tree is configured for language |LANG|.
+For any configuration run (including the first), the environment variable
+will be ignored if the |CMAKE_LANG_FLAGS| variable is already defined.
index bc43cb2..7df83a7 100644 (file)
@@ -3,11 +3,10 @@ RCFLAGS
 
 .. include:: ENV_VAR.txt
 
-Default compilation flags to be used when compiling ``resource`` files. Will
-only be used by CMake on the first configuration to determine ``resource``
-default compilation flags, after which the value for ``RCFLAGS`` is stored in
-the cache as :variable:`CMAKE_RC_FLAGS <CMAKE_<LANG>_FLAGS>`. For any
-configuration run (including the first), the environment variable will be ignored
-if the :variable:`CMAKE_RC_FLAGS <CMAKE_<LANG>_FLAGS>` variable is defined.
+Add default compilation flags to be used when compiling ``RC`` (resource) files.
+
+.. |CMAKE_LANG_FLAGS| replace:: :variable:`CMAKE_RC_FLAGS <CMAKE_<LANG>_FLAGS>`
+.. |LANG| replace:: ``RC``
+.. include:: LANG_FLAGS.txt
 
 See also :variable:`CMAKE_RC_FLAGS_INIT <CMAKE_<LANG>_FLAGS_INIT>`.
diff --git a/Help/envvar/SSL_CERT_DIR.rst b/Help/envvar/SSL_CERT_DIR.rst
new file mode 100644 (file)
index 0000000..1e678e4
--- /dev/null
@@ -0,0 +1,9 @@
+SSL_CERT_DIR
+------------
+
+.. versionadded:: 3.25
+
+.. include:: ENV_VAR.txt
+
+Specify default directory containing CA certificates.  It overrides
+the default CA directory used.
diff --git a/Help/envvar/SSL_CERT_FILE.rst b/Help/envvar/SSL_CERT_FILE.rst
new file mode 100644 (file)
index 0000000..23216c0
--- /dev/null
@@ -0,0 +1,9 @@
+SSL_CERT_FILE
+-------------
+
+.. versionadded:: 3.25
+
+.. include:: ENV_VAR.txt
+
+Specify the file name containing CA certificates.  It overrides the
+default, os-specific CA file used.
index 1b4739b..7a5993a 100644 (file)
@@ -28,12 +28,12 @@ Otherwise the ``primaryTarget`` will be composed from the values of :variable:`C
 and ``GHS_TARGET_PLATFORM``. Defaulting to the value of ``arm_integrity.tgt``
 
 * The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :manual:`cmake(1)` ``-A`` option.
+  via the :option:`cmake -A` option.
 
   | Typical values of ``arm``, ``ppc``, ``86``, etcetera, are used.
 
-* The variable ``GHS_TARGET_PLATFORM`` may be set, perhaps via the :manual:`cmake(1)`
-  ``-D`` option.
+* The variable ``GHS_TARGET_PLATFORM`` may be set, perhaps via the :option:`cmake -D`
+  option.
 
   | Defaults to ``integrity``.
   | Usual values are ``integrity``, ``threadx``, ``uvelosity``, ``velosity``,
@@ -55,11 +55,11 @@ The generator searches for the latest compiler or can be given a location to use
 ``GHS_TOOLSET_ROOT`` is the directory that is checked for the latest compiler.
 
 * The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-  via the :manual:`cmake(1)` ``-T`` option, to specify the location of the toolset.
+  via the :option:`cmake -T` option, to specify the location of the toolset.
   Both absolute and relative paths are valid. Paths are relative to ``GHS_TOOLSET_ROOT``.
 
-* The variable ``GHS_TOOLSET_ROOT`` may be set, perhaps via the :manual:`cmake(1)`
-  ``-D`` option.
+* The variable ``GHS_TOOLSET_ROOT`` may be set, perhaps via the :option:`cmake -D`
+  option.
 
   | Root path for toolset searches and relative paths.
   | Defaults to ``C:/ghs`` in Windows or ``/usr/ghs`` in Linux.
index e5ce4f5..2cf823a 100644 (file)
@@ -20,8 +20,8 @@ are intended to be run with ``ninja -f build-<Config>.ninja``. A
 :variable:`CMAKE_CONFIGURATION_TYPES`.
 
 ``cmake --build . --config <Config>`` will always use ``build-<Config>.ninja``
-to build. If no ``--config`` argument is specified, ``cmake --build .`` will
-use ``build.ninja``.
+to build. If no :option:`--config <cmake--build --config>` argument is
+specified, :option:`cmake --build . <cmake --build>` will use ``build.ninja``.
 
 Each ``build-<Config>.ninja`` file contains ``<target>`` targets as well as
 ``<target>:<Config>`` targets, where ``<Config>`` is the same as the
index 9ec33c3..888164f 100644 (file)
@@ -1,52 +1,8 @@
 Visual Studio 10 2010
 ---------------------
 
-Deprecated.  Generates Visual Studio 10 (VS 2010) project files.
-
-.. note::
-  This generator is deprecated and will be removed in a future version
-  of CMake.  It will still be possible to build with VS 10 2010 tools
-  using the :generator:`Visual Studio 11 2012` (or above) generator
-  with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v100``, or by
-  using the :generator:`NMake Makefiles` generator.
-
-For compatibility with CMake versions prior to 3.0, one may specify this
-generator using the name ``Visual Studio 10`` without the year component.
-
-Project Types
-^^^^^^^^^^^^^
-
-Only Visual C++ and C# projects may be generated (and Fortran with
-Intel compiler integration).  Other types of projects (Database,
-Website, etc.) are not supported.
-
-Platform Selection
-^^^^^^^^^^^^^^^^^^
-
-The default target platform name (architecture) is ``Win32``.
-
-.. versionadded:: 3.1
-  The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
-  name (architecture).  For example:
-
-  * ``cmake -G "Visual Studio 10 2010" -A Win32``
-  * ``cmake -G "Visual Studio 10 2010" -A x64``
-  * ``cmake -G "Visual Studio 10 2010" -A Itanium``
-
-For compatibility with CMake versions prior to 3.1, one may specify
-a target platform name optionally at the end of the generator name.
-This is supported only for:
-
-``Visual Studio 10 2010 Win64``
-  Specify target platform ``x64``.
-
-``Visual Studio 10 2010 IA64``
-  Specify target platform ``Itanium``.
-
-Toolset Selection
-^^^^^^^^^^^^^^^^^
-
-The ``v100`` toolset that comes with Visual Studio 10 2010 is selected by
-default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+Removed.  This once generated Visual Studio 10 2010 project files, but
+the generator has been removed since CMake 3.25.  It is still possible
+to build with VS 10 2010 tools using the :generator:`Visual Studio 12 2013`
+(or above) generator with :variable:`CMAKE_GENERATOR_TOOLSET` set to
+``v100``, or by using the :generator:`NMake Makefiles` generator.
index 3952228..4e7195c 100644 (file)
@@ -1,7 +1,14 @@
 Visual Studio 11 2012
 ---------------------
 
-Generates Visual Studio 11 (VS 2012) project files.
+Deprecated.  Generates Visual Studio 11 (VS 2012) 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 11 2012 tools
+  using the :generator:`Visual Studio 12 2013` (or above) generator
+  with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v110``, or by
+  using the :generator:`NMake Makefiles` generator.
 
 For compatibility with CMake versions prior to 3.0, one may specify this
 generator using the name "Visual Studio 11" without the year component.
@@ -20,7 +27,7 @@ The default target platform name (architecture) is ``Win32``.
 
 .. versionadded:: 3.1
   The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+  via the :option:`cmake -A` option, to specify a target platform
   name (architecture).  For example:
 
   * ``cmake -G "Visual Studio 11 2012" -A Win32``
@@ -47,4 +54,4 @@ Toolset Selection
 
 The ``v110`` toolset that comes with Visual Studio 11 2012 is selected by
 default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
index 54a4d7e..3dbcfe6 100644 (file)
@@ -20,7 +20,7 @@ The default target platform name (architecture) is ``Win32``.
 
 .. versionadded:: 3.1
   The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+  via the :option:`cmake -A` option, to specify a target platform
   name (architecture).  For example:
 
   * ``cmake -G "Visual Studio 12 2013" -A Win32``
@@ -42,7 +42,7 @@ Toolset Selection
 
 The ``v120`` toolset that comes with Visual Studio 12 2013 is selected by
 default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. |VS_TOOLSET_HOST_ARCH_DEFAULT| replace::
    By default this generator uses the 32-bit variant even on a 64-bit host.
index d704ab2..af7dfaa 100644 (file)
@@ -18,7 +18,7 @@ Platform Selection
 The default target platform name (architecture) is ``Win32``.
 
 The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+via the :option:`cmake -A` option, to specify a target platform
 name (architecture).  For example:
 
 * ``cmake -G "Visual Studio 14 2015" -A Win32``
@@ -40,7 +40,7 @@ Toolset Selection
 
 The ``v140`` toolset that comes with Visual Studio 14 2015 is selected by
 default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. |VS_TOOLSET_HOST_ARCH_DEFAULT| replace::
    By default this generator uses the 32-bit variant even on a 64-bit host.
index 912afad..6bcc033 100644 (file)
@@ -26,7 +26,7 @@ Platform Selection
 The default target platform name (architecture) is ``Win32``.
 
 The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+via the :option:`cmake -A` option, to specify a target platform
 name (architecture).  For example:
 
 * ``cmake -G "Visual Studio 15 2017" -A Win32``
@@ -49,7 +49,7 @@ Toolset Selection
 
 The ``v141`` toolset that comes with Visual Studio 15 2017 is selected by
 default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. |VS_TOOLSET_HOST_ARCH_DEFAULT| replace::
    By default this generator uses the 32-bit variant even on a 64-bit host.
index 6cefe6d..2918a6e 100644 (file)
@@ -25,7 +25,7 @@ The default target platform name (architecture) is that of the host
 and is provided in the :variable:`CMAKE_VS_PLATFORM_NAME_DEFAULT` variable.
 
 The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+via the :option:`cmake -A` option, to specify a target platform
 name (architecture).  For example:
 
 * ``cmake -G "Visual Studio 16 2019" -A Win32``
@@ -38,7 +38,7 @@ Toolset Selection
 
 The ``v142`` toolset that comes with Visual Studio 16 2019 is selected by
 default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. |VS_TOOLSET_HOST_ARCH_DEFAULT| replace::
    By default this generator uses the 64-bit variant on x64 hosts and
index edf9d60..ab101ac 100644 (file)
@@ -25,7 +25,7 @@ The default target platform name (architecture) is that of the host
 and is provided in the :variable:`CMAKE_VS_PLATFORM_NAME_DEFAULT` variable.
 
 The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+via the :option:`cmake -A` option, to specify a target platform
 name (architecture).  For example:
 
 * ``cmake -G "Visual Studio 17 2022" -A Win32``
@@ -38,7 +38,7 @@ Toolset Selection
 
 The ``v143`` toolset that comes with VS 17 2022 is selected by default.
 The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. |VS_TOOLSET_HOST_ARCH_DEFAULT| replace::
    By default this generator uses the 64-bit variant on x64 hosts and
index 644f936..3434956 100644 (file)
@@ -10,7 +10,7 @@ The default target platform name (architecture) is ``Win32``.
 
 .. versionadded:: 3.1
   The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :manual:`cmake(1)` ``-A`` option, to specify a target platform
+  via the :option:`cmake -A` option, to specify a target platform
   name (architecture).  For example:
 
   * ``cmake -G "Visual Studio 9 2008" -A Win32``
index e4900a1..9dd5015 100644 (file)
@@ -13,7 +13,7 @@ Toolset and Build System Selection
 
 By default Xcode is allowed to select its own default toolchain.
 The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :manual:`cmake(1)` ``-T`` option, to specify another toolset.
+via the :option:`cmake -T` option, to specify another toolset.
 
 .. versionadded:: 3.19
   This generator supports toolset specification using one of these forms:
@@ -34,7 +34,7 @@ Supported pairs are:
   See the :variable:`CMAKE_XCODE_BUILD_SYSTEM` variable for allowed values.
 
   For example, to select the original build system under Xcode 12,
-  run :manual:`cmake(1)` with the option ``-T buildsystem=1``.
+  run :manual:`cmake(1)` with the option :option:`-T buildsystem=1 <cmake -T>`.
 
 Swift Support
 ^^^^^^^^^^^^^
@@ -44,3 +44,13 @@ Swift Support
 When using the :generator:`Xcode` generator with Xcode 6.1 or higher,
 one may enable the ``Swift`` language with the :command:`enable_language`
 command or the :command:`project`.
+
+Limitations
+^^^^^^^^^^^
+
+The Xcode generator does not support per-configuration sources.
+Code like the following will result in a generation error:
+
+.. code-block:: cmake
+
+  add_executable(MyApp mymain-$<CONFIG>.cpp)
index 8473481..e198789 100644 (file)
@@ -47,8 +47,9 @@ does, and present the user with the presets listed in the file. Users should be
 able to see (and possibly edit) the CMake cache variables, environment
 variables, and command line options that are defined for a given preset. The
 IDE should then construct the list of appropriate :manual:`cmake(1)` command
-line arguments based on these settings, rather than using the ``--preset=``
-option directly. The ``--preset=`` option is intended only as a convenient
+line arguments based on these settings, rather than using the
+:option:`--preset= <cmake --preset>` option directly. The
+:option:`--preset= <cmake --preset>` option is intended only as a convenient
 frontend for command line users, and should not be used by the IDE.
 
 For example, if a preset named ``ninja`` specifies ``Ninja`` as the generator
@@ -66,10 +67,9 @@ run:
   cmake -S /path/to/source -B /path/to/source/build -G Ninja
 
 In cases where a preset contains lots of cache variables, and passing all of
-them as ``-D`` flags would cause the command line length limit of the platform
-to be exceeded, the IDE should instead construct a temporary cache script and
-pass it with the ``-C`` flag. See :ref:`CMake Options` for details on how the
-``-C`` flag is used.
+them as :option:`-D <cmake -D>` flags would cause the command line length limit
+of the platform to be exceeded, the IDE should instead construct a temporary
+cache script and pass it with the :option:`-C <cmake -C>` flag.
 
 While reading, parsing, and evaluating the contents of ``CMakePresets.json`` is
 straightforward, it is not trivial. In addition to the documentation, IDE
@@ -110,8 +110,9 @@ Building
 
 If a Makefile or Ninja generator is used to generate the build tree, it is not
 recommended to invoke ``make`` or ``ninja`` directly. Instead, it is
-recommended that the IDE invoke :manual:`cmake(1)` with the ``--build``
-argument, which will in turn invoke the appropriate build tool.
+recommended that the IDE invoke :manual:`cmake(1)` with the
+:option:`--build <cmake --build>` argument, which will in turn invoke the
+appropriate build tool.
 
 If an IDE project generator is used, such as :generator:`Xcode` or one of the
 Visual Studio generators, and the IDE understands the project format used, the
index cf1ec01..3dac68a 100644 (file)
@@ -1,34 +1,81 @@
 Step 1: A Basic Starting Point
 ==============================
 
-The most basic project is an executable built from source code files.
-For simple projects, a three line ``CMakeLists.txt`` file is all that is
-required. This will be the starting point for our tutorial. Create a
-``CMakeLists.txt`` file in the ``Step1`` directory that looks like:
-
-.. code-block:: cmake
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-start
-
-  cmake_minimum_required(VERSION 3.10)
+Where do I start with CMake? This step will provide an introduction to some of
+CMake's basic syntax, commands, and variables. As these concepts are
+introduced, we will work through three exercises and create a simple CMake
+project.
+
+Each exercise in this step will start with some background information. Then, a
+goal and list of helpful resources are provided. Each file in the
+``Files to Edit`` section is in the ``Step1`` directory and contains one or
+more ``TODO`` comments. Each ``TODO`` represents a line or two of code to
+change or add. The ``TODO`` s are intended to be completed in numerical order,
+first complete  ``TODO 1`` then ``TODO 2``, etc. The ``Getting Started``
+section will give some helpful hints and guide you through the exercise. Then
+the ``Build and Run`` section will walk step-by-step through how to build and
+test the exercise. Finally, at the end of each exercise the intended solution
+is discussed.
+
+Also note that each step in the tutorial builds on the next. So, for example,
+the starting code for ``Step2`` is the complete solution to ``Step1``.
+
+Exercise 1 - Building a Basic Project
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The most basic CMake project is an executable built from a single source code
+file. For simple projects like this, a ``CMakeLists.txt`` file with three
+commands is all that is required.
+
+**Note:** Although upper, lower and mixed case commands are supported by CMake,
+lower case commands are preferred and will be used throughout the tutorial.
+
+Any project's top most CMakeLists.txt must start by specifying a minimum CMake
+version using the :command:`cmake_minimum_required` command. This establishes
+policy settings and ensures that the following CMake functions are run with a
+compatible version of CMake.
+
+To start a project, we use the :command:`project` command to set the project
+name. This call is required with every project and should be called soon after
+:command:`cmake_minimum_required`. As we will see later, this command can
+also be used to specify other project level information such as the language
+or version number.
+
+Finally, the :command:`add_executable` command tells CMake to create an
+executable using the specified source code files.
+
+Goal
+----
+
+Understand how to create a simple CMake project.
+
+Helpful Resources
+-----------------
+
+* :command:`add_executable`
+* :command:`cmake_minimum_required`
+* :command:`project`
+
+Files to Edit
+-------------
 
-  # set the project name
-  project(Tutorial)
+* ``CMakeLists.txt``
 
-  # add the executable
-  add_executable(Tutorial tutorial.cxx)
+Getting Started
+----------------
 
+The source code for ``tutorial.cxx`` is provided in the
+``Help/guide/tutorial/Step1`` directory and can be used to compute the square
+root of a number. This file does not need to be edited in this step.
 
-Note that this example uses lower case commands in the ``CMakeLists.txt`` file.
-Upper, lower, and mixed case commands are supported by CMake. The source
-code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be
-used to compute the square root of a number.
+In the same directory is a ``CMakeLists.txt`` file which you will complete.
+Start with ``TODO 1`` and work through ``TODO 3``.
 
 Build and Run
 -------------
 
-That's all that is needed - we can build and run our project now! First, run
-the :manual:`cmake <cmake(1)>` executable or the
+Once ``TODO 1`` through ``TODO 3`` have been completed, we are ready to build
+and run our project! First, run the :manual:`cmake <cmake(1)>` executable or the
 :manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
 with your chosen build tool.
 
@@ -40,8 +87,9 @@ build directory:
 
   mkdir Step1_build
 
-Next, navigate to the build directory and run CMake to configure the project
-and generate a native build system:
+Next, navigate to that build directory and run
+:manual:`cmake <cmake(1)>` to configure the project and generate a native build
+system:
 
 .. code-block:: console
 
@@ -62,114 +110,352 @@ Finally, try to use the newly built ``Tutorial`` with these commands:
   Tutorial 10
   Tutorial
 
+Solution
+--------
 
-Adding a Version Number and Configured Header File
---------------------------------------------------
+As mentioned above, a three line ``CMakeLists.txt`` is all that we need to get
+up and running. The first line is to use :command:`cmake_minimum_required` to
+set the CMake version as follows:
 
-The first feature we will add is to provide our executable and project with a
-version number. While we could do this exclusively in the source code, using
-``CMakeLists.txt`` provides more flexibility.
+.. raw:: html
 
-First, modify the ``CMakeLists.txt`` file to use the :command:`project` command
-to set the project name and version number.
+  <details><summary>TODO 1: Click to show/hide answer</summary>
 
 .. literalinclude:: Step2/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-project-VERSION
+  :caption: TODO 1: CMakeLists.txt
+  :name: CMakeLists.txt-cmake_minimum_required
   :language: cmake
-  :end-before: # specify the C++ standard
+  :end-before: # set the project name and version
 
-Then, configure a header file to pass the version number to the source
-code:
+.. raw:: html
 
-.. literalinclude:: Step2/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-configure_file
-  :language: cmake
-  :start-after: # to the source code
-  :end-before: # add the executable
+  </details>
+
+The next step to make a basic project is to use the :command:`project`
+command as follows to set the project name:
+
+.. raw:: html
+
+  <details><summary>TODO 2: Click to show/hide answer</summary>
+
+.. code-block:: cmake
+  :caption: TODO 2: CMakeLists.txt
+  :name: CMakeLists.txt-project
+
+  project(Tutorial)
+
+.. raw:: html
 
-Since the configured file will be written into the binary tree, we
-must add that directory to the list of paths to search for include
-files. Add the following lines to the end of the ``CMakeLists.txt`` file:
+  </details>
+
+The last command to call for a basic project is
+:command:`add_executable`. We call it as follows:
+
+.. raw:: html
+
+  <details><summary>TODO 3: Click to show/hide answer</summary>
 
 .. literalinclude:: Step2/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-target_include_directories
+  :caption: TODO 3: CMakeLists.txt
+  :name: CMakeLists.txt-add_executable
   :language: cmake
-  :start-after: # so that we will find TutorialConfig.h
+  :start-after: # add the executable
+  :end-before: # TODO 9:
 
-Using your favorite editor, create ``TutorialConfig.h.in`` in the source
-directory with the following contents:
+.. raw:: html
 
-.. literalinclude:: Step2/TutorialConfig.h.in
-  :caption: TutorialConfig.h.in
-  :name: TutorialConfig.h.in
-  :language: c++
+  </details>
 
-When CMake configures this header file the values for
-``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will be
-replaced.
+Exercise 2 - Specifying the C++ Standard
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Next modify ``tutorial.cxx`` to include the configured header file,
-``TutorialConfig.h``.
+CMake has some special variables that are either created behind the scenes or
+have meaning to CMake when set by project code. Many of these variables start
+with ``CMAKE_``. Avoid this naming convention when creating variables for your
+projects. Two of these special user settable variables are
+:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
+These may be used together to specify the C++ standard needed to build the
+project.
 
-Finally, let's print out the executable name and version number by updating
-``tutorial.cxx`` as follows:
+Goal
+----
 
-.. literalinclude:: Step2/tutorial.cxx
-  :caption: tutorial.cxx
-  :name: tutorial.cxx-print-version
-  :language: c++
-  :start-after: {
-  :end-before: // convert input to double
+Add a feature that requires C++11.
+
+Helpful Resources
+-----------------
+
+* :variable:`CMAKE_CXX_STANDARD`
+* :variable:`CMAKE_CXX_STANDARD_REQUIRED`
+* :command:`set`
+
+Files to Edit
+-------------
 
-Specify the C++ Standard
--------------------------
+* ``CMakeLists.txt``
+* ``tutorial.cxx``
 
-Next let's add some C++11 features to our project by replacing ``atof`` with
-``std::stod`` in ``tutorial.cxx``.  At the same time, remove
-``#include <cstdlib>``.
+Getting Started
+---------------
+
+Continue editing files in the ``Step1`` directory. Start with ``TODO 4`` and
+complete through ``TODO 6``.
+
+First, edit ``tutorial.cxx`` by adding a feature that requires C++11. Then
+update ``CMakeLists.txt`` to require C++11.
+
+Build and Run
+-------------
+
+Let's build our project again. Since we already created a build directory and
+ran CMake for Exercise 1, we can skip to the build step:
+
+.. code-block:: console
+
+  cd Step1_build
+  cmake --build .
+
+Now we can try to use the newly built ``Tutorial`` with same commands as
+before:
+
+.. code-block:: console
+
+  Tutorial 4294967296
+  Tutorial 10
+  Tutorial
+
+Solution
+--------
+
+We start by adding some C++11 features to our project by replacing
+``atof`` with ``std::stod`` in ``tutorial.cxx``. This looks like
+the following:
+
+.. raw:: html
+
+  <details><summary>TODO 4: Click to show/hide answer</summary>
 
 .. literalinclude:: Step2/tutorial.cxx
-  :caption: tutorial.cxx
+  :caption: TODO 4: tutorial.cxx
   :name: tutorial.cxx-cxx11
   :language: c++
   :start-after: // convert input to double
-  :end-before: // calculate square root
+  :end-before: // TODO 12:
+
+.. raw:: html
+
+  </details>
+
+To complete ``TODO 5``, simply remove ``#include <cstdlib>``.
 
 We will need to explicitly state in the CMake code that it should use the
-correct flags. The easiest way to enable support for a specific C++ standard
-in CMake is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this
-tutorial, set the :variable:`CMAKE_CXX_STANDARD` variable in the
-``CMakeLists.txt`` file to ``11`` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`
-to ``True``. Make sure to add the ``CMAKE_CXX_STANDARD`` declarations above the
-call to ``add_executable``.
+correct flags. One way to enable support for a specific C++ standard in CMake
+is by using the :variable:`CMAKE_CXX_STANDARD` variable. For this tutorial, set
+the :variable:`CMAKE_CXX_STANDARD` variable in the ``CMakeLists.txt`` file to
+``11`` and :variable:`CMAKE_CXX_STANDARD_REQUIRED` to ``True``. Make sure to
+add the :variable:`CMAKE_CXX_STANDARD` declarations above the call to
+:command:`add_executable`.
+
+.. raw:: html
+
+  <details><summary>TODO 6: Click to show/hide answer</summary>
 
 .. literalinclude:: Step2/CMakeLists.txt
-  :caption: CMakeLists.txt
+  :caption: TODO 6: CMakeLists.txt
   :name: CMakeLists.txt-CXX_STANDARD
   :language: cmake
-  :end-before: # configure a header file to pass some of the CMake settings
+  :start-after: # specify the C++ standard
+  :end-before: # TODO 7:
+
+.. raw:: html
 
-Rebuild
--------
+  </details>
 
-Let's build our project again. We already created a build directory and ran
-CMake, so we can skip to the build step:
+Exercise 3 - Adding a Version Number and Configured Header File
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Sometimes it may be useful to have a variable that is defined in your
+``CMakelists.txt`` file also be available in your source code. In this case, we
+would like to print the project version.
+
+One way to accomplish this is by using a configured header file. We create an
+input file with one or more variables to replace. These variables have special
+syntax which looks like ``@VAR@``.
+Then, we use the :command:`configure_file` command to copy the input file to a
+given output file and replace these variables with the current value of ``VAR``
+in the ``CMakelists.txt`` file.
+
+While we could edit the version directly in the source code, using this
+feature is preferred since it creates a single source of truth and avoids
+duplication.
+
+Goal
+----
+
+Define and report the project's version number.
+
+Helpful Resources
+-----------------
+
+* :variable:`<PROJECT-NAME>_VERSION_MAJOR`
+* :variable:`<PROJECT-NAME>_VERSION_MINOR`
+* :command:`configure_file`
+* :command:`target_include_directories`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+* ``tutorial.cxx``
+
+Getting Started
+---------------
+
+Continue to edit files from ``Step1``. Start on ``TODO 7`` and complete through
+``TODO 12``. In this exercise, we start by adding a project version number in
+``CMakeLists.txt``. In that same file, use :command:`configure_file` to copy a
+given input file to an output file and substitute some variable values in the
+input file content.
+
+Next, create an input header file ``TutorialConfig.h.in`` defining version
+numbers which will accept variables passed from :command:`configure_file`.
+
+Finally, update ``tutorial.cxx`` to print out its version number.
+
+Build and Run
+-------------
+
+Let's build our project again. As before, we already created a build directory
+and ran CMake so we can skip to the build step:
 
 .. code-block:: console
 
   cd Step1_build
   cmake --build .
 
-Now we can try to use the newly built ``Tutorial`` with same commands as before:
+Verify that the version number is now reported when running the executable
+without any arguments.
 
-.. code-block:: console
+Solution
+--------
 
-  Tutorial 4294967296
-  Tutorial 10
-  Tutorial
+In this exercise, we improve our executable by printing a version number.
+While we could do this exclusively in the source code, using ``CMakeLists.txt``
+lets us maintain a single source of data for the version number.
+
+First, we modify the ``CMakeLists.txt`` file to use the
+:command:`project` command to set both the project name and version number.
+When the :command:`project` command is called, CMake defines
+``Tutorial_VERSION_MAJOR`` and ``Tutorial_VERSION_MINOR`` behind the scenes.
+
+.. raw:: html
+
+  <details><summary>TODO 7: Click to show/hide answer</summary>
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :caption: TODO 7: CMakeLists.txt
+  :name: CMakeLists.txt-project-VERSION
+  :language: cmake
+  :start-after: # set the project name and version
+  :end-before: # specify the C++ standard
+
+.. raw:: html
+
+  </details>
+
+Then we used :command:`configure_file` to copy the input file with the
+specified CMake variables replaced:
+
+.. raw:: html
+
+  <details><summary>TODO 8: Click to show/hide answer</summary>
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :caption: TODO 8: CMakeLists.txt
+  :name: CMakeLists.txt-configure_file
+  :language: cmake
+  :start-after: # to the source code
+  :end-before: # TODO 8:
+
+.. raw:: html
+
+  </details>
+
+Since the configured file will be written into the project binary
+directory, we must add that directory to the list of paths to search for
+include files.
+
+**Note:** Throughout this tutorial, we will refer to the project build and
+the project binary directory interchangeably. These are the same and are not
+meant to refer to a `bin/` directory.
+
+We used :command:`target_include_directories` to specify
+where the executable target should look for include files.
+
+.. raw:: html
+
+  <details><summary>TODO 9: Click to show/hide answer</summary>
+
+.. literalinclude:: Step2/CMakeLists.txt
+  :caption: TODO 9: CMakeLists.txt
+  :name: CMakeLists.txt-target_include_directories
+  :language: cmake
+  :start-after: # so that we will find TutorialConfig.h
+
+.. raw:: html
+
+  </details>
+
+``TutorialConfig.h.in`` is the input header file to be configured.
+When :command:`configure_file` is called from our ``CMakeLists.txt``, the
+values for ``@Tutorial_VERSION_MAJOR@`` and ``@Tutorial_VERSION_MINOR@`` will
+be replaced with the corresponding version numbers from the project in
+``TutorialConfig.h``.
+
+.. raw:: html
+
+  <details><summary>TODO 10: Click to show/hide answer</summary>
+
+.. literalinclude:: Step2/TutorialConfig.h.in
+  :caption: TODO 10: TutorialConfig.h.in
+  :name: TutorialConfig.h.in
+  :language: c++
+  :end-before: // TODO 13:
+
+.. raw:: html
+
+  </details>
+
+Next, we need to modify ``tutorial.cxx`` to include the configured header file,
+``TutorialConfig.h``.
+
+.. raw:: html
+
+  <details><summary>TODO 11: Click to show/hide answer</summary>
+
+.. code-block:: c++
+  :caption: TODO 11: tutorial.cxx
+
+  #include "TutorialConfig.h"
+
+.. raw:: html
+
+  </details>
+
+Finally, we print out the executable name and version number by updating
+``tutorial.cxx`` as follows:
+
+.. raw:: html
+
+  <details><summary>TODO 12: Click to show/hide answer</summary>
+
+.. literalinclude:: Step2/tutorial.cxx
+  :caption: TODO 12 : tutorial.cxx
+  :name: tutorial.cxx-print-version
+  :language: c++
+  :start-after: {
+  :end-before: // convert input to double
+
+.. raw:: html
 
-Check that the version number is now reported when running the executable without
-any arguments.
+  </details>
index 3bd6d64..eb14f42 100644 (file)
@@ -21,7 +21,7 @@ in ``MathFunctions/CMakeLists.txt`` to look like:
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-install-TARGETS-EXPORT
   :language: cmake
-  :start-after: # install rules
+  :start-after: # install libs
 
 Now that we have ``MathFunctions`` being exported, we also need to explicitly
 install the generated ``MathFunctionsTargets.cmake`` file. This is done by
index 7fcc97f..6f9714e 100644 (file)
@@ -1,4 +1,4 @@
-Step 10: Adding Generator Expressions
+Step 4: Adding Generator Expressions
 =====================================
 
 :manual:`Generator expressions <cmake-generator-expressions(7)>` are evaluated
@@ -27,58 +27,280 @@ expressions are the ``0`` and ``1`` expressions. A ``$<0:...>`` results in the
 empty string, and ``<1:...>`` results in the content of ``...``.  They can also
 be nested.
 
+Exercise 1 - Setting the C++ Standard with Interface Libraries
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Before we use :manual:`generator expressions <cmake-generator-expressions(7)>`
+let's refactor our existing code to use an ``INTERFACE`` library. We will
+use that library in the next step to demonstrate a common use for
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+Goal
+----
+
+Add an ``INTERFACE`` library target to specify the required C++ standard.
+
+Helpful Resources
+-----------------
+
+* :command:`add_library`
+* :command:`target_compile_features`
+* :command:`target_link_libraries`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+* ``MathFunctions/CMakeLists.txt``
+
+Getting Started
+---------------
+
+In this exercise, we will refactor our code to use an ``INTERFACE`` library to
+specify the C++ standard.
+
+The starting source code is provided in the ``Step4`` directory. In this
+exercise, complete ``TODO 1`` through ``TODO 3``.
+
+Start by editing the top level ``CMakeLists.txt`` file. Construct an
+``INTERFACE`` library target called ``tutorial_compiler_flags`` and
+specify ``cxx_std_11`` as a target compiler feature.
+
+Modify ``CMakeLists.txt`` and ``MathFunctions/CMakeLists.txt`` so that all
+targets have a :command:`target_link_libraries` call to
+``tutorial_compiler_flags``.
+
+Build and Run
+-------------
+
+Make a new directory called ``Step4_build``, run the :manual:`cmake <cmake(1)>`
+executable or the :manual:`cmake-gui <cmake-gui(1)>` to configure the project
+and then build it with your chosen build tool or by using ``cmake --build .``
+from the build directory.
+
+Here's a refresher of what that looks like from the command line:
+
+.. code-block:: console
+
+  mkdir Step4_build
+  cd Step4_build
+  cmake ../Step4
+  cmake --build .
+
+Next, use the newly built ``Tutorial`` and verify that it is working as
+expected.
+
+Solution
+--------
+
+Let's update our code from the previous step to use interface libraries
+to set our C++ requirements.
+
+To start, we need to remove the two :command:`set` calls on the variables
+:variable:`CMAKE_CXX_STANDARD` and :variable:`CMAKE_CXX_STANDARD_REQUIRED`.
+The specific lines to remove are as follows:
+
+.. literalinclude:: Step4/CMakeLists.txt
+  :caption: CMakeLists.txt
+  :name: CMakeLists.txt-CXX_STANDARD-variable-remove
+  :language: cmake
+  :start-after: # specify the C++ standard
+  :end-before: # TODO 5: 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
+``cxx_std_11``.
+
+
+.. raw:: html
+
+  <details><summary>TODO 1: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :caption: TODO 1: CMakeLists.txt
+  :name: CMakeLists.txt-cxx_std-feature
+  :language: cmake
+  :start-after: # specify the C++ standard
+  :end-before: # add compiler warning flags just
+
+.. raw:: html
+
+  </details>
+
+Finally, with our interface library set up, we need to link our
+executable ``Target`` and our ``MathFunctions`` library to our new
+``tutorial_compiler_flags`` library. Respectively, the code will look like
+this:
+
+.. raw:: html
+
+  <details><summary>TODO 2: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :caption: TODO 2: CMakeLists.txt
+  :name: CMakeLists.txt-target_link_libraries-step4
+  :language: cmake
+  :start-after: add_executable(Tutorial tutorial.cxx)
+  :end-before: # add the binary tree to the search path for include file
+
+.. raw:: html
+
+  </details>
+
+and this:
+
+.. raw:: html
+
+  <details><summary>TODO 3: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/MathFunctions/CMakeLists.txt
+  :caption: TODO 3: MathFunctions/CMakeLists.txt
+  :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
+  :language: cmake
+  :start-after: # link our compiler flags interface library
+  :end-before: # TODO 1
+
+.. 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
+source of truth in our interface library.
+
+Exercise 2 - Adding Compiler Warning Flags with Generator Expressions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 A common usage of
 :manual:`generator expressions <cmake-generator-expressions(7)>` is to
 conditionally add compiler flags, such as those for language levels or
 warnings. A nice pattern is to associate this information to an ``INTERFACE``
-target allowing this information to propagate. Let's start by constructing an
-``INTERFACE`` target and specifying the required C++ standard level of ``11``
-instead of using :variable:`CMAKE_CXX_STANDARD`.
+target allowing this information to propagate.
 
-So the following code:
+Goal
+----
 
-.. literalinclude:: Step10/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-CXX_STANDARD-variable-remove
+Add compiler warning flags when building but not for installed versions.
+
+Helpful Resources
+-----------------
+
+* :manual:`cmake-generator-expressions(7)`
+* :command:`cmake_minimum_required`
+* :command:`set`
+* :command:`target_compile_options`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+
+Getting Started
+---------------
+
+Start with the resulting files from Exercise 1. Complete ``TODO 4`` through
+``TODO 7``.
+
+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
+to use a generator expression which was introduced in CMake 3.15.
+
+Next we add the desired compiler warning flags that we want for our project.
+As warning flags vary based on the compiler, we use the
+``COMPILE_LANG_AND_ID`` generator expression to control which flags to apply
+given a language and a set of compiler ids.
+
+Build and Run
+-------------
+
+Since we have our build directory already configured from Exercise 1, simply
+rebuild our code by calling the following:
+
+.. code-block:: console
+
+  cd Step4_build
+  cmake --build .
+
+Solution
+--------
+
+Update the :command:`cmake_minimum_required` to require at least CMake
+version ``3.15``:
+
+.. raw:: html
+
+  <details><summary>TODO 4: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :caption: TODO 4: CMakeLists.txt
+  :name: MathFunctions-CMakeLists.txt-minimum-required-step4
   :language: cmake
-  :start-after: project(Tutorial VERSION 1.0)
-  :end-before: # control where the static and shared libraries are built so that on windows
+  :end-before: # set the project name and version
 
-Would be replaced with:
+.. raw:: html
 
-.. literalinclude:: Step11/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-cxx_std-feature
+  </details>
+
+Next we determine which compiler our system is currently using to build
+since warning flags vary based on the compiler we use. This is done with
+the ``COMPILE_LANG_AND_ID`` generator expression. We set the result in the
+variables ``gcc_like_cxx`` and ``msvc_cxx`` as follows:
+
+.. raw:: html
+
+  <details><summary>TODO 5: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :caption: TODO 5: CMakeLists.txt
+  :name: CMakeLists.txt-compile_lang_and_id
   :language: cmake
-  :start-after: project(Tutorial VERSION 1.0)
-  :end-before: # add compiler warning flags just when building this project via
+  :start-after: # the BUILD_INTERFACE genex
+  :end-before: target_compile_options(tutorial_compiler_flags INTERFACE
 
-**Note**:  This upcoming section will require a change to the
-:command:`cmake_minimum_required` usage in the code.  The Generator Expression
-that is about to be used was introduced in `3.15`.  Update the call to require
-that more recent version:
+.. raw:: html
+
+  </details>
+
+Next we add the desired compiler warning flags that we want for our project.
+Using our variables ``gcc_like_cxx`` and ``msvc_cxx``, we can use another
+generator expression to apply the respective flags only when the variables are
+true. We use :command:`target_compile_options` to apply these flags to our
+interface library.
+
+.. raw:: html
+
+  <details><summary>TODO 6: Click to show/hide answer</summary>
 
 .. code-block:: cmake
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-version-update
+  :caption: TODO 6: CMakeLists.txt
+  :name: CMakeLists.txt-compile_flags
 
-  cmake_minimum_required(VERSION 3.15)
+  target_compile_options(tutorial_compiler_flags INTERFACE
+    "$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
+    "$<${msvc_cxx}:-W3>"
+  )
 
-Next we add the desired compiler warning flags that we want for our project. As
-warning flags vary based on the compiler we use the ``COMPILE_LANG_AND_ID``
-generator expression to control which flags to apply given a language and a set
-of compiler ids as seen below:
+.. raw:: html
 
-.. literalinclude:: Step11/CMakeLists.txt
-  :caption: CMakeLists.txt
+  </details>
+
+Lastly, we only want these warning flags to be used during builds. Consumers
+of our installed project should not inherit our warning flags. To specify
+this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE``
+condition. The resulting full code looks like the following:
+
+.. raw:: html
+
+  <details><summary>TODO 7: Click to show/hide answer</summary>
+
+.. literalinclude:: Step5/CMakeLists.txt
+  :caption: TODO 7: CMakeLists.txt
   :name: CMakeLists.txt-target_compile_options-genex
   :language: cmake
-  :start-after: # the BUILD_INTERFACE genex
-  :end-before: # control where the static and shared libraries are built so that on windows
+  :start-after: set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+  :end-before: # should we use our own math functions
 
-Looking at this we see that the warning flags are encapsulated inside a
-``BUILD_INTERFACE`` condition. This is done so that consumers of our installed
-project will not inherit our warning flags.
+.. raw:: html
 
-**Exercise**: Modify ``MathFunctions/CMakeLists.txt`` so that all targets have
-a :command:`target_link_libraries` call to ``tutorial_compiler_flags``.
+  </details>
index c6e0fd0..45d5976 100644 (file)
@@ -1,4 +1,4 @@
-Step 8: Adding Support for a Testing Dashboard
+Step 6: Adding Support for a Testing Dashboard
 ==============================================
 
 Adding support for submitting our test results to a dashboard is simple. We
@@ -9,21 +9,21 @@ we include the :module:`CTest` module in our top-level ``CMakeLists.txt``.
 
 Replace:
 
-.. code-block:: cmake
+.. literalinclude:: Step6/CMakeLists.txt
   :caption: CMakeLists.txt
   :name: CMakeLists.txt-enable_testing-remove
-
-  # enable testing
-  enable_testing()
+  :language: cmake
+  :start-after: # enable testing
+  :end-before: # does the application run
 
 With:
 
-.. code-block:: cmake
+.. literalinclude:: Step7/CMakeLists.txt
   :caption: CMakeLists.txt
   :name: CMakeLists.txt-include-CTest
-
-  # enable dashboard scripting
-  include(CTest)
+  :language: cmake
+  :start-after: # enable testing
+  :end-before: # does the application run
 
 The :module:`CTest` module will automatically call ``enable_testing()``, so we
 can remove it from our CMake files.
@@ -46,7 +46,7 @@ downloaded from the ``Settings`` page of the project on the CDash
 instance that will host and display the test results.  Once downloaded from
 CDash, the file should not be modified locally.
 
-.. literalinclude:: Step9/CTestConfig.cmake
+.. literalinclude:: Step7/CTestConfig.cmake
   :caption: CTestConfig.cmake
   :name: CTestConfig.cmake
   :language: cmake
index 8db0cb8..ba91df4 100644 (file)
@@ -1,4 +1,4 @@
-Step 5: Adding System Introspection
+Step 7: Adding System Introspection
 ===================================
 
 Let us consider adding some code to our project that depends on features the
@@ -15,7 +15,7 @@ these functions using the :module:`CheckCXXSourceCompiles` module in
 Add the checks for ``log`` and ``exp`` to ``MathFunctions/CMakeLists.txt``,
 after the call to :command:`target_include_directories`:
 
-.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-check_cxx_source_compiles
   :language: cmake
@@ -25,19 +25,19 @@ after the call to :command:`target_include_directories`:
 If available, use :command:`target_compile_definitions` to specify
 ``HAVE_LOG`` and ``HAVE_EXP`` as ``PRIVATE`` compile definitions.
 
-.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-target_compile_definitions
   :language: cmake
   :start-after: # add compile definitions
-  :end-before: # install rules
+  :end-before: # install libs
 
 If ``log`` and ``exp`` are available on the system, then we will use them to
 compute the square root in the ``mysqrt`` function. Add the following code to
 the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the
 ``#endif`` before returning the result!):
 
-.. literalinclude:: Step6/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step8/MathFunctions/mysqrt.cxx
   :caption: MathFunctions/mysqrt.cxx
   :name: MathFunctions/mysqrt.cxx-ifdef
   :language: c++
@@ -46,7 +46,7 @@ the ``mysqrt`` function in ``MathFunctions/mysqrt.cxx`` (don't forget the
 
 We will also need to modify ``mysqrt.cxx`` to include ``cmath``.
 
-.. literalinclude:: Step6/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step8/MathFunctions/mysqrt.cxx
   :caption: MathFunctions/mysqrt.cxx
   :name: MathFunctions/mysqrt.cxx-include-cmath
   :language: c++
index a8e914e..4aef050 100644 (file)
 Step 3: Adding Usage Requirements for a Library
 ===============================================
 
-Usage requirements allow for far better control over a library or executable's
-link and include line while also giving more control over the transitive
-property of targets inside CMake. The primary commands that leverage usage
-requirements are:
-
-  - :command:`target_compile_definitions`
-  - :command:`target_compile_options`
-  - :command:`target_include_directories`
-  - :command:`target_link_libraries`
-
-Let's refactor our code from :guide:`tutorial/Adding a Library` to use the
-modern CMake approach of usage requirements. We first state that anybody
-linking to ``MathFunctions`` needs to include the current source directory,
-while ``MathFunctions`` itself doesn't. So this can become an ``INTERFACE``
-usage requirement.
-
-Remember ``INTERFACE`` means things that consumers require but the producer
-doesn't. Add the following lines to the end of
-``MathFunctions/CMakeLists.txt``:
+Exercise 1 - Adding Usage Requirements for a Library
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:ref:`Usage requirements <Target Usage Requirements>` of a target parameters
+allow for far better control over a library or executable's link and include
+line while also giving more control over the transitive property of targets
+inside CMake. The primary commands that
+leverage usage requirements are:
+
+* :command:`target_compile_definitions`
+* :command:`target_compile_options`
+* :command:`target_include_directories`
+* :command:`target_link_directories`
+* :command:`target_link_options`
+* :command:`target_precompile_headers`
+* :command:`target_sources`
+
+
+Goal
+----
+
+Add usage requirements for a library.
+
+Helpful Materials
+-----------------
+
+* :variable:`CMAKE_CURRENT_SOURCE_DIR`
+
+Files to Edit
+-------------
+
+* ``MathFunctions/CMakeLists.txt``
+* ``CMakeLists.txt``
+
+Getting Started
+---------------
+
+In this exercise, we will refactor our code from
+:guide:`tutorial/Adding a Library` to use the modern CMake approach. We will
+let our library define its own usage requirements so they are passed
+transitively to other targets as necessary. In this case, ``MathFunctions``
+will specify any needed include directories itself. Then, the consuming target
+``Tutorial`` simply needs to link to ``MathFunctions`` and not worry about
+any additional include directories.
+
+The starting source code is provided in the ``Step3`` directory. In this
+exercise, complete ``TODO 1`` through ``TODO 3``.
+
+First, add a call to :command:`target_include_directories` in
+``MathFunctions/CMakeLists``. Remember that
+:variable:`CMAKE_CURRENT_SOURCE_DIR` is the path to the source directory
+currently being processed.
+
+Then, update (and simplify!) the call to
+:command:`target_include_directories` in the top-level ``CMakeLists.txt``.
+
+Build and Run
+-------------
+
+Make a new directory called ``Step3_build``, run the :manual:`cmake
+<cmake(1)>` executable or the :manual:`cmake-gui <cmake-gui(1)>` to
+configure the project and then build it with your chosen build tool or by
+using :option:`cmake --build . <cmake --build>` from the build directory.
+Here's a refresher of what that looks like from the command line:
+
+.. code-block:: console
+
+  mkdir Step3_build
+  cd Step3_build
+  cmake ../Step3
+  cmake --build .
+
+Next, use the newly built ``Tutorial`` and verify that it is working as
+expected.
+
+Solution
+--------
+
+Let's update the code from the previous step to use the modern CMake
+approach of usage requirements.
+
+We want to state that anybody linking to ``MathFunctions`` needs to include
+the current source directory, while ``MathFunctions`` itself doesn't. This
+can be expressed with an ``INTERFACE`` usage requirement. Remember
+``INTERFACE`` means things that consumers require but the producer doesn't.
+
+At the end of ``MathFunctions/CMakeLists.txt``, use
+:command:`target_include_directories` with the ``INTERFACE`` keyword, as
+follows:
+
+.. raw:: html
+
+  <details><summary>TODO 1: Click to show/hide answer</summary>
 
 .. literalinclude:: Step4/MathFunctions/CMakeLists.txt
-  :caption: MathFunctions/CMakeLists.txt
+  :caption: TODO 1: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-target_include_directories-INTERFACE
   :language: cmake
   :start-after: # to find MathFunctions.h
+  :end-before: # TODO 3: Link to
+
+.. raw:: html
+
+  </details>
 
-Now that we've specified usage requirements for ``MathFunctions`` we can safely
-remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
+Now that we've specified usage requirements for ``MathFunctions`` we can
+safely remove our uses of the ``EXTRA_INCLUDES`` variable from the top-level
 ``CMakeLists.txt``, here:
 
+.. raw:: html
+
+  <details><summary>TODO 2: Click to show/hide answer</summary>
+
 .. literalinclude:: Step4/CMakeLists.txt
-  :caption: CMakeLists.txt
+  :caption: TODO 2: CMakeLists.txt
   :name: CMakeLists.txt-remove-EXTRA_INCLUDES
   :language: cmake
   :start-after: # add the MathFunctions library
   :end-before: # add the executable
 
+.. raw:: html
+
+  </details>
+
 And here:
 
+.. raw:: html
+
+  <details><summary>TODO 3: Click to show/hide answer</summary>
+
 .. literalinclude:: Step4/CMakeLists.txt
-  :caption: CMakeLists.txt
+  :caption: TODO 3: CMakeLists.txt
   :name: CMakeLists.txt-target_include_directories-remove-EXTRA_INCLUDES
   :language: cmake
   :start-after: # so that we will find TutorialConfig.h
 
-Once this is done, run the :manual:`cmake  <cmake(1)>` executable or the
-:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
-with your chosen build tool or by using ``cmake --build .`` from the build
-directory.
+.. raw:: html
+
+  </details>
+
+Notice that with this technique, the only thing our executable target does to
+use our library is call :command:`target_link_libraries` with the name
+of the library target. In larger projects, the classic method of specifying
+library dependencies manually becomes very complicated very quickly.
index 70c6695..b1f9840 100644 (file)
@@ -1,4 +1,4 @@
-Step 6: Adding a Custom Command and Generated File
+Step 8: Adding a Custom Command and Generated File
 ==================================================
 
 Suppose, for the purpose of this tutorial, we decide that we never want to use
@@ -26,7 +26,7 @@ accomplish this.
 First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for
 ``MakeTable`` is added as any other executable would be added.
 
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-add_executable-MakeTable
   :language: cmake
@@ -36,7 +36,7 @@ First, at the top of ``MathFunctions/CMakeLists.txt``, the executable for
 Then we add a custom command that specifies how to produce ``Table.h``
 by running MakeTable.
 
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-add_custom_command-Table.h
   :language: cmake
@@ -47,7 +47,7 @@ Next we have to let CMake know that ``mysqrt.cxx`` depends on the generated
 file ``Table.h``. This is done by adding the generated ``Table.h`` to the list
 of sources for the library MathFunctions.
 
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-add_library-Table.h
   :language: cmake
@@ -57,17 +57,17 @@ of sources for the library MathFunctions.
 We also have to add the current binary directory to the list of include
 directories so that ``Table.h`` can be found and included by ``mysqrt.cxx``.
 
-.. literalinclude:: Step7/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step9/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-target_include_directories-Table.h
   :language: cmake
   :start-after: # state that we depend on our bin
-  :end-before: # install rules
+  :end-before: # install libs
 
 Now let's use the generated table. First, modify ``mysqrt.cxx`` to include
 ``Table.h``. Next, we can rewrite the ``mysqrt`` function to use the table:
 
-.. literalinclude:: Step7/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step9/MathFunctions/mysqrt.cxx
   :caption: MathFunctions/mysqrt.cxx
   :name: MathFunctions/mysqrt.cxx
   :language: c++
index 71755be..a56c327 100644 (file)
 Step 2: Adding a Library
 ========================
 
-Now we will add a library to our project. This library will contain our own
+At this point, we have seen how to create a basic project using CMake. In this
+step, we will learn how to create and use a library in our project. We will
+also see how to make the use of our library optional.
+
+Exercise 1 - Creating a Library
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To add a library in CMake, use the :command:`add_library` command and specify
+which source files should make up the library.
+
+Rather than placing all of the source files in one directory, we can organize
+our project with one or more subdirectories. In this case, we will create a
+subdirectory specifically for our library. Here, we can add a new
+``CMakeLists.txt`` file and one or more source files. In the top level
+``CMakeLists.txt`` file, we will use the :command:`add_subdirectory` command
+to add the subdirectory to the build.
+
+Once the library is created, it is connected to our executable target with
+:command:`target_include_directories` and :command:`target_link_libraries`.
+
+Goal
+----
+
+Add and use a library.
+
+Helpful Resources
+-----------------
+
+* :command:`add_library`
+* :command:`add_subdirectory`
+* :command:`target_include_directories`
+* :command:`target_link_libraries`
+* :variable:`PROJECT_SOURCE_DIR`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+* ``tutorial.cxx``
+* ``MathFunctions/CMakeLists.txt``
+
+Getting Started
+---------------
+
+In this exercise, we will add a library to our project that contains our own
 implementation for computing the square root of a number. The executable can
 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``. The source file has one
-function called ``mysqrt`` that provides similar functionality to the
-compiler's ``sqrt`` function.
+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
+``mysqrt`` that provides similar functionality to the compiler's ``sqrt``
+function.
+
+From the ``Help/guide/tutorial/Step2`` directory, start with ``TODO 1`` and
+complete through ``TODO 6``.
+
+First, fill in the one line ``CMakeLists.txt`` in the ``MathFunctions``
+subdirectory.
+
+Next, edit the top level ``CMakeLists.txt``.
+
+Finally, use the newly created ``MathFunctions`` library in ``tutorial.cxx``
+
+Build and Run
+-------------
+
+Run the :manual:`cmake  <cmake(1)>` executable or the
+:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
+with your chosen build tool.
+
+Below is a refresher of what that looks like from the command line:
+
+.. code-block:: console
+
+  mkdir Step2_build
+  cd Step2_build
+  cmake ../Step2
+  cmake --build .
+
+Try to use the newly built ``Tutorial`` and ensure that it is still
+producing accurate square root values.
+
+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
+:command:`add_library`. This looks like the following line:
 
-Add the following one line ``CMakeLists.txt`` file to the ``MathFunctions``
-directory:
+.. raw:: html
+
+  <details><summary>TODO 1: Click to show/hide answer</summary>
 
 .. literalinclude:: Step3/MathFunctions/CMakeLists.txt
-  :caption: MathFunctions/CMakeLists.txt
-  :name: MathFunctions/CMakeLists.txt
+  :caption: TODO 1: MathFunctions/CMakeLists.txt
+  :name: MathFunctions/CMakeLists.txt-add_library
   :language: cmake
+  :end-before: # TODO 1
+
+.. raw:: html
+
+  </details>
 
 To make use of the new library we will add an :command:`add_subdirectory`
 call in the top-level ``CMakeLists.txt`` file so that the library will get
-built. We add the new library to the executable, and add ``MathFunctions`` as
-an include directory so that the ``MathFunctions.h`` header file can be found.
-The last few lines of the top-level ``CMakeLists.txt`` file should now look
-like:
+built.
+
+.. raw:: html
+
+  <details><summary>TODO 2: Click to show/hide answer</summary>
+
+.. code-block:: cmake
+  :caption: TODO 2: CMakeLists.txt
+  :name: CMakeLists.txt-add_subdirectory
+
+  add_subdirectory(MathFunctions)
+
+.. raw:: html
+
+  </details>
+
+Next, the new library target is linked to the executable target using
+:command:`target_link_libraries`.
+
+.. raw:: html
+
+  <details><summary>TODO 3: Click to show/hide answer</summary>
+
+.. code-block:: cmake
+  :caption: TODO 3: CMakeLists.txt
+  :name: CMakeLists.txt-target_link_libraries
+
+  target_link_libraries(Tutorial PUBLIC MathFunctions)
+
+.. raw:: html
+
+  </details>
+
+Finally we need to specify the library's header file location. Modify
+:command:`target_include_directories` to add the ``MathFunctions`` subdirectory
+as an include directory so that the ``MathFunctions.h`` header file can be
+found.
+
+.. raw:: html
+
+  <details><summary>TODO 4: Click to show/hide answer</summary>
 
 .. code-block:: cmake
-        :caption: CMakeLists.txt
-        :name: CMakeLists.txt-add_subdirectory
+  :caption: TODO 4: CMakeLists.txt
+  :name: CMakeLists.txt-target_include_directories-step2
+
+  target_include_directories(Tutorial PUBLIC
+                            "${PROJECT_BINARY_DIR}"
+                            "${PROJECT_SOURCE_DIR}/MathFunctions"
+                            )
+
+.. raw:: html
+
+  </details>
+
+Now let's use our library. In ``tutorial.cxx``, include ``MathFunctions.h``:
+
+.. raw:: html
+
+  <details><summary>TODO 5: Click to show/hide answer</summary>
 
-        # add the MathFunctions library
-        add_subdirectory(MathFunctions)
+.. code-block:: c++
+  :caption: TODO 5 : tutorial.cxx
+  :name: tutorial.cxx-include_MathFunctions.h
 
-        # add the executable
-        add_executable(Tutorial tutorial.cxx)
+  #include "MathFunctions.h"
 
-        target_link_libraries(Tutorial PUBLIC MathFunctions)
+.. raw:: html
 
-        # add the binary tree to the search path for include files
-        # so that we will find TutorialConfig.h
-        target_include_directories(Tutorial PUBLIC
-                                  "${PROJECT_BINARY_DIR}"
-                                  "${PROJECT_SOURCE_DIR}/MathFunctions"
-                                  )
+  </details>
 
-Now let us make the ``MathFunctions`` library optional. While for the tutorial
+Lastly, replace ``sqrt`` with our library function ``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);
+
+.. raw:: html
+
+  </details>
+
+Exercise 2 - Making Our Library Optional
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Now let us make the MathFunctions library optional. While for the tutorial
 there really isn't any need to do so, for larger projects this is a common
-occurrence. The first step is to add an option to the top-level
-``CMakeLists.txt`` file.
+occurrence.
+
+CMake can do this using the :command:`option` command. This gives users a
+variable which they can change when configuring their cmake build. This
+setting will be stored in the cache so that the user does not need to set
+the value each time they run CMake on a build directory.
+
+Goal
+----
+
+Add the option to build without ``MathFunctions``.
+
+
+Helpful Resources
+-----------------
+
+* :command:`if`
+* :command:`list`
+* :command:`option`
+* :command:`cmakedefine <configure_file>`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+* ``tutorial.cxx``
+* ``TutorialConfig.h.in``
+
+Getting Started
+---------------
+
+Start with the resulting files from Exercise 1. Complete ``TODO 7`` through
+``TODO 13``.
+
+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.
+
+Then, update ``tutorial.cxx`` and ``TutorialConfig.h.in`` to use
+``USE_MYMATH``.
+
+Build and Run
+-------------
+
+Since we have our build directory already configured from Exercise 1, we can
+rebuild by simply calling the following:
+
+.. code-block:: console
+
+  cd ../Step2_build
+  cmake --build .
+
+Next, run the ``Tutorial`` executable on a few numbers to verify that it's
+still correct.
+
+Now let's update the value of ``USE_MYMATH`` to ``OFF``. The easiest way is to
+use the :manual:`cmake-gui <cmake-gui(1)>` or  :manual:`ccmake <ccmake(1)>`
+if you're in the terminal. Or, alternatively, if you want to change the
+option from the command-line, try:
+
+.. code-block:: console
+
+  cmake ../Step2 -DUSE_MYMATH=OFF
+
+Now, rebuild the code with the following:
+
+.. code-block:: console
+
+  cmake --build .
+
+Then, run the executable again to ensure that it still works with
+``USE_MYMATH`` set to ``OFF``. Which function gives better results, ``sqrt``
+or ``mysqrt``?
+
+Solution
+--------
+
+The first step is to add an option to the top-level ``CMakeLists.txt`` file.
+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.
+
+.. raw:: html
+
+  <details><summary>TODO 7: Click to show/hide answer</summary>
 
 .. literalinclude:: Step3/CMakeLists.txt
-  :caption: CMakeLists.txt
+  :caption: TODO 7: CMakeLists.txt
   :name: CMakeLists.txt-option
   :language: cmake
   :start-after: # should we use our own math functions
-  :end-before: # add the MathFunctions library
+  :end-before: # configure a header file to pass some of the CMake settings
 
-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. This setting
-will be stored in the cache so that the user does not need to set the value
-each time they run CMake on a build directory.
-
-The next change is to make building and linking the ``MathFunctions`` library
-conditional. To do this,  we will create an ``if`` statement which checks the
-value of the option.  Inside the ``if`` block, put the
-:command:`add_subdirectory` command from above with some additional list
-commands to store information needed to link to the library and add the
-subdirectory as an include directory in the ``Tutorial`` target.
-The end of the top-level ``CMakeLists.txt`` file will now look like the
-following:
+.. 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``.
+
+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
+``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:
+
+.. raw:: html
+
+  <details><summary>TODO 8: Click to show/hide answer</summary>
 
 .. literalinclude:: Step3/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-target_link_libraries-EXTRA_LIBS
+  :caption: TODO 8: CMakeLists.txt
+  :name: CMakeLists.txt-USE_MYMATH
   :language: cmake
   :start-after: # add the MathFunctions library
+  :end-before: # add the executable
+
+.. raw:: html
+
+  </details>
 
-Note the use of the variable ``EXTRA_LIBS`` to collect up any optional
-libraries to later be linked into the executable. The variable
-``EXTRA_INCLUDES`` is used similarly for optional header files. This is a
-classic approach when dealing with many optional components, we will cover
-the modern approach in the next step.
+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.
+
+For :command:`target_link_libraries`, we replace the written out
+library names with ``EXTRA_LIBS``. This looks like the following:
+
+.. 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
+
+.. raw:: html
+
+  </details>
+
+Then, we do the same thing with :command:`target_include_directories` and
+``EXTRA_INCLUDES``.
+
+.. 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
+
+.. 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``, include the ``MathFunctions.h`` header if we
-need it:
+First, in ``tutorial.cxx``, we include the ``MathFunctions.h`` header if
+``USE_MYMATH`` is defined.
+
+.. raw:: html
+
+  <details><summary>TODO 11: Click to show/hide answer</summary>
 
 .. literalinclude:: Step3/tutorial.cxx
-  :caption: 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
 
-Then, in the same file, make ``USE_MYMATH`` control which square root
+.. raw:: html
+
+  </details>
+
+Then, in the same file, we make ``USE_MYMATH`` control which square root
 function is used:
 
+.. raw:: html
+
+  <details><summary>TODO 12: Click to show/hide answer</summary>
+
 .. literalinclude:: Step3/tutorial.cxx
-  :caption: 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
 
+.. raw:: html
+
+  </details>
+
 Since the source code now requires ``USE_MYMATH`` we can add it to
 ``TutorialConfig.h.in`` with the following line:
 
+.. raw:: html
+
+  <details><summary>TODO 13: Click to show/hide answer</summary>
+
 .. literalinclude:: Step3/TutorialConfig.h.in
-  :caption: TutorialConfig.h.in
+  :caption: TODO 13 : TutorialConfig.h.in
   :name: TutorialConfig.h.in-cmakedefine
   :language: c++
   :lines: 4
 
-**Exercise**: Why is it important that we configure ``TutorialConfig.h.in``
+.. 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?
 
-Run the :manual:`cmake  <cmake(1)>` executable or the
-:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
-with your chosen build tool. Then run the built Tutorial executable.
+Answer
+------
 
-Now let's update the value of ``USE_MYMATH``. The easiest way is to use the
-:manual:`cmake-gui <cmake-gui(1)>` or  :manual:`ccmake <ccmake(1)>` if you're
-in the terminal. Or, alternatively, if you want to change the option from the
-command-line, try:
+.. raw:: html
 
-.. code-block:: console
+  <details><summary>Click to show/hide answer</summary>
 
-  cmake ../Step2 -DUSE_MYMATH=OFF
+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``.
 
-Rebuild and run the tutorial again.
+.. raw:: html
 
-Which function gives better results, ``sqrt`` or ``mysqrt``?
+  </details>
index 41baf64..3cdaaae 100644 (file)
@@ -41,7 +41,7 @@ add_subdirectory(MathFunctions)
 add_executable(Tutorial tutorial.cxx)
 set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
 
-target_link_libraries(Tutorial PUBLIC MathFunctions)
+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
@@ -84,6 +84,7 @@ do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
+# setup installer
 include(InstallRequiredSystemLibraries)
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
index c62d00b..85760e5 100644 (file)
@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
+CMake/Tutorial/Step9...
index 40b9fd2..d256db2 100644 (file)
@@ -56,7 +56,7 @@ target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
 set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
 set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
 
-# install rules
+# install libs
 set(installable_libs MathFunctions tutorial_compiler_flags)
 if(TARGET SqrtLibrary)
   list(APPEND installable_libs SqrtLibrary)
@@ -64,4 +64,5 @@ endif()
 install(TARGETS ${installable_libs}
         EXPORT MathFunctionsTargets
         DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
index 394c986..fa13040 100644 (file)
-Step 4: Installing and Testing
+Step 5: Installing and Testing
 ==============================
 
-Now we can start adding install rules and testing support to our project.
+.. _`Tutorial Testing Support`:
+
+Exercise 1 - Install Rules
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often, it is not enough to only build an executable, it should also be
+installable. With CMake, we can specify install rules using the
+:command:`install` command. Supporting local installations for your builds in
+CMake is often as simple as specifying an install location and the targets and
+files to be installed.
+
+Goal
+----
+
+Install the ``Tutorial`` executable and the ``MathFunctions`` library.
+
+Helpful Materials
+-----------------
+
+* :command:`install`
 
-Install Rules
+Files to Edit
 -------------
 
-The install rules are fairly simple: for ``MathFunctions`` we want to install
-the library and header file and for the application we want to install the
-executable and configured header.
+* ``MathFunctions/CMakeLists.txt``
+* ``CMakeLists.txt``
 
-So to the end of ``MathFunctions/CMakeLists.txt`` we add:
+Getting Started
+---------------
 
-.. literalinclude:: Step5/MathFunctions/CMakeLists.txt
-  :caption: MathFunctions/CMakeLists.txt
-  :name: MathFunctions/CMakeLists.txt-install-TARGETS
-  :language: cmake
-  :start-after: # install rules
+The starting code is provided in the ``Step5`` directory. In this
+exercise, complete ``TODO 1`` through ``TODO 4``.
 
-And to the end of the top-level ``CMakeLists.txt`` we add:
+First, update ``MathFunctions/CMakeLists.txt`` to install the
+``MathFunctions`` and ``tutorial_compiler_flags`` libraries to the ``lib``
+directory. In that same file, specify the install rules needed to install
+``MathFunctions.h`` to the ``include`` directory.
 
-.. literalinclude:: Step5/CMakeLists.txt
-  :caption: CMakeLists.txt
-  :name: CMakeLists.txt-install-TARGETS
-  :language: cmake
-  :start-after: # add the install targets
-  :end-before: # enable testing
+Then, update the top level ``CMakeLists.txt`` to install
+the ``Tutorial`` executable to the ``bin`` directory. Lastly, any header files
+should be installed to the ``include`` directory. Remember that
+``TutorialConfig.h`` is in the :variable:`PROJECT_BINARY_DIR`.
 
-That is all that is needed to create a basic local install of the tutorial.
+Build and Run
+-------------
 
-Now run the :manual:`cmake  <cmake(1)>` executable or the
+Make a new directory called ``Step5_build``. Run the
+:manual:`cmake <cmake(1)>` executable or the
 :manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
 with your chosen build tool.
 
-Then run the install step by using the ``install`` option of the
-:manual:`cmake  <cmake(1)>` command (introduced in 3.15, older versions of
-CMake must use ``make install``) from the command line. For
-multi-configuration tools, don't forget to use the ``--config`` argument to
-specify the configuration. If using an IDE, simply build the ``INSTALL``
-target. This step will install the appropriate header files, libraries, and
-executables. For example:
+Then, run the install step by using the :option:`--install <cmake --install>`
+option of the :manual:`cmake  <cmake(1)>` command (introduced in 3.15, older
+versions of CMake must use ``make install``) from the command line. This step
+will install the appropriate header files, libraries, and executables.
+For example:
 
 .. code-block:: console
 
   cmake --install .
 
+For multi-configuration tools, don't forget to use the
+:option:`--config <cmake--build --config>` argument to specify the configuration.
+
+.. code-block:: console
+
+  cmake --install . --config Release
+
+If using an IDE, simply build the ``INSTALL`` target. You can build the same
+install target from the command line like the following:
+
+.. code-block:: console
+
+  cmake --build . --target install --config Debug
+
 The CMake variable :variable:`CMAKE_INSTALL_PREFIX` is used to determine the
-root of where the files will be installed. If using the ``cmake --install``
-command, the installation prefix can be overridden via the ``--prefix``
-argument. For example:
+root of where the files will be installed. If using the :option:`cmake --install`
+command, the installation prefix can be overridden via the
+:option:`--prefix <cmake--install --prefix>` argument. For example:
 
 .. code-block:: console
 
   cmake --install . --prefix "/home/myuser/installdir"
 
-Navigate to the install directory and verify that the installed Tutorial runs.
+Navigate to the install directory and verify that the installed ``Tutorial``
+runs.
 
-.. _`Tutorial Testing Support`:
+Solution
+--------
 
-Testing Support
----------------
+The install rules for our project are fairly simple:
 
-Next let's test our application. At the end of the top-level ``CMakeLists.txt``
-file we can enable testing and then add a number of basic tests to verify that
-the application is working correctly.
+* For ``MathFunctions``, we want to install the libraries and header file to
+  the ``lib`` and ``include`` directories respectively.
 
-.. literalinclude:: Step5/CMakeLists.txt
+* For the ``Tutorial`` executable, we want to install the executable and
+  configured header file to the ``bin`` and ``include`` directories
+  respectively.
+
+So to the end of ``MathFunctions/CMakeLists.txt`` we add:
+
+.. raw:: html
+
+  <details><summary>TODO 1: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+  :caption: TODO 1: MathFunctions/CMakeLists.txt
+  :name: MathFunctions/CMakeLists.txt-install-TARGETS
+  :language: cmake
+  :start-after: # install libs
+  :end-before: # install include headers
+
+.. raw:: html
+
+  </details>
+
+and
+
+.. raw:: html
+
+  <details><summary>TODO 2: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/MathFunctions/CMakeLists.txt
+  :caption: TODO 2: MathFunctions/CMakeLists.txt
+  :name: MathFunctions/CMakeLists.txt-install-headers
+  :language: cmake
+  :start-after: # install include headers
+
+.. raw:: html
+
+  </details>
+
+The install rules for the ``Tutorial`` executable and configured header file
+are similar. To the end of the top-level ``CMakeLists.txt`` we add:
+
+.. raw:: html
+
+  <details><summary>TODO 3,4: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/CMakeLists.txt
   :caption: CMakeLists.txt
-  :name: CMakeLists.txt-enable_testing
+  :name: TODO 3,4: CMakeLists.txt-install-TARGETS
   :language: cmake
-  :start-after: # enable testing
+  :start-after: # add the install targets
+  :end-before: # enable testing
+
+.. raw:: html
+
+  </details>
 
-The first test simply verifies that the application runs, does not segfault or
-otherwise crash, and has a zero return value. This is the basic form of a
-CTest test.
+That is all that is needed to create a basic local
+install of the tutorial.
 
-The next test makes use of the :prop_test:`PASS_REGULAR_EXPRESSION` test
-property to verify that the output of the test contains certain strings. In
-this case, verifying that the usage message is printed when an incorrect number
-of arguments are provided.
+Exercise 2 - Testing Support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Lastly, we have a function called ``do_test`` that runs the application and
-verifies that the computed square root is correct for given input. For each
-invocation of ``do_test``, another test is added to the project with a name,
-input, and expected results based on the passed arguments.
+CTest offers a way to easily manage tests for your project. Tests can be
+added through the :command:`add_test` command. Although it is not
+explicitly covered in this tutorial, there is a lot of compatibility
+between CTest and other testing frameworks such as :module:`GoogleTest`.
 
-Rebuild the application and then cd to the binary directory and run the
-:manual:`ctest <ctest(1)>` executable: ``ctest -N`` and ``ctest -VV``. For
+Goal
+----
+
+Create unit tests for our executable using CTest.
+
+Helpful Materials
+-----------------
+
+* :command:`enable_testing`
+* :command:`add_test`
+* :command:`function`
+* :command:`set_tests_properties`
+* :manual:`ctest <ctest(1)>`
+
+Files to Edit
+-------------
+
+* ``CMakeLists.txt``
+
+Getting Started
+---------------
+
+The starting source code is provided in the ``Step5`` directory. In this
+exercise, complete ``TODO 5`` through ``TODO 9``.
+
+First, we need to enable testing. Next, begin adding tests to our project
+using :command:`add_test`. We will work through adding 3 simple tests and
+then you can add additional testing as you see fit.
+
+Build and Run
+-------------
+
+Navigate to the build directory and rebuild the application. Then, run the
+``ctest`` executable: :option:`ctest -N` and :option:`ctest -VV`. For
 multi-config generators (e.g. Visual Studio), the configuration type must be
-specified with the ``-C <mode>`` flag.  For example, to run tests in Debug
-mode use ``ctest -C Debug -VV`` from the binary directory
+specified with the :option:`-C \<mode\> <ctest -C>` flag.  For example, to run tests in Debug
+mode use ``ctest -C Debug -VV`` from the build directory
 (not the Debug subdirectory!). Release mode would be executed from the same
-location but with a ``-C Release``.  Alternatively, build the ``RUN_TESTS``
+location but with a ``-C Release``. Alternatively, build the ``RUN_TESTS``
 target from the IDE.
+
+Solution
+--------
+
+Let's test our application. At the end of the top-level ``CMakeLists.txt``
+file we first need to enable testing with the
+:command:`enable_testing` command.
+
+.. raw:: html
+
+  <details><summary>TODO 5: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/CMakeLists.txt
+  :caption: TODO 5: CMakeLists.txt
+  :name: CMakeLists.txt-enable_testing
+  :language: cmake
+  :start-after: # enable testing
+  :end-before: # does the application run
+
+.. raw:: html
+
+  </details>
+
+With testing enabled, we will add a number of basic tests to verify
+that the application is working correctly. First, we create a test using
+:command:`add_test` which runs the ``Tutorial`` executable with the
+parameter 25 passed in. For this test, we are not going to check the
+executable's computed answer. This test will verify that
+application runs, does not segfault or otherwise crash, and has a zero
+return value. This is the basic form of a CTest test.
+
+.. raw:: html
+
+  <details><summary>TODO 6: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/CMakeLists.txt
+  :caption: TODO 6: CMakeLists.txt
+  :name: CMakeLists.txt-test-runs
+  :language: cmake
+  :start-after: # does the application run
+  :end-before: # does the usage message work
+
+.. raw:: html
+
+  </details>
+
+Next, let's use the :prop_test:`PASS_REGULAR_EXPRESSION` test property to
+verify that the output of the test contains certain strings. In this case,
+verifying that the usage message is printed when an incorrect number of
+arguments are provided.
+
+.. raw:: html
+
+  <details><summary>TODO 7: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/CMakeLists.txt
+  :caption: TODO 7: CMakeLists.txt
+  :name: CMakeLists.txt-test-usage
+  :language: cmake
+  :start-after: # does the usage message work?
+  :end-before: # define a function to simplify adding tests
+
+.. raw:: html
+
+  </details>
+
+The next test we will add verifies the computed value is truly the
+square root.
+
+.. raw:: html
+
+  <details><summary>TODO 8: Click to show/hide answer</summary>
+
+.. code-block:: cmake
+  :caption: TODO 8: CMakeLists.txt
+  :name: CMakeLists.txt-test-standard
+
+  add_test(NAME StandardUse COMMAND Tutorial 4)
+  set_tests_properties(StandardUse
+    PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
+    )
+
+.. raw:: html
+
+  </details>
+
+This one test is not enough to give us confidence that it will
+work for all values passed in. We should add more tests to verify this.
+To easily add more tests, we make a function called ``do_test`` that runs the
+application and verifies that the computed square root is correct for
+given input. For each invocation of ``do_test``, another test is added to
+the project with a name, input, and expected results based on the passed
+arguments.
+
+.. raw:: html
+
+  <details><summary>TODO 9: Click to show/hide answer</summary>
+
+.. literalinclude:: Step6/CMakeLists.txt
+  :caption: TODO 9: CMakeLists.txt
+  :name: CMakeLists.txt-generalized-tests
+  :language: cmake
+  :start-after: # define a function to simplify adding tests
+
+.. raw:: html
+
+  </details>
index 91b46a7..8c660a3 100644 (file)
@@ -10,8 +10,8 @@ possible, however, to setup CPack to bundle multiple build directories and
 construct a package that contains multiple configurations of the same project.
 
 First, we want to ensure that the debug and release builds use different names
-for the executables and libraries that will be installed. Let's use `d` as the
-postfix for the debug executable and libraries.
+for the libraries that will be installed. Let's use `d` as the
+postfix for the debug libraries.
 
 Set :variable:`CMAKE_DEBUG_POSTFIX` near the beginning of the top-level
 ``CMakeLists.txt`` file:
@@ -41,10 +41,10 @@ Let's also add version numbering to the ``MathFunctions`` library. In
   :name: MathFunctions/CMakeLists.txt-VERSION-properties
   :language: cmake
   :start-after: # setup the version numbering
-  :end-before: # install rules
+  :end-before: # install libs
 
 From the ``Step12`` directory, create ``debug`` and ``release``
-subbdirectories. The layout will look like:
+subdirectories. The layout will look like:
 
 .. code-block:: none
 
index 0ee5db2..11a1952 100644 (file)
@@ -1,4 +1,4 @@
-Step 7: Packaging an Installer
+Step 9: Packaging an Installer
 ==============================
 
 Next suppose that we want to distribute our project to other people so that
@@ -11,7 +11,7 @@ installations and package management features. To accomplish this we will use
 CPack to create platform specific installers. Specifically we need to add a
 few lines to the bottom of our top-level ``CMakeLists.txt`` file.
 
-.. literalinclude:: Step8/CMakeLists.txt
+.. literalinclude:: Step10/CMakeLists.txt
   :caption: CMakeLists.txt
   :name: CMakeLists.txt-include-CPack
   :language: cmake
@@ -38,15 +38,15 @@ binary directory run:
 
   cpack
 
-To specify the generator, use the ``-G`` option. For multi-config builds, use
-``-C`` to specify the configuration. For example:
+To specify the generator, use the :option:`-G <cpack -G>` option. For multi-config builds,
+use :option:`-C <cpack -C>` to specify the configuration. For example:
 
 .. code-block:: console
 
   cpack -G ZIP -C Debug
 
 For a list of available generators, see :manual:`cpack-generators(7)` or call
-``cpack --help``. An :cpack_gen:`archive generator <CPack Archive Generator>`
+:option:`cpack --help`. An :cpack_gen:`archive generator <CPack Archive Generator>`
 like ZIP creates a compressed archive of all *installed* files.
 
 To create an archive of the *full* source tree you would type:
index 2d5f70e..1c49c23 100644 (file)
@@ -1,5 +1,5 @@
-Step 9: Selecting Static or Shared Libraries
-============================================
+Step 10: Selecting Static or Shared Libraries
+=============================================
 
 In this section we will show how the :variable:`BUILD_SHARED_LIBS` variable can
 be used to control the default behavior of :command:`add_library`,
@@ -19,7 +19,7 @@ library.
 The first step is to update the starting section of the top-level
 ``CMakeLists.txt`` to look like:
 
-.. literalinclude:: Step10/CMakeLists.txt
+.. literalinclude:: Step11/CMakeLists.txt
   :caption: CMakeLists.txt
   :name: CMakeLists.txt-option-BUILD_SHARED_LIBS
   :language: cmake
@@ -33,7 +33,7 @@ explicitly require that SqrtLibrary is built statically.
 
 The end result is that ``MathFunctions/CMakeLists.txt`` should look like:
 
-.. literalinclude:: Step10/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step11/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-add_library-STATIC
   :language: cmake
@@ -42,7 +42,7 @@ The end result is that ``MathFunctions/CMakeLists.txt`` should look like:
 Next, update ``MathFunctions/mysqrt.cxx`` to use the ``mathfunctions`` and
 ``detail`` namespaces:
 
-.. literalinclude:: Step10/MathFunctions/mysqrt.cxx
+.. literalinclude:: Step11/MathFunctions/mysqrt.cxx
   :caption: MathFunctions/mysqrt.cxx
   :name: MathFunctions/mysqrt.cxx-namespace
   :language: c++
@@ -56,7 +56,7 @@ uses ``USE_MYMATH``:
 
 Finally, update ``MathFunctions/MathFunctions.h`` to use dll export defines:
 
-.. literalinclude:: Step10/MathFunctions/MathFunctions.h
+.. literalinclude:: Step11/MathFunctions/MathFunctions.h
   :caption: MathFunctions/MathFunctions.h
   :name: MathFunctions/MathFunctions.h
   :language: c++
@@ -67,7 +67,7 @@ library that has position independent code. The solution to this is to
 explicitly set the :prop_tgt:`POSITION_INDEPENDENT_CODE` target property of
 SqrtLibrary to be ``True`` no matter the build type.
 
-.. literalinclude:: Step10/MathFunctions/CMakeLists.txt
+.. literalinclude:: Step11/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-POSITION_INDEPENDENT_CODE
   :language: cmake
diff --git a/Help/guide/tutorial/Step1/CMakeLists.txt b/Help/guide/tutorial/Step1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6fcce90
--- /dev/null
@@ -0,0 +1,16 @@
+# TODO 1: Set the minimum required version of CMake to be 3.10
+
+# TODO 2: Create a project named Tutorial
+
+# TODO 7: Set the project version number as 1.0 in the above project command
+
+# TODO 6: Set the variable CMAKE_CXX_STANDARD to 11
+#         and the variable CMAKE_CXX_STANDARD_REQUIRED to True
+
+# TODO 8: Use configure_file to configure and copy TutorialConfig.h.in to
+#         TutorialConfig.h
+
+# TODO 3: Add an executable called Tutorial to the project
+# Hint: Be sure to specify the source file as tutorial.cxx
+
+# TODO 9: Use target_include_directories to include ${PROJECT_BINARY_DIR}
diff --git a/Help/guide/tutorial/Step1/TutorialConfig.h.in b/Help/guide/tutorial/Step1/TutorialConfig.h.in
new file mode 100644 (file)
index 0000000..990bfbd
--- /dev/null
@@ -0,0 +1,2 @@
+// the configured options and settings for Tutorial
+// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR
index 08323bf..64d0916 100644 (file)
@@ -1,17 +1,22 @@
 // A simple program that computes the square root of a number
 #include <cmath>
-#include <cstdlib>
+#include <cstdlib> // TODO 5: Remove this line
 #include <iostream>
 #include <string>
 
+// TODO 11: Include TutorialConfig.h
+
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
+    // TODO 12: Create a print statement using Tutorial_VERSION_MAJOR
+    //          and Tutorial_VERSION_MINOR
     std::cout << "Usage: " << argv[0] << " number" << std::endl;
     return 1;
   }
 
   // convert input to double
+  // TODO 4: Replace atof(argv[1]) with std::stod(argv[1])
   const double inputValue = atof(argv[1]);
 
   // calculate square root
index 55dc409..5c661aa 100644 (file)
@@ -1,29 +1,37 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
 
-# control where the static and shared libraries are built so that on windows
-# we don't need to tinker with the path to run the executable
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
 
-option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
+# should we use our own math functions
+option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
-# configure a header file to pass the version number only
+# 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
-add_subdirectory(MathFunctions)
+if(USE_MYMATH)
+  add_subdirectory(MathFunctions)
+  list(APPEND EXTRA_LIBS MathFunctions)
+endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC MathFunctions)
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
 
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
@@ -66,6 +74,7 @@ do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
+# setup installer
 include(InstallRequiredSystemLibraries)
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
index c62d00b..85760e5 100644 (file)
@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
+CMake/Tutorial/Step9...
index 0bfe20c..fa73321 100644 (file)
@@ -1,55 +1,32 @@
-# add the library that runs
-add_library(MathFunctions MathFunctions.cxx)
+# 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
+            )
 
 # 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}
-                           )
+          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+          PRIVATE   ${CMAKE_CURRENT_BINARY_DIR}
+          )
 
-# should we use our own math functions
-option(USE_MYMATH "Use tutorial provided math implementation" ON)
-if(USE_MYMATH)
+# link our compiler flags interface library
+target_link_libraries(MathFunctions tutorial_compiler_flags)
 
-  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
-
-  # 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
-    )
-
-  # 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}
-                             )
-
-  # state that SqrtLibrary need PIC when the default is shared libraries
-  set_target_properties(SqrtLibrary PROPERTIES
-                        POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
-                        )
-
-  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
-endif()
-
-# define the symbol stating we are using the declspec(dllexport) when
-# building on windows
-target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
-
-# install rules
-set(installable_libs MathFunctions)
-if(TARGET SqrtLibrary)
-  list(APPEND installable_libs SqrtLibrary)
-endif()
+# install libs
+set(installable_libs MathFunctions tutorial_compiler_flags)
 install(TARGETS ${installable_libs} DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
index 3fb547b..cd36bcc 100644 (file)
@@ -1,14 +1 @@
-
-#if defined(_WIN32)
-#  if defined(EXPORTING_MYMATH)
-#    define DECLSPEC __declspec(dllexport)
-#  else
-#    define DECLSPEC __declspec(dllimport)
-#  endif
-#else // non windows
-#  define DECLSPEC
-#endif
-
-namespace mathfunctions {
-double DECLSPEC sqrt(double x);
-}
+double mysqrt(double x);
index 8153f18..7d80ee9 100644 (file)
@@ -5,8 +5,6 @@
 // include the generated table
 #include "Table.h"
 
-namespace mathfunctions {
-namespace detail {
 // a hack square root calculation using simple operations
 double mysqrt(double x)
 {
@@ -33,5 +31,3 @@ double mysqrt(double x)
 
   return result;
 }
-}
-}
index 7e4d7fa..e23f521 100644 (file)
@@ -1,3 +1,4 @@
 // the configured options and settings for Tutorial
 #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
 #define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
+#cmakedefine USE_MYMATH
index 37a0333..b3c6a4f 100644 (file)
@@ -1,11 +1,15 @@
 // 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) {
@@ -19,7 +23,12 @@ int main(int argc, char* argv[])
   // convert input to double
   const double inputValue = std::stod(argv[1]);
 
-  const double outputValue = mathfunctions::sqrt(inputValue);
+  // which square root function should we use?
+#ifdef USE_MYMATH
+  const double outputValue = mysqrt(inputValue);
+#else
+  const double outputValue = sqrt(inputValue);
+#endif
 
   std::cout << "The square root of " << inputValue << " is " << outputValue
             << std::endl;
index 1044748..046bfc9 100644 (file)
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15)
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
+# specify the C++ standard
 add_library(tutorial_compiler_flags INTERFACE)
 target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
 
@@ -31,7 +32,7 @@ add_subdirectory(MathFunctions)
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC MathFunctions)
+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
@@ -46,7 +47,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
 # enable testing
-enable_testing()
+include(CTest)
 
 # does the application run
 add_test(NAME Runs COMMAND Tutorial 25)
@@ -74,6 +75,7 @@ do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
+# setup installer
 include(InstallRequiredSystemLibraries)
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
index c62d00b..85760e5 100644 (file)
@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
+CMake/Tutorial/Step9...
index 0d287ca..a60fb63 100644 (file)
@@ -47,13 +47,14 @@ endif()
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # define the symbol stating we are using the declspec(dllexport) when
-#building on windows
+# building on windows
 target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
 
-# install rules
+# 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)
index a4f44d5..37a0333 100644 (file)
@@ -1,5 +1,6 @@
 // A simple program that computes the square root of a number
 #include <iostream>
+#include <sstream>
 #include <string>
 
 #include "MathFunctions.h"
index 63f9643..220ed4b 100644 (file)
@@ -37,7 +37,7 @@ add_subdirectory(MathFunctions)
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC MathFunctions)
+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
@@ -80,6 +80,7 @@ do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
+# setup installer
 include(InstallRequiredSystemLibraries)
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
index c62d00b..85760e5 100644 (file)
@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
+CMake/Tutorial/Step9...
index d5961da..a85f3cb 100644 (file)
@@ -52,7 +52,7 @@ target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 # building on windows
 target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
 
-# install rules
+# install libs
 set(installable_libs MathFunctions tutorial_compiler_flags)
 if(TARGET SqrtLibrary)
   list(APPEND installable_libs SqrtLibrary)
@@ -60,4 +60,5 @@ endif()
 install(TARGETS ${installable_libs}
         EXPORT MathFunctionsTargets
         DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
index 7aa59e9..2b96128 100644 (file)
@@ -7,13 +7,36 @@ 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
new file mode 100644 (file)
index 0000000..b7779b7
--- /dev/null
@@ -0,0 +1,2 @@
+# TODO 1: Add a library called MathFunctions
+# Hint: You will need the add_library command
index 7e4d7fa..6c09e1a 100644 (file)
@@ -1,3 +1,5 @@
 // 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
index 53b0810..87f5e0f 100644 (file)
@@ -5,6 +5,10 @@
 
 #include "TutorialConfig.h"
 
+// TODO 11: Only include MathFunctions if USE_MYMATH is defined
+
+// TODO 5: Include MathFunctions.h
+
 int main(int argc, char* argv[])
 {
   if (argc < 2) {
@@ -18,6 +22,10 @@ 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
+
   // calculate square root
   const double outputValue = sqrt(inputValue);
   std::cout << "The square root of " << inputValue << " is " << outputValue
index 1c12816..007770a 100644 (file)
@@ -14,6 +14,8 @@ option(USE_MYMATH "Use tutorial provided math implementation" ON)
 # to the source code
 configure_file(TutorialConfig.h.in TutorialConfig.h)
 
+# TODO 2: Remove EXTRA_INCLUDES list
+
 # add the MathFunctions library
 if(USE_MYMATH)
   add_subdirectory(MathFunctions)
@@ -26,6 +28,8 @@ add_executable(Tutorial tutorial.cxx)
 
 target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
 
+# TODO 3: Remove use of EXTRA_INCLUDES
+
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
 target_include_directories(Tutorial PUBLIC
index 8b443a6..7bf05e0 100644 (file)
@@ -1 +1,5 @@
 add_library(MathFunctions mysqrt.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
index 38e9b1f..fa4aab2 100644 (file)
@@ -1,12 +1,36 @@
+# TODO 4: Update the minimum required version to 3.15
+
 cmake_minimum_required(VERSION 3.10)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
+# TODO 1: Replace the following code by:
+# * Creating an interface library called tutorial_compiler_flags
+#   Hint: use add_library() with the INTERFACE signature
+# * Add compiler feature cxx_std_11 to tutorial_compiler_flags
+#   Hint: Use target_compile_features()
+
 # specify the C++ standard
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED True)
 
+# TODO 5: 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
+# 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
+# build-tree
+# Hint: Use BUILD_INTERFACE
+
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
 
@@ -23,6 +47,8 @@ endif()
 # add the executable
 add_executable(Tutorial tutorial.cxx)
 
+# TODO 2: Link to tutorial_compiler_flags
+
 target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
 
 # add the binary tree to the search path for include files
index 0515852..5f7369c 100644 (file)
@@ -5,3 +5,5 @@ add_library(MathFunctions mysqrt.cxx)
 target_include_directories(MathFunctions
           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
           )
+
+# TODO 3: Link to tutorial_compiler_flags
index 82d00c8..a8f241a 100644 (file)
@@ -1,11 +1,20 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
@@ -22,7 +31,7 @@ endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
 
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
@@ -30,37 +39,26 @@ target_include_directories(Tutorial PUBLIC
                            "${PROJECT_BINARY_DIR}"
                            )
 
-# add the install targets
-install(TARGETS Tutorial DESTINATION bin)
-install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
-  DESTINATION include
-  )
+# TODO 3: Install Tutorial in the bin directory
+# Hint: Use the TARGETS and DESTINATION parameters
+
+# TODO 4: Install Tutorial.h to the include directory
+# Hint: Use the FILES and DESTINATION parameters
 
-# enable testing
-enable_testing()
+# TODO 5: Enable testing
 
-# does the application run
-add_test(NAME Runs COMMAND Tutorial 25)
+# TODO 6: Add a test called Runs which runs the following command:
+# $ Tutorial 25
 
-# does the usage message work?
-add_test(NAME Usage COMMAND Tutorial)
-set_tests_properties(Usage
-  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
-  )
+# TODO 7: Add a test called Usage which runs the following command:
+# $ Tutorial
+# Make sure the expected output is displayed.
+# Hint: Use the PASS_REGULAR_EXPRESSION property with "Usage.*number"
 
-# define a function to simplify adding tests
-function(do_test target arg result)
-  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
-  set_tests_properties(Comp${arg}
-    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
-    )
-endfunction()
+# TODO 8: Add a test which runs the following command:
+# $ Tutorial 4
+# Make sure the result is correct.
+# Hint: Use the PASS_REGULAR_EXPRESSION property with "4 is 2"
 
-# do a bunch of result based tests
-do_test(Tutorial 4 "4 is 2")
-do_test(Tutorial 9 "9 is 3")
-do_test(Tutorial 5 "5 is 2.236")
-do_test(Tutorial 7 "7 is 2.645")
-do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is (-nan|nan|0)")
-do_test(Tutorial 0.0001 "0.0001 is 0.01")
+# TODO 9: Add more tests. Create a function called do_test to avoid copy +
+# paste. Test the following values: 4, 9, 5, 7, 25, -25 and 0.00001.
index b12f27d..6cd88d7 100644 (file)
@@ -6,6 +6,13 @@ target_include_directories(MathFunctions
           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
           )
 
-# install rules
-install(TARGETS MathFunctions DESTINATION lib)
-install(FILES MathFunctions.h DESTINATION include)
+# link our compiler flags interface library
+target_link_libraries(MathFunctions 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)
+# Then install the installable libraries to the lib folder.
+# Hint: Use the TARGETS and DESTINATION parameters
+
+# TODO 2: Install the library headers to the include folder.
+# Hint: Use the FILES and DESTINATION parameters
index 82d00c8..da9e852 100644 (file)
@@ -1,11 +1,20 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
@@ -22,7 +31,7 @@ endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} 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/CTestConfig.cmake b/Help/guide/tutorial/Step6/CTestConfig.cmake
new file mode 100644 (file)
index 0000000..73efdb1
--- /dev/null
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
index 42e098a..b4724c4 100644 (file)
@@ -6,29 +6,11 @@ target_include_directories(MathFunctions
           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
           )
 
-# does this system provide the log and exp functions?
-include(CheckCXXSourceCompiles)
-check_cxx_source_compiles("
-  #include <cmath>
-  int main() {
-    std::log(1.0);
-    return 0;
-  }
-" HAVE_LOG)
-check_cxx_source_compiles("
-  #include <cmath>
-  int main() {
-    std::exp(1.0);
-    return 0;
-  }
-" HAVE_EXP)
+# link our compiler flags interface library
+target_link_libraries(MathFunctions tutorial_compiler_flags)
 
-# add compile definitions
-if(HAVE_LOG AND HAVE_EXP)
-  target_compile_definitions(MathFunctions
-                             PRIVATE "HAVE_LOG" "HAVE_EXP")
-endif()
-
-# install rules
-install(TARGETS MathFunctions DESTINATION lib)
+# install libs
+set(installable_libs MathFunctions tutorial_compiler_flags)
+install(TARGETS ${installable_libs} DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step6/MathFunctions/MakeTable.cxx
deleted file mode 100644 (file)
index ee58556..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that builds a sqrt table
-#include <cmath>
-#include <fstream>
-#include <iostream>
-
-int main(int argc, char* argv[])
-{
-  // make sure we have enough arguments
-  if (argc < 2) {
-    return 1;
-  }
-
-  std::ofstream fout(argv[1], std::ios_base::out);
-  const bool fileOpen = fout.is_open();
-  if (fileOpen) {
-    fout << "double sqrtTable[] = {" << std::endl;
-    for (int i = 0; i < 10; ++i) {
-      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
-    }
-    // close the table with a zero
-    fout << "0};" << std::endl;
-    fout.close();
-  }
-  return fileOpen ? 0 : 1; // return 0 if wrote the file
-}
index 7eecd26..abe767d 100644 (file)
@@ -1,4 +1,3 @@
-#include <cmath>
 #include <iostream>
 
 #include "MathFunctions.h"
@@ -10,12 +9,6 @@ double mysqrt(double x)
     return 0;
   }
 
-  // if we have both log and exp then use them
-#if defined(HAVE_LOG) && defined(HAVE_EXP)
-  double result = std::exp(std::log(x) * 0.5);
-  std::cout << "Computing sqrt of " << x << " to be " << result
-            << " using log and exp" << std::endl;
-#else
   double result = x;
 
   // do ten iterations
@@ -27,6 +20,5 @@ double mysqrt(double x)
     result = result + 0.5 * delta / result;
     std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-#endif
   return result;
 }
index 82d00c8..d26a90c 100644 (file)
@@ -1,11 +1,20 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
@@ -22,7 +31,7 @@ endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
 
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
@@ -37,7 +46,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
 # enable testing
-enable_testing()
+include(CTest)
 
 # does the application run
 add_test(NAME Runs COMMAND Tutorial 25)
diff --git a/Help/guide/tutorial/Step7/CTestConfig.cmake b/Help/guide/tutorial/Step7/CTestConfig.cmake
new file mode 100644 (file)
index 0000000..73efdb1
--- /dev/null
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Help/guide/tutorial/Step7/License.txt b/Help/guide/tutorial/Step7/License.txt
deleted file mode 100644 (file)
index c62d00b..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
index 9ede4b3..b4724c4 100644 (file)
@@ -1,29 +1,16 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  )
-
-# add the main library
-add_library(MathFunctions
-            mysqrt.cxx
-            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-            )
+add_library(MathFunctions mysqrt.cxx)
 
 # state that anybody linking to us needs to include the current source dir
 # to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-# state that we depend on our binary dir to find Table.h
 target_include_directories(MathFunctions
           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
           )
 
-# install rules
-install(TARGETS MathFunctions DESTINATION lib)
+# link our compiler flags interface library
+target_link_libraries(MathFunctions tutorial_compiler_flags)
+
+# install libs
+set(installable_libs MathFunctions tutorial_compiler_flags)
+install(TARGETS ${installable_libs} DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step7/MathFunctions/MakeTable.cxx b/Help/guide/tutorial/Step7/MathFunctions/MakeTable.cxx
deleted file mode 100644 (file)
index ee58556..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-// A simple program that builds a sqrt table
-#include <cmath>
-#include <fstream>
-#include <iostream>
-
-int main(int argc, char* argv[])
-{
-  // make sure we have enough arguments
-  if (argc < 2) {
-    return 1;
-  }
-
-  std::ofstream fout(argv[1], std::ios_base::out);
-  const bool fileOpen = fout.is_open();
-  if (fileOpen) {
-    fout << "double sqrtTable[] = {" << std::endl;
-    for (int i = 0; i < 10; ++i) {
-      fout << sqrt(static_cast<double>(i)) << "," << std::endl;
-    }
-    // close the table with a zero
-    fout << "0};" << std::endl;
-    fout.close();
-  }
-  return fileOpen ? 0 : 1; // return 0 if wrote the file
-}
index 7d80ee9..abe767d 100644 (file)
@@ -2,9 +2,6 @@
 
 #include "MathFunctions.h"
 
-// include the generated table
-#include "Table.h"
-
 // a hack square root calculation using simple operations
 double mysqrt(double x)
 {
@@ -12,12 +9,7 @@ double mysqrt(double x)
     return 0;
   }
 
-  // use the table to help find an initial value
   double result = x;
-  if (x >= 1 && x < 10) {
-    std::cout << "Use the table to help find an initial value " << std::endl;
-    result = sqrtTable[static_cast<int>(x)];
-  }
 
   // do ten iterations
   for (int i = 0; i < 10; ++i) {
@@ -28,6 +20,5 @@ double mysqrt(double x)
     result = result + 0.5 * delta / result;
     std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-
   return result;
 }
index 4c78b94..cb87281 100644 (file)
@@ -1,11 +1,21 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
+
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
@@ -22,7 +32,7 @@ endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
 
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
@@ -37,7 +47,7 @@ install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
   )
 
 # enable testing
-enable_testing()
+include(CTest)
 
 # does the application run
 add_test(NAME Runs COMMAND Tutorial 25)
@@ -64,11 +74,3 @@ do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-# setup installer
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-set(CPACK_SOURCE_GENERATOR "TGZ")
-include(CPack)
diff --git a/Help/guide/tutorial/Step8/License.txt b/Help/guide/tutorial/Step8/License.txt
deleted file mode 100644 (file)
index c62d00b..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
index 9ede4b3..f81b563 100644 (file)
@@ -1,29 +1,39 @@
-# first we add the executable that generates the table
-add_executable(MakeTable MakeTable.cxx)
-
-# add the command to generate the source code
-add_custom_command(
-  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-  DEPENDS MakeTable
-  )
-
-# add the main library
-add_library(MathFunctions
-            mysqrt.cxx
-            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
-            )
+add_library(MathFunctions mysqrt.cxx)
 
 # state that anybody linking to us needs to include the current source dir
 # to find MathFunctions.h, while we don't.
-# state that we depend on Tutorial_BINARY_DIR but consumers don't, as the
-# TutorialConfig.h include is an implementation detail
-# state that we depend on our binary dir to find Table.h
 target_include_directories(MathFunctions
           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
-          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
           )
 
-# install rules
-install(TARGETS MathFunctions DESTINATION lib)
+# 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()
+
+# install libs
+set(installable_libs MathFunctions tutorial_compiler_flags)
+install(TARGETS ${installable_libs} DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
index 7d80ee9..7eecd26 100644 (file)
@@ -1,10 +1,8 @@
+#include <cmath>
 #include <iostream>
 
 #include "MathFunctions.h"
 
-// include the generated table
-#include "Table.h"
-
 // a hack square root calculation using simple operations
 double mysqrt(double x)
 {
@@ -12,12 +10,13 @@ double mysqrt(double x)
     return 0;
   }
 
-  // use the table to help find an initial value
+  // if we have both log and exp then use them
+#if defined(HAVE_LOG) && defined(HAVE_EXP)
+  double result = std::exp(std::log(x) * 0.5);
+  std::cout << "Computing sqrt of " << x << " to be " << result
+            << " using log and exp" << std::endl;
+#else
   double result = x;
-  if (x >= 1 && x < 10) {
-    std::cout << "Use the table to help find an initial value " << std::endl;
-    result = sqrtTable[static_cast<int>(x)];
-  }
 
   // do ten iterations
   for (int i = 0; i < 10; ++i) {
@@ -28,6 +27,6 @@ double mysqrt(double x)
     result = result + 0.5 * delta / result;
     std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
   }
-
+#endif
   return result;
 }
index 6bae26e..d26a90c 100644 (file)
@@ -1,11 +1,20 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.15)
 
 # set the project name and version
 project(Tutorial VERSION 1.0)
 
 # specify the C++ standard
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
+add_library(tutorial_compiler_flags INTERFACE)
+target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
+
+# add compiler warning flags just when building this project via
+# the BUILD_INTERFACE genex
+set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
+set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
+target_compile_options(tutorial_compiler_flags INTERFACE
+  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
+  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
+)
 
 # should we use our own math functions
 option(USE_MYMATH "Use tutorial provided math implementation" ON)
@@ -22,7 +31,7 @@ endif()
 
 # add the executable
 add_executable(Tutorial tutorial.cxx)
-target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
+target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
 
 # add the binary tree to the search path for include files
 # so that we will find TutorialConfig.h
@@ -64,10 +73,3 @@ do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
 do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
-
-include(InstallRequiredSystemLibraries)
-set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
-set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
-set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
-set(CPACK_SOURCE_GENERATOR "TGZ")
-include(CPack)
index c62d00b..85760e5 100644 (file)
@@ -1,2 +1,2 @@
 This is the open source License.txt file introduced in
-CMake/Tutorial/Step7...
+CMake/Tutorial/Step9...
index 50f0701..8e04f97 100644 (file)
@@ -16,12 +16,19 @@ add_library(MathFunctions
 
 # 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}
           )
 
-# install rules
-install(TARGETS MathFunctions DESTINATION lib)
+# link our compiler flags interface library
+target_link_libraries(MathFunctions tutorial_compiler_flags)
+
+# install libs
+set(installable_libs MathFunctions tutorial_compiler_flags)
+install(TARGETS ${installable_libs} DESTINATION lib)
+# install include headers
 install(FILES MathFunctions.h DESTINATION include)
diff --git a/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx b/Help/guide/tutorial/Step9/MathFunctions/MathFunctions.cxx
deleted file mode 100644 (file)
index 0145300..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-
-#include "MathFunctions.h"
-
-#include <cmath>
-
-#ifdef USE_MYMATH
-#  include "mysqrt.h"
-#endif
-
-namespace mathfunctions {
-double sqrt(double x)
-{
-#ifdef USE_MYMATH
-  return detail::mysqrt(x);
-#else
-  return std::sqrt(x);
-#endif
-}
-}
diff --git a/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h b/Help/guide/tutorial/Step9/MathFunctions/mysqrt.h
deleted file mode 100644 (file)
index e1c42ef..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-
-namespace mathfunctions {
-namespace detail {
-double mysqrt(double x);
-}
-}
index 09553cb..1ab0009 100644 (file)
@@ -24,13 +24,13 @@ provides the complete solution for the previous step.
   A Basic Starting Point
   Adding a Library
   Adding Usage Requirements for a Library
+  Adding Generator Expressions
   Installing and Testing
+  Adding Support for a Testing Dashboard
   Adding System Introspection
   Adding a Custom Command and Generated File
   Packaging an Installer
-  Adding Support for a Testing Dashboard
   Selecting Static or Shared Libraries
-  Adding Generator Expressions
   Adding Export Configuration
   Packaging Debug and Release
 
index ba8196b..3355992 100644 (file)
@@ -71,8 +71,8 @@ The CMake tooling may report warnings which are intended
 for the provider of the software, not intended for the
 consumer of the software.  Such warnings end with "This
 warning is for project developers".  Users may disable
-such warnings by passing the ``-Wno-dev`` flag to
-:manual:`cmake(1)`.
+such warnings by passing the :option:`-Wno-dev <cmake -Wno-dev>`
+flag to :manual:`cmake(1)`.
 
 cmake-gui tool
 --------------
@@ -153,13 +153,13 @@ platform.  Usually, the default generator is sufficient
 to allow the user to proceed to build the software.
 
 The user may override the default generator with
-the ``-G`` option:
+the :option:`-G <cmake -G>` option:
 
 .. code-block:: console
 
   $ cmake .. -G Ninja
 
-The output of ``cmake --help`` includes a list of
+The output of :option:`cmake --help` includes a list of
 :manual:`generators <cmake-generators(7)>` available
 for the user to choose from.  Note that generator
 names are case sensitive.
@@ -196,7 +196,8 @@ VisualC++ compiler, or a combination of the two:
   $ cmake .. -G "Visual Studio 16 2019"
 
 Visual Studio generators can target different architectures.
-One can specify the target architecture using the `-A` option:
+One can specify the target architecture using the
+:option:`-A <cmake -A>` option:
 
 .. code-block:: console
 
@@ -214,8 +215,8 @@ generator to use, typically a choice between a ``Makefile``
 or a ``Ninja`` based generator.
 
 Note that it is not possible to change the generator
-with ``-G`` after the first invocation of CMake.  To
-change the generator, the build directory must be
+with :option:`-G <cmake -G>` after the first invocation of CMake.
+To change the generator, the build directory must be
 deleted and the build must be started from scratch.
 
 When generating Visual Studio project and solutions
@@ -223,7 +224,7 @@ files several other options are available to use when
 initially running :manual:`cmake(1)`.
 
 The Visual Studio toolset can be specified with the
-``-T`` option:
+:option:`cmake -T` option:
 
 .. code-block:: console
 
@@ -232,9 +233,9 @@ The Visual Studio toolset can be specified with the
     $ # Build targeting Windows XP
     $ cmake.exe .. -G "Visual Studio 16 2019" -A x64 -T v120_xp
 
-Whereas the ``-A`` option specifies the _target_
-architecture, the ``-T`` option can be used to specify
-details of the toolchain used.  For example, `-Thost=x64`
+Whereas the :option:`-A <cmake -A>` option specifies the _target_
+architecture, the :option:`-T <cmake -T>` option can be used to specify
+details of the toolchain used.  For example, ``-Thost=x64``
 can be given to select the 64-bit version of the host
 tools.  The following demonstrates how to use 64-bit
 tools and also build for a 64-bit target architecture:
@@ -337,7 +338,7 @@ or later on a subsequent invocation of
     $ cd build
     $ cmake . -DCMAKE_BUILD_TYPE=Debug
 
-The ``-U`` flag may be used to unset variables
+The :option:`-U <cmake -U>` flag may be used to unset variables
 on the :manual:`cmake(1)` command line:
 
 .. code-block:: console
@@ -351,7 +352,7 @@ on the command line can be modified using the
 
 The :manual:`cmake(1)` tool allows specifying a
 file to use to populate the initial cache using
-the ``-C`` option.  This can be useful to simplify
+the :option:`-C <cmake -C>` option.  This can be useful to simplify
 commands and scripts which repeatedly require the
 same cache entries.
 
@@ -427,10 +428,10 @@ Using presets on the command-line
 ---------------------------------
 
 When using the :manual:`cmake(1)` command line tool, a
-preset can be invoked by using the ``--preset`` option. If
-``--preset`` is specified, the generator and build
-directory are not required, but can be specified to
-override them. For example, if you have the following
+preset can be invoked by using the :option:`--preset <cmake --preset>`
+option. If :option:`--preset <cmake --preset>` is specified,
+the generator and build directory are not required, but can be
+specified to override them. For example, if you have the following
 ``CMakePresets.json`` file:
 
 .. code-block:: json
@@ -502,23 +503,25 @@ command may be invoked in the build directory:
 
   $ cmake --build .
 
-The ``--build`` flag enables a particular mode of
-operation for the :manual:`cmake(1)` tool.  It invokes
-the  :variable:`CMAKE_MAKE_PROGRAM` command associated
-with the :manual:`generator <cmake-generators(7)>`, or
+The :option:`--build <cmake --build>` flag enables a
+particular mode of operation for the :manual:`cmake(1)`
+tool.  It invokes the  :variable:`CMAKE_MAKE_PROGRAM`
+command associated with the
+:manual:`generator <cmake-generators(7)>`, or
 the build tool configured by the user.
 
-The ``--build`` mode also accepts the parameter
-``--target`` to specify a particular target to build,
-for example a particular library, executable or
-custom target, or a particular special target like
-``install``:
+The :option:`--build <cmake --build>` mode also accepts
+the parameter :option:`--target <cmake--build --target>` to
+specify a particular target to build, for example a
+particular library, executable or custom target, or a
+particular special target like ``install``:
 
 .. code-block:: console
 
   $ cmake --build . --target myexe
 
-The ``--build`` mode also accepts a ``--config`` parameter
+The :option:`--build <cmake --build>` mode also accepts a
+:option:`--config <cmake--build --config>` parameter
 in the case of multi-config generators to specify which
 particular configuration to build:
 
@@ -526,23 +529,23 @@ particular configuration to build:
 
   $ cmake --build . --target myexe --config Release
 
-The ``--config`` option has no effect if the generator
-generates a buildsystem specific to a configuration which
-is chosen when invoking cmake with the
-:variable:`CMAKE_BUILD_TYPE` variable.
+The :option:`--config <cmake--build --config>` option has no
+effect if the generator generates a buildsystem specific
+to a configuration which is chosen when invoking cmake
+with the :variable:`CMAKE_BUILD_TYPE` variable.
 
 Some buildsystems omit details of command lines invoked
-during the build.  The ``--verbose`` flag can be used to
-cause those command lines to be shown:
+during the build.  The :option:`--verbose <cmake--build --verbose>`
+flag can be used to cause those command lines to be shown:
 
 .. code-block:: console
 
   $ cmake --build . --target myexe --verbose
 
-The ``--build`` mode can also pass particular command
-line options to the underlying build tool by listing
-them after ``--``.  This can be useful to specify
-options to the build tool, such as to continue the
+The :option:`--build <cmake --build>` mode can also pass
+particular command line options to the underlying build
+tool by listing them after ``--``.  This can be useful
+to specify options to the build tool, such as to continue the
 build after a failed job, where CMake does not
 provide a high-level user interface.
 
@@ -638,9 +641,9 @@ building the ``foo.i`` target will preprocess both files.
 Specifying a Build Program
 --------------------------
 
-The program invoked by the ``--build`` mode is determined
-by the :variable:`CMAKE_MAKE_PROGRAM` variable.  For most
-generators, the particular program does not need to be
+The program invoked by the :option:`--build <cmake --build>`
+mode is determined by the :variable:`CMAKE_MAKE_PROGRAM` variable.
+For most generators, the particular program does not need to be
 configured.
 
 ===================== =========================== ===========================
@@ -661,7 +664,8 @@ The ``jom`` tool is capable of reading makefiles of the
 ``NMake`` flavor and building in parallel, while the
 ``nmake`` tool always builds serially.  After generating
 with the :generator:`NMake Makefiles` generator a user
-can run ``jom`` instead of ``nmake``.  The ``--build``
+can run ``jom`` instead of ``nmake``.  The
+:option:`--build <cmake --build>`
 mode would also use ``jom`` if the
 :variable:`CMAKE_MAKE_PROGRAM` was set to ``jom`` while
 using the :generator:`NMake Makefiles` generator, and
@@ -671,7 +675,7 @@ and use it as the :variable:`CMAKE_MAKE_PROGRAM`. For
 completeness, ``nmake`` is an alternative tool which
 can process the output of the
 :generator:`NMake Makefiles JOM` generator, but doing
-so would be a pessimisation.
+so would be a pessimization.
 
 Software Installation
 =====================
@@ -745,8 +749,8 @@ run only tests without ``Qt`` in their name:
 
   $ ctest -E Qt
 
-Tests can be run in parallel by passing ``-j`` arguments
-to :manual:`ctest(1)`:
+Tests can be run in parallel by passing :option:`-j <ctest -j>`
+arguments to :manual:`ctest(1)`:
 
 .. code-block:: console
 
@@ -754,14 +758,15 @@ to :manual:`ctest(1)`:
 
 The environment variable :envvar:`CTEST_PARALLEL_LEVEL`
 can alternatively be set to avoid the need to pass
-``-j``.
+:option:`-j <ctest -j>`.
 
 By default :manual:`ctest(1)` does not print the output
-from the tests. The command line argument ``-V`` (or
-``--verbose``) enables verbose mode to print the
+from the tests. The command line argument :option:`-V <ctest -V>`
+(or ``--verbose``) enables verbose mode to print the
 output from all tests.
-The ``--output-on-failure`` option prints the test
-output for failing tests only.  The environment variable
-:envvar:`CTEST_OUTPUT_ON_FAILURE`
+The :option:`--output-on-failure <ctest --output-on-failure>`
+option prints the test output for failing tests only.
+The environment variable :envvar:`CTEST_OUTPUT_ON_FAILURE`
 can be set to ``1`` as an alternative to passing the
-``--output-on-failure`` option to :manual:`ctest(1)`.
+:option:`--output-on-failure <ctest --output-on-failure>`
+option to :manual:`ctest(1)`.
index 8e23b77..94adac8 100644 (file)
@@ -1,12 +1,15 @@
-``-S <path-to-source>``
+.. option:: -S <path-to-source>
+
  Path to root directory of the CMake project to build.
 
-``-B <path-to-build>``
+.. option:: -B <path-to-build>
+
  Path to directory which CMake will use as the root of build directory.
 
  If the directory doesn't already exist CMake will make it.
 
-``-C <initial-cache>``
+.. option:: -C <initial-cache>
+
  Pre-load a script to populate the cache.
 
  When CMake is first run in an empty build tree, it creates a
@@ -21,7 +24,8 @@
  References to :variable:`CMAKE_SOURCE_DIR` and :variable:`CMAKE_BINARY_DIR`
  within the script evaluate to the top-level source and build tree.
 
-``-D <var>:<type>=<value>, -D <var>=<value>``
+.. option:: -D <var>:<type>=<value>, -D <var>=<value>
+
  Create or update a CMake ``CACHE`` entry.
 
  When CMake is first run in an empty build tree, it creates a
  This option may also be given as a single argument:
  ``-D<var>:<type>=<value>`` or ``-D<var>=<value>``.
 
-``-U <globbing_expr>``
+ It's important to note that the order of ``-C`` and ``-D`` arguments is
+ significant. They will be carried out in the order they are listed, with the
+ last argument taking precedence over the previous ones. For example, if you
+ specify ``-DCMAKE_BUILD_TYPE=Debug``, followed by a ``-C`` argument with a
+ file that calls:
+
+ .. code-block:: cmake
+
+   set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
+
+ then the ``-C`` argument will take precedence, and ``CMAKE_BUILD_TYPE`` will
+ be set to ``Release``. However, if the ``-D`` argument comes after the ``-C``
+ argument, it will be set to ``Debug``.
+
+ If a ``set(... CACHE ...)`` call in the ``-C`` file does not use ``FORCE``,
+ and a ``-D`` argument sets the same variable, the ``-D`` argument will take
+ precedence regardless of order because of the nature of non-``FORCE``
+ ``set(... CACHE ...)`` calls.
+
+.. option:: -U <globbing_expr>
+
  Remove matching entries from CMake ``CACHE``.
 
  This option may be used to remove one or more variables from the
@@ -51,7 +75,8 @@
 
  Use with care, you can make your ``CMakeCache.txt`` non-working.
 
-``-G <generator-name>``
+.. option:: -G <generator-name>
+
  Specify a build system generator.
 
  CMake may support multiple native build systems on certain
  If not specified, CMake checks the :envvar:`CMAKE_GENERATOR` environment
  variable and otherwise falls back to a builtin default selection.
 
-``-T <toolset-spec>``
+.. option:: -T <toolset-spec>
+
  Toolset specification for the generator, if supported.
 
  Some CMake generators support a toolset specification to tell
  the native build system how to choose a compiler.  See the
  :variable:`CMAKE_GENERATOR_TOOLSET` variable for details.
 
-``-A <platform-name>``
+.. option:: -A <platform-name>
+
  Specify platform name if supported by generator.
 
  Some CMake generators support a platform name to be given to the
  native build system to choose a compiler or SDK.  See the
  :variable:`CMAKE_GENERATOR_PLATFORM` variable for details.
 
-``--toolchain <path-to-file>``
+.. option:: --toolchain <path-to-file>
+
  Specify the cross compiling toolchain file, equivalent to setting
  :variable:`CMAKE_TOOLCHAIN_FILE` variable.
 
-``--install-prefix <directory>``
+.. option:: --install-prefix <directory>
+
  Specify the installation directory, used by the
  :variable:`CMAKE_INSTALL_PREFIX` variable. Must be an absolute path.
 
-``-Wno-dev``
+.. option:: -Wno-dev
+
  Suppress developer warnings.
 
  Suppress warnings that are meant for the author of the
  ``CMakeLists.txt`` files. By default this will also turn off
  deprecation warnings.
 
-``-Wdev``
+.. option:: -Wdev
+
  Enable developer warnings.
 
  Enable warnings that are meant for the author of the ``CMakeLists.txt``
  files. By default this will also turn on deprecation warnings.
 
-``-Werror=dev``
- Make developer warnings errors.
-
- Make warnings that are meant for the author of the ``CMakeLists.txt`` files
- errors. By default this will also turn on deprecated warnings as errors.
+.. option:: -Wdeprecated
 
-``-Wno-error=dev``
- Make developer warnings not errors.
-
- Make warnings that are meant for the author of the ``CMakeLists.txt`` files not
- errors. By default this will also turn off deprecated warnings as errors.
-
-``-Wdeprecated``
  Enable deprecated functionality warnings.
 
  Enable warnings for usage of deprecated functionality, that are meant
  for the author of the ``CMakeLists.txt`` files.
 
-``-Wno-deprecated``
+.. option:: -Wno-deprecated
+
  Suppress deprecated functionality warnings.
 
  Suppress warnings for usage of deprecated functionality, that are meant
  for the author of the ``CMakeLists.txt`` files.
 
-``-Werror=deprecated``
- Make deprecated macro and function warnings errors.
+.. option:: -Werror=<what>
+
+ Treat CMake warnings as errors. ``<what>`` must be one of the following:
+
+ ``dev``
+   Make developer warnings errors.
+
+   Make warnings that are meant for the author of the ``CMakeLists.txt`` files
+   errors. By default this will also turn on deprecated warnings as errors.
+
+ ``deprecated``
+  Make deprecated macro and function warnings errors.
+
+  Make warnings for usage of deprecated macros and functions, that are meant
+  for the author of the ``CMakeLists.txt`` files, errors.
+
+.. option:: -Wno-error=<what>
 
- Make warnings for usage of deprecated macros and functions, that are meant
- for the author of the ``CMakeLists.txt`` files, errors.
+ Do not treat CMake warnings as errors. ``<what>`` must be one of the following:
 
-``-Wno-error=deprecated``
- Make deprecated macro and function warnings not errors.
+ ``dev``
+  Make warnings that are meant for the author of the ``CMakeLists.txt`` files not
+  errors. By default this will also turn off deprecated warnings as errors.
 
- Make warnings for usage of deprecated macros and functions, that are meant
- for the author of the ``CMakeLists.txt`` files, not errors.
+ ``deprecated``
+  Make warnings for usage of deprecated macros and functions, that are meant
+  for the author of the ``CMakeLists.txt`` files, not errors.
index feeca7d..78ee245 100644 (file)
-.. |file| replace:: The help is printed to a named <f>ile if given.
+.. |file| replace:: The output is printed to a named ``<file>`` if given.
+
+.. option:: -version [<file>], --version [<file>], /V [<file>]
+
+ Show program name/version banner and exit.
+ |file|
+
+.. option:: -h, -H, --help, -help, -usage, /?
 
-``--help,-help,-usage,-h,-H,/?``
  Print usage information and exit.
 
  Usage describes the basic command line interface and its options.
 
-``--version,-version,/V [<f>]``
- Show program name/version banner and exit.
+.. option:: --help-full [<file>]
 
- If a file is specified, the version is written into it.
- |file|
-
-``--help-full [<f>]``
  Print all help manuals and exit.
 
  All manuals are printed in a human-readable text format.
  |file|
 
-``--help-manual <man> [<f>]``
+.. option:: --help-manual <man> [<file>]
+
  Print one help manual and exit.
 
  The specified manual is printed in a human-readable text format.
  |file|
 
-``--help-manual-list [<f>]``
+.. option:: --help-manual-list [<file>]
+
  List help manuals available and exit.
 
  The list contains all manuals for which help may be obtained by
  using the ``--help-manual`` option followed by a manual name.
  |file|
 
-``--help-command <cmd> [<f>]``
+.. option:: --help-command <cmd> [<file>]
+
  Print help for one command and exit.
 
  The :manual:`cmake-commands(7)` manual entry for ``<cmd>`` is
  printed in a human-readable text format.
  |file|
 
-``--help-command-list [<f>]``
+.. option:: --help-command-list [<file>]
+
  List commands with help available and exit.
 
  The list contains all commands for which help may be obtained by
  using the ``--help-command`` option followed by a command name.
  |file|
 
-``--help-commands [<f>]``
+.. option:: --help-commands [<file>]
+
  Print cmake-commands manual and exit.
 
  The :manual:`cmake-commands(7)` manual is printed in a
  human-readable text format.
  |file|
 
-``--help-module <mod> [<f>]``
+.. option:: --help-module <mod> [<file>]
+
  Print help for one module and exit.
 
  The :manual:`cmake-modules(7)` manual entry for ``<mod>`` is printed
  in a human-readable text format.
  |file|
 
-``--help-module-list [<f>]``
+.. option:: --help-module-list [<file>]
+
  List modules with help available and exit.
 
  The list contains all modules for which help may be obtained by
  using the ``--help-module`` option followed by a module name.
  |file|
 
-``--help-modules [<f>]``
+.. option:: --help-modules [<file>]
+
  Print cmake-modules manual and exit.
 
  The :manual:`cmake-modules(7)` manual is printed in a human-readable
  text format.
  |file|
 
-``--help-policy <cmp> [<f>]``
+.. option:: --help-policy <cmp> [<file>]
+
  Print help for one policy and exit.
 
  The :manual:`cmake-policies(7)` manual entry for ``<cmp>`` is
  printed in a human-readable text format.
  |file|
 
-``--help-policy-list [<f>]``
+.. option:: --help-policy-list [<file>]
+
  List policies with help available and exit.
 
  The list contains all policies for which help may be obtained by
  using the ``--help-policy`` option followed by a policy name.
  |file|
 
-``--help-policies [<f>]``
+.. option:: --help-policies [<file>]
+
  Print cmake-policies manual and exit.
 
  The :manual:`cmake-policies(7)` manual is printed in a
  human-readable text format.
  |file|
 
-``--help-property <prop> [<f>]``
+.. option:: --help-property <prop> [<file>]
+
  Print help for one property and exit.
 
  The :manual:`cmake-properties(7)` manual entries for ``<prop>`` are
  printed in a human-readable text format.
  |file|
 
-``--help-property-list [<f>]``
+.. option:: --help-property-list [<file>]
+
  List properties with help available and exit.
 
  The list contains all properties for which help may be obtained by
  using the ``--help-property`` option followed by a property name.
  |file|
 
-``--help-properties [<f>]``
+.. option:: --help-properties [<file>]
+
  Print cmake-properties manual and exit.
 
  The :manual:`cmake-properties(7)` manual is printed in a
  human-readable text format.
  |file|
 
-``--help-variable <var> [<f>]``
+.. option:: --help-variable <var> [<file>]
+
  Print help for one variable and exit.
 
  The :manual:`cmake-variables(7)` manual entry for ``<var>`` is
  printed in a human-readable text format.
  |file|
 
-``--help-variable-list [<f>]``
+.. option:: --help-variable-list [<file>]
+
  List variables with help available and exit.
 
  The list contains all variables for which help may be obtained by
  using the ``--help-variable`` option followed by a variable name.
  |file|
 
-``--help-variables [<f>]``
+.. option:: --help-variables [<file>]
+
  Print cmake-variables manual and exit.
 
  The :manual:`cmake-variables(7)` manual is printed in a
index 60d45a3..cd66d51 100644 (file)
@@ -8,7 +8,7 @@ Synopsis
 
 .. parsed-literal::
 
- ccmake [<options>] {<path-to-source> | <path-to-existing-build>}
+ ccmake [<options>] <path-to-source | path-to-existing-build>
 
 Description
 ===========
@@ -27,6 +27,8 @@ native tool on their platform.
 Options
 =======
 
+.. program:: ccmake
+
 .. include:: OPTIONS_BUILD.txt
 
 .. include:: OPTIONS_HELP.txt
index bceff2d..b14160c 100644 (file)
@@ -257,7 +257,7 @@ targets in multiple different directories convenient through use of the
 Transitive Usage Requirements
 -----------------------------
 
-The usage requirements of a target can transitively propagate to dependents.
+The usage requirements of a target can transitively propagate to the dependents.
 The :command:`target_link_libraries` command has ``PRIVATE``,
 ``INTERFACE`` and ``PUBLIC`` keywords to control the propagation.
 
@@ -279,8 +279,10 @@ The :command:`target_link_libraries` command has ``PRIVATE``,
   # consumer is compiled with -DUSING_ARCHIVE_LIB
   target_link_libraries(consumer archiveExtras)
 
-Because ``archive`` is a ``PUBLIC`` dependency of ``archiveExtras``, the
-usage requirements of it are propagated to ``consumer`` too.  Because
+Because the ``archive`` is a ``PUBLIC`` dependency of ``archiveExtras``, the
+usage requirements of it are propagated to ``consumer`` too.
+
+Because
 ``serialization`` is a ``PRIVATE`` dependency of ``archiveExtras``, the usage
 requirements of it are not propagated to ``consumer``.
 
index 036fa8f..0f35632 100644 (file)
@@ -15,6 +15,7 @@ These commands are always available.
 .. toctree::
    :maxdepth: 1
 
+   /command/block
    /command/break
    /command/cmake_host_system_information
    /command/cmake_language
@@ -26,6 +27,7 @@ These commands are always available.
    /command/continue
    /command/else
    /command/elseif
+   /command/endblock
    /command/endforeach
    /command/endfunction
    /command/endif
index 737b22c..50fcf75 100644 (file)
@@ -21,6 +21,8 @@ Environment Variables that Change Behavior
    :maxdepth: 1
 
    /envvar/CMAKE_PREFIX_PATH
+   /envvar/SSL_CERT_DIR
+   /envvar/SSL_CERT_FILE
 
 Environment Variables that Control the Build
 ============================================
index 7a6188a..69e3f20 100644 (file)
@@ -594,6 +594,10 @@ Configuration Expressions
   expression when it is evaluated on a property of an :prop_tgt:`IMPORTED`
   target.
 
+  .. versionchanged:: 3.19
+    Multiple configurations can be specified for ``cfgs``.
+    CMake 3.18 and earlier only accepted a single configuration.
+
 .. genex:: $<OUTPUT_CONFIG:...>
 
   .. versionadded:: 3.20
@@ -627,9 +631,8 @@ Platform
 
 .. genex:: $<PLATFORM_ID:platform_ids>
 
-  where ``platform_ids`` is a comma-separated list.
   ``1`` if CMake's platform id matches any one of the entries in
-  ``platform_ids``, otherwise ``0``.
+  comma-separated list ``platform_ids``, otherwise ``0``.
   See also the :variable:`CMAKE_SYSTEM_NAME` variable.
 
 Compiler Version
@@ -844,10 +847,15 @@ related to most of the expressions in this sub-section.
 
   .. versionadded:: 3.3
 
-  ``1`` when the language used for compilation unit matches any of the entries
-  in ``languages``, otherwise ``0``.  This expression may be used to specify
-  compile options, compile definitions, and include directories for source
-  files of a particular language in a target. For example:
+  .. versionchanged:: 3.15
+    Multiple languages can be specified for ``languages``.
+    CMake 3.14 and earlier only accepted a single language.
+
+  ``1`` when the language used for compilation unit matches any of the
+  comma-separated entries in ``languages``, otherwise ``0``. This expression
+  may be used to specify compile options, compile definitions, and include
+  directories for source files of a particular language in a target. For
+  example:
 
   .. code-block:: cmake
 
@@ -892,8 +900,8 @@ related to most of the expressions in this sub-section.
 
   ``1`` when the language used for compilation unit matches ``language`` and
   CMake's compiler id of the ``language`` compiler matches any one of the
-  entries in ``compiler_ids``, otherwise ``0``. This expression is a short form
-  for the combination of ``$<COMPILE_LANGUAGE:language>`` and
+  comma-separated entries in ``compiler_ids``, otherwise ``0``. This expression
+  is a short form for the combination of ``$<COMPILE_LANGUAGE:language>`` and
   ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
   compile options, compile definitions, and include directories for source
   files of a particular language and compiler combination in a target.
@@ -967,10 +975,10 @@ Linker Language And ID
 
   .. versionadded:: 3.18
 
-  ``1`` when the language used for link step matches any of the entries
-  in ``languages``, otherwise ``0``.  This expression may be used to specify
-  link libraries, link options, link directories and link dependencies of a
-  particular language in a target. For example:
+  ``1`` when the language used for link step matches any of the comma-separated
+  entries in ``languages``, otherwise ``0``.  This expression may be used to
+  specify link libraries, link options, link directories and link dependencies
+  of a particular language in a target. For example:
 
   .. code-block:: cmake
 
@@ -1033,9 +1041,9 @@ Linker Language And ID
   .. versionadded:: 3.18
 
   ``1`` when the language used for link step matches ``language`` and the
-  CMake's compiler id of the language linker matches any one of the entries
-  in ``compiler_ids``, otherwise ``0``. This expression is a short form for the
-  combination of ``$<LINK_LANGUAGE:language>`` and
+  CMake's compiler id of the language linker matches any one of the comma-separated
+  entries in ``compiler_ids``, otherwise ``0``. This expression is a short form
+  for the combination of ``$<LINK_LANGUAGE:language>`` and
   ``$<LANG_COMPILER_ID:compiler_ids>``. This expression may be used to specify
   link libraries, link options, link directories and link dependencies of a
   particular language and linker combination in a target. For example:
index 034e218..ed5bbbf 100644 (file)
@@ -18,11 +18,11 @@ as a variant of some of the `Command-Line Build Tool Generators`_ to
 produce project files for an auxiliary IDE.
 
 CMake Generators are platform-specific so each may be available only
-on certain platforms.  The :manual:`cmake(1)` command-line tool ``--help``
-output lists available generators on the current platform.  Use its ``-G``
-option to specify the generator for a new build tree.
-The :manual:`cmake-gui(1)` offers interactive selection of a generator
-when creating a new build tree.
+on certain platforms.  The :manual:`cmake(1)` command-line tool
+:option:`--help <cmake --help>` output lists available generators on the
+current platform.  Use its :option:`-G <cmake -G>` option to specify the
+generator for a new build tree. The :manual:`cmake-gui(1)` offers
+interactive selection of a generator when creating a new build tree.
 
 CMake Generators
 ================
@@ -108,9 +108,9 @@ Extra Generators
 ================
 
 Some of the `CMake Generators`_ listed in the :manual:`cmake(1)`
-command-line tool ``--help`` output may have variants that specify
-an extra generator for an auxiliary IDE tool.  Such generator
-names have the form ``<extra-generator> - <main-generator>``.
+command-line tool :option:`--help <cmake --help>` output may have
+variants that specify an extra generator for an auxiliary IDE tool.
+Such generator names have the form ``<extra-generator> - <main-generator>``.
 The following extra generators are known to CMake.
 
 .. toctree::
index 281986f..dd0eeca 100644 (file)
@@ -9,7 +9,7 @@ Synopsis
 .. parsed-literal::
 
  cmake-gui [<options>]
- cmake-gui [<options>] {<path-to-source> | <path-to-existing-build>}
+ 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
 
@@ -29,19 +29,25 @@ native tool on their platform.
 Options
 =======
 
-``-S <path-to-source>``
+.. program:: cmake-gui
+
+.. option:: -S <path-to-source>
+
  Path to root directory of the CMake project to build.
 
-``-B <path-to-build>``
+.. option:: -B <path-to-build>
+
  Path to directory which CMake will use as the root of build directory.
 
  If the directory doesn't already exist CMake will make it.
 
-``--preset=<preset-name>``
+.. option:: --preset=<preset-name>
+
  Name of the preset to use from the project's
  :manual:`presets <cmake-presets(7)>` files, if it has them.
 
-``--browse-manual``
+.. option:: --browse-manual
+
  Open the CMake reference manual in a browser and immediately exit.
 
 .. include:: OPTIONS_HELP.txt
index 16917ff..0bd561f 100644 (file)
@@ -74,7 +74,7 @@ encoded in UTF-8 on Windows (using UTF-16 to call system APIs).
 Furthermore, CMake 3.0 and above allow a leading UTF-8
 `Byte-Order Mark`_ in source files.
 
-.. _`Byte-Order Mark`: http://en.wikipedia.org/wiki/Byte_order_mark
+.. _Byte-Order Mark: https://en.wikipedia.org/wiki/Byte_order_mark
 
 Source Files
 ------------
index 93beea9..d161a28 100644 (file)
@@ -193,6 +193,7 @@ They are normally called through the :command:`find_package` command.
    /module/FindOpenGL
    /module/FindOpenMP
    /module/FindOpenSceneGraph
+   /module/FindOpenSP
    /module/FindOpenSSL
    /module/FindOpenThreads
    /module/Findosg
@@ -236,6 +237,7 @@ They are normally called through the :command:`find_package` command.
    /module/FindRuby
    /module/FindSDL
    /module/FindSDL_image
+   /module/FindSDL_gfx
    /module/FindSDL_mixer
    /module/FindSDL_net
    /module/FindSDL_sound
index f6ab0c7..d6a30ff 100644 (file)
@@ -52,6 +52,16 @@ to determine whether to report an error on use of deprecated macros or
 functions.
 
 
+Policies Introduced by CMake 3.25
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0142: The Xcode generator does not append per-config suffixes to library search paths. </policy/CMP0142>
+   CMP0141: MSVC debug information format flags are selected by an abstraction. </policy/CMP0141>
+   CMP0140: The return() command checks its arguments. </policy/CMP0140>
+
 Policies Introduced by CMake 3.24
 =================================
 
index a96c704..da699d8 100644 (file)
@@ -10,6 +10,8 @@ cmake-presets(7)
 Introduction
 ============
 
+.. versionadded:: 3.19
+
 One problem that CMake users often face is sharing settings with other people
 for common ways to configure a project. This may be done to support CI builds,
 or for users who frequently use the same build. CMake supports two main files,
@@ -19,10 +21,10 @@ supports files included with the ``include`` field.
 
 ``CMakePresets.json`` and ``CMakeUserPresets.json`` live in the project's root
 directory. They both have exactly the same format, and both are optional
-(though at least one must be present if ``--preset`` is specified).
-``CMakePresets.json`` is meant to specify project-wide build details, while
-``CMakeUserPresets.json`` is meant for developers to specify their own local
-build details.
+(though at least one must be present if :option:`--preset <cmake --preset>`
+is specified).  ``CMakePresets.json`` is meant to specify project-wide build
+details, while ``CMakeUserPresets.json`` is meant for developers to specify
+their own local build details.
 
 ``CMakePresets.json`` may be checked into a version control system, and
 ``CMakeUserPresets.json`` should NOT be checked in. For example, if a
@@ -40,7 +42,6 @@ The files are a JSON document with an object as the root:
 The root object recognizes the following fields:
 
 ``version``
-
   A required integer representing the version of the JSON schema.
   The supported versions are:
 
@@ -59,32 +60,29 @@ The root object recognizes the following fields:
   ``5``
     .. versionadded:: 3.24
 
-``cmakeMinimumRequired``
+  ``6``
+    .. versionadded:: 3.25
 
+``cmakeMinimumRequired``
   An optional object representing the minimum version of CMake needed to
   build this project. This object consists of the following fields:
 
   ``major``
-
     An optional integer representing the major version.
 
   ``minor``
-
     An optional integer representing the minor version.
 
   ``patch``
-
     An optional integer representing the patch version.
 
 ``include``
-
   An optional array of strings representing files to include. If the filenames
   are not absolute, they are considered relative to the current file.
   This is allowed in preset files specifying version ``4`` or above.
   See `Includes`_ for discussion of the constraints on included files.
 
 ``vendor``
-
   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
@@ -93,20 +91,25 @@ The root object recognizes the following fields:
   desired by the vendor, though will typically be a map.
 
 ``configurePresets``
-
   An optional array of `Configure Preset`_ objects.
   This is allowed in preset files specifying version ``1`` or above.
 
 ``buildPresets``
-
   An optional array of `Build Preset`_ objects.
   This is allowed in preset files specifying version ``2`` or above.
 
 ``testPresets``
-
   An optional array of `Test Preset`_ objects.
   This is allowed in preset files specifying version ``2`` or above.
 
+``packagePresets``
+  An optional array of `Package Preset`_ objects.
+  This is allowed in preset files specifying version ``6`` or above.
+
+``workflowPresets``
+  An optional array of `Workflow Preset`_ objects.
+  This is allowed in preset files specifying version ``6`` or above.
+
 Includes
 ^^^^^^^^
 
@@ -134,15 +137,14 @@ Each entry of the ``configurePresets`` array is a JSON object
 that may contain the following fields:
 
 ``name``
-
   A required string representing the machine-friendly name of the preset.
   This identifier is used in the :ref:`cmake --preset <CMake Options>` option.
   There must not be two configure presets in the union of ``CMakePresets.json``
   and ``CMakeUserPresets.json`` in the same directory with the same name.
-  However, a configure preset may have the same name as a build or test preset.
+  However, a configure preset may have the same name as a build, test,
+  package, or workflow preset.
 
 ``hidden``
-
   An optional boolean specifying whether or not a preset should be hidden.
   If a preset is hidden, it cannot be used in the ``--preset=`` argument,
   will not show up in the :manual:`CMake GUI <cmake-gui(1)>`, and does not
@@ -151,7 +153,6 @@ that may contain the following fields:
   other presets to inherit via the ``inherits`` field.
 
 ``inherits``
-
   An optional array of strings representing the names of presets to inherit
   from. This field can also be a string, which is equivalent to an array
   containing one string.
@@ -160,7 +161,7 @@ that may contain the following fields:
   presets by default (except ``name``, ``hidden``, ``inherits``,
   ``description``, and ``displayName``), but can override them as
   desired. If multiple ``inherits`` presets provide conflicting values for
-  the same field, the earlier preset in the ``inherits`` list will be
+  the same field, the earlier preset in the ``inherits`` array will be
   preferred.
 
   A preset can only inherit from another preset that is defined in the
@@ -169,12 +170,10 @@ that may contain the following fields:
   ``CMakeUserPresets.json``.
 
 ``condition``
-
   An optional `Condition`_ object. This is allowed in preset files specifying
   version ``3`` or above.
 
 ``vendor``
-
   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, it should follow the same conventions as the
@@ -183,47 +182,43 @@ that may contain the following fields:
   when appropriate.
 
 ``displayName``
-
   An optional string with a human-friendly name of the preset.
 
 ``description``
-
   An optional string with a human-friendly description of the preset.
 
 ``generator``
-
   An optional string representing the generator to use for the preset. If
   ``generator`` is not specified, it must be inherited from the
   ``inherits`` preset (unless this preset is ``hidden``). In version ``3``
   or above, this field may be omitted to fall back to regular generator
   discovery procedure.
 
-  Note that for Visual Studio generators, unlike in the command line ``-G``
-  argument, you cannot include the platform name in the generator name. Use
-  the ``architecture`` field instead.
+  Note that for Visual Studio generators, unlike in the command line
+  :option:`-G <cmake -G>` argument, you cannot include the platform name
+  in the generator name. Use the ``architecture`` field instead.
 
 ``architecture``, ``toolset``
-
   Optional fields representing the platform and toolset, respectively, for
-  generators that support them. Each may be either a string or an object
-  with the following fields:
+  :manual:`generators <cmake-generators(7)>` that support them.
 
-  ``value``
+  See :option:`cmake -A` option for possible values for ``architecture``
+  and :option:`cmake -T` for ``toolset``.
+
+  Each may be either a string or an object with the following fields:
 
+  ``value``
     An optional string representing the value.
 
   ``strategy``
-
     An optional string telling CMake how to handle the ``architecture`` or
     ``toolset`` field. Valid values are:
 
     ``"set"``
-
       Set the respective value. This will result in an error for generators
       that do not support the respective field.
 
     ``"external"``
-
       Do not set the value, even if the generator supports it. This is
       useful if, for example, a preset uses the Ninja generator, and an IDE
       knows how to set up the Visual C++ environment from the
@@ -231,8 +226,10 @@ that may contain the following fields:
       ignore the field, but the IDE can use them to set up the environment
       before invoking CMake.
 
-``toolchainFile``
+    If no ``strategy`` field is given, or if the field uses the string form
+    rather than the object form, the behavior is the same as ``"set"``.
 
+``toolchainFile``
   An optional string representing the path to the toolchain file.
   This field supports `macro expansion`_. If a relative path is specified,
   it is calculated relative to the build directory, and if not found,
@@ -241,7 +238,6 @@ that may contain the following fields:
   specifying version ``3`` or above.
 
 ``binaryDir``
-
   An optional string representing the path to the output binary directory.
   This field supports `macro expansion`_. If a relative path is specified,
   it is calculated relative to the source directory. If ``binaryDir`` is not
@@ -250,20 +246,17 @@ that may contain the following fields:
   omitted.
 
 ``installDir``
-
   An optional string representing the path to the installation directory.
   This field supports `macro expansion`_. If a relative path is specified,
   it is calculated relative to the source directory. This is allowed in
   preset files specifying version ``3`` or above.
 
 ``cmakeExecutable``
-
   An optional string representing the path to the CMake executable to use
   for this preset. This is reserved for use by IDEs, and is not used by
   CMake itself. IDEs that use this field should expand any macros in it.
 
 ``cacheVariables``
-
   An optional map of cache variables. The key is the variable name (which
   may not be an empty string), and the value is either ``null``, a boolean
   (which is equivalent to a value of ``"TRUE"`` or ``"FALSE"`` and a type
@@ -271,11 +264,9 @@ that may contain the following fields:
   supports `macro expansion`_), or an object with the following fields:
 
   ``type``
-
     An optional string representing the type of the variable.
 
   ``value``
-
     A required string or boolean representing the value of the variable.
     A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. This field
     supports `macro expansion`_.
@@ -288,7 +279,6 @@ that may contain the following fields:
   a value was inherited from another preset.
 
 ``environment``
-
   An optional map of environment variables. The key is the variable name
   (which may not be an empty string), and the value is either ``null`` or
   a string representing the value of the variable. Each variable is set
@@ -306,73 +296,68 @@ that may contain the following fields:
   a value was inherited from another preset.
 
 ``warnings``
-
   An optional object specifying the warnings to enable. The object may
   contain the following fields:
 
   ``dev``
-
-    An optional boolean. Equivalent to passing ``-Wdev`` or ``-Wno-dev``
-    on the command line. This may not be set to ``false`` if ``errors.dev``
-    is set to ``true``.
+    An optional boolean. Equivalent to passing :option:`-Wdev <cmake -Wdev>`
+    or :option:`-Wno-dev <cmake -Wno-dev>` on the command line. This may not
+    be set to ``false`` if ``errors.dev`` is set to ``true``.
 
   ``deprecated``
-
-    An optional boolean. Equivalent to passing ``-Wdeprecated`` or
-    ``-Wno-deprecated`` on the command line. This may not be set to
-    ``false`` if ``errors.deprecated`` is set to ``true``.
+    An optional boolean. Equivalent to passing
+    :option:`-Wdeprecated <cmake -Wdeprecated>` or
+    :option:`-Wno-deprecated <cmake -Wno-deprecated>` on the command line.
+    This may not be set to ``false`` if ``errors.deprecated`` is set to
+    ``true``.
 
   ``uninitialized``
-
     An optional boolean. Setting this to ``true`` is equivalent to passing
-    ``--warn-uninitialized`` on the command line.
+    :option:`--warn-uninitialized <cmake --warn-uninitialized>` on the command
+    line.
 
   ``unusedCli``
-
     An optional boolean. Setting this to ``false`` is equivalent to passing
-    ``--no-warn-unused-cli`` on the command line.
+    :option:`--no-warn-unused-cli <cmake --no-warn-unused-cli>` on the command
+    line.
 
   ``systemVars``
-
     An optional boolean. Setting this to ``true`` is equivalent to passing
-    ``--check-system-vars`` on the command line.
+    :option:`--check-system-vars <cmake --check-system-vars>` on the command
+    line.
 
 ``errors``
-
   An optional object specifying the errors to enable. The object may
   contain the following fields:
 
   ``dev``
-
-    An optional boolean. Equivalent to passing ``-Werror=dev`` or
-    ``-Wno-error=dev`` on the command line. This may not be set to ``true``
-    if ``warnings.dev`` is set to ``false``.
+    An optional boolean. Equivalent to passing :option:`-Werror=dev <cmake -Werror>`
+    or :option:`-Wno-error=dev <cmake -Werror>` on the command line.
+    This may not be set to ``true`` if ``warnings.dev`` is set to ``false``.
 
   ``deprecated``
-
-    An optional boolean. Equivalent to passing ``-Werror=deprecated`` or
-    ``-Wno-error=deprecated`` on the command line. This may not be set to
-    ``true`` if ``warnings.deprecated`` is set to ``false``.
+    An optional boolean. Equivalent to passing
+    :option:`-Werror=deprecated <cmake -Werror>` or
+    :option:`-Wno-error=deprecated <cmake -Werror>` on the command line.
+    This may not be set to ``true`` if ``warnings.deprecated`` is set to
+    ``false``.
 
 ``debug``
-
   An optional object specifying debug options. The object may contain the
   following fields:
 
   ``output``
-
     An optional boolean. Setting this to ``true`` is equivalent to passing
-    ``--debug-output`` on the command line.
+    :option:`--debug-output <cmake --debug-output>` on the command line.
 
   ``tryCompile``
-
     An optional boolean. Setting this to ``true`` is equivalent to passing
-    ``--debug-trycompile`` on the command line.
+    :option:`--debug-trycompile <cmake --debug-trycompile>` on the command
+    line.
 
   ``find``
-
     An optional boolean. Setting this to ``true`` is equivalent to passing
-    ``--debug-find`` on the command line.
+    :option:`--debug-find <cmake --debug-find>` on the command line.
 
 Build Preset
 ^^^^^^^^^^^^
@@ -381,24 +366,23 @@ Each entry of the ``buildPresets`` array is a JSON object
 that may contain the following fields:
 
 ``name``
-
   A required string representing the machine-friendly name of the preset.
   This identifier is used in the
   :ref:`cmake --build --preset <Build Tool Mode>` option.
   There must not be two build presets in the union of ``CMakePresets.json``
   and ``CMakeUserPresets.json`` in the same directory with the same name.
-  However, a build preset may have the same name as a configure or test preset.
+  However, a build preset may have the same name as a configure, test,
+  package, or workflow preset.
 
 ``hidden``
-
   An optional boolean specifying whether or not a preset should be hidden.
-  If a preset is hidden, it cannot be used in the ``--preset`` argument
+  If a preset is hidden, it cannot be used in the
+  :option:`--preset <cmake --preset>` argument
   and does not have to have a valid ``configurePreset``, even from
   inheritance. ``hidden`` presets are intended to be used as a base for
   other presets to inherit via the ``inherits`` field.
 
 ``inherits``
-
   An optional array of strings representing the names of presets to inherit
   from. This field can also be a string, which is equivalent to an array
   containing one string.
@@ -407,7 +391,7 @@ that may contain the following fields:
   ``inherits`` presets by default (except ``name``, ``hidden``,
   ``inherits``, ``description``, and ``displayName``), but can override
   them as desired. If multiple ``inherits`` presets provide conflicting
-  values for the same field, the earlier preset in the ``inherits`` list
+  values for the same field, the earlier preset in the ``inherits`` array
   will be preferred.
 
   A preset can only inherit from another preset that is defined in the
@@ -416,12 +400,10 @@ that may contain the following fields:
   ``CMakeUserPresets.json``.
 
 ``condition``
-
   An optional `Condition`_ object. This is allowed in preset files specifying
   version ``3`` or above.
 
 ``vendor``
-
   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, it should follow the same conventions as the
@@ -430,15 +412,12 @@ that may contain the following fields:
   when appropriate.
 
 ``displayName``
-
   An optional string with a human-friendly name of the preset.
 
 ``description``
-
   An optional string with a human-friendly description of the preset.
 
 ``environment``
-
   An optional map of environment variables. The key is the variable name
   (which may not be an empty string), and the value is either ``null`` or
   a string representing the value of the variable. Each variable is set
@@ -469,7 +448,6 @@ that may contain the following fields:
     project.
 
 ``configurePreset``
-
   An optional string specifying the name of a configure preset to
   associate with this build preset. If ``configurePreset`` is not
   specified, it must be inherited from the inherits preset (unless this
@@ -478,36 +456,30 @@ that may contain the following fields:
   configuration did.
 
 ``inheritConfigureEnvironment``
-
   An optional boolean that defaults to true. If true, the environment
   variables from the associated configure preset are inherited after all
   inherited build preset environments, but before environment variables
   explicitly specified in this build preset.
 
 ``jobs``
-
-  An optional integer. Equivalent to passing ``--parallel`` or ``-j`` on
-  the command line.
+  An optional integer. Equivalent to passing
+  :option:`--parallel <cmake--build --parallel>` or ``-j`` on the command line.
 
 ``targets``
-
   An optional string or array of strings. Equivalent to passing
-  ``--target`` or ``-t`` on the command line. Vendors may ignore the
-  targets property or hide build presets that explicitly specify targets.
-  This field supports macro expansion.
+  :option:`--target <cmake--build --target>` or ``-t`` on the command line.
+  Vendors may ignore the targets property or hide build presets that
+  explicitly specify targets. This field supports macro expansion.
 
 ``configuration``
-
-  An optional string. Equivalent to passing ``--config`` on the command
-  line.
+  An optional string. Equivalent to passing
+  :option:`--config <cmake--build --config>` on the command line.
 
 ``cleanFirst``
-
-  An optional bool. If true, equivalent to passing ``--clean-first`` on
-  the command line.
+  An optional bool. If true, equivalent to passing
+  :option:`--clean-first <cmake--build --clean-first>` on the command line.
 
 ``resolvePackageReferences``
-
   An optional string that specifies the package resolve mode. This is
   allowed in preset files specifying version ``4`` or above.
 
@@ -517,24 +489,23 @@ that may contain the following fields:
   package references, this option does nothing. Valid values are:
 
   ``on``
-
     Causes package references to be resolved before attempting a build.
 
   ``off``
-
     Package references will not be resolved. Note that this may cause
     errors in some build environments, such as .NET SDK style projects.
 
   ``only``
-
     Only resolve package references, but do not perform a build.
 
   .. note::
 
-    The command line parameter ``--resolve-package-references`` will take
-    priority over this setting. If the command line parameter is not provided
-    and this setting is not specified, an environment-specific cache variable
-    will be evaluated to decide, if package restoration should be performed.
+    The command line parameter
+    :option:`--resolve-package-references <cmake--build --resolve-package-references>`
+    will take priority over this setting. If the command line parameter is not
+    provided and this setting is not specified, an environment-specific cache
+    variable will be evaluated to decide, if package restoration should be
+    performed.
 
     When using the Visual Studio generator, package references are defined
     using the :prop_tgt:`VS_PACKAGE_REFERENCES` property. Package references
@@ -543,12 +514,10 @@ that may contain the following fields:
     done from within a configure preset.
 
 ``verbose``
-
-  An optional bool. If true, equivalent to passing ``--verbose`` on the
-  command line.
+  An optional bool. If true, equivalent to passing
+  :option:`--verbose <cmake--build --verbose>` on the command line.
 
 ``nativeToolOptions``
-
   An optional array of strings. Equivalent to passing options after ``--``
   on the command line. The array values support macro expansion.
 
@@ -559,23 +528,22 @@ Each entry of the ``testPresets`` array is a JSON object
 that may contain the following fields:
 
 ``name``
-
   A required string representing the machine-friendly name of the preset.
-  This identifier is used in the :ref:`ctest --preset <CTest Options>` option.
+  This identifier is used in the :option:`ctest --preset` option.
   There must not be two test presets in the union of ``CMakePresets.json``
   and ``CMakeUserPresets.json`` in the same directory with the same name.
-  However, a test preset may have the same name as a configure or build preset.
+  However, a test preset may have the same name as a configure, build,
+  package, or workflow preset.
 
 ``hidden``
-
   An optional boolean specifying whether or not a preset should be hidden.
-  If a preset is hidden, it cannot be used in the ``--preset`` argument
+  If a preset is hidden, it cannot be used in the
+  :option:`--preset <ctest --preset>` argument
   and does not have to have a valid ``configurePreset``, even from
   inheritance. ``hidden`` presets are intended to be used as a base for
   other presets to inherit via the ``inherits`` field.
 
 ``inherits``
-
   An optional array of strings representing the names of presets to inherit
   from. This field can also be a string, which is equivalent to an array
   containing one string.
@@ -584,7 +552,7 @@ that may contain the following fields:
   ``inherits`` presets by default (except ``name``, ``hidden``,
   ``inherits``, ``description``, and ``displayName``), but can override
   them as desired. If multiple ``inherits`` presets provide conflicting
-  values for the same field, the earlier preset in the ``inherits`` list
+  values for the same field, the earlier preset in the ``inherits`` array
   will be preferred.
 
   A preset can only inherit from another preset that is defined in the
@@ -593,12 +561,10 @@ that may contain the following fields:
   ``CMakeUserPresets.json``.
 
 ``condition``
-
   An optional `Condition`_ object. This is allowed in preset files specifying
   version ``3`` or above.
 
 ``vendor``
-
   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, it should follow the same conventions as the
@@ -607,15 +573,12 @@ that may contain the following fields:
   when appropriate.
 
 ``displayName``
-
   An optional string with a human-friendly name of the preset.
 
 ``description``
-
   An optional string with a human-friendly description of the preset.
 
 ``environment``
-
   An optional map of environment variables. The key is the variable name
   (which may not be an empty string), and the value is either ``null`` or
   a string representing the value of the variable. Each variable is set
@@ -633,7 +596,6 @@ that may contain the following fields:
   even if a value was inherited from another preset.
 
 ``configurePreset``
-
   An optional string specifying the name of a configure preset to
   associate with this test preset. If ``configurePreset`` is not
   specified, it must be inherited from the inherits preset (unless this
@@ -642,233 +604,210 @@ that may contain the following fields:
   configuration did and build did.
 
 ``inheritConfigureEnvironment``
-
   An optional boolean that defaults to true. If true, the environment
   variables from the associated configure preset are inherited after all
   inherited test preset environments, but before environment variables
   explicitly specified in this test preset.
 
 ``configuration``
-
-  An optional string. Equivalent to passing ``--build-config`` on the
-  command line.
+  An optional string. Equivalent to passing
+  :option:`--build-config <ctest --build-config>` on the command line.
 
 ``overwriteConfigurationFile``
-
   An optional array of configuration options to overwrite options
   specified in the CTest configuration file. Equivalent to passing
-  ``--overwrite`` for each value in the array. The array values
-  support macro expansion.
+  :option:`--overwrite <ctest --overwrite>` for each value in the array.
+  The array values support macro expansion.
 
 ``output``
-
   An optional object specifying output options. The object may contain the
   following fields.
 
   ``shortProgress``
-
-    An optional bool. If true, equivalent to passing ``--progress`` on the
-    command line.
+    An optional bool. If true, equivalent to passing
+    :option:`--progress <ctest --progress>` on the command line.
 
   ``verbosity``
-
     An optional string specifying verbosity level. Must be one of the
     following:
 
     ``default``
-
       Equivalent to passing no verbosity flags on the command line.
 
     ``verbose``
-
-      Equivalent to passing ``--verbose`` on the command line.
+      Equivalent to passing :option:`--verbose <ctest --verbose>` on
+      the command line.
 
     ``extra``
-
-      Equivalent to passing ``--extra-verbose`` on the command line.
+      Equivalent to passing :option:`--extra-verbose <ctest --extra-verbose>`
+      on the command line.
 
   ``debug``
-
-    An optional bool. If true, equivalent to passing ``--debug`` on the
-    command line.
+    An optional bool. If true, equivalent to passing
+    :option:`--debug <ctest --debug>` on the command line.
 
   ``outputOnFailure``
-
     An optional bool. If true, equivalent to passing
-    ``--output-on-failure`` on the command line.
+    :option:`--output-on-failure <ctest --output-on-failure>` on the command
+    line.
 
   ``quiet``
-
-    An optional bool. If true, equivalent to passing ``--quiet`` on the
-    command line.
+    An optional bool. If true, equivalent to passing
+    :option:`--quiet <ctest --quiet>` on the command line.
 
   ``outputLogFile``
-
     An optional string specifying a path to a log file. Equivalent to
-    passing ``--output-log`` on the command line. This field supports
-    macro expansion.
+    passing :option:`--output-log <ctest --output-log>` on the command line.
+    This field supports macro expansion.
 
-  ``labelSummary``
+  ``outputJUnitFile``
+    An optional string specifying a path to a JUnit file. Equivalent to
+    passing :option:`--output-junit <ctest --output-junit>` on the command line.
+    This field supports macro expansion. This is allowed in preset files
+    specifying version ``6`` or above.
 
+  ``labelSummary``
     An optional bool. If false, equivalent to passing
-    ``--no-label-summary`` on the command line.
+    :option:`--no-label-summary <ctest --no-label-summary>` on the command
+    line.
 
   ``subprojectSummary``
-
     An optional bool. If false, equivalent to passing
-    ``--no-subproject-summary`` on the command line.
+    :option:`--no-subproject-summary <ctest --no-subproject-summary>`
+    on the command line.
 
   ``maxPassedTestOutputSize``
-
     An optional integer specifying the maximum output for passed tests in
-    bytes. Equivalent to passing ``--test-output-size-passed`` on the
-    command line.
+    bytes. Equivalent to passing
+    :option:`--test-output-size-passed <ctest --test-output-size-passed>`
+    on the command line.
 
   ``maxFailedTestOutputSize``
-
     An optional integer specifying the maximum output for failed tests in
-    bytes. Equivalent to passing ``--test-output-size-failed`` on the
-    command line.
+    bytes. Equivalent to passing
+    :option:`--test-output-size-failed <ctest --test-output-size-failed>`
+    on the command line.
 
   ``testOutputTruncation``
-
     An optional string specifying the test output truncation mode. Equivalent
-    to passing ``--test-output-truncation`` on the command line."
-    This is allowed in preset files specifying version ``5`` or above.
+    to passing
+    :option:`--test-output-truncation <ctest --test-output-truncation>` on
+    the command line. This is allowed in preset files specifying version
+    ``5`` or above.
 
   ``maxTestNameWidth``
-
     An optional integer specifying the maximum width of a test name to
-    output. Equivalent to passing ``--max-width`` on the command line.
+    output. Equivalent to passing :option:`--max-width <ctest --max-width>`
+    on the command line.
 
 ``filter``
-
   An optional object specifying how to filter the tests to run. The object
   may contain the following fields.
 
   ``include``
-
     An optional object specifying which tests to include. The object may
     contain the following fields.
 
     ``name``
-
       An optional string specifying a regex for test names. Equivalent to
-      passing ``--tests-regex`` on the command line. This field supports
-      macro expansion. CMake regex syntax is described under
-      :ref:`string(REGEX) <Regex Specification>`.
-
+      passing :option:`--tests-regex <ctest --tests-regex>` on the command
+      line. This field supports macro expansion. CMake regex syntax is
+      described under :ref:`string(REGEX) <Regex Specification>`.
 
     ``label``
-
       An optional string specifying a regex for test labels. Equivalent to
-      passing ``--label-regex`` on the command line. This field supports
-      macro expansion.
+      passing :option:`--label-regex <ctest --label-regex>` on the command
+      line. This field supports macro expansion.
 
     ``useUnion``
-
-      An optional bool. Equivalent to passing ``--union`` on the command
-      line.
+      An optional bool. Equivalent to passing :option:`--union <ctest --union>`
+      on the command line.
 
     ``index``
-
       An optional object specifying tests to include by test index. The
       object may contain the following fields. Can also be an optional
       string specifying a file with the command line syntax for
-      ``--tests-information``. If specified as a string, this field
-      supports macro expansion.
+      :option:`--tests-information <ctest --tests-information>`.
+      If specified as a string, this field supports macro expansion.
 
       ``start``
-
         An optional integer specifying a test index to start testing at.
 
       ``end``
-
         An optional integer specifying a test index to stop testing at.
 
       ``stride``
-
         An optional integer specifying the increment.
 
       ``specificTests``
-
         An optional array of integers specifying specific test indices to
         run.
 
   ``exclude``
-
     An optional object specifying which tests to exclude. The object may
     contain the following fields.
 
     ``name``
-
       An optional string specifying a regex for test names. Equivalent to
-      passing ``--exclude-regex`` on the command line. This field supports
-      macro expansion.
+      passing :option:`--exclude-regex <ctest --exclude-regex>` on the
+      command line. This field supports macro expansion.
 
     ``label``
-
       An optional string specifying a regex for test labels. Equivalent to
-      passing ``--label-exclude`` on the command line. This field supports
-      macro expansion.
+      passing :option:`--label-exclude <ctest --label-exclude>` on the
+      command line. This field supports macro expansion.
 
     ``fixtures``
-
       An optional object specifying which fixtures to exclude from adding
       tests. The object may contain the following fields.
 
       ``any``
-
         An optional string specifying a regex for text fixtures to exclude
-        from adding any tests. Equivalent to ``--fixture-exclude-any`` on
+        from adding any tests. Equivalent to
+        :option:`--fixture-exclude-any <ctest --fixture-exclude-any>` on
         the command line. This field supports macro expansion.
 
       ``setup``
-
         An optional string specifying a regex for text fixtures to exclude
-        from adding setup tests. Equivalent to ``--fixture-exclude-setup``
+        from adding setup tests. Equivalent to
+        :option:`--fixture-exclude-setup <ctest --fixture-exclude-setup>`
         on the command line. This field supports macro expansion.
 
       ``cleanup``
-
         An optional string specifying a regex for text fixtures to exclude
         from adding cleanup tests. Equivalent to
-        ``--fixture-exclude-cleanup`` on the command line. This field
-        supports macro expansion.
+        :option:`--fixture-exclude-cleanup <ctest --fixture-exclude-cleanup>`
+        on the command line. This field supports macro expansion.
 
 ``execution``
-
   An optional object specifying options for test execution. The object may
   contain the following fields.
 
   ``stopOnFailure``
-
-    An optional bool. If true, equivalent to passing ``--stop-on-failure``
-    on the command line.
+    An optional bool. If true, equivalent to passing
+    :option:`--stop-on-failure <ctest --stop-on-failure>` on the command
+    line.
 
   ``enableFailover``
-
-    An optional bool. If true, equivalent to passing ``-F`` on the command
-    line.
+    An optional bool. If true, equivalent to passing :option:`-F <ctest -F>`
+    on the command line.
 
   ``jobs``
-
-    An optional integer. Equivalent to passing ``--parallel`` on the
-    command line.
+    An optional integer. Equivalent to passing
+    :option:`--parallel <ctest --parallel>` on the command line.
 
   ``resourceSpecFile``
-
-    An optional string. Equivalent to passing ``--resource-spec-file`` on
+    An optional string. Equivalent to passing
+    :option:`--resource-spec-file <ctest --resource-spec-file>` on
     the command line. This field supports macro expansion.
 
   ``testLoad``
-
-    An optional integer. Equivalent to passing ``--test-load`` on the
-    command line.
+    An optional integer. Equivalent to passing
+    :option:`--test-load <ctest --test-load>` on the command line.
 
   ``showOnly``
-
-    An optional string. Equivalent to passing ``--show-only`` on the
+    An optional string. Equivalent to passing
+    :option:`--show-only <ctest --show-only>` on the
     command line. The string must be one of the following values:
 
     ``human``
@@ -876,13 +815,11 @@ that may contain the following fields:
     ``json-v1``
 
   ``repeat``
-
     An optional object specifying how to repeat tests. Equivalent to
-    passing ``--repeat`` on the command line. The object must have the
-    following fields.
+    passing :option:`--repeat <ctest --repeat>` on the command line.
+    The object must have the following fields.
 
     ``mode``
-
       A required string. Must be one of the following values:
 
       ``until-fail``
@@ -892,42 +829,204 @@ that may contain the following fields:
       ``after-timeout``
 
     ``count``
-
       A required integer.
 
   ``interactiveDebugging``
-
     An optional bool. If true, equivalent to passing
-    ``--interactive-debug-mode 1`` on the command line. If false,
-    equivalent to passing ``--interactive-debug-mode 0`` on the command
-    line.
+    :option:`--interactive-debug-mode 1 <ctest --interactive-debug-mode>`
+    on the command line. If false, equivalent to passing
+    :option:`--interactive-debug-mode 0 <ctest --interactive-debug-mode>`
+    on the command line.
 
   ``scheduleRandom``
-
-    An optional bool. If true, equivalent to passing ``--schedule-random``
-    on the command line.
+    An optional bool. If true, equivalent to passing
+    :option:`--schedule-random <ctest --schedule-random>` on the command
+    line.
 
   ``timeout``
-
-    An optional integer. Equivalent to passing ``--timeout`` on the
-    command line.
+    An optional integer. Equivalent to passing
+    :option:`--timeout <ctest --timeout>` on the command line.
 
   ``noTestsAction``
-
     An optional string specifying the behavior if no tests are found. Must
     be one of the following values:
 
     ``default``
-
       Equivalent to not passing any value on the command line.
 
     ``error``
-
-      Equivalent to passing ``--no-tests=error`` on the command line.
+      Equivalent to passing :option:`--no-tests=error <ctest --no-tests>`
+      on the command line.
 
     ``ignore``
+      Equivalent to passing :option:`--no-tests=ignore <ctest --no-tests>`
+      on the command line.
+
+Package Preset
+^^^^^^^^^^^^^^
+
+Package presets may be used in schema version ``6`` or above. Each entry of
+the ``packagePresets`` array is a JSON object that may contain the following
+fields:
+
+``name``
+  A required string representing the machine-friendly name of the preset.
+  This identifier is used in the :option:`cpack --preset` option.
+  There must not be two package presets in the union of ``CMakePresets.json``
+  and ``CMakeUserPresets.json`` in the same directory with the same name.
+  However, a package preset may have the same name as a configure, build,
+  test, or workflow preset.
+
+``hidden``
+  An optional boolean specifying whether or not a preset should be hidden.
+  If a preset is hidden, it cannot be used in the
+  :option:`--preset <cpack --preset>` argument
+  and does not have to have a valid ``configurePreset``, even from
+  inheritance. ``hidden`` presets are intended to be used as a base for
+  other presets to inherit via the ``inherits`` field.
+
+``inherits``
+  An optional array of strings representing the names of presets to inherit
+  from. This field can also be a string, which is equivalent to an array
+  containing one string.
+
+  The preset will inherit all of the fields from the
+  ``inherits`` presets by default (except ``name``, ``hidden``,
+  ``inherits``, ``description``, and ``displayName``), but can override
+  them as desired. If multiple ``inherits`` presets provide conflicting
+  values for the same field, the earlier preset in the ``inherits`` array
+  will be preferred.
+
+  A preset can only inherit from another preset that is defined in the
+  same file or in one of the files it includes (directly or indirectly).
+  Presets in ``CMakePresets.json`` may not inherit from presets in
+  ``CMakeUserPresets.json``.
+
+``condition``
+  An optional `Condition`_ object.
+
+``vendor``
+  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, it should follow the same conventions as the
+  root-level ``vendor`` field. If vendors use their own per-preset
+  ``vendor`` field, they should implement inheritance in a sensible manner
+  when appropriate.
+
+``displayName``
+  An optional string with a human-friendly name of the preset.
 
-      Equivalent to passing ``--no-tests=ignore`` on the command line.
+``description``
+  An optional string with a human-friendly description of the preset.
+
+``environment``
+  An optional map of environment variables. The key is the variable name
+  (which may not be an empty string), and the value is either ``null`` or
+  a string representing the value of the variable. Each variable is set
+  regardless of whether or not a value was given to it by the process's
+  environment. This field supports macro expansion, and environment
+  variables in this map may reference each other, and may be listed in any
+  order, as long as such references do not cause a cycle (for example, if
+  ``ENV_1`` is ``$env{ENV_2}``, ``ENV_2`` may not be ``$env{ENV_1}``.)
+
+  Environment variables are inherited through the ``inherits`` field, and
+  the preset's environment will be the union of its own ``environment``
+  and the ``environment`` from all its parents. If multiple presets in
+  this union define the same variable, the standard rules of ``inherits``
+  are applied. Setting a variable to ``null`` causes it to not be set,
+  even if a value was inherited from another preset.
+
+``configurePreset``
+  An optional string specifying the name of a configure preset to
+  associate with this package preset. If ``configurePreset`` is not
+  specified, it must be inherited from the inherits preset (unless this
+  preset is hidden). The build directory is inferred from the configure
+  preset, so packaging will run in the same ``binaryDir`` that the
+  configuration did and build did.
+
+``inheritConfigureEnvironment``
+  An optional boolean that defaults to true. If true, the environment
+  variables from the associated configure preset are inherited after all
+  inherited package preset environments, but before environment variables
+  explicitly specified in this package preset.
+
+``generators``
+  An optional array of strings representing generators for CPack to use.
+
+``configurations``
+  An optional array of strings representing build configurations for CPack to
+  package.
+
+``variables``
+  An optional map of variables to pass to CPack, equivalent to
+  :option:`-D <cpack -D>` arguments. Each key is the name of a variable, and
+  the value is the string to assign to that variable.
+
+``configFile``
+  An optional string representing the config file for CPack to use.
+
+``output``
+  An optional object specifying output options. Valid keys are:
+
+  ``debug``
+    An optional boolean specifying whether or not to print debug information.
+    A value of ``true`` is equivalent to passing
+    :option:`--debug <cpack --debug>` on the command line.
+
+  ``verbose``
+    An optional boolean specifying whether or not to print verbosely. A value
+    of ``true`` is equivalent to passing :option:`--verbose <cpack --verbose>`
+    on the command line.
+
+``packageName``
+  An optional string representing the package name.
+
+``packageVersion``
+  An optional string representing the package version.
+
+``packageDirectory``
+  An optional string representing the directory in which to place the package.
+
+``vendorName``
+  An optional string representing the vendor name.
+
+.. _`Workflow Preset`:
+
+Workflow Preset
+^^^^^^^^^^^^^^^
+
+Workflow presets may be used in schema version ``6`` or above. Each entry of
+the ``workflowPresets`` array is a JSON object that may contain the following
+fields:
+
+``name``
+  A required string representing the machine-friendly name of the preset.
+  This identifier is used in the
+  :ref:`cmake --workflow --preset <Workflow Mode>` option. There must not be
+  two workflow presets in the union of ``CMakePresets.json`` and
+  ``CMakeUserPresets.json`` in the same directory with the same name. However,
+  a workflow preset may have the same name as a configure, build, test, or
+  package preset.
+
+``displayName``
+  An optional string with a human-friendly name of the preset.
+
+``description``
+  An optional string with a human-friendly description of the preset.
+
+``steps``
+  A required array of objects describing the steps of the workflow. The first
+  step must be a configure preset, and all subsequent steps must be non-
+  configure presets whose ``configurePreset`` field matches the starting
+  configure preset. Each object may contain the following fields:
+
+  ``type``
+    A required string. The first step must be ``configure``. Subsequent steps
+    must be either ``build``, ``test``, or ``package``.
+
+  ``name``
+    A required string representing the name of the configure, build, test, or
+    package preset to run as this workflow step.
 
 Condition
 ^^^^^^^^^
@@ -943,65 +1042,53 @@ a ``not``, ``anyOf``, or ``allOf`` condition) may not be ``null``. If it is an
 object, it has the following fields:
 
 ``type``
-
   A required string with one of the following values:
 
   ``"const"``
-
     Indicates that the condition is constant. This is equivalent to using a
     boolean in place of the object. The condition object will have the
     following additional fields:
 
     ``value``
-
       A required boolean which provides a constant value for the condition's
       evaluation.
 
   ``"equals"``
 
   ``"notEquals"``
-
     Indicates that the condition compares two strings to see if they are equal
     (or not equal). The condition object will have the following additional
     fields:
 
     ``lhs``
-
       First string to compare. This field supports macro expansion.
 
     ``rhs``
-
       Second string to compare. This field supports macro expansion.
 
   ``"inList"``
 
   ``"notInList"``
-
     Indicates that the condition searches for a string in a list of strings.
     The condition object will have the following additional fields:
 
     ``string``
-
       A required string to search for. This field supports macro expansion.
 
     ``list``
-
-      A required list of strings to search. This field supports macro
+      A required array of strings to search. This field supports macro
       expansion, and uses short-circuit evaluation.
 
   ``"matches"``
 
   ``"notMatches"``
-
     Indicates that the condition searches for a regular expression in a string.
     The condition object will have the following additional fields:
 
     ``string``
-
       A required string to search. This field supports macro expansion.
 
     ``regex``
-
       A required regular expression to search for. This field supports macro
       expansion.
 
@@ -1013,17 +1100,14 @@ object, it has the following fields:
     conditions. The condition object will have the following additional fields:
 
     ``conditions``
-
       A required array of condition objects. These conditions use short-circuit
       evaluation.
 
   ``"not"``
-
     Indicates that the condition is an inversion of another condition. The
     condition object will have the following additional fields:
 
     ``condition``
-
       A required condition object.
 
 Macro Expansion
@@ -1045,46 +1129,37 @@ interpreted as a literal dollar sign.
 Recognized macros include:
 
 ``${sourceDir}``
-
   Path to the project source directory (i.e. the same as
   :variable:`CMAKE_SOURCE_DIR`).
 
 ``${sourceParentDir}``
-
   Path to the project source directory's parent directory.
 
 ``${sourceDirName}``
-
   The last filename component of ``${sourceDir}``. For example, if
   ``${sourceDir}`` is ``/path/to/source``, this would be ``source``.
 
 ``${presetName}``
-
   Name specified in the preset's ``name`` field.
 
 ``${generator}``
-
   Generator specified in the preset's ``generator`` field. For build and
   test presets, this will evaluate to the generator specified by
   ``configurePreset``.
 
 ``${hostSystemName}``
-
   The name of the host operating system. Contains the same value as
   :variable:`CMAKE_HOST_SYSTEM_NAME`. This is allowed in preset files
   specifying version ``3`` or above.
 
 ``${fileDir}``
-
   Path to the directory containing the preset file which contains the macro.
   This is allowed in preset files specifying version ``4`` or above.
 
 ``${dollar}``
-
   A literal dollar sign (``$``).
 
 ``${pathListSep}``
-
   Native character for separating lists of paths, such as ``:`` or ``;``.
 
   For example, by setting ``PATH`` to
@@ -1095,7 +1170,6 @@ Recognized macros include:
   This is allowed in preset files specifying version ``5`` or above.
 
 ``$env{<variable-name>}``
-
   Environment variable with name ``<variable-name>``. The variable name may
   not be an empty string. If the variable is defined in the ``environment``
   field, that value is used instead of the value from the parent environment.
@@ -1108,7 +1182,6 @@ Recognized macros include:
   the casing of environment variable names consistent.
 
 ``$penv{<variable-name>}``
-
   Similar to ``$env{<variable-name>}``, except that the value only comes from
   the parent environment, and never from the ``environment`` field. This
   allows you to prepend or append values to existing environment variables.
@@ -1118,7 +1191,6 @@ Recognized macros include:
   references.
 
 ``$vendor{<macro-name>}``
-
   An extension point for vendors to insert their own macros. CMake will not
   be able to use presets which have a ``$vendor{<macro-name>}`` macro, and
   effectively ignores such presets. However, it will still be able to use
index d88322c..b17be82 100644 (file)
@@ -90,6 +90,7 @@ Properties on Directories
    /prop_dir/RULE_LAUNCH_LINK
    /prop_dir/SOURCE_DIR
    /prop_dir/SUBDIRECTORIES
+   /prop_dir/SYSTEM
    /prop_dir/TESTS
    /prop_dir/TEST_INCLUDE_FILES
    /prop_dir/VARIABLES
@@ -184,6 +185,16 @@ Properties on Targets
    /prop_tgt/CUDA_STANDARD
    /prop_tgt/CUDA_STANDARD_REQUIRED
    /prop_tgt/CXX_EXTENSIONS
+   /prop_tgt/CXX_MODULE_DIRS
+   /prop_tgt/CXX_MODULE_DIRS_NAME
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SET
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME
+   /prop_tgt/CXX_MODULE_HEADER_UNIT_SETS
+   /prop_tgt/CXX_MODULE_SET
+   /prop_tgt/CXX_MODULE_SET_NAME
+   /prop_tgt/CXX_MODULE_SETS
    /prop_tgt/CXX_STANDARD
    /prop_tgt/CXX_STANDARD_REQUIRED
    /prop_tgt/DEBUG_POSTFIX
@@ -202,6 +213,7 @@ Properties on Targets
    /prop_tgt/EXCLUDE_FROM_DEFAULT_BUILD_CONFIG
    /prop_tgt/EXPORT_COMPILE_COMMANDS
    /prop_tgt/EXPORT_NAME
+   /prop_tgt/EXPORT_NO_SYSTEM
    /prop_tgt/EXPORT_PROPERTIES
    /prop_tgt/FOLDER
    /prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES
@@ -262,6 +274,8 @@ Properties on Targets
    /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
    /prop_tgt/INTERFACE_COMPILE_FEATURES
    /prop_tgt/INTERFACE_COMPILE_OPTIONS
+   /prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS
+   /prop_tgt/INTERFACE_CXX_MODULE_SETS
    /prop_tgt/INTERFACE_HEADER_SETS
    /prop_tgt/INTERFACE_HEADER_SETS_TO_VERIFY
    /prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
@@ -327,6 +341,7 @@ Properties on Targets
    /prop_tgt/MACOSX_RPATH
    /prop_tgt/MANUALLY_ADDED_DEPENDENCIES
    /prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
+   /prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT
    /prop_tgt/MSVC_RUNTIME_LIBRARY
    /prop_tgt/NAME
    /prop_tgt/NO_SONAME
@@ -342,8 +357,8 @@ Properties on Targets
    /prop_tgt/OSX_ARCHITECTURES_CONFIG
    /prop_tgt/OUTPUT_NAME
    /prop_tgt/OUTPUT_NAME_CONFIG
-   /prop_tgt/PCH_WARN_INVALID
    /prop_tgt/PCH_INSTANTIATE_TEMPLATES
+   /prop_tgt/PCH_WARN_INVALID
    /prop_tgt/PDB_NAME
    /prop_tgt/PDB_NAME_CONFIG
    /prop_tgt/PDB_OUTPUT_DIRECTORY
@@ -375,6 +390,7 @@ Properties on Targets
    /prop_tgt/Swift_LANGUAGE_VERSION
    /prop_tgt/Swift_MODULE_DIRECTORY
    /prop_tgt/Swift_MODULE_NAME
+   /prop_tgt/SYSTEM
    /prop_tgt/TYPE
    /prop_tgt/UNITY_BUILD
    /prop_tgt/UNITY_BUILD_BATCH_SIZE
@@ -444,13 +460,17 @@ Properties on Targets
    /prop_tgt/XCODE_SCHEME_ARGUMENTS
    /prop_tgt/XCODE_SCHEME_DEBUG_AS_ROOT
    /prop_tgt/XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
-   /prop_tgt/XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
    /prop_tgt/XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
    /prop_tgt/XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
    /prop_tgt/XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
+   /prop_tgt/XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+   /prop_tgt/XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
+   /prop_tgt/XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
    /prop_tgt/XCODE_SCHEME_ENVIRONMENT
    /prop_tgt/XCODE_SCHEME_EXECUTABLE
    /prop_tgt/XCODE_SCHEME_GUARD_MALLOC
+   /prop_tgt/XCODE_SCHEME_LAUNCH_CONFIGURATION
+   /prop_tgt/XCODE_SCHEME_LAUNCH_MODE
    /prop_tgt/XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
    /prop_tgt/XCODE_SCHEME_MALLOC_GUARD_EDGES
    /prop_tgt/XCODE_SCHEME_MALLOC_SCRIBBLE
index f156f95..d0d6ea0 100644 (file)
@@ -14,7 +14,7 @@ CMake can find and use Qt 4 and Qt 5 libraries.  The Qt 4 libraries are found
 by the :module:`FindQt4` find-module shipped with CMake, whereas the
 Qt 5 libraries are found using "Config-file Packages" shipped with Qt 5. See
 :manual:`cmake-packages(7)` for more information about CMake packages, and
-see `the Qt cmake manual <http://qt-project.org/doc/qt-5/cmake-manual.html>`_
+see `the Qt cmake manual <https://contribute.qt-project.org/doc/qt-5/cmake-manual.html>`_
 for your Qt version.
 
 Qt 4 and Qt 5 may be used together in the same
index e194df0..8a83807 100644 (file)
@@ -17,6 +17,9 @@ determines the toolchain for host builds based on system introspection and
 defaults. In cross-compiling scenarios, a toolchain file may be specified
 with information about compiler and utility paths.
 
+.. versionadded:: 3.19
+  One may use :manual:`cmake-presets(7)` to specify toolchain files.
+
 Languages
 =========
 
@@ -58,20 +61,24 @@ Variables and Properties
 ========================
 
 Several variables relate to the language components of a toolchain which are
-enabled. :variable:`CMAKE_<LANG>_COMPILER` is the full path to the compiler used
-for ``<LANG>``. :variable:`CMAKE_<LANG>_COMPILER_ID` is the identifier used
-by CMake for the compiler and :variable:`CMAKE_<LANG>_COMPILER_VERSION` is the
-version of the compiler.
-
-The :variable:`CMAKE_<LANG>_FLAGS` variables and the configuration-specific
-equivalents contain flags that will be added to the compile command when
-compiling a file of a particular language.
-
-As the linker is invoked by the compiler driver, CMake needs a way to determine
-which compiler to use to invoke the linker. This is calculated by the
-:prop_sf:`LANGUAGE` of source files in the target, and in the case of static
-libraries, the language of the dependent libraries. The choice CMake makes may
-be overridden with the :prop_tgt:`LINKER_LANGUAGE` target property.
+enabled:
+
+:variable:`CMAKE_<LANG>_COMPILER`
+  The full path to the compiler used for ``<LANG>``
+:variable:`CMAKE_<LANG>_COMPILER_ID`
+  The compiler identifier used by CMake
+:variable:`CMAKE_<LANG>_COMPILER_VERSION`
+  The version of the compiler.
+:variable:`CMAKE_<LANG>_FLAGS`
+  The variables and the configuration-specific equivalents contain flags that
+  will be added to the compile command when compiling a file of a particular
+  language.
+
+CMake needs a way to determine which compiler to use to invoke the linker.
+This is determined by the :prop_sf:`LANGUAGE` property of source files of the
+:manual:`target <cmake-buildsystem(7)>`, and in the case of static libraries,
+the ``LANGUAGE`` of the dependent libraries. The choice CMake makes may be overridden
+with the :prop_tgt:`LINKER_LANGUAGE` target property.
 
 Toolchain Features
 ==================
@@ -96,7 +103,8 @@ Cross Compiling
 ===============
 
 If :manual:`cmake(1)` is invoked with the command line parameter
-``--toolchain path/to/file`` or ``-DCMAKE_TOOLCHAIN_FILE=path/to/file``, the
+:option:`--toolchain path/to/file <cmake --toolchain>` or
+:option:`-DCMAKE_TOOLCHAIN_FILE=path/to/file <cmake -D>`, the
 file will be loaded early to set values for the compilers.
 The :variable:`CMAKE_CROSSCOMPILING` variable is set to true when CMake is
 cross-compiling.
@@ -132,24 +140,24 @@ as:
   set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
   set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
 
-The :variable:`CMAKE_SYSTEM_NAME` is the CMake-identifier of the target platform
-to build for.
-
-The :variable:`CMAKE_SYSTEM_PROCESSOR` is the CMake-identifier of the target architecture
-to build for.
+Where:
 
-The :variable:`CMAKE_SYSROOT` is optional, and may be specified if a sysroot
-is available.
-
-The :variable:`CMAKE_STAGING_PREFIX` is also optional. It may be used to specify
-a path on the host to install to. The :variable:`CMAKE_INSTALL_PREFIX` is always
-the runtime installation location, even when cross-compiling.
-
-The :variable:`CMAKE_<LANG>_COMPILER` variables may be set to full paths, or to
-names of compilers to search for in standard locations.   For toolchains that
-do not support linking binaries without custom flags or scripts one may set
-the :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` variable to ``STATIC_LIBRARY``
-to tell CMake not to try to link executables during its checks.
+:variable:`CMAKE_SYSTEM_NAME`
+  is the CMake-identifier of the target platform to build for.
+:variable:`CMAKE_SYSTEM_PROCESSOR`
+  is the CMake-identifier of the target architecture.
+:variable:`CMAKE_SYSROOT`
+  is optional, and may be specified if a sysroot is available.
+:variable:`CMAKE_STAGING_PREFIX`
+  is also optional. It may be used to specify a path on the host to install to.
+  The :variable:`CMAKE_INSTALL_PREFIX` is always the runtime installation
+  location, even when cross-compiling.
+:variable:`CMAKE_<LANG>_COMPILER`
+  variable may be set to full paths, or to names of compilers to search for
+  in standard locations.  For toolchains that do not support linking binaries
+  without custom flags or scripts one may set the
+  :variable:`CMAKE_TRY_COMPILE_TARGET_TYPE` variable to ``STATIC_LIBRARY`` to
+  tell CMake not to try to link executables during its checks.
 
 CMake ``find_*`` commands will look in the sysroot, and the :variable:`CMAKE_FIND_ROOT_PATH`
 entries by default in all cases, as well as looking in the host system root prefix.
index 7c8a7fa..cd46615 100644 (file)
@@ -128,9 +128,9 @@ Variables that Provide Information
    /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR
    /variable/CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE
    /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION
-   /variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION
    /variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
    /variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
+   /variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM
    /variable/CMAKE_XCODE_BUILD_SYSTEM
@@ -207,9 +207,9 @@ Variables that Change Behavior
    /variable/CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
    /variable/CMAKE_FIND_ROOT_PATH_MODE_PROGRAM
    /variable/CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH
-   /variable/CMAKE_FIND_USE_INSTALL_PREFIX
    /variable/CMAKE_FIND_USE_CMAKE_PATH
    /variable/CMAKE_FIND_USE_CMAKE_SYSTEM_PATH
+   /variable/CMAKE_FIND_USE_INSTALL_PREFIX
    /variable/CMAKE_FIND_USE_PACKAGE_REGISTRY
    /variable/CMAKE_FIND_USE_PACKAGE_ROOT_PATH
    /variable/CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH
@@ -228,12 +228,12 @@ Variables that Change Behavior
    /variable/CMAKE_LIBRARY_PATH
    /variable/CMAKE_LINK_DIRECTORIES_BEFORE
    /variable/CMAKE_LINK_LIBRARIES_ONLY_TARGETS
-   /variable/CMAKE_MFC_FLAG
    /variable/CMAKE_MAXIMUM_RECURSION_DEPTH
    /variable/CMAKE_MESSAGE_CONTEXT
    /variable/CMAKE_MESSAGE_CONTEXT_SHOW
    /variable/CMAKE_MESSAGE_INDENT
    /variable/CMAKE_MESSAGE_LOG_LEVEL
+   /variable/CMAKE_MFC_FLAG
    /variable/CMAKE_MODULE_PATH
    /variable/CMAKE_POLICY_DEFAULT_CMPNNNN
    /variable/CMAKE_POLICY_WARNING_CMPNNNN
@@ -272,12 +272,16 @@ Variables that Change Behavior
    /variable/CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER
    /variable/CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN
    /variable/CMAKE_XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING
-   /variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
    /variable/CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
    /variable/CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
    /variable/CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
+   /variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+   /variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
+   /variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
    /variable/CMAKE_XCODE_SCHEME_ENVIRONMENT
    /variable/CMAKE_XCODE_SCHEME_GUARD_MALLOC
+   /variable/CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION
+   /variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE
    /variable/CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
    /variable/CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES
    /variable/CMAKE_XCODE_SCHEME_MALLOC_SCRIBBLE
@@ -300,10 +304,13 @@ Variables that Describe the System
    /variable/ANDROID
    /variable/APPLE
    /variable/BORLAND
+   /variable/BSD
    /variable/CMAKE_ANDROID_NDK_VERSION
    /variable/CMAKE_CL_64
    /variable/CMAKE_COMPILER_2005
    /variable/CMAKE_HOST_APPLE
+   /variable/CMAKE_HOST_BSD
+   /variable/CMAKE_HOST_LINUX
    /variable/CMAKE_HOST_SOLARIS
    /variable/CMAKE_HOST_SYSTEM
    /variable/CMAKE_HOST_SYSTEM_NAME
@@ -321,6 +328,7 @@ Variables that Describe the System
    /variable/CYGWIN
    /variable/GHSMULTI
    /variable/IOS
+   /variable/LINUX
    /variable/MINGW
    /variable/MSVC
    /variable/MSVC10
@@ -412,19 +420,19 @@ Variables that Control the Build
    /variable/CMAKE_DEBUG_POSTFIX
    /variable/CMAKE_DEFAULT_BUILD_TYPE
    /variable/CMAKE_DEFAULT_CONFIGS
-   /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_DEPENDS_USE_COMPILER
+   /variable/CMAKE_DISABLE_PRECOMPILE_HEADERS
    /variable/CMAKE_ENABLE_EXPORTS
    /variable/CMAKE_EXE_LINKER_FLAGS
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_EXE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_EXE_LINKER_FLAGS_INIT
    /variable/CMAKE_FOLDER
-   /variable/CMAKE_FRAMEWORK
-   /variable/CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG
    /variable/CMAKE_Fortran_FORMAT
    /variable/CMAKE_Fortran_MODULE_DIRECTORY
    /variable/CMAKE_Fortran_PREPROCESS
+   /variable/CMAKE_FRAMEWORK
+   /variable/CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_CONFIG
    /variable/CMAKE_GHS_NO_SOURCE_GROUP_FILE
    /variable/CMAKE_GLOBAL_AUTOGEN_TARGET
    /variable/CMAKE_GLOBAL_AUTOGEN_TARGET_NAME
@@ -445,14 +453,14 @@ Variables that Control the Build
    /variable/CMAKE_LANG_CPPCHECK
    /variable/CMAKE_LANG_CPPLINT
    /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE
-   /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE
-   /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
-   /variable/CMAKE_LANG_LINKER_LAUNCHER
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
+   /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE
+   /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
+   /variable/CMAKE_LANG_LINKER_LAUNCHER
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -475,8 +483,9 @@ Variables that Control the Build
    /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG
    /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_MODULE_LINKER_FLAGS_INIT
-   /variable/CMAKE_MSVCIDE_RUN_PATH
+   /variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
    /variable/CMAKE_MSVC_RUNTIME_LIBRARY
+   /variable/CMAKE_MSVCIDE_RUN_PATH
    /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
    /variable/CMAKE_NO_BUILTIN_CHRPATH
    /variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
@@ -484,8 +493,8 @@ Variables that Control the Build
    /variable/CMAKE_OSX_ARCHITECTURES
    /variable/CMAKE_OSX_DEPLOYMENT_TARGET
    /variable/CMAKE_OSX_SYSROOT
-   /variable/CMAKE_PCH_WARN_INVALID
    /variable/CMAKE_PCH_INSTANTIATE_TEMPLATES
+   /variable/CMAKE_PCH_WARN_INVALID
    /variable/CMAKE_PDB_OUTPUT_DIRECTORY
    /variable/CMAKE_PDB_OUTPUT_DIRECTORY_CONFIG
    /variable/CMAKE_PLATFORM_NO_VERSIONED_SONAME
@@ -502,6 +511,7 @@ Variables that Control the Build
    /variable/CMAKE_STATIC_LINKER_FLAGS_CONFIG
    /variable/CMAKE_STATIC_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_STATIC_LINKER_FLAGS_INIT
+   /variable/CMAKE_TASKING_TOOLSET
    /variable/CMAKE_TRY_COMPILE_CONFIGURATION
    /variable/CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES
    /variable/CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
@@ -538,6 +548,10 @@ Variables for Languages
 .. toctree::
    :maxdepth: 1
 
+   /variable/CMAKE_C_COMPILE_FEATURES
+   /variable/CMAKE_C_EXTENSIONS
+   /variable/CMAKE_C_STANDARD
+   /variable/CMAKE_C_STANDARD_REQUIRED
    /variable/CMAKE_COMPILER_IS_GNUCC
    /variable/CMAKE_COMPILER_IS_GNUCXX
    /variable/CMAKE_COMPILER_IS_GNUG77
@@ -552,10 +566,6 @@ Variables for Languages
    /variable/CMAKE_CXX_EXTENSIONS
    /variable/CMAKE_CXX_STANDARD
    /variable/CMAKE_CXX_STANDARD_REQUIRED
-   /variable/CMAKE_C_COMPILE_FEATURES
-   /variable/CMAKE_C_EXTENSIONS
-   /variable/CMAKE_C_STANDARD
-   /variable/CMAKE_C_STANDARD_REQUIRED
    /variable/CMAKE_Fortran_MODDIR_DEFAULT
    /variable/CMAKE_Fortran_MODDIR_FLAG
    /variable/CMAKE_Fortran_MODOUT_FLAG
@@ -573,6 +583,7 @@ Variables for Languages
    /variable/CMAKE_LANG_ARCHIVE_CREATE
    /variable/CMAKE_LANG_ARCHIVE_FINISH
    /variable/CMAKE_LANG_BYTE_ORDER
+   /variable/CMAKE_LANG_COMPILE_OBJECT
    /variable/CMAKE_LANG_COMPILER
    /variable/CMAKE_LANG_COMPILER_EXTERNAL_TOOLCHAIN
    /variable/CMAKE_LANG_COMPILER_ID
@@ -580,7 +591,6 @@ Variables for Languages
    /variable/CMAKE_LANG_COMPILER_PREDEFINES_COMMAND
    /variable/CMAKE_LANG_COMPILER_TARGET
    /variable/CMAKE_LANG_COMPILER_VERSION
-   /variable/CMAKE_LANG_COMPILE_OBJECT
    /variable/CMAKE_LANG_CREATE_SHARED_LIBRARY
    /variable/CMAKE_LANG_CREATE_SHARED_MODULE
    /variable/CMAKE_LANG_CREATE_STATIC_LIBRARY
@@ -653,12 +663,12 @@ Variables for CTest
    /variable/CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS
    /variable/CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS
    /variable/CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE
-   /variable/CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION
    /variable/CTEST_CUSTOM_MEMCHECK_IGNORE
    /variable/CTEST_CUSTOM_POST_MEMCHECK
    /variable/CTEST_CUSTOM_POST_TEST
    /variable/CTEST_CUSTOM_PRE_MEMCHECK
    /variable/CTEST_CUSTOM_PRE_TEST
+   /variable/CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION
    /variable/CTEST_CUSTOM_TESTS_IGNORE
    /variable/CTEST_CUSTOM_WARNING_EXCEPTION
    /variable/CTEST_CUSTOM_WARNING_MATCH
@@ -694,9 +704,9 @@ Variables for CTest
    /variable/CTEST_SCP_COMMAND
    /variable/CTEST_SCRIPT_DIRECTORY
    /variable/CTEST_SITE
+   /variable/CTEST_SOURCE_DIRECTORY
    /variable/CTEST_SUBMIT_INACTIVITY_TIMEOUT
    /variable/CTEST_SUBMIT_URL
-   /variable/CTEST_SOURCE_DIRECTORY
    /variable/CTEST_SVN_COMMAND
    /variable/CTEST_SVN_OPTIONS
    /variable/CTEST_SVN_UPDATE_OPTIONS
index 38105dd..b31ad11 100644 (file)
@@ -9,8 +9,7 @@ Synopsis
 .. parsed-literal::
 
  `Generate a Project Buildsystem`_
-  cmake [<options>] <path-to-source>
-  cmake [<options>] <path-to-existing-build>
+  cmake [<options>] <path-to-source | path-to-existing-build>
   cmake [<options>] -S <path-to-source> -B <path-to-build>
 
  `Build a Project`_
@@ -23,7 +22,7 @@ Synopsis
   cmake --open <dir>
 
  `Run a Script`_
-  cmake [{-D <var>=<value>}...] -P <cmake-script-file>
+  cmake [-D <var>=<value>]... -P <cmake-script-file>
 
  `Run a Command-Line Tool`_
   cmake -E <command> [<options>]
@@ -31,6 +30,9 @@ Synopsis
  `Run the Find-Package Tool`_
   cmake --find-package [<options>]
 
+ `Run a Workflow Preset`_
+  cmake --workflow [<options>]
+
  `View Help`_
   cmake --help[-<topic>]
 
@@ -96,9 +98,10 @@ Build Tree
 Generator
   This chooses the kind of buildsystem to generate.  See the
   :manual:`cmake-generators(7)` manual for documentation of all generators.
-  Run ``cmake --help`` to see a list of generators available locally.
-  Optionally use the ``-G`` option below to specify a generator, or simply
-  accept the default CMake chooses for the current platform.
+  Run :option:`cmake --help` to see a list of generators available locally.
+  Optionally use the :option:`-G <cmake -G>` option below to specify a
+  generator, or simply accept the default CMake chooses for the current
+  platform.
 
   When using one of the :ref:`Command-Line Build Tool Generators`
   CMake expects that the environment needed by the compiler toolchain
@@ -139,6 +142,9 @@ source and build trees and generate a buildsystem:
     $ 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
@@ -152,11 +158,11 @@ source and build trees and generate a buildsystem:
 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.
-Paths specified with ``-S`` or ``-B`` are always classified as source or
-build trees, respectively.  Paths specified with plain arguments are
-classified based on their content and the types of paths given earlier.
-If only one type of path is given, the current working directory (cwd)
-is used for the other.  For example:
+Paths specified with :option:`-S <cmake -S>` or :option:`-B <cmake -B>`
+are always classified as source or build trees, respectively.  Paths
+specified with plain arguments are classified based on their content
+and the types of paths given earlier.  If only one type of path is given,
+the current working directory (cwd) is used for the other.  For example:
 
 ============================== ============ ===========
  Command Line                   Source Dir   Build Dir
@@ -195,51 +201,60 @@ automatically choosing and invoking the appropriate native build tool.
 Options
 -------
 
+.. program:: cmake
+
 .. include:: OPTIONS_BUILD.txt
 
-``--fresh``
+.. option:: --fresh
+
  .. versionadded:: 3.24
 
  Perform a fresh configuration of the build tree.
  This removes any existing ``CMakeCache.txt`` file and associated
  ``CMakeFiles/`` directory, and recreates them from scratch.
 
-``-L[A][H]``
+.. option:: -L[A][H]
+
  List non-advanced cached variables.
 
  List ``CACHE`` variables will run CMake and list all the variables from
  the CMake ``CACHE`` that are not marked as ``INTERNAL`` or :prop_cache:`ADVANCED`.
  This will effectively display current CMake settings, which can then be
- changed with ``-D`` option.  Changing some of the variables may result
- in more variables being created.  If ``A`` is specified, then it will
- display also advanced variables.  If ``H`` is specified, it will also
+ changed with :option:`-D <cmake -D>` option.  Changing some of the variables
+ may result in more variables being created.  If ``A`` is specified, then it
will display also advanced variables.  If ``H`` is specified, it will also
  display help for each variable.
 
-``-N``
+.. option:: -N
+
  View mode only.
 
  Only load the cache.  Do not actually run configure and generate
  steps.
 
-``--graphviz=[file]``
+.. option:: --graphviz=<file>
+
  Generate graphviz of dependencies, see :module:`CMakeGraphVizOptions` for more.
 
  Generate a graphviz input file that will contain all the library and
  executable dependencies in the project.  See the documentation for
  :module:`CMakeGraphVizOptions` for more details.
 
-``--system-information [file]``
+.. option:: --system-information [file]
+
  Dump information about this system.
 
  Dump a wide range of information about the current system.  If run
  from the top of a binary tree for a CMake project it will dump
  additional information such as the cache, log files etc.
 
-``--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>``
- Set the log level.
+.. option:: --log-level=<level>
+
+ Set the log ``<level>``.
 
  The :command:`message` command will only output messages of the specified
- log level or higher.  The default log level is ``STATUS``.
+ log level or higher.  The valid log levels are ``ERROR``, ``WARNING``,
+ ``NOTICE``, ``STATUS`` (default), ``VERBOSE``, ``DEBUG``, or ``TRACE``.
 
  To make a log level persist between CMake runs, set
  :variable:`CMAKE_MESSAGE_LOG_LEVEL` as a cache variable instead.
@@ -249,7 +264,12 @@ Options
  For backward compatibility reasons, ``--loglevel`` is also accepted as a
  synonym for this option.
 
-``--log-context``
+ .. versionadded:: 3.25
+   See the :command:`cmake_language` command for a way to
+   :ref:`query the current message logging level <query_message_log_level>`.
+
+.. option:: --log-context
+
  Enable the :command:`message` command outputting context attached to each
  message.
 
@@ -259,24 +279,36 @@ Options
  When this command line option is given, :variable:`CMAKE_MESSAGE_CONTEXT_SHOW`
  is ignored.
 
-``--debug-trycompile``
- Do not delete the :command:`try_compile` build tree.
- Only useful on one :command:`try_compile` at a time.
+.. option:: --debug-trycompile
+
+ Do not delete the files and directories created for
+ :command:`try_compile` / :command:`try_run` calls.
+ This is useful in debugging failed checks.
+
+ Note that some uses of :command:`try_compile` may use the same build tree,
+ which will limit the usefulness of this option if a project executes more
+ than one :command:`try_compile`.  For example, such uses may change results
+ as artifacts from a previous try-compile may cause a different test to either
+ pass or fail incorrectly.  This option is best used only when debugging.
+
+ (With respect to the preceding, the :command:`try_run` command
+ is effectively a :command:`try_compile`.  Any combination of the two
+ is subject to the potential issues described.)
 
- Do not delete the files and directories created for :command:`try_compile`
- calls.  This is useful in debugging failed try_compiles.  It may
- however change the results of the try-compiles as old junk from a
- previous try-compile may cause a different test to either pass or
- fail incorrectly.  This option is best used for one try-compile at a
- time, and only when debugging.
+ .. versionadded:: 3.25
+
+   When this option is enabled, every try-compile check prints a log
+   message reporting the directory in which the check is performed.
+
+.. option:: --debug-output
 
-``--debug-output``
  Put cmake in a debug mode.
 
  Print extra information during the cmake run like stack traces with
  :command:`message(SEND_ERROR)` calls.
 
-``--debug-find``
+.. option:: --debug-find
+
  Put cmake find commands in a debug mode.
 
  Print extra find call information during the cmake run to standard
@@ -284,32 +316,39 @@ Options
  See also the :variable:`CMAKE_FIND_DEBUG_MODE` variable for debugging
  a more local part of the project.
 
-``--debug-find-pkg=<pkg>[,...]``
+.. option:: --debug-find-pkg=<pkg>[,...]
+
  Put cmake find commands in a debug mode when running under calls
  to :command:`find_package(\<pkg\>) <find_package>`, where ``<pkg>``
  is an entry in the given comma-separated list of case-sensitive package
  names.
 
- Like ``--debug-find``, but limiting scope to the specified packages.
+ Like :option:`--debug-find <cmake --debug-find>`, but limiting scope
+ to the specified packages.
+
+.. option:: --debug-find-var=<var>[,...]
 
-``--debug-find-var=<var>[,...]``
  Put cmake find commands in a debug mode when called with ``<var>``
  as the result variable, where ``<var>`` is an entry in the given
  comma-separated list.
 
- Like ``--debug-find``, but limiting scope to the specified variable names.
+ Like :option:`--debug-find <cmake --debug-find>`, but limiting scope
+ to the specified variable names.
+
+.. option:: --trace
 
-``--trace``
  Put cmake in trace mode.
 
  Print a trace of all calls made and from where.
 
-``--trace-expand``
+.. option:: --trace-expand
+
  Put cmake in trace mode.
 
- Like ``--trace``, but with variables expanded.
+ Like :option:`--trace <cmake --trace>`, but with variables expanded.
+
+.. option:: --trace-format=<format>
 
-``--trace-format=<format>``
  Put cmake in trace mode and sets the trace output format.
 
  ``<format>`` can be one of the following values.
@@ -395,46 +434,57 @@ Options
        Indicates the version of the JSON format. The version has a
        major and minor components following semantic version conventions.
 
-``--trace-source=<file>``
+.. option:: --trace-source=<file>
+
  Put cmake in trace mode, but output only lines of a specified file.
 
  Multiple options are allowed.
 
-``--trace-redirect=<file>``
+.. option:: --trace-redirect=<file>
+
  Put cmake in trace mode and redirect trace output to a file instead of stderr.
 
-``--warn-uninitialized``
+.. option:: --warn-uninitialized
+
  Warn about uninitialized values.
 
  Print a warning when an uninitialized variable is used.
 
-``--warn-unused-vars``
+.. option:: --warn-unused-vars
+
  Does nothing.  In CMake versions 3.2 and below this enabled warnings about
  unused variables.  In CMake versions 3.3 through 3.18 the option was broken.
  In CMake 3.19 and above the option has been removed.
 
-``--no-warn-unused-cli``
+.. option:: --no-warn-unused-cli
+
  Don't warn about command line options.
 
  Don't find variables that are declared on the command line, but not
  used.
 
-``--check-system-vars``
+.. option:: --check-system-vars
+
  Find problems with variable usage in system files.
 
  Normally, unused and uninitialized variables are searched for only
  in :variable:`CMAKE_SOURCE_DIR` and :variable:`CMAKE_BINARY_DIR`.
  This flag tells CMake to warn about other files as well.
 
-``--compile-no-warning-as-error``
+.. option:: --compile-no-warning-as-error
+
  Ignore target property :prop_tgt:`COMPILE_WARNING_AS_ERROR` and variable
  :variable:`CMAKE_COMPILE_WARNING_AS_ERROR`, preventing warnings from being
  treated as errors on compile.
 
-``--profiling-output=<path>``
- Used in conjunction with ``--profiling-format`` to output to a given path.
+.. option:: --profiling-output=<path>
+
+ Used in conjunction with
+ :option:`--profiling-format <cmake --profiling-format>` to output to a
+ given path.
+
+.. option:: --profiling-format=<file>
 
-``--profiling-format=<file>``
  Enable the output of profiling data of CMake script in the given format.
 
  This can aid performance analysis of CMake scripts executed. Third party
@@ -445,7 +495,8 @@ Options
  about:tracing tab of Google Chrome or using a plugin for a tool like Trace
  Compass.
 
-``--preset <preset>``, ``--preset=<preset>``
+.. option:: --preset <preset>, --preset=<preset>
+
  Reads a :manual:`preset <cmake-presets(7)>` from
  ``<path-to-source>/CMakePresets.json`` and
  ``<path-to-source>/CMakeUserPresets.json``. The preset may specify the
@@ -461,15 +512,20 @@ Options
  a variable called ``MYVAR`` to ``1``, but the user sets it to ``2`` with a
  ``-D`` argument, the value ``2`` is preferred.
 
-``--list-presets, --list-presets=<[configure | build | test | all]>``
- Lists the available presets. If no option is specified only configure presets
- will be listed. The current working directory must contain CMake preset files.
+.. option:: --list-presets[=<type>]
+
+ Lists the available presets of the specified ``<type>``.  Valid values for
+ ``<type>`` are ``configure``, ``build``, ``test``, ``package``, or ``all``.
+ If ``<type>`` is omitted, ``configure`` is assumed.  The current working
+ directory must contain CMake preset files.
 
 .. _`Build Tool Mode`:
 
 Build a Project
 ===============
 
+.. program:: cmake
+
 CMake provides a command-line signature to build an already-generated
 project binary tree:
 
@@ -481,21 +537,29 @@ project binary tree:
 This abstracts a native build tool's command-line interface with the
 following options:
 
-``--build <dir>``
+.. option:: --build <dir>
+
   Project binary directory to be built.  This is required (unless a preset
   is specified) and must be first.
 
-``--preset <preset>``, ``--preset=<preset>``
+.. program:: cmake--build
+
+.. option:: --preset <preset>, --preset=<preset>
+
   Use a build preset to specify build options. The project binary directory
   is inferred from the ``configurePreset`` key. The current working directory
   must contain CMake preset files.
   See :manual:`preset <cmake-presets(7)>` for more details.
 
-``--list-presets``
+.. option:: --list-presets
+
   Lists the available build presets. The current working directory must
   contain CMake preset files.
 
-``--parallel [<jobs>], -j [<jobs>]``
+.. option:: -j [<jobs>], --parallel [<jobs>]
+
+  .. versionadded:: 3.12
+
   The maximum number of concurrent processes to use when building.
   If ``<jobs>`` is omitted the native build tool's default number is used.
 
@@ -505,24 +569,29 @@ following options:
   Some native build tools always build in parallel.  The use of ``<jobs>``
   value of ``1`` can be used to limit to a single job.
 
-``--target <tgt>..., -t <tgt>...``
+.. option:: -t <tgt>..., --target <tgt>...
+
   Build ``<tgt>`` instead of the default target.  Multiple targets may be
   given, separated by spaces.
 
-``--config <cfg>``
+.. option:: --config <cfg>
+
   For multi-configuration tools, choose configuration ``<cfg>``.
 
-``--clean-first``
+.. option:: --clean-first
+
   Build target ``clean`` first, then build.
-  (To clean only, use ``--target clean``.)
+  (To clean only, use :option:`--target clean <cmake--build --target>`.)
+
+.. option:: --resolve-package-references=<value>
 
-``--resolve-package-references=<on|off|only>``
   .. versionadded:: 3.23
 
   Resolve remote package references from external package managers (e.g. NuGet)
-  before build. When set to ``on`` (default), packages will be restored before
-  building a target. When set to ``only``, the packages will be restored, but no
-  build will be performed. When set to ``off``, no packages will be restored.
+  before build. When ``<value>`` is set to ``on`` (default), packages will be
+  restored before building a target.  When ``<value>`` is set to ``only``, the
+  packages will be restored, but no build will be performed.  When
+  ``<value>`` is set to ``off``, no packages will be restored.
 
   If the target does not define any package references, this option does nothing.
 
@@ -539,10 +608,12 @@ following options:
   are restored using NuGet. It can be disabled by setting the
   ``CMAKE_VS_NUGET_PACKAGE_RESTORE`` variable to ``OFF``.
 
-``--use-stderr``
+.. option:: --use-stderr
+
   Ignored.  Behavior is default in CMake >= 3.0.
 
-``--verbose, -v``
+.. option:: -v, --verbose
+
   Enable verbose output - if supported - including the build commands to be
   executed.
 
@@ -550,14 +621,17 @@ following options:
   :variable:`CMAKE_VERBOSE_MAKEFILE` cached variable is set.
 
 
-``--``
+.. option:: --
+
   Pass remaining options to the native tool.
 
-Run ``cmake --build`` with no options for quick help.
+Run :option:`cmake --build` with no options for quick help.
 
 Install a Project
 =================
 
+.. program:: cmake
+
 CMake provides a command-line signature to install an already-generated
 project binary tree:
 
@@ -569,34 +643,45 @@ This may be used after building a project to run installation without
 using the generated build system or the native build tool.
 The options are:
 
-``--install <dir>``
+.. option:: --install <dir>
+
   Project binary directory to install. This is required and must be first.
 
-``--config <cfg>``
+.. program:: cmake--install
+
+.. option:: --config <cfg>
+
   For multi-configuration generators, choose configuration ``<cfg>``.
 
-``--component <comp>``
+.. option:: --component <comp>
+
   Component-based install. Only install component ``<comp>``.
 
-``--default-directory-permissions <permissions>``
+.. option:: --default-directory-permissions <permissions>
+
   Default directory install permissions. Permissions in format ``<u=rwx,g=rx,o=rx>``.
 
-``--prefix <prefix>``
+.. option:: --prefix <prefix>
+
   Override the installation prefix, :variable:`CMAKE_INSTALL_PREFIX`.
 
-``--strip``
+.. option:: --strip
+
   Strip before installing.
 
-``-v, --verbose``
+.. option:: -v, --verbose
+
   Enable verbose output.
 
   This option can be omitted if :envvar:`VERBOSE` environment variable is set.
 
-Run ``cmake --install`` with no options for quick help.
+Run :option:`cmake --install` with no options for quick help.
 
 Open a Project
 ==============
 
+.. program:: cmake
+
 .. code-block:: shell
 
   cmake --open <dir>
@@ -610,14 +695,26 @@ supported by some generators.
 Run a Script
 ============
 
+.. program:: cmake
+
 .. code-block:: shell
 
-  cmake [{-D <var>=<value>}...] -P <cmake-script-file> [-- <unparsed-options>...]
+  cmake [-D <var>=<value>]... -P <cmake-script-file> [-- <unparsed-options>...]
+
+.. program:: cmake-P
+
+.. option:: -D <var>=<value>
+
+ Define a variable for script mode.
 
-Process the given cmake file as a script written in the CMake
-language.  No configure or generate step is performed and the cache
-is not modified.  If variables are defined using ``-D``, this must be
-done before the ``-P`` argument.
+.. program:: cmake
+
+.. option:: -P <cmake-script-file>
+
+ Process the given cmake file as a script written in the CMake
+ language.  No configure or generate step is performed and the cache
+ is not modified.  If variables are defined using ``-D``, this must be
+ done before the ``-P`` argument.
 
 Any options after ``--`` are not parsed by CMake, but they are still included
 in the set of :variable:`CMAKE_ARGV<n> <CMAKE_ARGV0>` variables passed to the
@@ -629,16 +726,24 @@ script (including the ``--`` itself).
 Run a Command-Line Tool
 =======================
 
+.. program:: cmake
+
 CMake provides builtin command-line tools through the signature
 
 .. code-block:: shell
 
   cmake -E <command> [<options>]
 
-Run ``cmake -E`` or ``cmake -E help`` for a summary of commands.
+.. option:: -E [help]
+
+  Run ``cmake -E`` or ``cmake -E help`` for a summary of commands.
+
+.. program:: cmake-E
+
 Available commands are:
 
-``capabilities``
+.. option:: capabilities
+
   .. versionadded:: 3.7
 
   Report cmake capabilities in JSON format. The output is a JSON object
@@ -648,7 +753,7 @@ Available commands are:
     A JSON object with version information. Keys are:
 
     ``string``
-      The full version string as displayed by cmake ``--version``.
+      The full version string as displayed by cmake :option:`--version <cmake --version>`.
     ``major``
       The major version number in integer form.
     ``minor``
@@ -675,7 +780,8 @@ Available commands are:
 
       Optional member that may be present when the generator supports
       platform specification via :variable:`CMAKE_GENERATOR_PLATFORM`
-      (``-A ...``).  The value is a list of platforms known to be supported.
+      (: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
       the generator.
@@ -700,30 +806,52 @@ Available commands are:
     ``true`` if cmake supports server-mode and ``false`` otherwise.
     Always false since CMake 3.20.
 
-``cat [--] <files>...``
+  ``tls``
+    .. versionadded:: 3.25
+
+    ``true`` if TLS support is enabled and ``false`` otherwise.
+
+.. option:: cat [--] <files>...
+
   .. versionadded:: 3.18
 
   Concatenate files and print on the standard output.
 
-  .. versionadded:: 3.24
+  .. program:: cmake-E_cat
+
+  .. option:: --
+
+    .. versionadded:: 3.24
+
     Added support for the double dash argument ``--``. This basic implementation
     of ``cat`` does not support any options, so using a option starting with
     ``-`` will result in an error. Use ``--`` to indicate the end of options, in
     case a file starts with ``-``.
 
-``chdir <dir> <cmd> [<arg>...]``
+.. program:: cmake-E
+
+.. option:: chdir <dir> <cmd> [<arg>...]
+
   Change the current working directory and run a command.
 
-``compare_files [--ignore-eol] <file1> <file2>``
+.. option:: compare_files [--ignore-eol] <file1> <file2>
+
   Check if ``<file1>`` is same as ``<file2>``. If files are the same,
   then returns ``0``, if not it returns ``1``.  In case of invalid
   arguments, it returns 2.
 
-  .. versionadded:: 3.14
-    The ``--ignore-eol`` option implies line-wise comparison and ignores
-    LF/CRLF differences.
+  .. program:: cmake-E_compare_files
+
+  .. option:: --ignore-eol
+
+    .. versionadded:: 3.14
+
+    The option implies line-wise comparison and ignores LF/CRLF differences.
+
+.. program:: cmake-E
+
+.. option:: copy <file>... <destination>
 
-``copy <file>... <destination>``
   Copy files to ``<destination>`` (either file or directory).
   If multiple files are specified, the ``<destination>`` must be
   directory and it must exist. Wildcards are not supported.
@@ -733,7 +861,8 @@ Available commands are:
   .. versionadded:: 3.5
     Support for multiple input files.
 
-``copy_directory <dir>... <destination>``
+.. option:: copy_directory <dir>... <destination>
+
   Copy content of ``<dir>...`` directories to ``<destination>`` directory.
   If ``<destination>`` directory does not exist it will be created.
   ``copy_directory`` does follow symlinks.
@@ -745,7 +874,8 @@ Available commands are:
     The command now fails when the source directory does not exist.
     Previously it succeeded by creating an empty destination directory.
 
-``copy_if_different <file>... <destination>``
+.. option:: copy_if_different <file>... <destination>
+
   Copy files to ``<destination>`` (either file or directory) if
   they have changed.
   If multiple files are specified, the ``<destination>`` must be
@@ -755,7 +885,8 @@ Available commands are:
   .. versionadded:: 3.5
     Support for multiple input files.
 
-``create_symlink <old> <new>``
+.. option:: create_symlink <old> <new>
+
   Create a symbolic link ``<new>`` naming ``<old>``.
 
   .. versionadded:: 3.13
@@ -764,7 +895,8 @@ Available commands are:
   .. note::
     Path to where ``<new>`` symbolic link will be created has to exist beforehand.
 
-``create_hardlink <old> <new>``
+.. option:: create_hardlink <old> <new>
+
   .. versionadded:: 3.19
 
   Create a hard link ``<new>`` naming ``<old>``.
@@ -773,31 +905,65 @@ Available commands are:
     Path to where ``<new>`` hard link will be created has to exist beforehand.
     ``<old>`` has to exist beforehand.
 
-``echo [<string>...]``
+.. option:: echo [<string>...]
+
   Displays arguments as text.
 
-``echo_append [<string>...]``
+.. option:: echo_append [<string>...]
+
   Displays arguments as text but no new line.
 
-``env [--unset=NAME ...] [NAME=VALUE ...] [--] <command> [<arg>...]``
+.. option:: env [<options>] [--] <command> [<arg>...]
+
   .. versionadded:: 3.1
 
-  Run command in a modified environment.
+  Run command in a modified environment. Options are:
+
+  .. program:: cmake-E_env
+
+  .. option:: NAME=VALUE
+
+    Replaces the current value of ``NAME`` with ``VALUE``.
+
+  .. option:: --unset=NAME
+
+    Unsets the current value of ``NAME``.
+
+  .. option:: --modify ENVIRONMENT_MODIFICATION
+
+    .. versionadded:: 3.25
+
+    Apply a single :prop_test:`ENVIRONMENT_MODIFICATION` operation to the
+    modified environment.
+
+    The ``NAME=VALUE`` and ``--unset=NAME`` options are equivalent to
+    ``--modify NAME=set:VALUE`` and ``--modify NAME=unset:``, respectively.
+    Note that ``--modify NAME=reset:`` resets ``NAME`` to the value it had
+    when ``cmake`` launched (or unsets it), not to the most recent
+    ``NAME=VALUE`` option.
+
+  .. option:: --
+
+    .. versionadded:: 3.24
 
-  .. versionadded:: 3.24
     Added support for the double dash argument ``--``. Use ``--`` to stop
     interpreting options/environment variables and treat the next argument as
     the command, even if it start with ``-`` or contains a ``=``.
 
-``environment``
+.. program:: cmake-E
+
+.. option:: environment
+
   Display the current environment variables.
 
-``false``
+.. option:: false
+
   .. versionadded:: 3.16
 
   Do nothing, with an exit code of 1.
 
-``make_directory <dir>...``
+.. option:: make_directory <dir>...
+
   Create ``<dir>`` directories.  If necessary, create parent
   directories too.  If a directory already exists it will be
   silently ignored.
@@ -805,13 +971,15 @@ Available commands are:
   .. versionadded:: 3.5
     Support for multiple input directories.
 
-``md5sum <file>...``
+.. option:: md5sum <file>...
+
   Create MD5 checksum of files in ``md5sum`` compatible format::
 
      351abe79cd3800b38cdfb25d45015a15  file1.txt
      052f86c15bbde68af55c7f7b340ab639  file2.txt
 
-``sha1sum <file>...``
+.. option:: sha1sum <file>...
+
   .. versionadded:: 3.10
 
   Create SHA1 checksum of files in ``sha1sum`` compatible format::
@@ -819,7 +987,8 @@ Available commands are:
      4bb7932a29e6f73c97bb9272f2bdc393122f86e0  file1.txt
      1df4c8f318665f9a5f2ed38f55adadb7ef9f559c  file2.txt
 
-``sha224sum <file>...``
+.. option:: sha224sum <file>...
+
   .. versionadded:: 3.10
 
   Create SHA224 checksum of files in ``sha224sum`` compatible format::
@@ -827,7 +996,8 @@ Available commands are:
      b9b9346bc8437bbda630b0b7ddfc5ea9ca157546dbbf4c613192f930  file1.txt
      6dfbe55f4d2edc5fe5c9197bca51ceaaf824e48eba0cc453088aee24  file2.txt
 
-``sha256sum <file>...``
+.. option:: sha256sum <file>...
+
   .. versionadded:: 3.10
 
   Create SHA256 checksum of files in ``sha256sum`` compatible format::
@@ -835,7 +1005,8 @@ Available commands are:
      76713b23615d31680afeb0e9efe94d47d3d4229191198bb46d7485f9cb191acc  file1.txt
      15b682ead6c12dedb1baf91231e1e89cfc7974b3787c1e2e01b986bffadae0ea  file2.txt
 
-``sha384sum <file>...``
+.. option:: sha384sum <file>...
+
   .. versionadded:: 3.10
 
   Create SHA384 checksum of files in ``sha384sum`` compatible format::
@@ -843,7 +1014,8 @@ Available commands are:
      acc049fedc091a22f5f2ce39a43b9057fd93c910e9afd76a6411a28a8f2b8a12c73d7129e292f94fc0329c309df49434  file1.txt
      668ddeb108710d271ee21c0f3acbd6a7517e2b78f9181c6a2ff3b8943af92b0195dcb7cce48aa3e17893173c0a39e23d  file2.txt
 
-``sha512sum <file>...``
+.. option:: sha512sum <file>...
+
   .. versionadded:: 3.10
 
   Create SHA512 checksum of files in ``sha512sum`` compatible format::
@@ -851,7 +1023,8 @@ Available commands are:
      2a78d7a6c5328cfb1467c63beac8ff21794213901eaadafd48e7800289afbc08e5fb3e86aa31116c945ee3d7bf2a6194489ec6101051083d1108defc8e1dba89  file1.txt
      7a0b54896fe5e70cca6dd643ad6f672614b189bf26f8153061c4d219474b05dad08c4e729af9f4b009f1a1a280cb625454bf587c690f4617c27e3aebdf3b7a2d  file2.txt
 
-``remove [-f] <file>...``
+.. option:: remove [-f] <file>...
+
   .. deprecated:: 3.17
 
   Remove the file(s). The planned behavior was that if any of the
@@ -864,7 +1037,8 @@ Available commands are:
   The implementation was buggy and always returned 0. It cannot be fixed without
   breaking backwards compatibility. Use ``rm`` instead.
 
-``remove_directory <dir>...``
+.. option:: remove_directory <dir>...
+
   .. deprecated:: 3.17
 
   Remove ``<dir>`` directories and their contents. If a directory does
@@ -877,11 +1051,13 @@ Available commands are:
   .. versionadded:: 3.16
     If ``<dir>`` is a symlink to a directory, just the symlink will be removed.
 
-``rename <oldname> <newname>``
+.. option:: rename <oldname> <newname>
+
   Rename a file or directory (on one volume). If file with the ``<newname>`` name
   already exists, then it will be silently replaced.
 
-``rm [-rRf] [--] <file|dir>...``
+.. option:: rm [-rRf] [--] <file|dir>...
+
   .. versionadded:: 3.17
 
   Remove the files ``<file>`` or directories ``<dir>``.
@@ -892,22 +1068,29 @@ Available commands are:
   situations instead. Use ``--`` to stop interpreting options and treat all
   remaining arguments as paths, even if they start with ``-``.
 
-``server``
+.. option:: server
+
   Launch :manual:`cmake-server(7)` mode.
 
-``sleep <number>...``
+.. option:: sleep <number>...
+
   .. versionadded:: 3.0
 
   Sleep for given number of seconds.
 
-``tar [cxt][vf][zjJ] file.tar [<options>] [--] [<pathname>...]``
+.. option:: tar [cxt][vf][zjJ] file.tar [<options>] [--] [<pathname>...]
+
   Create or extract a tar or zip archive.  Options are:
 
-  ``c``
+  .. program:: cmake-E_tar
+
+  .. option:: c
+
     Create a new archive containing the specified files.
     If used, the ``<pathname>...`` argument is mandatory.
 
-  ``x``
+  .. option:: x
+
     Extract to disk from the archive.
 
     .. versionadded:: 3.15
@@ -916,33 +1099,40 @@ Available commands are:
       When extracting selected files or directories, you must provide their exact
       names including the path, as printed by list (``-t``).
 
-  ``t``
+  .. option:: t
+
     List archive contents.
 
     .. versionadded:: 3.15
       The ``<pathname>...`` argument could be used to list only selected files
       or directories.
 
-  ``v``
+  .. option:: v
+
     Produce verbose output.
 
-  ``z``
+  .. option:: z
+
     Compress the resulting archive with gzip.
 
-  ``j``
+  .. option:: j
+
     Compress the resulting archive with bzip2.
 
-  ``J``
+  .. option:: J
+
     .. versionadded:: 3.1
 
     Compress the resulting archive with XZ.
 
-  ``--zstd``
+  .. option:: --zstd
+
     .. versionadded:: 3.15
 
     Compress the resulting archive with Zstandard.
 
-  ``--files-from=<file>``
+  .. option:: --files-from=<file>
+
     .. versionadded:: 3.1
 
     Read file names from the given file, one per line.
@@ -950,25 +1140,29 @@ Available commands are:
     except for ``--add-file=<name>`` to add files whose
     names start in ``-``.
 
-  ``--format=<format>``
+  .. option:: --format=<format>
+
     .. versionadded:: 3.3
 
     Specify the format of the archive to be created.
     Supported formats are: ``7zip``, ``gnutar``, ``pax``,
     ``paxr`` (restricted pax, default), and ``zip``.
 
-  ``--mtime=<date>``
+  .. option:: --mtime=<date>
+
     .. versionadded:: 3.1
 
     Specify modification time recorded in tarball entries.
 
-  ``--touch``
+  .. option:: --touch
+
     .. versionadded:: 3.24
 
     Use current local timestamp instead of extracting file timestamps
     from the archive.
 
-  ``--``
+  .. option:: --
+
     .. versionadded:: 3.1
 
     Stop interpreting options and treat all remaining arguments
@@ -983,7 +1177,10 @@ Available commands are:
     ``tar`` tool. The command now also parses all flags, and if an invalid flag
     was provided, a warning is issued.
 
-``time <command> [<args>...]``
+.. program:: cmake-E
+
+.. option:: time <command> [<args>...]
+
   Run command and display elapsed time.
 
   .. versionadded:: 3.5
@@ -991,15 +1188,18 @@ Available commands are:
     through to the child process. This may break scripts that worked around the
     bug with their own extra quoting or escaping.
 
-``touch <file>...``
+.. option:: touch <file>...
+
   Creates ``<file>`` if file do not exist.
   If ``<file>`` exists, it is changing ``<file>`` access and modification times.
 
-``touch_nocreate <file>...``
+.. option:: touch_nocreate <file>...
+
   Touch a file if it exists but do not create it.  If a file does
   not exist it will be silently ignored.
 
-``true``
+.. option:: true
+
   .. versionadded:: 3.16
 
   Do nothing, with an exit code of 0.
@@ -1009,28 +1209,34 @@ Windows-specific Command-Line Tools
 
 The following ``cmake -E`` commands are available only on Windows:
 
-``delete_regv <key>``
+.. option:: delete_regv <key>
+
   Delete Windows registry value.
 
-``env_vs8_wince <sdkname>``
+.. option:: env_vs8_wince <sdkname>
+
   .. versionadded:: 3.2
 
   Displays a batch file which sets the environment for the provided
   Windows CE SDK installed in VS2005.
 
-``env_vs9_wince <sdkname>``
+.. option:: env_vs9_wince <sdkname>
+
   .. versionadded:: 3.2
 
   Displays a batch file which sets the environment for the provided
   Windows CE SDK installed in VS2008.
 
-``write_regv <key> <value>``
+.. option:: write_regv <key> <value>
+
   Write Windows registry value.
 
 
 Run the Find-Package Tool
 =========================
 
+.. program:: cmake--find-package
+
 CMake provides a pkg-config like helper for Makefile-based projects:
 
 .. code-block:: shell
@@ -1046,10 +1252,51 @@ autoconf-based projects (via ``share/aclocal/cmake.m4``).
   This mode is not well-supported due to some technical limitations.
   It is kept for compatibility but should not be used in new projects.
 
+.. _`Workflow Mode`:
+
+Run a Workflow Preset
+=====================
+
+.. program:: cmake
+
+:manual:`CMake Presets <cmake-presets(7)>` provides a way to execute multiple
+build steps in order:
+
+.. code-block:: shell
+
+  cmake --workflow [<options>]
+
+The options are:
+
+.. option:: --workflow
+
+  Select a :ref:`Workflow Preset` using one of the following options.
+
+.. program:: cmake--workflow
+
+.. option:: --preset <preset>, --preset=<preset>
+
+  Use a workflow preset to specify a workflow. The project binary directory
+  is inferred from the initial configure preset. The current working directory
+  must contain CMake preset files.
+  See :manual:`preset <cmake-presets(7)>` for more details.
+
+.. option:: --list-presets
+
+  Lists the available workflow presets. The current working directory must
+  contain CMake preset files.
+
+.. option:: --fresh
+
+  Perform a fresh configuration of the build tree.
+  This removes any existing ``CMakeCache.txt`` file and associated
+  ``CMakeFiles/`` directory, and recreates them from scratch.
 
 View Help
 =========
 
+.. program:: cmake
+
 To print selected pages from the CMake documentation, use
 
 .. code-block:: shell
index 395cd41..3f26d72 100644 (file)
@@ -26,12 +26,12 @@ All supported generators are specified in the :manual:`cpack-generators
 <cpack-generators(7)>` manual.  The command ``cpack --help`` prints a
 list of generators supported for the target platform.  Which of them are
 to be used can be selected through the :variable:`CPACK_GENERATOR` variable
-or through the command-line option ``-G``.
+or through the command-line option :option:`-G <cpack -G>`.
 
 The **cpack** program is steered by a configuration file written in the
 :manual:`CMake language <cmake-language(7)>`. Unless chosen differently
-through the command-line option ``--config``, the file ``CPackConfig.cmake``
-in the current directory is used.
+through the command-line option :option:`--config <cpack --config>`, the
+file ``CPackConfig.cmake`` in the current directory is used.
 
 In the standard CMake workflow, the file ``CPackConfig.cmake`` is generated
 by the :manual:`cmake <cmake(1)>` executable, provided the :module:`CPack`
@@ -40,7 +40,10 @@ module is included by the project's ``CMakeLists.txt`` file.
 Options
 =======
 
-``-G <generators>``
+.. program:: cpack
+
+.. option:: -G <generators>
+
   ``<generators>`` is a :ref:`semicolon-separated list <CMake Language Lists>`
   of generator names.  ``cpack`` will iterate through this list and produce
   package(s) in that generator's format according to the details provided in
@@ -48,7 +51,8 @@ Options
   the :variable:`CPACK_GENERATOR` variable determines the default set of
   generators that will be used.
 
-``-C <configs>``
+.. option:: -C <configs>
+
   Specify the project configuration(s) to be packaged (e.g. ``Debug``,
   ``Release``, etc.), where ``<configs>`` is a
   :ref:`semicolon-separated list <CMake Language Lists>`.
@@ -58,36 +62,44 @@ Options
   The user is responsible for ensuring that the configuration(s) listed
   have already been built before invoking ``cpack``.
 
-``-D <var>=<value>``
+.. option:: -D <var>=<value>
+
   Set a CPack variable.  This will override any value set for ``<var>`` in the
   input file read by ``cpack``.
 
-``--config <configFile>``
+.. option:: --config <configFile>
+
   Specify the configuration file read by ``cpack`` to provide the packaging
   details.  By default, ``CPackConfig.cmake`` in the current directory will
   be used.
 
-``--verbose, -V``
+.. option:: -V, --verbose
+
   Run ``cpack`` with verbose output.  This can be used to show more details
   from the package generation tools and is suitable for project developers.
 
-``--debug``
+.. option:: --debug
+
   Run ``cpack`` with debug output.  This option is intended mainly for the
   developers of ``cpack`` itself and is not normally needed by project
   developers.
 
-``--trace``
+.. option:: --trace
+
   Put the underlying cmake scripts in trace mode.
 
-``--trace-expand``
+.. option:: --trace-expand
+
   Put the underlying cmake scripts in expanded trace mode.
 
-``-P <packageName>``
+.. option:: -P <packageName>
+
   Override/define the value of the :variable:`CPACK_PACKAGE_NAME` variable used
   for packaging.  Any value set for this variable in the ``CPackConfig.cmake``
   file will then be ignored.
 
-``-R <packageVersion>``
+.. option:: -R <packageVersion>
+
   Override/define the value of the :variable:`CPACK_PACKAGE_VERSION`
   variable used for packaging.  It will override a value set in the
   ``CPackConfig.cmake`` file or one automatically computed from
@@ -95,16 +107,26 @@ Options
   :variable:`CPACK_PACKAGE_VERSION_MINOR` and
   :variable:`CPACK_PACKAGE_VERSION_PATCH`.
 
-``-B <packageDirectory>``
+.. option:: -B <packageDirectory>
+
   Override/define :variable:`CPACK_PACKAGE_DIRECTORY`, which controls the
   directory where CPack will perform its packaging work.  The resultant
   package(s) will be created at this location by default and a
   ``_CPack_Packages`` subdirectory will also be created below this directory to
   use as a working area during package creation.
 
-``--vendor <vendorName>``
+.. option:: --vendor <vendorName>
+
   Override/define :variable:`CPACK_PACKAGE_VENDOR`.
 
+.. option:: --preset <presetName>
+
+  Use a preset from :manual:`cmake-presets(7)`.
+
+.. option:: --list-presets
+
+  List presets from :manual:`cmake-presets(7)`.
+
 .. include:: OPTIONS_HELP.txt
 
 See Also
index 06f0d4e..3497a9f 100644 (file)
@@ -10,12 +10,24 @@ Synopsis
 
 .. parsed-literal::
 
- ctest [<options>]
- ctest --build-and-test <path-to-source> <path-to-build>
-       --build-generator <generator> [<options>...]
-       [--build-options <opts>...] [--test-command <command> [<args>...]]
- ctest {-D <dashboard> | -M <model> -T <action> | -S <script> | -SP <script>}
-       [-- <dashboard-options>...]
+ `Run Tests`_
+  ctest [<options>]
+
+ `Build and Test Mode`_
+  ctest --build-and-test <path-to-source> <path-to-build>
+        --build-generator <generator> [<options>...]
+       [--build-options <opts>...]
+       [--test-command <command> [<args>...]]
+
+ `Dashboard Client`_
+  ctest -D <dashboard>         [-- <dashboard-options>...]
+  ctest -M <model> -T <action> [-- <dashboard-options>...]
+  ctest -S <script>            [-- <dashboard-options>...]
+  ctest -SP <script>           [-- <dashboard-options>...]
+
+ `View Help`_
+  ctest --help[-<topic>]
+
 
 Description
 ===========
@@ -25,22 +37,27 @@ CMake-generated build trees created for projects that use the
 :command:`enable_testing` and :command:`add_test` commands have testing support.
 This program will run the tests and report results.
 
-.. _`CTest Options`:
+.. _`Run Tests`:
 
-Options
-=======
+Run Tests
+=========
+
+.. program:: ctest
+
+.. option:: --preset <preset>, --preset=<preset>
 
-``--preset <preset>``, ``--preset=<preset>``
  Use a test preset to specify test options. The project binary directory
  is inferred from the ``configurePreset`` key. The current working directory
  must contain CMake preset files.
  See :manual:`preset <cmake-presets(7)>` for more details.
 
-``--list-presets``
+.. option:: --list-presets
+
  Lists the available test presets. The current working directory must contain
  CMake preset files.
 
-``-C <cfg>, --build-config <cfg>``
+.. option:: -C <cfg>, --build-config <cfg>
+
  Choose configuration to test.
 
  Some CMake-generated build trees can have multiple build
@@ -48,7 +65,8 @@ Options
  which one should be tested.  Example configurations are ``Debug`` and
  ``Release``.
 
-``--progress``
+.. option:: --progress
+
  Enable short progress output from tests.
 
  When the output of **ctest** is being sent directly to a terminal, the
@@ -61,40 +79,47 @@ Options
  This option can also be enabled by setting the environment variable
  :envvar:`CTEST_PROGRESS_OUTPUT`.
 
-``-V,--verbose``
+.. option:: -V, --verbose
+
  Enable verbose output from tests.
 
  Test output is normally suppressed and only summary information is
  displayed.  This option will show all test output.
 
-``-VV,--extra-verbose``
+.. option:: -VV, --extra-verbose
+
  Enable more verbose output from tests.
 
  Test output is normally suppressed and only summary information is
  displayed.  This option will show even more test output.
 
-``--debug``
+.. option:: --debug
+
  Displaying more verbose internals of CTest.
 
  This feature will result in a large number of output that is mostly
  useful for debugging dashboard problems.
 
-``--output-on-failure``
+.. option:: --output-on-failure
+
  Output anything outputted by the test program if the test should fail.
  This option can also be enabled by setting the
  :envvar:`CTEST_OUTPUT_ON_FAILURE` environment variable
 
-``--stop-on-failure``
+.. option:: --stop-on-failure
+
  Stop running the tests when the first failure happens.
 
-``-F``
+.. option:: -F
+
  Enable failover.
 
  This option allows CTest to resume a test set execution that was
  previously interrupted.  If no interruption occurred, the ``-F`` option
  will have no effect.
 
-``-j <jobs>, --parallel <jobs>``
+.. option:: -j <jobs>, --parallel <jobs>
+
  Run the tests in parallel using the given number of jobs.
 
  This option tells CTest to run the tests in parallel using given
@@ -105,7 +130,8 @@ Options
 
  See `Label and Subproject Summary`_.
 
-``--resource-spec-file <file>``
+.. option:: --resource-spec-file <file>
+
  Run CTest with :ref:`resource allocation <ctest-resource-allocation>` enabled,
  using the
  :ref:`resource specification file <ctest-resource-specification-file>`
@@ -114,40 +140,54 @@ Options
  When ``ctest`` is run as a `Dashboard Client`_ this sets the
  ``ResourceSpecFile`` option of the `CTest Test Step`_.
 
-``--test-load <level>``
- While running tests in parallel (e.g. with ``-j``), try not to start
- tests when they may cause the CPU load to pass above a given threshold.
+.. option:: --test-load <level>
+
+ While running tests in parallel (e.g. with :option:`-j <ctest -j>`), try
+ not to start tests when they may cause the CPU load to pass above a given
+ threshold.
 
  When ``ctest`` is run as a `Dashboard Client`_ this sets the
  ``TestLoad`` option of the `CTest Test Step`_.
 
-``-Q,--quiet``
+.. option:: -Q, --quiet
+
  Make CTest quiet.
 
  This option will suppress all the output.  The output log file will
- still be generated if the ``--output-log`` is specified.  Options such
- as ``--verbose``, ``--extra-verbose``, and ``--debug`` are ignored
+ still be generated if the :option:`--output-log <ctest --output-log>` is
+ specified.  Options such as :option:`--verbose <ctest --verbose>`,
+ :option:`--extra-verbose <ctest --extra-verbose>`, and
+ :option:`--debug <ctest --debug>` are ignored
  if ``--quiet`` is specified.
 
-``-O <file>, --output-log <file>``
+.. option:: -O <file>, --output-log <file>
+
  Output to log file.
 
  This option tells CTest to write all its output to a ``<file>`` log file.
 
-``--output-junit <file>``
+.. option:: --output-junit <file>
+
+ .. versionadded:: 3.21
+
  Write test results in JUnit format.
 
  This option tells CTest to write test results to ``<file>`` in JUnit XML
  format. If ``<file>`` already exists, it will be overwritten. If using the
- ``-S`` option to run a dashboard script, use the ``OUTPUT_JUNIT`` keyword
- with the :command:`ctest_test` command instead.
+ :option:`-S <ctest -S>` option to run a dashboard script, use the
+ ``OUTPUT_JUNIT`` keyword with the :command:`ctest_test` command instead.
+
+.. option:: -N, --show-only[=<format>]
 
-``-N,--show-only[=<format>]``
  Disable actual execution of tests.
 
  This option tells CTest to list the tests that would be run but not
- actually run them.  Useful in conjunction with the ``-R`` and ``-E``
- options.
+ actually run them.  Useful in conjunction with the :option:`-R <ctest -R>`
+ and :option:`-E <ctest -E>` options.
+
+ .. versionadded:: 3.14
+
+   The ``--show-only`` option accepts a ``<format>`` value.
 
  ``<format>`` can be one of the following values.
 
@@ -159,7 +199,8 @@ Options
      Dump the test information in JSON format.
      See `Show as JSON Object Model`_.
 
-``-L <regex>, --label-regex <regex>``
+.. option:: -L <regex>, --label-regex <regex>
+
  Run tests with labels matching regular expression as described under
  :ref:`string(REGEX) <Regex Specification>`.
 
@@ -169,19 +210,22 @@ Options
  of the test's labels (i.e. the multiple ``-L`` labels form an ``AND``
  relationship).  See `Label Matching`_.
 
-``-R <regex>, --tests-regex <regex>``
+.. option:: -R <regex>, --tests-regex <regex>
+
  Run tests matching regular expression.
 
  This option tells CTest to run only the tests whose names match the
  given regular expression.
 
-``-E <regex>, --exclude-regex <regex>``
+.. option:: -E <regex>, --exclude-regex <regex>
+
  Exclude tests matching regular expression.
 
  This option tells CTest to NOT run the tests whose names match the
  given regular expression.
 
-``-LE <regex>, --label-exclude <regex>``
+.. option:: -LE <regex>, --label-exclude <regex>
+
  Exclude tests with labels matching regular expression.
 
  This option tells CTest to NOT run the tests whose labels match the
@@ -190,7 +234,8 @@ Options
  of the test's labels (i.e. the multiple ``-LE`` labels form an ``AND``
  relationship).  See `Label Matching`_.
 
-``-FA <regex>, --fixture-exclude-any <regex>``
+.. option:: -FA <regex>, --fixture-exclude-any <regex>
+
  Exclude fixtures matching ``<regex>`` from automatically adding any tests to
  the test set.
 
@@ -201,72 +246,18 @@ Options
  including test dependencies and skipping tests that have fixture setup tests
  that fail.
 
-``-FS <regex>, --fixture-exclude-setup <regex>``
- Same as ``-FA`` except only matching setup tests are excluded.
-
-``-FC <regex>, --fixture-exclude-cleanup <regex>``
- Same as ``-FA`` except only matching cleanup tests are excluded.
-
-``-D <dashboard>, --dashboard <dashboard>``
- Execute dashboard test.
-
- This option tells CTest to act as a CDash client and perform a
- dashboard test.  All tests are ``<Mode><Test>``, where ``<Mode>`` can be
- ``Experimental``, ``Nightly``, and ``Continuous``, and ``<Test>`` can be
- ``Start``, ``Update``, ``Configure``, ``Build``, ``Test``,
- ``Coverage``, and ``Submit``.
-
- See `Dashboard Client`_.
-
-``-D <var>:<type>=<value>``
- Define a variable for script mode.
-
- Pass in variable values on the command line.  Use in conjunction
- with ``-S`` to pass variable values to a dashboard script.  Parsing ``-D``
- arguments as variable values is only attempted if the value
- following ``-D`` does not match any of the known dashboard types.
-
-``-M <model>, --test-model <model>``
- Sets the model for a dashboard.
-
- This option tells CTest to act as a CDash client where the ``<model>``
- can be ``Experimental``, ``Nightly``, and ``Continuous``.
- Combining ``-M`` and ``-T`` is similar to ``-D``.
+.. option:: -FS <regex>, --fixture-exclude-setup <regex>
 
- See `Dashboard Client`_.
+ Same as :option:`-FA <ctest -FA>` except only matching setup tests are
+ excluded.
 
-``-T <action>, --test-action <action>``
- Sets the dashboard action to perform.
-
- This option tells CTest to act as a CDash client and perform some
- action such as ``start``, ``build``, ``test`` etc. See
- `Dashboard Client Steps`_ for the full list of actions.
- Combining ``-M`` and ``-T`` is similar to ``-D``.
+.. option:: -FC <regex>, --fixture-exclude-cleanup <regex>
 
- See `Dashboard Client`_.
+ Same as :option:`-FA <ctest -FA>` except only matching cleanup tests are
+ excluded.
 
-``-S <script>, --script <script>``
- Execute a dashboard for a configuration.
+.. option:: -I [Start,End,Stride,test#,test#|Test file], --tests-information
 
- This option tells CTest to load in a configuration script which sets
- a number of parameters such as the binary and source directories.
- Then CTest will do what is required to create and run a dashboard.
- This option basically sets up a dashboard and then runs ``ctest -D``
- with the appropriate options.
-
- See `Dashboard Client`_.
-
-``-SP <script>, --script-new-process <script>``
- Execute a dashboard for a configuration.
-
- This option does the same operations as ``-S`` but it will do them in a
- separate process.  This is primarily useful in cases where the
- script may modify the environment and you do not want the modified
- environment to impact other ``-S`` scripts.
-
- See `Dashboard Client`_.
-
-``-I [Start,End,Stride,test#,test#|Test file], --tests-information``
  Run a specific number of tests by number.
 
  This option causes CTest to run tests starting at number ``Start``,
@@ -275,23 +266,29 @@ Options
  ``End``, or ``Stride`` can be empty.  Optionally a file can be given that
  contains the same syntax as the command line.
 
-``-U, --union``
- Take the Union of ``-I`` and ``-R``.
+.. option:: -U, --union
+
+ Take the Union of :option:`-I <ctest -I>` and :option:`-R <ctest -R>`.
+
+ When both :option:`-R <ctest -R>` and :option:`-I <ctest -I>` are specified
+ by default the intersection of tests are run.  By specifying ``-U`` the union
+ of tests is run instead.
 
- When both ``-R`` and ``-I`` are specified by default the intersection of
- tests are run.  By specifying ``-U`` the union of tests is run instead.
+.. option:: --rerun-failed
 
-``--rerun-failed``
  Run only the tests that failed previously.
 
  This option tells CTest to perform only the tests that failed during
  its previous run.  When this option is specified, CTest ignores all
- other options intended to modify the list of tests to run (``-L``, ``-R``,
- ``-E``, ``-LE``, ``-I``, etc).  In the event that CTest runs and no tests
- fail, subsequent calls to CTest with the ``--rerun-failed`` option will run
- the set of tests that most recently failed (if any).
+ other options intended to modify the list of tests to run (
+ :option:`-L <ctest -L>`, :option:`-R <ctest -R>`, :option:`-E <ctest -E>`,
+ :option:`-LE <ctest -LE>`, :option:`-I <ctest -I>`, etc).  In the event that
+ CTest runs and no tests fail, subsequent calls to CTest with the
+ ``--rerun-failed`` option will run the set of tests that most recently
+ failed (if any).
+
+.. option:: --repeat <mode>:<n>
 
-``--repeat <mode>:<n>``
   Run tests repeatedly based on the given ``<mode>`` up to ``<n>`` times.
   The modes are:
 
@@ -310,17 +307,20 @@ Options
     This is useful in tolerating sporadic timeouts in test cases
     on busy machines.
 
-``--repeat-until-fail <n>``
- Equivalent to ``--repeat until-fail:<n>``.
+.. option:: --repeat-until-fail <n>
+
+ Equivalent to :option:`--repeat until-fail:\<n\> <ctest --repeat>`.
+
+.. option:: --max-width <width>
 
-``--max-width <width>``
  Set the max width for a test name to output.
 
  Set the maximum width for each test name to show in the output.
  This allows the user to widen the output to avoid clipping the test
  name which can be very annoying.
 
-``--interactive-debug-mode [0|1]``
+.. option:: --interactive-debug-mode [0|1]
+
  Set the interactive mode to ``0`` or ``1``.
 
  This option causes CTest to run tests in either an interactive mode
@@ -332,7 +332,8 @@ Options
  popup windows to appear.  Now, due to CTest's use of ``libuv`` to launch
  test processes, all system debug popup windows are always blocked.
 
-``--no-label-summary``
+.. option:: --no-label-summary
+
  Disable timing summary information for labels.
 
  This option tells CTest not to print summary information for each
@@ -341,7 +342,8 @@ Options
 
  See `Label and Subproject Summary`_.
 
-``--no-subproject-summary``
+.. option:: --no-subproject-summary
+
  Disable timing summary information for subprojects.
 
  This option tells CTest not to print summary information for each
@@ -350,79 +352,95 @@ Options
 
  See `Label and Subproject Summary`_.
 
-``--build-and-test``
-See `Build and Test Mode`_.
+.. option:: --test-dir <dir>
+
+ Specify the directory in which to look for tests.
 
-``--test-dir <dir>``
-Specify the directory in which to look for tests.
+.. option:: --test-output-size-passed <size>
 
-``--test-output-size-passed <size>``
  .. versionadded:: 3.4
 
  Limit the output for passed tests to ``<size>`` bytes.
 
-``--test-output-size-failed <size>``
+.. option:: --test-output-size-failed <size>
+
  .. versionadded:: 3.4
 
  Limit the output for failed tests to ``<size>`` bytes.
 
-``--test-output-truncation <mode>``
+.. option:: --test-output-truncation <mode>
+
  .. versionadded:: 3.24
 
  Truncate ``tail`` (default), ``middle`` or ``head`` of test output once
  maximum output size is reached.
 
-``--overwrite``
+.. option:: --overwrite
+
  Overwrite CTest configuration option.
 
  By default CTest uses configuration options from configuration file.
  This option will overwrite the configuration option.
 
-``--force-new-ctest-process``
+.. option:: --force-new-ctest-process
+
  Run child CTest instances as new processes.
 
  By default CTest will run child CTest instances within the same
  process.  If this behavior is not desired, this argument will
  enforce new processes for child CTest processes.
 
-``--schedule-random``
+.. option:: --schedule-random
+
  Use a random order for scheduling tests.
 
  This option will run the tests in a random order.  It is commonly
  used to detect implicit dependencies in a test suite.
 
-``--submit-index``
+.. option:: --submit-index
+
  Legacy option for old Dart2 dashboard server feature.
  Do not use.
 
-``--timeout <seconds>``
+.. option:: --timeout <seconds>
+
  Set the default test timeout.
 
  This option effectively sets a timeout on all tests that do not
  already have a timeout set on them via the :prop_test:`TIMEOUT`
  property.
 
-``--stop-time <time>``
+.. option:: --stop-time <time>
+
  Set a time at which all tests should stop running.
 
  Set a real time of day at which all tests should timeout.  Example:
  ``7:00:00 -0400``.  Any time format understood by the curl date parser
  is accepted.  Local time is assumed if no timezone is specified.
 
-``--print-labels``
+.. option:: --print-labels
+
  Print all available test labels.
 
  This option will not run any tests, it will simply print the list of
  all labels associated with the test set.
 
-``--no-tests=<[error|ignore]>``
- Regard no tests found either as error or ignore it.
+.. option:: --no-tests=<action>
+
+ Regard no tests found either as error (when ``<action>`` is set to
+ ``error``) or ignore it (when ``<action>`` is set to ``ignore``).
 
  If no tests were found, the default behavior of CTest is to always log an
  error message but to return an error code in script mode only.  This option
  unifies the behavior of CTest by either returning an error code if no tests
  were found or by ignoring it.
 
+View Help
+=========
+
+To print version details or selected pages from the CMake documentation,
+use one of the following options:
+
 .. include:: OPTIONS_HELP.txt
 
 .. _`Label Matching`:
@@ -435,17 +453,17 @@ or excluded from a test run by filtering on the labels.
 Each individual filter is a regular expression applied to
 the labels attached to a test.
 
-When ``-L`` is used, in order for a test to be included in a
+When :option:`-L <ctest -L>` is used, in order for a test to be included in a
 test run, each regular expression must match at least one
-label.  Using more than one ``-L`` option means "match **all**
+label.  Using more than one :option:`-L <ctest -L>` option means "match **all**
 of these".
 
-The ``-LE`` option works just like ``-L``, but excludes tests
-rather than including them. A test is excluded if each regular
-expression matches at least one label.
+The :option:`-LE <ctest -LE>` option works just like :option:`-L <ctest -L>`,
+but excludes tests rather than including them. A test is excluded if each
+regular expression matches at least one label.
 
-If a test has no labels attached to it, then ``-L`` will never
-include that test, and ``-LE`` will never exclude that test.
+If a test has no labels attached to it, then :option:`-L <ctest -L>` will never
+include that test, and :option:`-LE <ctest -LE>` will never exclude that test.
 As an example of tests with labels, consider five tests,
 with the following labels:
 
@@ -532,62 +550,85 @@ be provided to use ``--build-and-test``.  If ``--test-command`` is specified
 then that will be run after the build is complete.  Other options that affect
 this mode include:
 
-``--build-target``
- Specify a specific target to build.
+.. option:: --build-and-test
 
- If left out the ``all`` target is built.
+ Switch into the build and test mode.
+
+.. option:: --build-target
+
+ Specify a specific target to build.  The option can be given multiple times
+ with different targets, in which case each target is built in turn.
+ A clean will be done before building each target unless the
+ :option:`--build-noclean` option is given.
+
+ If no ``--build-target`` is specified, the ``all`` target is built.
+
+.. option:: --build-nocmake
 
-``--build-nocmake``
  Run the build without running cmake first.
 
  Skip the cmake step.
 
-``--build-run-dir``
+.. option:: --build-run-dir
+
  Specify directory to run programs from.
 
  Directory where programs will be after it has been compiled.
 
-``--build-two-config``
+.. option:: --build-two-config
+
  Run CMake twice.
 
-``--build-exe-dir``
+.. option:: --build-exe-dir
+
  Specify the directory for the executable.
 
-``--build-generator``
+.. option:: --build-generator
+
  Specify the generator to use. See the :manual:`cmake-generators(7)` manual.
 
-``--build-generator-platform``
+.. option:: --build-generator-platform
+
  Specify the generator-specific platform.
 
-``--build-generator-toolset``
+.. option:: --build-generator-toolset
+
  Specify the generator-specific toolset.
 
-``--build-project``
+.. option:: --build-project
+
  Specify the name of the project to build.
 
-``--build-makeprogram``
+.. option:: --build-makeprogram
+
  Specify the explicit make program to be used by CMake when configuring and
  building the project. Only applicable for Make and Ninja based generators.
 
-``--build-noclean``
+.. option:: --build-noclean
+
  Skip the make clean step.
 
-``--build-config-sample``
+.. option:: --build-config-sample
+
  A sample executable to use to determine the configuration that
  should be used.  e.g.  ``Debug``, ``Release`` etc.
 
-``--build-options``
+.. option:: --build-options
+
  Additional options for configuring the build (i.e. for CMake, not for
  the build tool).  Note that if this is specified, the ``--build-options``
  keyword and its arguments must be the last option given on the command
  line, with the possible exception of ``--test-command``.
 
-``--test-command``
- The command to run as the test step with the ``--build-and-test`` option.
+.. option:: --test-command
+
+ The command to run as the test step with the
+ :option:`--build-and-test <ctest --build-and-test>` option.
  All arguments following this keyword will be assumed to be part of the
  test command line, so it must be the last option given.
 
-``--test-timeout``
+.. option:: --test-timeout
+
  The time limit in seconds
 
 .. _`Dashboard Client`:
@@ -600,12 +641,80 @@ application.  As a dashboard client, CTest performs a sequence of steps
 to configure, build, and test software, and then submits the results to
 a `CDash`_ server. The command-line signature used to submit to `CDash`_ is::
 
-  ctest (-D <dashboard> | -M <model> -T <action> | -S <script> | -SP <script>)
-        [-- <dashboard-options>...]
+  ctest -D <dashboard>         [-- <dashboard-options>...]
+  ctest -M <model> -T <action> [-- <dashboard-options>...]
+  ctest -S <script>            [-- <dashboard-options>...]
+  ctest -SP <script>           [-- <dashboard-options>...]
 
 Options for Dashboard Client include:
 
-``--group <group>``
+.. option:: -D <dashboard>, --dashboard <dashboard>
+
+ Execute dashboard test.
+
+ This option tells CTest to act as a CDash client and perform a
+ dashboard test.  All tests are ``<Mode><Test>``, where ``<Mode>`` can be
+ ``Experimental``, ``Nightly``, and ``Continuous``, and ``<Test>`` can be
+ ``Start``, ``Update``, ``Configure``, ``Build``, ``Test``,
+ ``Coverage``, and ``Submit``.
+
+ If ``<dashboard>`` is not one of the recognized ``<Mode><Test>`` values,
+ this will be treated as a variable definition instead (see the
+ :ref:`dashboard-options <Dashboard Options>` further below).
+
+.. option:: -M <model>, --test-model <model>
+
+ Sets the model for a dashboard.
+
+ This option tells CTest to act as a CDash client where the ``<model>``
+ can be ``Experimental``, ``Nightly``, and ``Continuous``.
+ Combining ``-M`` and :option:`-T <ctest -T>` is similar to
+ :option:`-D <ctest -D>`.
+
+.. option:: -T <action>, --test-action <action>
+
+ Sets the dashboard action to perform.
+
+ This option tells CTest to act as a CDash client and perform some
+ action such as ``start``, ``build``, ``test`` etc. See
+ `Dashboard Client Steps`_ for the full list of actions.
+ Combining :option:`-M <ctest -M>` and ``-T`` is similar to
+ :option:`-D <ctest -D>`.
+
+.. option:: -S <script>, --script <script>
+
+ Execute a dashboard for a configuration.
+
+ This option tells CTest to load in a configuration script which sets
+ a number of parameters such as the binary and source directories.
+ Then CTest will do what is required to create and run a dashboard.
+ This option basically sets up a dashboard and then runs :option:`ctest -D`
+ with the appropriate options.
+
+.. option:: -SP <script>, --script-new-process <script>
+
+ Execute a dashboard for a configuration.
+
+ This option does the same operations as :option:`-S <ctest -S>` but it
+ will do them in a separate process.  This is primarily useful in cases
+ where the script may modify the environment and you do not want the modified
+ environment to impact other :option:`-S <ctest -S>` scripts.
+
+.. _`Dashboard Options`:
+
+The available ``<dashboard-options>`` are the following:
+
+.. option:: -D <var>:<type>=<value>
+
+ Define a variable for script mode.
+
+ Pass in variable values on the command line.  Use in conjunction
+ with :option:`-S <ctest -S>` to pass variable values to a dashboard script.
+ Parsing ``-D`` arguments as variable values is only attempted if the value
+ following ``-D`` does not match any of the known dashboard types.
+
+.. option:: --group <group>
+
  Specify what group you'd like to submit results to
 
  Submit dashboard to specified group instead of default one.  By
@@ -616,29 +725,34 @@ Options for Dashboard Client include:
  This replaces the deprecated option ``--track``.
  Despite the name change its behavior is unchanged.
 
-``-A <file>, --add-notes <file>``
+.. option:: -A <file>, --add-notes <file>
+
  Add a notes file with submission.
 
  This option tells CTest to include a notes file when submitting
  dashboard.
 
-``--tomorrow-tag``
+.. option:: --tomorrow-tag
+
  ``Nightly`` or ``Experimental`` starts with next day tag.
 
  This is useful if the build will not finish in one day.
 
-``--extra-submit <file>[;<file>]``
+.. option:: --extra-submit <file>[;<file>]
+
  Submit extra files to the dashboard.
 
  This option will submit extra files to the dashboard.
 
-``--http1.0``
+.. option:: --http1.0
+
  Submit using `HTTP 1.0`.
 
  This option will force CTest to use `HTTP 1.0` to submit files to the
  dashboard, instead of `HTTP 1.1`.
 
-``--no-compress-output``
+.. option:: --no-compress-output
+
  Do not compress test output when submitting.
 
  This flag will turn off automatic compression of test output.  Use
@@ -722,7 +836,7 @@ Run the ``ctest`` command with the current working directory set
 to the build tree and use one of these signatures::
 
   ctest -D <mode>[<step>]
-  ctest -M <mode> [ -T <step> ]...
+  ctest -M <mode> [-T <step>]...
 
 The ``<mode>`` must be one of the above `Dashboard Client Modes`_,
 and each ``<step>`` must be one of the above `Dashboard Client Steps`_.
@@ -1027,9 +1141,9 @@ Configuration settings include:
 ``DefaultCTestConfigurationType``
   When the build system to be launched allows build-time selection
   of the configuration (e.g. ``Debug``, ``Release``), this specifies
-  the default configuration to be built when no ``-C`` option is
-  given to the ``ctest`` command.  The value will be substituted into
-  the value of ``MakeCommand`` to replace the literal string
+  the default configuration to be built when no :option:`-C <ctest -C>`
+  option is given to the ``ctest`` command.  The value will be substituted
+  into the value of ``MakeCommand`` to replace the literal string
   ``${CTEST_CONFIGURATION_TYPE}`` if it appears.
 
   * `CTest Script`_ variable: :variable:`CTEST_CONFIGURATION_TYPE`
@@ -1101,8 +1215,9 @@ Configuration settings include:
   See `Label and Subproject Summary`_.
 
 ``TestLoad``
-  While running tests in parallel (e.g. with ``-j``), try not to start
-  tests when they may cause the CPU load to pass above a given threshold.
+  While running tests in parallel (e.g. with :option:`-j <ctest -j>`),
+  try not to start tests when they may cause the CPU load to pass above
+  a given threshold.
 
   * `CTest Script`_ variable: :variable:`CTEST_TEST_LOAD`
   * :module:`CTest` module variable: ``CTEST_TEST_LOAD``
@@ -1382,6 +1497,8 @@ Configuration settings include:
 Show as JSON Object Model
 =========================
 
+.. versionadded:: 3.14
+
 When the ``--show-only=json-v1`` command line option is given, the test
 information is output in JSON format.  Version 1.0 of the JSON object
 model is defined as follows:
@@ -1518,12 +1635,12 @@ Resource Specification File
 ---------------------------
 
 The resource specification file is a JSON file which is passed to CTest, either
-on the :manual:`ctest(1)` command line as ``--resource-spec-file``, or as the
+on the command line as :option:`ctest --resource-spec-file`, or as the
 ``RESOURCE_SPEC_FILE`` argument of :command:`ctest_test`. If a dashboard script
 is used and ``RESOURCE_SPEC_FILE`` is not specified, the value of
 :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script is used instead.
-If ``--resource-spec-file``, ``RESOURCE_SPEC_FILE``, and
-:variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script are not specified,
+If :option:`--resource-spec-file <ctest --resource-spec-file>`, ``RESOURCE_SPEC_FILE``,
+and :variable:`CTEST_RESOURCE_SPEC_FILE` in the dashboard script are not specified,
 the value of :variable:`CTEST_RESOURCE_SPEC_FILE` in the CMake build is used
 instead. If none of these are specified, no resource spec file is used.
 
index be5c791..696ab47 100644 (file)
@@ -1,5 +1,5 @@
 {
-  "version": 5,
+  "version": 6,
   "cmakeMinimumRequired": {
     "major": 3,
     "minor": 23,
       "execution": {"noTestsAction": "error", "stopOnFailure": true}
     }
   ],
+  "packagePresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "generators": [
+        "TGZ"
+      ]
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "build",
+          "name": "default"
+        },
+        {
+          "type": "test",
+          "name": "default"
+        },
+        {
+          "type": "package",
+          "name": "default"
+        }
+      ]
+    }
+  ],
   "vendor": {
     "example.com/ExampleIDE/1.0": {
       "autoFormat": false
index c96405c..348116b 100644 (file)
         "include": { "$ref": "#/definitions/include"}
       },
       "additionalProperties": false
+    },
+    {
+      "properties": {
+        "version": {
+          "const": 6,
+          "description": "A required integer representing the version of the JSON schema."
+        },
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "vendor": { "$ref": "#/definitions/vendor" },
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
+        "testPresets": { "$ref": "#/definitions/testPresetsV6"},
+        "packagePresets": { "$ref": "#/definitions/packagePresetsV6"},
+        "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" },
+        "include": { "$ref": "#/definitions/include"}
+      },
+      "additionalProperties": false
     }
   ],
   "required": [
         "properties": {
           "name": {
             "type": "string",
-            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, or test) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
+            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
             "minLength": 1
           },
           "hidden": {
             "type": "boolean",
-            "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument, will not show up in the CMake GUI, and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field."
+            "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field."
           },
           "inherits": {
             "anyOf": [
         "additionalProperties": false
       }
     },
+    "testPresetsItemsV6": {
+      "type": "array",
+      "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 6 and higher.",
+      "items": {
+        "type": "object",
+        "properties": {
+          "output": {
+            "type": "object",
+            "description": "An optional object specifying output options.",
+            "properties": {
+              "outputJUnitFile": {
+                "type": "string",
+                "description": "An optional string specifying a path to a JUnit file. Equivalent to passing --output-junit on the command line."
+              }
+            }
+          }
+        }
+      }
+    },
     "testPresetsItemsV5": {
       "type": "array",
       "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 5 and higher.",
         "properties": {
           "name": {
             "type": "string",
-            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, or test) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
+            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
             "minLength": 1
           },
           "hidden": {
             "type": "boolean",
-            "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument, will not show up in the CMake GUI, and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field."
+            "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field."
           },
           "inherits": {
             "anyOf": [
         ]
       }
     },
+    "testPresetsV6": {
+      "type": "array",
+      "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 6 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/testPresetsItemsV2" },
+        { "$ref": "#/definitions/testPresetsItemsV3" },
+        { "$ref": "#/definitions/testPresetsItemsV5" },
+        { "$ref": "#/definitions/testPresetsItemsV6" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "configuration": {},
+          "overwriteConfigurationFile": {},
+          "output": {
+            "type": "object",
+            "properties": {
+              "shortProgress": {},
+              "verbosity": {},
+              "debug": {},
+              "outputOnFailure": {},
+              "quiet": {},
+              "outputLogFile": {},
+              "outputJUnitFile": {},
+              "labelSummary": {},
+              "subprojectSummary": {},
+              "maxPassedTestOutputSize": {},
+              "maxFailedTestOutputSize": {},
+              "maxTestNameWidth": {},
+              "testOutputTruncation": {}
+            },
+            "additionalProperties": false
+          },
+          "filter": {},
+          "execution": {},
+          "condition": {}
+        },
+        "required": [
+          "name"
+        ],
+        "additionalProperties": false
+      }
+    },
     "testPresetsV5": {
       "type": "array",
       "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 5 and higher.",
           "environment": {},
           "configuration": {},
           "overwriteConfigurationFile": {},
-          "output": {},
+          "output": {
+            "type": "object",
+            "properties": {
+              "shortProgress": {},
+              "verbosity": {},
+              "debug": {},
+              "outputOnFailure": {},
+              "quiet": {},
+              "outputLogFile": {},
+              "labelSummary": {},
+              "subprojectSummary": {},
+              "maxPassedTestOutputSize": {},
+              "maxFailedTestOutputSize": {},
+              "maxTestNameWidth": {},
+              "testOutputTruncation": {}
+            },
+            "additionalProperties": false
+          },
           "filter": {},
           "execution": {},
           "condition": {}
           "environment": {},
           "configuration": {},
           "overwriteConfigurationFile": {},
-          "output": {},
+          "output": {
+            "type": "object",
+            "properties": {
+              "shortProgress": {},
+              "verbosity": {},
+              "debug": {},
+              "outputOnFailure": {},
+              "quiet": {},
+              "outputLogFile": {},
+              "labelSummary": {},
+              "subprojectSummary": {},
+              "maxPassedTestOutputSize": {},
+              "maxFailedTestOutputSize": {},
+              "maxTestNameWidth": {}
+            },
+            "additionalProperties": false
+          },
           "filter": {},
           "execution": {},
           "condition": {}
           "environment": {},
           "configuration": {},
           "overwriteConfigurationFile": {},
-          "output": {},
+          "output": {
+            "type": "object",
+            "properties": {
+              "shortProgress": {},
+              "verbosity": {},
+              "debug": {},
+              "outputOnFailure": {},
+              "quiet": {},
+              "outputLogFile": {},
+              "labelSummary": {},
+              "subprojectSummary": {},
+              "maxPassedTestOutputSize": {},
+              "maxFailedTestOutputSize": {},
+              "maxTestNameWidth": {}
+            },
+            "additionalProperties": false
+          },
           "filter": {},
           "execution": {}
         },
         "additionalProperties": false
       }
     },
+    "packagePresetsItemsV6": {
+      "type": "array",
+      "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 6 and higher.",
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {
+            "type": "string",
+            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
+            "minLength": 1
+          },
+          "hidden": {
+            "type": "boolean",
+            "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset argument and does not have to have a valid configurePreset, even from inheritance. Hidden presets are intended to be used as a base for other presets to inherit via the inherits field."
+          },
+          "inherits": {
+            "anyOf": [
+              {
+                "type": "string",
+                "description": "An optional string representing the name of the package preset to inherit from.",
+                "minLength": 1
+              },
+              {
+                "type": "array",
+                "description": "An optional array of strings representing the names of package presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.",
+                "items": {
+                  "type": "string",
+                  "description": "An optional string representing the name of the preset to inherit from.",
+                  "minLength": 1
+                }
+              }
+            ]
+          },
+          "configurePreset": {
+            "type": "string",
+            "description": "An optional string specifying the name of a configure preset to associate with this package preset. If configurePreset is not specified, it must be inherited from the inherits preset (unless this preset is hidden). The build tree directory is inferred from the configure preset.",
+            "minLength": 1
+          },
+          "vendor": {
+            "type": "object",
+            "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, it should follow the same conventions as the root-level vendor field. If vendors use their own per-preset vendor field, they should implement inheritance in a sensible manner when appropriate.",
+            "properties": {}
+          },
+          "displayName": {
+            "type": "string",
+            "description": "An optional string with a human-friendly name of the preset."
+          },
+          "description": {
+            "type": "string",
+            "description": "An optional string with a human-friendly description of the preset."
+          },
+          "inheritConfigureEnvironment": {
+            "type": "boolean",
+            "description": "An optional boolean that defaults to true. If true, the environment variables from the associated configure preset are inherited after all inherited package preset environments, but before environment variables explicitly specified in this package preset."
+          },
+          "environment": {
+            "type": "object",
+            "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.",
+            "properties": {},
+            "additionalProperties": {
+              "anyOf": [
+                {
+                  "type": "null",
+                  "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset."
+                },
+                {
+                  "type": "string",
+                  "description": "A string representing the value of the variable."
+                }
+              ]
+            },
+            "propertyNames": {
+              "pattern": "^.+$"
+            }
+          },
+          "condition": { "$ref": "#/definitions/topCondition" },
+          "generators": {
+            "type": "array",
+            "description": "An optional list of strings representing generators for CPack to use.",
+            "items": {
+              "type": "string",
+              "description": "An optional string representing the name of the CPack generator to use."
+            }
+          },
+          "configurations": {
+            "type": "array",
+            "description": "An optional list of strings representing build configurations for CPack to package.",
+            "items": {
+              "type": "string",
+              "description": "An optional string representing the name of the configuration to use."
+            }
+          },
+          "variables": {
+            "type": "object",
+            "description": "An optional map of variables to pass to CPack, equivalent to -D arguments. Each key is the name of a variable, and the value is the string to assign to that variable.",
+            "items": {
+              "type": "string",
+              "description": "An optional string representing the value of the variable."
+            }
+          },
+          "configFile": {
+            "type": "string",
+            "description": "An optional string representing the config file for CPack to use."
+          },
+          "output": {
+            "type": "object",
+            "description": "An optional object specifying output options.",
+            "properties": {
+              "debug": {
+                "type": "boolean",
+                "description": "An optional boolean specifying whether or not to print debug information. A value of true is equivalent to passing --debug on the command line."
+              },
+              "verbose": {
+                "type": "boolean",
+                "description": "An optional boolean specifying whether or not to print verbosely. A value of true is equivalent to passing --verbose on the command line."
+              }
+            },
+            "additionalProperties": false
+          },
+          "packageName": {
+            "type": "string",
+            "description": "An optional string representing the package name."
+          },
+          "packageVersion": {
+            "type": "string",
+            "description": "An optional string representing the package version."
+          },
+          "packageDirectory": {
+            "type": "string",
+            "description": "An optional string representing the directory in which to place the package."
+          },
+          "vendorName": {
+            "type": "string",
+            "description": "An optional string representing the vendor name."
+          }
+        },
+        "required": [
+          "name"
+        ]
+      }
+    },
+    "packagePresetsV6": {
+      "type": "array",
+      "description": "An optional array of package preset objects. Used to specify arguments to cpack. Available in version 6 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/packagePresetsItemsV6" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "condition": {},
+          "generators": {},
+          "configurations": {},
+          "variables": {},
+          "configFile": {},
+          "output": {},
+          "packageName": {},
+          "packageVersion": {},
+          "packageDirectory": {},
+          "vendorName": {}
+        },
+        "required": [
+          "name"
+        ],
+        "additionalProperties": false
+      }
+    },
+    "workflowPresetsItemsV6": {
+      "type": "array",
+      "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 6 and higher.",
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {
+            "type": "string",
+            "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets (configure, build, test, package, or workflow) in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.",
+            "minLength": 1
+          },
+          "vendor": {
+            "type": "object",
+            "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, it should follow the same conventions as the root-level vendor field.",
+            "properties": {}
+          },
+          "displayName": {
+            "type": "string",
+            "description": "An optional string with a human-friendly name of the preset."
+          },
+          "description": {
+            "type": "string",
+            "description": "An optional string with a human-friendly description of the preset."
+          },
+          "steps": {
+            "type": "array",
+            "description": "A required array of objects describing the steps of the workflow. The first step must be a configure preset, and all subsequent steps must be non-configure presets whose configurePreset field matches the starting configure preset.",
+            "items": {
+              "type": "object",
+              "properties": {
+                "type": {
+                  "type": "string",
+                  "description": "A required string. The first step must be configure. Subsequent steps must be either build, test, or package.",
+                  "enum": ["configure", "build", "test", "package"]
+                },
+                "name": {
+                  "type": "string",
+                  "description": "A required string representing the name of the configure, build, test, or package preset to run as this workflow step.",
+                  "minLength": 1
+                }
+              },
+              "required": [
+                "type",
+                "name"
+              ],
+              "additionalProperties": false
+            }
+          }
+        },
+        "required": [
+          "name",
+          "steps"
+        ],
+        "additionalProperties": false
+      }
+    },
+    "workflowPresetsV6": {
+      "type": "array",
+      "description": "An optional array of workflow preset objects. Used to execute configure, build, test, and package presets in order. Available in version 6 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/workflowPresetsItemsV6" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "steps": {}
+        },
+        "required": [
+          "name",
+          "steps"
+        ],
+        "additionalProperties": false
+      }
+    },
     "condition": {
       "anyOf": [
         {
diff --git a/Help/module/FindOpenSP.rst b/Help/module/FindOpenSP.rst
new file mode 100644 (file)
index 0000000..1a3da01
--- /dev/null
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/FindOpenSP.cmake
diff --git a/Help/module/FindSDL_gfx.rst b/Help/module/FindSDL_gfx.rst
new file mode 100644 (file)
index 0000000..e05d661
--- /dev/null
@@ -0,0 +1 @@
+.. cmake-module:: ../../Modules/FindSDL_gfx.cmake
index 06cc74b..faab1cb 100644 (file)
@@ -50,7 +50,7 @@ that ensure ``byproduct.txt`` will exist before its consumers
 need it.  See discussion of this problem in `Ninja Issue 760`_
 for further details on why Ninja works this way.
 
-.. _`Ninja Issue 760`: https://github.com/martine/ninja/issues/760
+.. _Ninja Issue 760: https://github.com/ninja-build/ninja/issues/760
 
 Instead of leaving byproducts undeclared in the rules that generate
 them, Ninja expects byproducts to be listed along with other outputs.
@@ -71,6 +71,9 @@ every custom command dependency, even on source files, needs to
 be treated this way because CMake does not have enough information
 to know which files are generated as byproducts of custom commands.
 
+Introducing Byproducts
+^^^^^^^^^^^^^^^^^^^^^^
+
 CMake 3.2 introduced the ``BYPRODUCTS`` option to the
 :command:`add_custom_command` and :command:`add_custom_target`
 commands.  This option allows byproducts to be specified explicitly:
diff --git a/Help/policy/CMP0140.rst b/Help/policy/CMP0140.rst
new file mode 100644 (file)
index 0000000..dea8989
--- /dev/null
@@ -0,0 +1,17 @@
+CMP0140
+-------
+
+.. versionadded:: 3.25
+
+The :command:`return` command checks its parameters.
+
+The ``OLD`` behavior for this policy is to ignore any parameters given to the
+command.
+The ``NEW`` behavior is to check the validity of the parameters.
+
+This policy was introduced in CMake version 3.25.
+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/CMP0141.rst b/Help/policy/CMP0141.rst
new file mode 100644 (file)
index 0000000..970e41d
--- /dev/null
@@ -0,0 +1,55 @@
+CMP0141
+-------
+
+.. versionadded:: 3.25
+
+MSVC debug information format flags are selected by an abstraction.
+
+Compilers targeting the MSVC ABI have flags to select the debug information
+format. Debug information format selection typically varies with build
+configuration.
+
+In CMake 3.24 and below, debug information format flags are added to
+the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
+automatically.  This allows users to edit their cache entries to adjust the
+flags.  However, the presence of such default flags is problematic for
+projects that want to choose a different runtime library programmatically.
+In particular, it requires string editing of the
+:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
+CMake builtin defaults so they can be replaced.
+
+CMake 3.25 and above prefer to leave the debug information format flags
+out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
+offer a first-class abstraction.  The
+:variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable and
+:prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` target property may be set to
+select the MSVC debug information format.  If they are not set, CMake
+enables debug information in debug configurations using the default value
+``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the
+compiler, and otherwise ``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+This policy provides compatibility with projects that have not been updated
+to be aware of the abstraction.  The policy setting takes effect as of the
+first :command:`project` or :command:`enable_language` command that enables
+a language whose compiler targets the MSVC ABI.
+
+.. note::
+
+  Once the policy has taken effect at the top of a project, that choice
+  will be used throughout the tree.  In projects that have nested projects
+  in subdirectories, be sure to confirm if everything is working with the
+  selected policy behavior.
+
+The ``OLD`` behavior for this policy is to place MSVC debug information
+format flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
+entries and ignore the :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT`
+abstraction.  The ``NEW`` behavior for this policy is to *not* place MSVC
+debug information format flags in the default cache entries and use
+the abstraction instead.
+
+This policy was introduced in CMake version 3.25.  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/CMP0142.rst b/Help/policy/CMP0142.rst
new file mode 100644 (file)
index 0000000..1f928f0
--- /dev/null
@@ -0,0 +1,27 @@
+CMP0142
+-------
+
+.. versionadded:: 3.25
+
+The :generator:`Xcode` generator does not append per-config suffixes to
+library search paths.
+
+In CMake 3.24 and below, the :generator:`Xcode` generator preceded each
+entry of a library search path with a copy of itself appended with
+``$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)``.  This was left from
+very early versions of CMake in which per-config directories were not well
+modeled.  Such paths often do not exist, resulting in warnings from the
+toolchain.  CMake 3.25 and above prefer to not add such library search
+paths.  This policy provides compatibility for projects that may have been
+accidentally relying on the old behavior.
+
+The ``OLD`` behavior for this policy is to append
+``$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)`` to all library search paths.
+The ``NEW`` behavior is to not modify library search paths.
+
+This policy was introduced in CMake version 3.25.  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_dir/SYSTEM.rst b/Help/prop_dir/SYSTEM.rst
new file mode 100644 (file)
index 0000000..4f923b7
--- /dev/null
@@ -0,0 +1,10 @@
+SYSTEM
+------
+
+.. versionadded:: 3.25
+
+This directory property is used to initialize the :prop_tgt:`SYSTEM`
+target property for targets created in that directory. It is set to
+true by :command:`add_subdirectory` and
+:command:`FetchContent_Declare` when the ``SYSTEM`` option is given
+as an argument to those commands.
index d93a9c1..a31ee3a 100644 (file)
@@ -36,4 +36,9 @@ The features known to this version of CMake are:
 
   Compiler mode is at least CUDA/C++ 23.
 
+``cuda_std_26``
+  .. versionadded:: 3.25
+
+  Compiler mode is at least CUDA/C++ 26.
+
 .. include:: CMAKE_LANG_STD_FLAGS.txt
index 6846850..e54b927 100644 (file)
@@ -46,6 +46,11 @@ but it does not necessarily imply complete conformance to that standard.
 
   Compiler mode is at least C++ 23.
 
+``cxx_std_26``
+  .. versionadded:: 3.25
+
+  Compiler mode is at least C++ 26.
+
 .. include:: CMAKE_LANG_STD_FLAGS.txt
 
 Low level individual compile features
@@ -74,233 +79,233 @@ Individual features from C++ 11
 ``cxx_alias_templates``
   Template aliases, as defined in N2258_.
 
-  .. _N2258: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf
+  .. _N2258: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf
 
 ``cxx_alignas``
   Alignment control ``alignas``, as defined in N2341_.
 
-  .. _N2341: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf
+  .. _N2341: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf
 
 ``cxx_alignof``
   Alignment control ``alignof``, as defined in N2341_.
 
-  .. _N2341: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf
+  .. _N2341: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf
 
 ``cxx_attributes``
   Generic attributes, as defined in N2761_.
 
-  .. _N2761: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf
+  .. _N2761: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf
 
 ``cxx_auto_type``
   Automatic type deduction, as defined in N1984_.
 
-  .. _N1984: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1984.pdf
+  .. _N1984: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1984.pdf
 
 ``cxx_constexpr``
   Constant expressions, as defined in N2235_.
 
-  .. _N2235: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf
+  .. _N2235: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf
 
 
 ``cxx_decltype_incomplete_return_types``
   Decltype on incomplete return types, as defined in N3276_.
 
-  .. _N3276 : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3276.pdf
+  .. _N3276 : https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3276.pdf
 
 ``cxx_decltype``
   Decltype, as defined in N2343_.
 
-  .. _N2343: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2343.pdf
+  .. _N2343: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2343.pdf
 
 ``cxx_default_function_template_args``
   Default template arguments for function templates, as defined in DR226_
 
-  .. _DR226: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#226
+  .. _DR226: https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#226
 
 ``cxx_defaulted_functions``
   Defaulted functions, as defined in N2346_.
 
-  .. _N2346: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm
+  .. _N2346: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm
 
 ``cxx_defaulted_move_initializers``
   Defaulted move initializers, as defined in N3053_.
 
-  .. _N3053: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html
+  .. _N3053: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html
 
 ``cxx_delegating_constructors``
   Delegating constructors, as defined in N1986_.
 
-  .. _N1986: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf
+  .. _N1986: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf
 
 ``cxx_deleted_functions``
   Deleted functions, as defined in N2346_.
 
-  .. _N2346: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm
+  .. _N2346: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm
 
 ``cxx_enum_forward_declarations``
   Enum forward declarations, as defined in N2764_.
 
-  .. _N2764: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
+  .. _N2764: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf
 
 ``cxx_explicit_conversions``
   Explicit conversion operators, as defined in N2437_.
 
-  .. _N2437: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf
+  .. _N2437: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf
 
 ``cxx_extended_friend_declarations``
   Extended friend declarations, as defined in N1791_.
 
-  .. _N1791: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf
+  .. _N1791: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf
 
 ``cxx_extern_templates``
   Extern templates, as defined in N1987_.
 
-  .. _N1987: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm
+  .. _N1987: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm
 
 ``cxx_final``
   Override control ``final`` keyword, as defined in N2928_, N3206_ and N3272_.
 
-  .. _N2928: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm
-  .. _N3206: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm
-  .. _N3272: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm
+  .. _N2928: https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm
+  .. _N3206: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm
+  .. _N3272: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm
 
 ``cxx_func_identifier``
   Predefined ``__func__`` identifier, as defined in N2340_.
 
-  .. _N2340: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2340.htm
+  .. _N2340: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2340.htm
 
 ``cxx_generalized_initializers``
   Initializer lists, as defined in N2672_.
 
-  .. _N2672: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm
+  .. _N2672: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm
 
 ``cxx_inheriting_constructors``
   Inheriting constructors, as defined in N2540_.
 
-  .. _N2540: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm
+  .. _N2540: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm
 
 ``cxx_inline_namespaces``
   Inline namespaces, as defined in N2535_.
 
-  .. _N2535: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm
+  .. _N2535: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm
 
 ``cxx_lambdas``
   Lambda functions, as defined in N2927_.
 
-  .. _N2927: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf
+  .. _N2927: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2927.pdf
 
 ``cxx_local_type_template_args``
   Local and unnamed types as template arguments, as defined in N2657_.
 
-  .. _N2657: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm
+  .. _N2657: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm
 
 ``cxx_long_long_type``
   ``long long`` type, as defined in N1811_.
 
-  .. _N1811: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1811.pdf
+  .. _N1811: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1811.pdf
 
 ``cxx_noexcept``
   Exception specifications, as defined in N3050_.
 
-  .. _N3050: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html
+  .. _N3050: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html
 
 ``cxx_nonstatic_member_init``
   Non-static data member initialization, as defined in N2756_.
 
-  .. _N2756: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2756.htm
+  .. _N2756: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2756.htm
 
 ``cxx_nullptr``
   Null pointer, as defined in N2431_.
 
-  .. _N2431: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf
+  .. _N2431: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf
 
 ``cxx_override``
   Override control ``override`` keyword, as defined in N2928_, N3206_
   and N3272_.
 
-  .. _N2928: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm
-  .. _N3206: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm
-  .. _N3272: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm
+  .. _N2928: https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm
+  .. _N3206: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm
+  .. _N3272: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm
 
 ``cxx_range_for``
   Range-based for, as defined in N2930_.
 
-  .. _N2930: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2930.html
+  .. _N2930: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2930.html
 
 ``cxx_raw_string_literals``
   Raw string literals, as defined in N2442_.
 
-  .. _N2442: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm
+  .. _N2442: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm
 
 ``cxx_reference_qualified_functions``
   Reference qualified functions, as defined in N2439_.
 
-  .. _N2439: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm
+  .. _N2439: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm
 
 ``cxx_right_angle_brackets``
   Right angle bracket parsing, as defined in N1757_.
 
-  .. _N1757: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html
+  .. _N1757: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html
 
 ``cxx_rvalue_references``
   R-value references, as defined in N2118_.
 
-  .. _N2118: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html
+  .. _N2118: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html
 
 ``cxx_sizeof_member``
   Size of non-static data members, as defined in N2253_.
 
-  .. _N2253: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html
+  .. _N2253: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html
 
 ``cxx_static_assert``
   Static assert, as defined in N1720_.
 
-  .. _N1720: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html
+  .. _N1720: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html
 
 ``cxx_strong_enums``
   Strongly typed enums, as defined in N2347_.
 
-  .. _N2347: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf
+  .. _N2347: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf
 
 ``cxx_thread_local``
   Thread-local variables, as defined in N2659_.
 
-  .. _N2659: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2659.htm
+  .. _N2659: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2659.htm
 
 ``cxx_trailing_return_types``
   Automatic function return type, as defined in N2541_.
 
-  .. _N2541: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm
+  .. _N2541: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm
 
 ``cxx_unicode_literals``
   Unicode string literals, as defined in N2442_.
 
-  .. _N2442: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm
+  .. _N2442: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm
 
 ``cxx_uniform_initialization``
   Uniform initialization, as defined in N2640_.
 
-  .. _N2640: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf
+  .. _N2640: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf
 
 ``cxx_unrestricted_unions``
   Unrestricted unions, as defined in N2544_.
 
-  .. _N2544: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
+  .. _N2544: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
 
 ``cxx_user_literals``
   User-defined literals, as defined in N2765_.
 
-  .. _N2765: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf
+  .. _N2765: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf
 
 ``cxx_variadic_macros``
   Variadic macros, as defined in N1653_.
 
-  .. _N1653: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm
+  .. _N1653: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm
 
 ``cxx_variadic_templates``
   Variadic templates, as defined in N2242_.
 
-  .. _N2242: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf
+  .. _N2242: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf
 
 
 Individual features from C++ 14
@@ -309,54 +314,54 @@ Individual features from C++ 14
 ``cxx_aggregate_default_initializers``
   Aggregate default initializers, as defined in N3605_.
 
-  .. _N3605: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3605.html
+  .. _N3605: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3605.html
 
 ``cxx_attribute_deprecated``
   ``[[deprecated]]`` attribute, as defined in N3760_.
 
-  .. _N3760: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html
+  .. _N3760: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3760.html
 
 ``cxx_binary_literals``
   Binary literals, as defined in N3472_.
 
-  .. _N3472: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3472.pdf
+  .. _N3472: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3472.pdf
 
 ``cxx_contextual_conversions``
   Contextual conversions, as defined in N3323_.
 
-  .. _N3323: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3323.pdf
+  .. _N3323: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3323.pdf
 
 ``cxx_decltype_auto``
   ``decltype(auto)`` semantics, as defined in N3638_.
 
-  .. _N3638: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3638.html
+  .. _N3638: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3638.html
 
 ``cxx_digit_separators``
   Digit separators, as defined in N3781_.
 
-  .. _N3781: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3781.pdf
+  .. _N3781: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3781.pdf
 
 ``cxx_generic_lambdas``
   Generic lambdas, as defined in N3649_.
 
-  .. _N3649: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3649.html
+  .. _N3649: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3649.html
 
 ``cxx_lambda_init_captures``
   Initialized lambda captures, as defined in N3648_.
 
-  .. _N3648: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3648.html
+  .. _N3648: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3648.html
 
 ``cxx_relaxed_constexpr``
   Relaxed constexpr, as defined in N3652_.
 
-  .. _N3652: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html
+  .. _N3652: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html
 
 ``cxx_return_type_deduction``
   Return type deduction on normal functions, as defined in N3386_.
 
-  .. _N3386: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3386.html
+  .. _N3386: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3386.html
 
 ``cxx_variable_templates``
   Variable templates, as defined in N3651_.
 
-  .. _N3651: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3651.pdf
+  .. _N3651: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3651.pdf
index fb8807f..e70eeb4 100644 (file)
@@ -3,7 +3,7 @@ CPACK_START_MENU_SHORTCUTS
 
 .. versionadded:: 3.3
 
-Species a list of shortcut names that should be created in the `Start Menu`
+Species a list of shortcut names that should be created in the ``Start Menu``
 for this file.
 
 The property is currently only supported by the :cpack_gen:`CPack WIX Generator`.
index f850830..7a05601 100644 (file)
@@ -19,10 +19,11 @@ be executed. The cleanup tests for the fixture will always be executed, even if
 some setup tests fail.
 
 When CTest is asked to execute only a subset of tests (e.g. by the use of
-regular expressions or when run with the ``--rerun-failed`` command line
-option), it will automatically add any setup or cleanup tests for fixtures
-required by any of the tests that are in the execution set. This behavior can
-be overridden with the ``-FS``, ``-FC`` and ``-FA`` command line options to
+regular expressions or when run with the :option:`--rerun-failed <ctest --rerun-failed>`
+command line option), it will automatically add any setup or cleanup tests for
+fixtures required by any of the tests that are in the execution set. This
+behavior can be overridden with the :option:`-FS <ctest -FS>`,
+:option:`-FC <ctest -FC>` and :option:`-FA <ctest -FA>` command line options to
 :manual:`ctest(1)` if desired.
 
 Since setup and cleanup tasks are also tests, they can have an ordering
index a06f152..d827adc 100644 (file)
@@ -4,7 +4,7 @@ LABELS
 Specify a list of text labels associated with a test.  The labels are
 reported in both the ``ctest`` output summary and in dashboard submissions.
 They can also be used to filter the set of tests to be executed (see the
-``ctest -L`` and ``ctest -LE`` :ref:`CTest Options`).
+:option:`ctest -L` and :option:`ctest -LE` options).
 
 See :ref:`Additional Labels` for adding labels to a test dynamically during
 test execution.
index 19323cb..0d2b295 100644 (file)
@@ -5,8 +5,8 @@ COMPILE_WARNING_AS_ERROR
 
 Specify whether to treat warnings on compile as errors.
 If enabled, adds a flag to treat warnings on compile as errors.
-If the ``--compile-no-warning-as-error`` option is given on the
-:manual:`cmake(1)` command line, this property is ignored.
+If the :option:`cmake --compile-no-warning-as-error` option is given
+on the :manual:`cmake(1)` command line, this property is ignored.
 
 This property is not implemented for all compilers.  It is silently ignored
 if there is no implementation for the compiler being used.  The currently
@@ -26,10 +26,12 @@ implemented :variable:`compiler IDs <CMAKE_<LANG>_COMPILER_ID>` are:
 * ``NVIDIA`` (CUDA)
 * ``QCC``
 * ``SunPro``
+* ``Tasking``
 * ``TI``
 * ``VisualAge``
 * ``XL``
 * ``XLClang``
 
 This property is initialized by the value of the variable
-:variable:`CMAKE_COMPILE_WARNING_AS_ERROR` if it is set when a target is created.
+:variable:`CMAKE_COMPILE_WARNING_AS_ERROR` if it is set when a target is
+created.
index 5c2fbd7..69caa39 100644 (file)
@@ -1,13 +1,13 @@
 <CONFIG>_POSTFIX
 ----------------
 
-Postfix to append to the target file name for configuration <CONFIG>.
+Postfix to append to the target file name for configuration ``<CONFIG>``.
 
-When building with configuration <CONFIG> the value of this property
+When building with configuration ``<CONFIG>`` the value of this property
 is appended to the target file name built on disk.  For non-executable
-targets, this property is initialized by the value of the variable
-CMAKE_<CONFIG>_POSTFIX if it is set when a target is created.  This
-property is ignored on the Mac for Frameworks and App Bundles.
+targets, this property is initialized by the value of the
+:variable:`CMAKE_<CONFIG>_POSTFIX` variable if it is set when a target is
+created.  This property is ignored on macOS for Frameworks and App Bundles.
 
 For macOS see also the :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>`
 target property.
index 950ba12..ada69b9 100644 (file)
@@ -39,6 +39,12 @@ Supported values are:
 
   CUDA C++23
 
+``26``
+  .. versionadded:: 3.25
+
+  CUDA C++26. CMake 3.25 and later *recognize* ``26`` as a valid value,
+  no version has support for any compiler.
+
 If the value requested does not result in a compile flag being added for
 the compiler in use, a previous standard flag will be added instead.  This
 means that using:
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS.rst b/Help/prop_tgt/CXX_MODULE_DIRS.rst
new file mode 100644 (file)
index 0000000..a32b5b1
--- /dev/null
@@ -0,0 +1,19 @@
+CXX_MODULE_DIRS
+---------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's default
+C++ module set (i.e. the file set with name and type ``CXX_MODULES``). The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_DIRS_<NAME>` for the list of base directories in
+other C++ module sets.
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
new file mode 100644 (file)
index 0000000..9190991
--- /dev/null
@@ -0,0 +1,19 @@
+CXX_MODULE_DIRS_<NAME>
+----------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's ``<NAME>`` C++
+module set, which has the set type ``CXX_MODULES``. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_DIRS` for the list of base directories in the
+default C++ module set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names
+of all C++ module sets.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS.rst
new file mode 100644 (file)
index 0000000..5f33111
--- /dev/null
@@ -0,0 +1,19 @@
+CXX_MODULE_HEADER_UNIT_DIRS
+---------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's default C++
+module header set (i.e. the file set with name and type
+``CXX_MODULE_HEADER_UNITS``). The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS_<NAME>` for the list of base
+directories in other C++ module header sets.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_DIRS_NAME.rst
new file mode 100644 (file)
index 0000000..b6163da
--- /dev/null
@@ -0,0 +1,21 @@
+CXX_MODULE_HEADER_UNIT_DIRS_<NAME>
+----------------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of base directories of the target's ``<NAME>`` C++
+module header set, which has the set type ``CXX_MODULE_HEADER_UNITS``. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_DIRS` for the list of base directories
+in the default C++ module header set. See
+:prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for the file set names of all C++
+module header sets.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET.rst
new file mode 100644 (file)
index 0000000..3b1bd04
--- /dev/null
@@ -0,0 +1,20 @@
+CXX_MODULE_HEADER_UNIT_SET
+--------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's default C++ module header
+set, (i.e. the file set with name and type ``CXX_MODULE_HEADER_UNITS``). If
+any of the paths are relative, they are computed relative to the target's
+source directory. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>` for the list of files in
+other C++ module header sets.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SETS.rst
new file mode 100644 (file)
index 0000000..ffc2daf
--- /dev/null
@@ -0,0 +1,20 @@
+CXX_MODULE_HEADER_UNIT_SETS
+---------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module header
+sets (i.e. all file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files
+listed in these file sets are treated as source files for the purpose of IDE
+integration.
+
+C++ module header sets may be defined using the :command:`target_sources`
+command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``.
+
+See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET_<NAME>`,
+:prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` and
+:prop_tgt:`INTERFACE_CXX_MODULE_HEADER_UNIT_SETS`.
diff --git a/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_HEADER_UNIT_SET_NAME.rst
new file mode 100644 (file)
index 0000000..4bf5069
--- /dev/null
@@ -0,0 +1,21 @@
+CXX_MODULE_HEADER_UNIT_SET_<NAME>
+---------------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's ``<NAME>`` C++ module header
+set, which has the set type ``CXX_MODULE_HEADER_UNITS``. If any of the paths
+are relative, they are computed relative to the target's source directory. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SET` for the list of files in the
+default C++ module header set. See :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS` for
+the file set names of all C++ module header sets.
diff --git a/Help/prop_tgt/CXX_MODULE_SET.rst b/Help/prop_tgt/CXX_MODULE_SET.rst
new file mode 100644 (file)
index 0000000..f5cd8b2
--- /dev/null
@@ -0,0 +1,20 @@
+CXX_MODULE_SET
+--------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's default C++ module set,
+(i.e. the file set with name and type ``CXX_MODULES``). If any of the paths
+are relative, they are computed relative to the target's source directory. The
+property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_SET_<NAME>` for the list of files in other C++
+module sets.
diff --git a/Help/prop_tgt/CXX_MODULE_SETS.rst b/Help/prop_tgt/CXX_MODULE_SETS.rst
new file mode 100644 (file)
index 0000000..0e8945a
--- /dev/null
@@ -0,0 +1,18 @@
+CXX_MODULE_SETS
+---------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module sets (i.e.
+all file sets with the type ``CXX_MODULES``). Files listed in these file sets
+are treated as source files for the purpose of IDE integration.
+
+C++ module sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``CXX_MODULES``.
+
+See also :prop_tgt:`CXX_MODULE_SET_<NAME>`, :prop_tgt:`CXX_MODULE_SET` and
+:prop_tgt:`INTERFACE_CXX_MODULE_SETS`.
diff --git a/Help/prop_tgt/CXX_MODULE_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst
new file mode 100644 (file)
index 0000000..5674c99
--- /dev/null
@@ -0,0 +1,20 @@
+CXX_MODULE_SET_<NAME>
+---------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Semicolon-separated list of files in the target's ``<NAME>`` C++ module set,
+which has the set type ``CXX_MODULES``. If any of the paths are relative, they
+are computed relative to the target's source directory. The property supports
+:manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+This property is normally only set by :command:`target_sources(FILE_SET)`
+rather than being manipulated directly.
+
+See :prop_tgt:`CXX_MODULE_SET` for the list of files in the default C++ module
+set. See :prop_tgt:`CXX_MODULE_SETS` for the file set names of all C++ module
+sets.
index b10d201..9b381e4 100644 (file)
@@ -37,6 +37,12 @@ Supported values are:
 
   C++23
 
+``26``
+  .. versionadded:: 3.25
+
+  C++26. CMake 3.25 and later *recognize* ``26`` as a valid value,
+  no version has support for any compiler.
+
 If the value requested does not result in a compile flag being added for
 the compiler in use, a previous standard flag will be added instead.  This
 means that using:
diff --git a/Help/prop_tgt/EXPORT_NO_SYSTEM.rst b/Help/prop_tgt/EXPORT_NO_SYSTEM.rst
new file mode 100644 (file)
index 0000000..61f0a8d
--- /dev/null
@@ -0,0 +1,11 @@
+EXPORT_NO_SYSTEM
+----------------
+
+.. versionadded:: 3.25
+
+Specifies that :command:`install(EXPORT)` and :command:`export` commands will
+generate an imported target with :prop_tgt:`SYSTEM` property `OFF`.
+
+See the :prop_tgt:`NO_SYSTEM_FROM_IMPORTED` target property to set this
+behavior on the target *consuming* the include directories rather than the
+one *providing* them.
index 0c767c6..9de8730 100644 (file)
@@ -25,6 +25,12 @@ Supported values are:
 ``23``
   HIP C++23
 
+``26``
+  .. versionadded:: 3.25
+
+  HIP C++26. CMake 3.25 and later *recognize* ``26`` as a valid value,
+  no version has support for any compiler.
+
 If the value requested does not result in a compile flag being added for
 the compiler in use, a previous standard flag will be added instead.  This
 means that using:
index ee22d6f..913d9f2 100644 (file)
@@ -3,11 +3,21 @@ IMPORTED_NO_SYSTEM
 
 .. versionadded:: 3.23
 
+.. deprecated:: 3.25
+
+  ``IMPORTED_NO_SYSTEM`` is deprecated. Set :prop_tgt:`SYSTEM` to `OFF`
+  instead if you don't want target's include directories to be ``SYSTEM``
+  when compiling consumers. Set :prop_tgt:`EXPORT_NO_SYSTEM` to `ON` instead
+  if you don't want the include directories of the imported target generated
+  by :command:`install(EXPORT)` and :command:`export` commands to be
+  ``SYSTEM`` when compiling consumers.
+
 Specifies that an :ref:`Imported Target <Imported Targets>` is not
 a ``SYSTEM`` library.  This has the following effects:
 
 * Entries of :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` are not treated
-  as ``SYSTEM`` include directories when compiling consumers, as they
+  as ``SYSTEM`` include directories when compiling consumers (regardless of
+  the value of the consumed target's :prop_tgt:`SYSTEM` property), as they
   would be by default.   Entries of
   :prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` are not affected,
   and will always be treated as ``SYSTEM`` include directories.
diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_HEADER_UNIT_SETS.rst
new file mode 100644 (file)
index 0000000..3fe6d9a
--- /dev/null
@@ -0,0 +1,18 @@
+INTERFACE_CXX_MODULE_HEADER_UNIT_SETS
+-------------------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PUBLIC`` C++ module header sets (i.e. all
+file sets with the type ``CXX_MODULE_HEADER_UNITS``). Files listed in these
+C++ module header sets can be installed with :command:`install(TARGETS)` and
+exported with :command:`install(EXPORT)` and :command:`export`.
+
+C++ module header sets may be defined using the :command:`target_sources`
+command ``FILE_SET`` option with type ``CXX_MODULE_HEADER_UNITS``.
+
+See also :prop_tgt:`CXX_MODULE_HEADER_UNIT_SETS`.
diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
new file mode 100644 (file)
index 0000000..c7ed46d
--- /dev/null
@@ -0,0 +1,18 @@
+INTERFACE_CXX_MODULE_SETS
+-------------------------
+
+.. versionadded:: 3.25
+
+.. note ::
+
+  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+
+Read-only list of the target's ``PUBLIC`` C++ module sets (i.e. all file sets
+with the type ``CXX_MODULES``). Files listed in these C++ module sets can be
+installed with :command:`install(TARGETS)` and exported with
+:command:`install(EXPORT)` and :command:`export`.
+
+C++ module sets may be defined using the :command:`target_sources` command
+``FILE_SET`` option with type ``CXX_MODULES``.
+
+See also :prop_tgt:`CXX_MODULE_SETS`.
index af16d3c..31f1876 100644 (file)
@@ -3,13 +3,25 @@
 
 .. versionadded:: 3.6
 
-This property is implemented only when ``<LANG>`` is ``C``, ``CXX``, ``OBJC`` or ``OBJCXX``.
+This property is implemented only when ``<LANG>`` is ``C``, ``CXX``, ``OBJC``
+or ``OBJCXX``.
 
-Specify a :ref:`semicolon-separated list <CMake Language Lists>` containing a command
-line for the ``clang-tidy`` tool.  The :ref:`Makefile Generators`
+Specify a :ref:`semicolon-separated list <CMake Language Lists>` containing
+a command line for the ``clang-tidy`` tool.  The :ref:`Makefile Generators`
 and the :generator:`Ninja` generator will run this tool along with the
 compiler and report a warning if the tool reports any problems.
 
+The specified ``clang-tidy`` command line will be invoked with additional
+arguments specifying the source file and, after ``--``, the full compiler
+command line.
+
+.. versionchanged:: 3.25
+
+  If the specified ``clang-tidy`` command line includes the ``-p`` option,
+  it will be invoked without ``--`` and the full compiler command line.
+  ``clang-tidy`` will look up the source file in the specified compiler
+  commands database.
+
 This property is initialized by the value of
 the :variable:`CMAKE_<LANG>_CLANG_TIDY` variable if it is set
 when a target is created.
index cba8ac9..2e039bd 100644 (file)
@@ -14,3 +14,8 @@ its arguments to the tool. Some example tools are distcc and ccache.
 This property is initialized by the value of
 the :variable:`CMAKE_<LANG>_COMPILER_LAUNCHER` variable if it is set
 when a target is created.
+
+.. versionadded:: 3.25
+
+  The property value may use
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
diff --git a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
new file mode 100644 (file)
index 0000000..7f19bc0
--- /dev/null
@@ -0,0 +1,16 @@
+``Embedded``
+  Compile with ``-Z7`` or equivalent flag(s) to produce object files
+  with full symbolic debugging information.
+``ProgramDatabase``
+  Compile with ``-Zi`` or equivalent flag(s) to produce a program
+  database that contains all the symbolic debugging information.
+``EditAndContinue``
+  Compile with ``-ZI`` or equivalent flag(s) to produce a program
+  database that supports the Edit and Continue feature.
+
+The value is ignored on compilers not targeting the MSVC ABI, but an
+unsupported value will be rejected as an error when using a compiler
+targeting the MSVC ABI.
+
+The value may also be the empty string (``""``), in which case no debug
+information format flag will be added explicitly by CMake.
diff --git a/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst
new file mode 100644 (file)
index 0000000..0c7845c
--- /dev/null
@@ -0,0 +1,33 @@
+MSVC_DEBUG_INFORMATION_FORMAT
+-----------------------------
+
+.. versionadded:: 3.25
+
+Select debug information format when targeting the MSVC ABI.
+
+The allowed values are:
+
+.. include:: MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  add_executable(foo foo.c)
+  set_property(TARGET foo PROPERTY
+    MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+
+selects for the target ``foo`` the program database debug information format
+for the Debug configuration.
+
+If this property is not set, CMake selects a debug information format using
+the default value ``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if
+supported by the compiler, and otherwise
+``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+.. note::
+
+  This property has effect only when policy :policy:`CMP0141` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.
index 6c61341..5f8b82d 100644 (file)
@@ -11,8 +11,9 @@
   Compile with ``-MDd`` or equivalent flag(s) to use a multi-threaded
   dynamically-linked runtime library.
 
-The value is ignored on non-MSVC compilers but an unsupported value will
-be rejected as an error when using a compiler targeting the MSVC ABI.
+The value is ignored on compilers not targeting the MSVC ABI, but an
+unsupported value will be rejected as an error when using a compiler
+targeting the MSVC ABI.
 
 The value may also be the empty string (``""``) in which case no runtime
 library selection flag will be added explicitly by CMake.  Note that with
index 9b978b2..9f13d38 100644 (file)
@@ -21,7 +21,9 @@ support per-configuration specification.  For example, the code:
 selects for the target ``foo`` a multi-threaded statically-linked runtime
 library with or without debug information depending on the configuration.
 
-If this property is not set then CMake uses the default value
+The property is initialized from the value of the
+:variable:`CMAKE_MSVC_RUNTIME_LIBRARY` variable, if it is set.
+If the property is not set, then CMake uses the default value
 ``MultiThreaded$<$<CONFIG:Debug>:Debug>DLL`` to select a MSVC runtime library.
 
 .. note::
index 654e687..6ac8216 100644 (file)
@@ -31,6 +31,12 @@ Supported values are:
 
   Objective C++23
 
+``26``
+  .. versionadded:: 3.25
+
+  Objective C++26. CMake 3.25 and later *recognize* ``26`` as a valid value,
+  no version has support for any compiler.
+
 If the value requested does not result in a compile flag being added for
 the compiler in use, a previous standard flag will be added instead.  This
 means that using:
diff --git a/Help/prop_tgt/SYSTEM.rst b/Help/prop_tgt/SYSTEM.rst
new file mode 100644 (file)
index 0000000..a267738
--- /dev/null
@@ -0,0 +1,22 @@
+SYSTEM
+------
+
+.. versionadded:: 3.25
+
+Specifies that a target is a ``SYSTEM`` library.  This has the following
+effects:
+
+* Entries of :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` are treated as
+  ``SYSTEM`` include directories when compiling consumers.
+  Entries of :prop_tgt:`INTERFACE_SYSTEM_INCLUDE_DIRECTORIES` are not
+  affected, and will always be treated as ``SYSTEM`` include directories.
+
+For imported targets, this property defaults to true, which means
+that their :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` are treated
+as ``SYSTEM`` by default. If their ``SYSTEM`` property is false,
+then their :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` will not be
+treated as ``SYSTEM``, regardless of the value of the
+:prop_tgt:`IMPORTED_NO_SYSTEM` property.
+
+This target property is initialized from the :prop_dir:`SYSTEM`
+directory property when the target is created.
index 58476d6..1e84c00 100644 (file)
@@ -9,5 +9,5 @@ The property value may use
 This is defined in ``<LocalDebuggerCommand>`` in the Visual Studio
 project file.
 
-This property only works for Visual Studio 2010 and above;
+This property only works for Visual Studio 11 2012 and above;
 it is ignored on other generators.
index 6c26601..e54e140 100644 (file)
@@ -9,5 +9,5 @@ The property value may use
 This is defined in ``<LocalDebuggerCommandArguments>`` in the Visual Studio
 project file.
 
-This property only works for Visual Studio 2010 and above;
+This property only works for Visual Studio 11 2012 and above;
 it is ignored on other generators.
index 2f59a82..60bc2f0 100644 (file)
@@ -9,5 +9,5 @@ The property value may use
 This is defined in ``<LocalDebuggerEnvironment>`` in the Visual Studio
 project file.
 
-This property only works for Visual Studio 2010 and above;
+This property only works for Visual Studio 11 2012 and above;
 it is ignored on other generators.
index c163abf..f9ce7aa 100644 (file)
@@ -9,5 +9,5 @@ The property value may use
 This is defined in ``<LocalDebuggerWorkingDirectory>`` in the Visual Studio
 project file.
 
-This property only works for Visual Studio 2010 and above;
+This property only works for Visual Studio 11 2012 and above;
 it is ignored on other generators.
index 5212293..8a85ba4 100644 (file)
@@ -12,7 +12,7 @@ If the property is unset, Visual Studio uses the first matching
 than one ``Main()`` method is available in the current project, the property
 becomes mandatory for building the project.
 
-This property only works for Visual Studio 2010 and above;
+This property only works for Visual Studio 11 2012 and above;
 it is ignored on other generators.
 
 .. code-block:: cmake
index 6c2e042..221b986 100644 (file)
@@ -7,4 +7,4 @@ Can be set to change the visual studio keyword, for example Qt
 integration works better if this is set to Qt4VSv1.0.
 
 Use the :prop_tgt:`VS_GLOBAL_KEYWORD` target property to set the
-keyword for Visual Studio 10 (2010) and newer.
+keyword for Visual Studio 11 (2012) and newer.
index 8f46d2f..fa3c5bc 100644 (file)
@@ -30,6 +30,9 @@ at target creation time.
 - :prop_tgt:`XCODE_SCHEME_THREAD_SANITIZER_STOP`
 - :prop_tgt:`XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER`
 - :prop_tgt:`XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP`
+- :prop_tgt:`XCODE_SCHEME_LAUNCH_CONFIGURATION`
+- :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_API_VALIDATION`
+- :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION`
 - :prop_tgt:`XCODE_SCHEME_ZOMBIE_OBJECTS`
 
 The following target properties will be applied on the
@@ -41,4 +44,5 @@ The following target properties will be applied on the
 - :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE`
 - :prop_tgt:`XCODE_SCHEME_ENVIRONMENT`
 - :prop_tgt:`XCODE_SCHEME_EXECUTABLE`
+- :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE`
 - :prop_tgt:`XCODE_SCHEME_WORKING_DIRECTORY`
diff --git a/Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst b/Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst
new file mode 100644 (file)
index 0000000..3b801c5
--- /dev/null
@@ -0,0 +1,14 @@
+XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+--------------------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Metal: API Validation`` in the Options section of
+the generated Xcode scheme.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION`
+if it is set when a target is created.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst b/Help/prop_tgt/XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst
new file mode 100644 (file)
index 0000000..5358087
--- /dev/null
@@ -0,0 +1,14 @@
+XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
+-----------------------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Metal: Shader Validation`` in the Options section of
+the generated Xcode scheme.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION`
+if it is set when a target is created.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/prop_tgt/XCODE_SCHEME_LAUNCH_CONFIGURATION.rst b/Help/prop_tgt/XCODE_SCHEME_LAUNCH_CONFIGURATION.rst
new file mode 100644 (file)
index 0000000..9643322
--- /dev/null
@@ -0,0 +1,13 @@
+XCODE_SCHEME_LAUNCH_CONFIGURATION
+---------------------------------
+
+.. versionadded:: 3.25
+
+Set the build configuration to run the target.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION`
+if it is set when a target is created.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst b/Help/prop_tgt/XCODE_SCHEME_LAUNCH_MODE.rst
new file mode 100644 (file)
index 0000000..df5ae07
--- /dev/null
@@ -0,0 +1,22 @@
+XCODE_SCHEME_LAUNCH_MODE
+------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Launch`` in the Info section of the generated Xcode
+scheme.
+
+Possible values are:
+
+``AUTO``
+  Launch automatically. This is the default.
+
+``WAIT``
+  Wait for the executable to be launched.
+
+This property is initialized by the value of the variable
+:variable:`CMAKE_XCODE_SCHEME_LAUNCH_MODE` if it is set when a target is
+created.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
index 64491e3..e37c130 100644 (file)
@@ -11,7 +11,7 @@ Documentation Changes
 =====================
 
 * The CMake documentation has been converted to reStructuredText and
-  now transforms via Sphinx (`<http://sphinx-doc.org>`__) into man and
+  now transforms via Sphinx (`<https://www.sphinx-doc.org>`__) into man and
   html pages.  This allows the documentation to be properly indexed
   and to contain cross-references.
 
index a80657d..957dd4f 100644 (file)
@@ -174,7 +174,7 @@ Modules
   to removal of the ``javah`` tool by `JEP 313`_.
 
 .. _`FLAME`: https://github.com/flame
-.. _`JEP 313`: http://openjdk.java.net/jeps/313
+.. _`JEP 313`: https://openjdk.java.net/jeps/313
 
 Autogen
 -------
index 481027e..dcdae1d 100644 (file)
@@ -242,7 +242,7 @@ CPack
 * A :cpack_gen:`CPack NuGet Generator` was was added with basic
   support for `NuGet`_.
 
-.. _NuGet: https://docs.microsoft.com/en-us/nuget/what-is-nuget
+.. _NuGet: https://learn.microsoft.com/en-us/nuget/what-is-nuget
 
 Other
 -----
@@ -302,4 +302,4 @@ Other Changes
   (and legacy ``swig_add_module`` command) now set the prefix of
   Java modules to ``""`` for MINGW, MSYS, and CYGWIN environments.
 
-.. _`Fortran Submodules`: http://fortranwiki.org/fortran/show/Submodules
+.. _Fortran Submodules: https://fortranwiki.org/fortran/show/Submodules
index 42e95be..40cac41 100644 (file)
@@ -217,8 +217,8 @@ Autogen
 CTest
 -----
 
-* :manual:`ctest(1)` gained a ``--test-dir`` option to specify the directory
-  in which to look for tests.
+* :manual:`ctest(1)` gained a :option:`--test-dir <ctest --test-dir>`
+  option to specify the directory in which to look for tests.
 
 CPack
 -----
@@ -263,7 +263,7 @@ CPack
     :variable:`CPACK_NUGET_<compName>_PACKAGE_LANGUAGE` allow the locale
     for a package to be specified, for example ``en_CA``.
 
-.. _Software Package Data Exchange: https://spdx.org/
+.. _Software Package Data Exchange: https://spdx.dev/
 
 Deprecated and Removed Features
 ===============================
index 462d2be..91c3768 100644 (file)
@@ -49,15 +49,15 @@ Languages
 Command-Line
 ------------
 
-* :manual:`cmake(1)` gained the ``--install-prefix <dir>``
+* :manual:`cmake(1)` gained the :option:`--install-prefix <cmake --install-prefix>`
   command-line option to specify the location of the install prefix.
 
-* :manual:`cmake(1)` gained the ``--toolchain <path/to/file>``
+* :manual:`cmake(1)` gained the :option:`--toolchain <cmake --toolchain>`
   command-line option to specify a toolchain file.
 
-* :manual:`cmake(1)` ``-E capabilities`` output, for some generators,
-  may now contain a ``supportedPlatforms`` field listing platforms
-  known to be supported in :variable:`CMAKE_GENERATOR_PLATFORM`.
+* :manual:`cmake(1)` :option:`-E capabilities <cmake-E capabilities>` output,
+  for some generators, may now contain a ``supportedPlatforms`` field listing
+  platforms known to be supported in :variable:`CMAKE_GENERATOR_PLATFORM`.
 
 * Messages printed to a terminal now may be colored by message type.
 
@@ -208,8 +208,8 @@ CTest
   :prop_test:`ATTACHED_FILES_ON_FAIL` test properties.
   See :ref:`Additional Test Measurements` for more information.
 
-* :manual:`ctest(1)` gained a ``--output-junit`` option to write test results
-  to a JUnit XML file.
+* :manual:`ctest(1)` gained a :option:`--output-junit <ctest --output-junit>`
+  option to write test results to a JUnit XML file.
 
 * The :command:`ctest_build` command gained a ``PARALLEL_LEVEL`` option.
 
index 47b1869..6376d77 100644 (file)
@@ -43,19 +43,22 @@ Generators
 Command-Line
 ------------
 
-* The :manual:`cmake(1)` ``--build`` command, when used with
+* The :manual:`cmake(1)` :option:`--build <cmake --build>` command, when used with
   :ref:`Visual Studio Generators` on projects that set the
   :prop_tgt:`VS_PACKAGE_REFERENCES` target property, now automatically
   restores package references from NuGet.  The cache variable
   :variable:`CMAKE_VS_NUGET_PACKAGE_RESTORE` may be set to toggle this behavior
-  in a build tree.  Use the ``--resolve-package-references=<on|off|only>``
+  in a build tree.  Use the
+  :option:`--resolve-package-references <cmake--build --resolve-package-references>`
   command-line option to control the behavior on one invocation.
 
-* The :manual:`cmake(1)` command line tool gained a ``--debug-find-pkg=``
+* The :manual:`cmake(1)` command line tool gained a
+  :option:`--debug-find-pkg <cmake --debug-find-pkg>`
   option to enable debug messages under specific :command:`find_package`
   calls.
 
-* The :manual:`cmake(1)` command line tool gained a ``--debug-find-var=``
+* The :manual:`cmake(1)` command line tool gained a
+  :option:`--debug-find-var <cmake --debug-find-var>`
   option to enable debug messages for ``find_*`` calls that use specific
   result variables.
 
index 9317e51..f484e1a 100644 (file)
@@ -34,25 +34,28 @@ Generators
 Command-Line
 ------------
 
-* :manual:`cmake(1)` gained the ``--fresh`` command-line option to remove
-  any existing ``CMakeCache.txt`` file and associated ``CMakeFiles/``
+* :manual:`cmake(1)` gained the :option:`--fresh <cmake --fresh>` command-line
+  option to remove any existing ``CMakeCache.txt`` file and associated ``CMakeFiles/``
   directory, when configuring a build tree, thus starting a new configuration
   as if the build tree were freshly created.
 
-* :manual:`cmake(1)` gained the ``--compile-no-warning-as-error`` command-line
-  option which causes the effects of the :prop_tgt:`COMPILE_WARNING_AS_ERROR`
-  target property and :variable:`CMAKE_COMPILE_WARNING_AS_ERROR` variable
-  to be ignored.
+* :manual:`cmake(1)` gained the
+  :option:`--compile-no-warning-as-error <cmake --compile-no-warning-as-error>`
+  command-line option which causes the effects of the
+  :prop_tgt:`COMPILE_WARNING_AS_ERROR` target property and
+  :variable:`CMAKE_COMPILE_WARNING_AS_ERROR` variable to be ignored.
 
-* The :manual:`cmake(1)` ``--trace=json-v1`` trace format gained fields
-  ``global_frame`` and ``line_end``.
+* The :manual:`cmake(1)` :option:`--trace=json-v1 <cmake --trace>` trace
+  format gained fields ``global_frame`` and ``line_end``.
 
-* The :manual:`cmake(1)` ``-E`` commands ``cat`` and ``env`` learned to respect
-  a double dash (``--``) argument that acts as a delimiter indicating the end of
-  options. Any following arguments are treated as operands/positional arguments,
-  even if they begin with a dash ``-`` character.
+* The :manual:`cmake(1)` :option:`-E <cmake -E>` commands :option:`cat <cmake-E cat>`
+  and :option:`env <cmake-E env>` learned to respect a double dash
+  (:option:`-- <cmake-E_env -->`) argument that acts as a delimiter indicating
+  the end of options. Any following arguments are treated as operands/positional
+  arguments, even if they begin with a dash ``-`` character.
 
-* The :manual:`cmake(1)` ``-E tar`` command gained the ``--touch`` option
+* The :manual:`cmake(1)` :option:`-E tar <cmake-E tar>` command gained the
+  :option:`--touch <cmake-E_tar --touch>` option
   to keep the current local timestamp instead of extracting file timestamps
   from the archive.
 
@@ -289,9 +292,10 @@ Generator Expressions
 CTest
 -----
 
-* :manual:`ctest(1)` gained a ``--test-output-truncation`` option (and
-  corresponding :variable:`CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION` variable) to
-  specify the truncation mode once the maximum test output size has been
+* :manual:`ctest(1)` gained a
+  :option:`--test-output-truncation <ctest --test-output-truncation>` option
+  (and corresponding :variable:`CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION` variable)
+  to specify the truncation mode once the maximum test output size has been
   reached. Possible values are ``tail`` (default), ``middle`` or ``head``.
 
 CPack
diff --git a/Help/release/3.25.rst b/Help/release/3.25.rst
new file mode 100644 (file)
index 0000000..7d0cf0a
--- /dev/null
@@ -0,0 +1,240 @@
+CMake 3.25 Release Notes
+************************
+
+.. only:: html
+
+  .. contents::
+
+Changes made since CMake 3.24 include the following.
+
+New Features
+============
+
+Presets
+-------
+
+* The :manual:`cmake-presets(7)` schema version has been bumped to ``6``.
+
+* The :manual:`cmake-presets(7)` format now supports a
+  ``packagePresets`` field to specify presets for :option:`cpack --preset`.
+
+* The :manual:`cmake-presets(7)` format now supports a
+  ``workflowPresets`` field to specify presets for :option:`cmake --workflow`.
+
+* The :manual:`cmake-presets(7)` format now supports an
+  ``outputJUnitFile`` field to specify JUnit output in test presets.
+
+Languages
+---------
+
+* The :manual:`Compile Features <cmake-compile-features(7)>` functionality
+  is now aware of C++26, and defines a ``cxx_std_26`` meta-feature.
+  C++26 compiler modes may also be specified via the :prop_tgt:`CXX_STANDARD`,
+  :prop_tgt:`CUDA_STANDARD`, :prop_tgt:`HIP_STANDARD`,
+  or :prop_tgt:`OBJCXX_STANDARD` target properties.
+
+* ``CUDA`` language support now includes device link-time optimization when
+  using ``nvcc``.  The :variable:`CMAKE_INTERPROCEDURAL_OPTIMIZATION` variable
+  and the associated :prop_tgt:`INTERPROCEDURAL_OPTIMIZATION` target property
+  will activate device LTO.
+
+Command-Line
+------------
+
+* A :option:`cmake --workflow --preset <cmake--workflow --preset>` mode was
+  added to drive sequences of configure, build, test, and package operations
+  through a single command.
+
+* The :option:`cmake -E capabilities <cmake-E capabilities>` command
+  gained a new ``tls`` field that tells whether or not TLS is enabled.
+
+* The :option:`cmake -E env <cmake-E env>` command-line tool gained
+  a ``--modify`` flag to support :prop_test:`ENVIRONMENT_MODIFICATION`
+  operations.
+
+* The :option:`cmake --debug-trycompile` option now prints log messages
+  reporting the directory in which each try-compile check is done.
+
+Compilers
+---------
+
+* Support for the `Tasking compiler toolsets`_ (SmartCode, TriCore,
+  Standalone: ARM, MCS, 8051) was added with compiler id ``Tasking``.
+  See the :variable:`CMAKE_TASKING_TOOLSET` variable.
+
+.. _Tasking compiler toolsets: https://www.tasking.com
+
+Commands
+--------
+
+* The :command:`add_subdirectory` command gained a ``SYSTEM`` option
+  to enable the :prop_dir:`SYSTEM` directory property in the subdirectory.
+
+* The :command:`block` and :command:`endblock` commands were added to manage
+  specific scopes (policy or variable) for a contained block of commands.
+
+* The :command:`cmake_language` command gained a new
+  ``GET_MESSAGE_LOG_LEVEL`` sub-command.  It can be used to
+  query the current message logging level.
+
+* The :command:`find_file`, :command:`find_path`, :command:`find_library`, and
+  :command:`find_program` commands gained a ``VALIDATOR`` option to specify a
+  function to be called for each candidate item to validate it.
+
+* The :command:`find_package` command now considers paths of
+  the form ``<prefix>/<name>*/(cmake|CMake)/<name>*/`` when
+  searching for package configuration files.
+
+* The :command:`return` command gained a ``PROPAGATE`` option to propagate
+  variables to the scope to which control returns.
+  See policy :policy:`CMP0140`.
+
+* The :command:`try_compile` and :command:`try_run` commands gained new
+  signatures that more consistently use keyword dispatch and do not require a
+  binary directory to be specified.  Additionally, these signatures use a
+  unique directory for each invocation, which allows multiple outputs to be
+  preserved when using :option:`cmake --debug-trycompile`.
+
+* The :command:`try_compile` and :command:`try_run` commands gained the
+  option ``NO_CACHE`` to store results in normal variables.
+
+* The :command:`try_run` command gained ``RUN_OUTPUT_STDOUT_VARIABLE``
+  and ``RUN_OUTPUT_STDERR_VARIABLE`` options to capture stdout and stderr
+  separately from the output of the compiled program.
+
+Variables
+---------
+
+* The :variable:`BSD` and :variable:`CMAKE_HOST_BSD` variables are now set
+  to a string value when the target or host system is BSD, respectively.
+
+* The :variable:`LINUX` and :variable:`CMAKE_HOST_LINUX` variables are
+  now set to true when the target or host system is Linux, respectively.
+
+* The :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable and
+  :prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` target property were introduced
+  to select the debug information format for compilers targeting the MSVC ABI.
+  See policy :policy:`CMP0141`.
+
+* The :variable:`CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION` variable and
+  corresponding :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_API_VALIDATION` target
+  property were added to tell the :generator:`Xcode` generator what to put
+  in the scheme's ``Metal: API Validation`` setting.
+
+* The :variable:`CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION` variable and
+  corresponding :prop_tgt:`XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION` target
+  property were added to tell the :generator:`Xcode` generator what to put
+  in the scheme's ``Metal: Shader Validation`` setting.
+
+* The :variable:`CMAKE_XCODE_SCHEME_LAUNCH_MODE` variable and corresponding
+  :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE` target property were added to tell
+  the :generator:`Xcode` generator what to put in the scheme's "Launch"
+  mode setting.
+
+* The :variable:`CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION` variable and
+  corresponding :prop_tgt:`XCODE_SCHEME_LAUNCH_CONFIGURATION` target
+  property were added to tell the :generator:`Xcode` generator what
+  configuration to put in the scheme's Launch action.
+
+Properties
+----------
+
+* The :prop_tgt:`<LANG>_COMPILER_LAUNCHER` target property now supports
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+* The :prop_tgt:`EXPORT_NO_SYSTEM` target property was added to
+  specify that :command:`install(EXPORT)` and :command:`export`
+  commands will generate na imported target with
+  :prop_tgt:`SYSTEM` property ``OFF``.
+
+* The :prop_tgt:`SYSTEM` target property was added to specify
+  whether a target should be treated as a system library (i.e.
+  its include directories are automatically ``SYSTEM`` when
+  compiling consumers).  If not set, the default is the previous
+  behavior: on for imported targets and off for other targets.
+
+* The :prop_dir:`SYSTEM` directory property was added to initialize the
+  :prop_tgt:`SYSTEM` target property for targets created in that directory.
+
+Modules
+-------
+
+* The :module:`FetchContent` module :command:`FetchContent_Declare`
+  command gained a ``SYSTEM`` option to enable the :prop_dir:`SYSTEM`
+  directory property in the subdirectory.
+
+* The :module:`FindCUDAToolkit` module now provides a target for
+  :ref:`nvtx3 <cuda_toolkit_nvtx3>` for CUDA 10.0+, which supersedes
+  :ref:`nvToolsExt <cuda_toolkit_nvToolsExt>`. A deprecation warning
+  is emitted when using ``nvToolsExt`` if the project requires CMake
+  3.25 and CUDA 10.0+ is used.
+
+* The :module:`FindDoxygen` module's version handling has been improved:
+
+  * Multiple candidate installations will now be considered, if needed,
+    to satisfy version constraints.  Previously, only the first one
+    encountered would be considered.
+
+  * Version ranges are supported.
+
+  * Variations in the version format reported by Doxygen are now
+    tolerated (e.g. a trailing git commit hash).
+
+* The :module:`FindOpenAL` module now provides an imported target.
+
+* The :module:`FindOpenSP` module was added to find the OpenSP library.
+
+* The :module:`FindVulkan` module gained support for new components:
+
+  ``dxc``
+    DirectX Shader Compiler.
+
+  ``volk``
+    Volk open-source vulkan meta-loader.
+
+CPack
+-----
+
+* The :cpack_gen:`CPack Archive Generator` gained a new
+  :variable:`CPACK_ARCHIVE_FILE_EXTENSION` variable to control
+  the package file name extension.
+
+* The :cpack_gen:`CPack NSIS Generator` gained two new variables
+  :variable:`CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS` and
+  :variable:`CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS`
+  to provide arguments to the nsis executable invocation.
+
+* The :module:`CPack` module gained the :variable:`CPACK_READELF_EXECUTABLE`,
+  :variable:`CPACK_OBJCOPY_EXECUTABLE`, and
+  :variable:`CPACK_OBJDUMP_EXECUTABLE` variables to control the locations
+  of binutils used by :manual:`cpack(1)`.
+
+Deprecated and Removed Features
+===============================
+
+* The :prop_tgt:`IMPORTED_NO_SYSTEM` target property has been deprecated
+  in favor of :prop_tgt:`SYSTEM` and :prop_tgt:`EXPORT_NO_SYSTEM`.
+
+* The :generator:`Visual Studio 10 2010` generator has been removed.
+
+* The :generator:`Visual Studio 11 2012` generator is now deprecated
+  and will be removed in a future version of CMake.
+
+Other Changes
+=============
+
+* On Windows, when targeting the MSVC ABI, the :command:`find_library` command
+  now accepts ``.a`` file names after first considering ``.lib``.  This is
+  symmetric with existing behavior when targeting the GNU ABI, in which the
+  command accepts ``.lib`` file names after first considering ``.a``.
+
+* The :envvar:`SSL_CERT_FILE` and :envvar:`SSL_CERT_DIR` environment
+  variables can now be used to override where to find certificate
+  authorities for TLS/SSL operations.
+
+* If :prop_tgt:`<LANG>_CLANG_TIDY` includes a ``-p`` argument, the
+  full compiler command line is no longer appended after ``--``.
+
+* The :generator:`Xcode` generator no longer adds the per-config suffix
+  ``$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)`` to library search paths.
+  See policy :policy:`CMP0142`.
index 44f4e19..2dc237f 100644 (file)
@@ -26,7 +26,7 @@ Generators
   added on Windows.  `Green Hills MULTI`_ is an IDE for embedded
   real-time systems.
 
-.. _`Green Hills MULTI`: http://www.ghs.com/products/MULTI_IDE.html
+.. _`Green Hills MULTI`: https://www.ghs.com/products/MULTI_IDE.html
 
 Commands
 --------
index 943d267..abfede6 100644 (file)
@@ -227,7 +227,7 @@ Other
   will be merged with linker-generated manifests and embedded in the
   binary.
 
-* The `Concurrent Fortran 77 <https://ccur.com>`__ compiler is now supported.
+* The Concurrent Fortran 77 compiler is now supported.
   Its :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` is ``CCur``.
 
 * :manual:`cmake(1)` gained a new ``--trace-expand`` command line option
index 345c056..9656a54 100644 (file)
@@ -315,5 +315,5 @@ Other Changes
 * Vim support files ``indent/cmake.vim`` and ``syntax/cmake.vim``
   from the `vim-cmake-syntax`_ project are now distributed with CMake.
 
-.. _`Fortran Submodules`: http://fortranwiki.org/fortran/show/Submodules
+.. _`Fortran Submodules`: https://fortranwiki.org/fortran/show/Submodules
 .. _`vim-cmake-syntax`: https://github.com/pboettch/vim-cmake-syntax
index 4dfac8a..b6ecf7b 100644 (file)
@@ -13,6 +13,7 @@ Releases
 .. toctree::
    :maxdepth: 1
 
+   3.25 <3.25>
    3.24 <3.24>
    3.23 <3.23>
    3.22 <3.22>
diff --git a/Help/variable/BSD.rst b/Help/variable/BSD.rst
new file mode 100644 (file)
index 0000000..fdfe5ec
--- /dev/null
@@ -0,0 +1,7 @@
+BSD
+---
+
+.. versionadded:: 3.25
+
+Set to a string value when the target system is BSD. This value can be one of
+the following: DragonFlyBSD, FreeBSD, OpenBSD, or NetBSD.
index 4388bf2..aba9b6e 100644 (file)
@@ -8,6 +8,7 @@ Edition`, this variable may be set to specify the default value for the
 :prop_tgt:`ANDROID_API` target property.  See that target property for
 additional information.
 
-Otherwise, when :ref:`Cross Compiling for Android`, this variable provides
-the Android API version number targeted.  This will be the same value as
-the :variable:`CMAKE_SYSTEM_VERSION` variable for ``Android`` platforms.
+When :ref:`Cross Compiling for Android`, the :variable:`CMAKE_SYSTEM_VERSION`
+variable represents the Android API version number targeted.  For historical
+reasons, if a toolchain file sets ``CMAKE_ANDROID_API``, but not
+``CMAKE_SYSTEM_VERSION``, the latter will be initialized using the former.
index 3b323b7..e601eb8 100644 (file)
@@ -7,7 +7,7 @@ This is the full path to the top level of the current CMake build
 tree.  For an in-source build, this would be the same as
 :variable:`CMAKE_SOURCE_DIR`.
 
-When run in -P script mode, CMake sets the variables
+When run in :option:`cmake -P` script mode, CMake sets the variables
 :variable:`CMAKE_BINARY_DIR`, :variable:`CMAKE_SOURCE_DIR`,
 :variable:`CMAKE_CURRENT_BINARY_DIR` and
 :variable:`CMAKE_CURRENT_SOURCE_DIR` to the current working directory.
index 43668ea..3a57659 100644 (file)
@@ -19,7 +19,7 @@ Example values:
 ::
 
   $(ConfigurationName) = Visual Studio 9
-  $(Configuration)     = Visual Studio 10
+  $(Configuration)     = Visual Studio 11 and above
   $(CONFIGURATION)     = Xcode
   .                    = Make-based tools
   .                    = Ninja
index f80b46c..e65ff8c 100644 (file)
@@ -4,5 +4,5 @@ CMAKE_COMMAND
 The full path to the :manual:`cmake(1)` executable.
 
 This is the full path to the CMake executable :manual:`cmake(1)` which is
-useful from custom commands that want to use the ``cmake -E`` option for
+useful from custom commands that want to use the :option:`cmake -E` option for
 portable system commands.  (e.g.  ``/usr/local/bin/cmake``)
index 3a81d68..f851a18 100644 (file)
@@ -5,6 +5,6 @@ CMAKE_CPACK_COMMAND
 
 Full path to :manual:`cpack(1)` command installed with CMake.
 
-This is the full path to the CPack executable :manual:`cpack(1)` which is
-useful from custom commands that want to use the :manual:`cmake(1)` ``-E``
-option for portable system commands.
+This is the full path to the CPack executable :manual:`cpack(1)`
+that can be used for custom commands or tests to invoke
+CPack commands.
index b2942e2..a3b751f 100644 (file)
@@ -3,6 +3,6 @@ CMAKE_CTEST_COMMAND
 
 Full path to :manual:`ctest(1)` command installed with CMake.
 
-This is the full path to the CTest executable :manual:`ctest(1)` which is
-useful from custom commands that want to use the :manual:`cmake(1)` ``-E``
-option for portable system commands.
+This is the full path to the CTest executable :manual:`ctest(1)`
+that can be used for custom commands or tests to invoke
+CTest commands.
index 8fc85ee..15f81d2 100644 (file)
@@ -9,7 +9,7 @@ create a binary directory in the build tree, and as it is being
 processed this variable will be set.  For in-source builds this is the
 current source directory being processed.
 
-When run in -P script mode, CMake sets the variables
+When run in :option:`cmake -P` script mode, CMake sets the variables
 :variable:`CMAKE_BINARY_DIR`, :variable:`CMAKE_SOURCE_DIR`,
 :variable:`CMAKE_CURRENT_BINARY_DIR` and
 :variable:`CMAKE_CURRENT_SOURCE_DIR` to the current working directory.
index 1a25efc..5b86026 100644 (file)
@@ -6,7 +6,7 @@ The path to the source directory currently being processed.
 This is the full path to the source directory that is currently being
 processed by cmake.
 
-When run in -P script mode, CMake sets the variables
+When run in :option:`cmake -P` script mode, CMake sets the variables
 :variable:`CMAKE_BINARY_DIR`, :variable:`CMAKE_SOURCE_DIR`,
 :variable:`CMAKE_CURRENT_BINARY_DIR` and
 :variable:`CMAKE_CURRENT_SOURCE_DIR` to the current working directory.
index f77e939..58818f4 100644 (file)
@@ -13,6 +13,6 @@ This switch should be used during the initial CMake run.  Otherwise if
 the package has already been found in a previous CMake run, the
 variables which have been stored in the cache will still be there.  In
 that case it is recommended to remove the cache variables for this
-package from the cache using the cache editor or :manual:`cmake(1)` ``-U``
+package from the cache using the cache editor or :option:`cmake -U`.
 
 See also the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable.
index 53a19dc..a7e9029 100644 (file)
@@ -33,7 +33,7 @@ property for all targets.
 
 .. note::
   This option is implemented only by :ref:`Makefile Generators`
-  and the :generator:`Ninja`.  It is ignored on other generators.
+  and :ref:`Ninja Generators`.  It is ignored on other generators.
 
   This option currently does not work well in combination with
   the :prop_tgt:`UNITY_BUILD` target property or the
index 8f2a82f..48b0dce 100644 (file)
@@ -13,7 +13,7 @@ error:
 * :command:`find_package`
 
 Output is designed for human consumption and not for parsing.
-Enabling this variable is equivalent to using :manual:`cmake <cmake(1)>` ``--debug-find``
+Enabling this variable is equivalent to using :option:`cmake --debug-find`
 with the added ability to enable debugging for a subset of find calls.
 
 .. code-block:: cmake
index 5d45fb0..b27a3d9 100644 (file)
@@ -25,4 +25,4 @@ CMake configures the project.
 
 ``CMAKE_FIND_PACKAGE_REDIRECTS_DIR`` is only set in CMake project mode.
 It is not set when CMake is run in script mode
-(i.e. :manual:`cmake -P ... <cmake(1)>`).
+(i.e. :option:`cmake -P`).
index ec52cd4..0e249eb 100644 (file)
@@ -7,6 +7,6 @@ The name of the generator that is being used to generate the build
 files.  (e.g.  ``Unix Makefiles``, ``Ninja``, etc.)
 
 The value of this variable should never be modified by project code.
-A generator may be selected via the :manual:`cmake(1)` ``-G`` option,
+A generator may be selected via the :option:`cmake -G` option,
 interactively in :manual:`cmake-gui(1)`, or via the :envvar:`CMAKE_GENERATOR`
 environment variable.
index b17d83a..acb7b2e 100644 (file)
@@ -7,7 +7,7 @@ Generator-specific target platform specification provided by user.
 
 Some CMake generators support a target platform name to be given
 to the native build system to choose a compiler toolchain.
-If the user specifies a platform name (e.g. via the :manual:`cmake(1)` ``-A``
+If the user specifies a platform name (e.g. via the :option:`cmake -A`
 option or via the :envvar:`CMAKE_GENERATOR_PLATFORM` environment variable)
 the value will be available in this variable.
 
index 45f2d32..4855477 100644 (file)
@@ -5,7 +5,7 @@ Native build system toolset specification provided by user.
 
 Some CMake generators support a toolset specification to tell the
 native build system how to choose a compiler.  If the user specifies
-a toolset (e.g. via the :manual:`cmake(1)` ``-T`` option or via
+a toolset (e.g. via the :option:`cmake -T` option or via
 the :envvar:`CMAKE_GENERATOR_TOOLSET` environment variable) the value
 will be available in this variable.
 
diff --git a/Help/variable/CMAKE_HOST_BSD.rst b/Help/variable/CMAKE_HOST_BSD.rst
new file mode 100644 (file)
index 0000000..d3554f9
--- /dev/null
@@ -0,0 +1,7 @@
+CMAKE_HOST_BSD
+--------------
+
+.. versionadded:: 3.25
+
+Set to a string value when the host system is BSD. This value can be one of
+the following: DragonFlyBSD, FreeBSD, OpenBSD, or NetBSD.
diff --git a/Help/variable/CMAKE_HOST_LINUX.rst b/Help/variable/CMAKE_HOST_LINUX.rst
new file mode 100644 (file)
index 0000000..e2361bd
--- /dev/null
@@ -0,0 +1,6 @@
+CMAKE_HOST_LINUX
+----------------
+
+.. versionadded:: 3.25
+
+Set to true when the host system is Linux.
index 6d42ea8..c76727e 100644 (file)
@@ -27,8 +27,9 @@ first :command:`project` invocation.
 
 The ``CMAKE_INSTALL_PREFIX`` may be defined when configuring a build tree
 to set its installation prefix.  Or, when using the :manual:`cmake(1)`
-command-line tool's ``--install`` mode, one may specify a different prefix
-using the ``--prefix`` option:
+command-line tool's :option:`--install <cmake --install>` mode, one may specify
+a different prefix using the :option:`--prefix <cmake--install --prefix>`
+option:
 
 .. code-block:: shell
 
index e694b33..db8f4e1 100644 (file)
@@ -14,7 +14,8 @@ This variable can be set by the user during the first time a build tree is confi
 If a non-full path value is supplied then CMake will resolve the full path of
 the compiler.
 
-The variable could be set in a user supplied toolchain file or via `-D` on the command line.
+The variable could be set in a user supplied toolchain file or via
+:option:`-D <cmake -D>` on the command line.
 
 .. note::
   Options that are required to make the compiler work correctly can be included
index 6a0a1d9..e311d1b 100644 (file)
@@ -6,43 +6,62 @@ Compiler identification string.
 A short string unique to the compiler vendor.  Possible values
 include:
 
-::
-
-  Absoft = Absoft Fortran (absoft.com)
-  ADSP = Analog VisualDSP++ (analog.com)
-  AppleClang = Apple Clang (apple.com)
-  ARMCC = ARM Compiler (arm.com)
-  ARMClang = ARM Compiler based on Clang (arm.com)
-  Bruce = Bruce C Compiler
-  CCur = Concurrent Fortran (ccur.com)
-  Clang = LLVM Clang (clang.llvm.org)
-  Cray = Cray Compiler (cray.com)
-  Embarcadero, Borland = Embarcadero (embarcadero.com)
-  Flang = Classic Flang Fortran Compiler (https://github.com/flang-compiler/flang)
-  LLVMFlang = LLVM Flang Fortran Compiler (https://github.com/llvm/llvm-project/tree/main/flang)
-  Fujitsu = Fujitsu HPC compiler (Trad mode)
-  FujitsuClang = Fujitsu HPC compiler (Clang mode)
-  G95 = G95 Fortran (g95.org)
-  GNU = GNU Compiler Collection (gcc.gnu.org)
-  GHS = Green Hills Software (www.ghs.com)
-  HP = Hewlett-Packard Compiler (hp.com)
-  IAR = IAR Systems (iar.com)
-  Intel = Intel Compiler (intel.com)
-  IntelLLVM = Intel LLVM-Based Compiler (intel.com)
-  LCC = MCST Elbrus C/C++/Fortran Compiler (mcst.ru)
-  MSVC = Microsoft Visual Studio (microsoft.com)
-  NVHPC = NVIDIA HPC SDK Compiler (nvidia.com)
-  NVIDIA = NVIDIA CUDA Compiler (nvidia.com)
-  OpenWatcom = Open Watcom (openwatcom.org)
-  PGI = The Portland Group (pgroup.com)
-  PathScale = PathScale (pathscale.com)
-  SDCC = Small Device C Compiler (sdcc.sourceforge.net)
-  SunPro = Oracle Solaris Studio (oracle.com)
-  TI = Texas Instruments (ti.com)
-  TinyCC = Tiny C Compiler (tinycc.org)
-  XL, VisualAge, zOS = IBM XL (ibm.com)
-  XLClang = IBM Clang-based XL (ibm.com)
-  IBMClang = IBM LLVM-based Compiler (ibm.com)
+=============================== ===============================================
+Value                           Name
+=============================== ===============================================
+``Absoft``                      `Absoft Fortran`_
+``ADSP``                        Analog VisualDSP++
+``AppleClang``                  Apple Clang
+``ARMCC``                       ARM Compiler
+``ARMClang``                    ARM Compiler based on Clang
+``Bruce``                       Bruce C Compiler
+``CCur``                        Concurrent Fortran
+``Clang``                       `LLVM Clang`_
+``Cray``                        Cray Compiler
+``Embarcadero``, ``Borland``    `Embarcadero`_
+``Flang``                       `Classic Flang Fortran Compiler`_
+``LLVMFlang``                   `LLVM Flang Fortran Compiler`_
+``Fujitsu``                     Fujitsu HPC compiler (Trad mode)
+``FujitsuClang``                Fujitsu HPC compiler (Clang mode)
+``G95``                         `G95 Fortran`_
+``GNU``                         `GNU Compiler Collection`_
+``GHS``                         `Green Hills Software`_
+``HP``                          Hewlett-Packard Compiler
+``IAR``                         IAR Systems
+``Intel``                       Intel Compiler
+``IntelLLVM``                   Intel LLVM-Based Compiler
+``LCC``                         MCST Elbrus C/C++/Fortran Compiler
+``MSVC``                        `Microsoft Visual Studio`_
+``NVHPC``                       `NVIDIA HPC Compiler`_
+``NVIDIA``                      `NVIDIA CUDA Compiler`_
+``OpenWatcom``                  `Open Watcom`_
+``PGI``                         The Portland Group
+``PathScale``                   PathScale
+``SDCC``                        `Small Device C Compiler`_
+``SunPro``                      Oracle Solaris Studio
+``Tasking``                     `Tasking Compiler Toolsets`_
+``TI``                          Texas Instruments
+``TinyCC``                      `Tiny C Compiler`_
+``XL``, ``VisualAge``, ``zOS``  IBM XL
+``XLClang``                     IBM Clang-based XL
+``IBMClang``                    IBM LLVM-based Compiler
+=============================== ===============================================
 
 This variable is not guaranteed to be defined for all compilers or
 languages.
+
+.. _Absoft Fortran: https://www.absoft.com
+.. _LLVM Clang: https://clang.llvm.org
+.. _Embarcadero: https://www.embarcadero.com
+.. _Classic Flang Fortran Compiler: https://github.com/flang-compiler/flang
+.. _LLVM Flang Fortran Compiler: https://github.com/llvm/llvm-project/tree/main/flang
+.. _G95 Fortran: https://g95.sourceforge.net
+.. _GNU Compiler Collection: https://gcc.gnu.org
+.. _Green Hills Software: https://www.ghs.com/products/compiler.html
+.. _Microsoft Visual Studio: https://visualstudio.microsoft.com
+.. _NVIDIA HPC Compiler: https://developer.nvidia.com/hpc-compilers
+.. _NVIDIA CUDA Compiler: https://developer.nvidia.com/cuda-llvm-compiler
+.. _Open Watcom: https://open-watcom.github.io
+.. _Small Device C Compiler: https://sdcc.sourceforge.net
+.. _Tiny C Compiler: https://bellard.org/tcc
+.. _Tasking Compiler Toolsets: https://www.tasking.com
index 2784397..4b39b1d 100644 (file)
@@ -5,7 +5,9 @@ Flags for all build types.
 
 ``<LANG>`` flags used regardless of the value of :variable:`CMAKE_BUILD_TYPE`.
 
-This is initialized for each language from environment variables:
+For each language, if this variable is not defined, it is initialized
+and stored in the cache using values from environment variables in
+combination with CMake's builtin defaults for the toolchain:
 
 * ``CMAKE_C_FLAGS``:
   Initialized by the :envvar:`CFLAGS` environment variable.
@@ -15,6 +17,12 @@ This is initialized for each language from environment variables:
   Initialized by the :envvar:`CUDAFLAGS` environment variable.
 * ``CMAKE_Fortran_FLAGS``:
   Initialized by the :envvar:`FFLAGS` environment variable.
+* ``CMAKE_CSharp_FLAGS``:
+  Initialized by the :envvar:`CSFLAGS` environment variable.
+* ``CMAKE_HIP_FLAGS``:
+  Initialized by the :envvar:`HIPFLAGS` environment variable.
+* ``CMAKE_ISPC_FLAGS``:
+  Initialized by the :envvar:`ISPCFLAGS` environment variable.
 
 This value is a command-line string fragment. Therefore, multiple options
 should be separated by spaces, and options with spaces should be quoted.
index a3c8b7c..9769c7a 100644 (file)
@@ -59,6 +59,6 @@ to configure the project:
   variable, changing the value has undefined behavior.
 
 The ``CMAKE_MAKE_PROGRAM`` variable is set for use by project code.
-The value is also used by the :manual:`cmake(1)` ``--build`` and
-:manual:`ctest(1)` ``--build-and-test`` tools to launch the native
+The value is also used by the :option:`cmake --build` and
+:option:`ctest --build-and-test` tools to launch the native
 build process.
index 41ace43..7274b7b 100644 (file)
@@ -3,7 +3,7 @@ CMAKE_MESSAGE_CONTEXT
 
 .. versionadded:: 3.17
 
-When enabled by the :manual:`cmake <cmake(1)>` ``--log-context`` command line
+When enabled by the :option:`cmake --log-context` command line
 option or the :variable:`CMAKE_MESSAGE_CONTEXT_SHOW` variable, the
 :command:`message` command converts the ``CMAKE_MESSAGE_CONTEXT`` list into a
 dot-separated string surrounded by square brackets and prepends it to each line
index 3b45d1d..4539c90 100644 (file)
@@ -5,8 +5,9 @@ CMAKE_MESSAGE_LOG_LEVEL
 
 When set, this variable specifies the logging level used by the
 :command:`message` command.  Valid values are the same as those for the
-``--log-level`` command line option of the :manual:`cmake(1)` program.
-If this variable is set and the ``--log-level`` command line option is
+:option:`--log-level <cmake --log-level>` command line option of the
+:manual:`cmake(1)` program.  If this variable is set and the
+:option:`--log-level <cmake --log-level>` command line option is
 given, the command line option takes precedence.
 
 The main advantage to using this variable is to make a log level persist
@@ -15,3 +16,8 @@ subsequent CMake runs will continue to use the chosen log level.
 
 Projects should not set this variable, it is intended for users so that
 they may control the log level according to their own needs.
+
+.. versionadded:: 3.25
+  See the :command:`cmake_language`
+  :ref:`cmake_language <query_message_log_level>` command for a way to query
+  the current message logging level.
diff --git a/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst b/Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst
new file mode 100644 (file)
index 0000000..80df8fc
--- /dev/null
@@ -0,0 +1,36 @@
+CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
+-----------------------------------
+
+.. versionadded:: 3.25
+
+Select the MSVC debug information format targeting the MSVC ABI.
+This variable is used to initialize the
+:prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT` property on all targets as they are
+created.  It is also propagated by calls to the :command:`try_compile` command
+into the test project.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+
+selects for all following targets the program database debug information format
+for the Debug configuration.
+
+If this variable is not set, the :prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT`
+target property will not be set automatically.  If that property is not set,
+CMake selects a debug information format using the default value
+``$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>``, if supported by the
+compiler, and otherwise ``$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>``.
+
+.. note::
+
+  This variable has effect only when policy :policy:`CMP0141` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.
index 9dce760..f844105 100644 (file)
@@ -42,5 +42,8 @@ only for the policies that do not warn by default:
 This variable should not be set by a project in CMake code.  Project
 developers running CMake may set this variable in their cache to
 enable the warning (e.g. ``-DCMAKE_POLICY_WARNING_CMP<NNNN>=ON``).
-Alternatively, running :manual:`cmake(1)` with the ``--debug-output``,
-``--trace``, or ``--trace-expand`` option will also enable the warning.
+Alternatively, running :manual:`cmake(1)` with the
+:option:`--debug-output <cmake --debug-output>`,
+:option:`--trace <cmake --trace>`, or
+:option:`--trace-expand <cmake --trace-expand>` option will also
+enable the warning.
index 981af60..313fb4e 100644 (file)
@@ -1,9 +1,9 @@
 CMAKE_SCRIPT_MODE_FILE
 ----------------------
 
-Full path to the :manual:`cmake(1)` ``-P`` script file currently being
+Full path to the :option:`cmake -P` script file currently being
 processed.
 
-When run in :manual:`cmake(1)` ``-P`` script mode, CMake sets this variable to
+When run in :option:`cmake -P` script mode, CMake sets this variable to
 the full path of the script file.  When run to configure a ``CMakeLists.txt``
 file, this variable is not set.
index d1f1798..7210f75 100644 (file)
@@ -7,7 +7,7 @@ This is the full path to the top level of the current CMake source
 tree.  For an in-source build, this would be the same as
 :variable:`CMAKE_BINARY_DIR`.
 
-When run in ``-P`` script mode, CMake sets the variables
+When run in :option:`cmake -P` script mode, CMake sets the variables
 :variable:`CMAKE_BINARY_DIR`, :variable:`CMAKE_SOURCE_DIR`,
 :variable:`CMAKE_CURRENT_BINARY_DIR` and
 :variable:`CMAKE_CURRENT_SOURCE_DIR` to the current working directory.
diff --git a/Help/variable/CMAKE_TASKING_TOOLSET.rst b/Help/variable/CMAKE_TASKING_TOOLSET.rst
new file mode 100644 (file)
index 0000000..6bd1479
--- /dev/null
@@ -0,0 +1,34 @@
+CMAKE_TASKING_TOOLSET
+---------------------
+
+.. versionadded:: 3.25
+
+Select the Tasking toolset which provides the compiler
+
+Architecture compilers are provided by different toolchains with
+incompatible versioning schemes.  Set this variable in a
+:variable:`toolchain file <CMAKE_TOOLCHAIN_FILE>` so CMake can detect
+the compiler features correctly. If no toolset is specified,
+``Standalone`` is assumed.
+
+Due to the different versioning schemes, the compiler version
+(:variable:`CMAKE_<LANG>_COMPILER_VERSION`) depends on the toolset and
+architecture in use. If projects can be built with multiple toolsets or
+architectures, the specified :variable:`CMAKE_TASKING_TOOLSET` and the
+automatically determined :variable:`CMAKE_<LANG>_COMPILER_ARCHITECTURE_ID`
+must be taken into account when comparing against the
+:variable:`CMAKE_<LANG>_COMPILER_VERSION`.
+
+``TriCore``
+  Compilers are provided by the TriCore toolset.
+
+``SmartCode``
+  Compilers are provided by the SmartCode toolset.
+
+``Standalone``
+  Compilers are provided by the standalone toolsets.
+
+  .. note::
+
+    For the TriCore architecture, the compiler from the TriCore toolset is
+    selected as standalone compiler.
index f109a9e..2bb97c4 100644 (file)
@@ -10,5 +10,5 @@ This variable is not defined by other generators even if ``devenv.com``
 is installed on the computer.
 
 The :variable:`CMAKE_VS_MSBUILD_COMMAND` is also provided for
-:generator:`Visual Studio 10 2010` and above.
+:generator:`Visual Studio 11 2012` and above.
 See also the :variable:`CMAKE_MAKE_PROGRAM` variable.
index 58f2bef..8a521a3 100644 (file)
@@ -1,7 +1,7 @@
 CMAKE_VS_MSBUILD_COMMAND
 ------------------------
 
-The generators for :generator:`Visual Studio 10 2010` and above set this
+The generators for :generator:`Visual Studio 11 2012` and above set this
 variable to the ``MSBuild.exe`` command installed with the corresponding
 Visual Studio version.
 
index 4a224fa..c7b6159 100644 (file)
@@ -7,4 +7,5 @@ If not ``FALSE``, use of deprecated functionality will issue warnings.
 If this variable is not set, CMake behaves as if it were set to ``TRUE``.
 
 When running :manual:`cmake(1)`, this option can be enabled with the
-``-Wdeprecated`` option, or disabled with the ``-Wno-deprecated`` option.
+:option:`-Wdeprecated <cmake -Wdeprecated>` option, or disabled with the
+:option:`-Wno-deprecated <cmake -Wno-deprecated>` option.
index 210da52..6d72d14 100644 (file)
@@ -5,5 +5,5 @@ Xcode compiler selection.
 
 :generator:`Xcode` supports selection of a compiler from one of the installed
 toolsets.  CMake provides the name of the chosen toolset in this
-variable, if any is explicitly selected (e.g.  via the :manual:`cmake(1)`
-``-T`` option).
+variable, if any is explicitly selected (e.g.  via the :option:`cmake -T`
+option).
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst b/Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION.rst
new file mode 100644 (file)
index 0000000..ce5c4b3
--- /dev/null
@@ -0,0 +1,13 @@
+CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+--------------------------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Metal: API Validation`` in the Options section of
+the generated Xcode scheme.
+
+This variable initializes the
+:prop_tgt:`XCODE_SCHEME_ENABLE_GPU_API_VALIDATION` property on all targets.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst b/Help/variable/CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION.rst
new file mode 100644 (file)
index 0000000..073a6c9
--- /dev/null
@@ -0,0 +1,13 @@
+CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
+-----------------------------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Metal: Shader Validation`` in the Options section of
+the generated Xcode scheme.
+
+This variable initializes the
+:prop_tgt:`XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION` property on all targets.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION.rst b/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION.rst
new file mode 100644 (file)
index 0000000..e5b4d18
--- /dev/null
@@ -0,0 +1,12 @@
+CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION
+---------------------------------------
+
+.. versionadded:: 3.25
+
+Set the build configuration to run the target.
+
+This variable initializes the :prop_tgt:`XCODE_SCHEME_LAUNCH_CONFIGURATION`
+property on all targets.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
diff --git a/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst b/Help/variable/CMAKE_XCODE_SCHEME_LAUNCH_MODE.rst
new file mode 100644 (file)
index 0000000..c15b1ea
--- /dev/null
@@ -0,0 +1,13 @@
+CMAKE_XCODE_SCHEME_LAUNCH_MODE
+------------------------------
+
+.. versionadded:: 3.25
+
+Property value for ``Launch`` in the Info section of the generated Xcode
+scheme.
+
+This variable initializes the :prop_tgt:`XCODE_SCHEME_LAUNCH_MODE` property on
+all targets.
+
+Please refer to the :prop_tgt:`XCODE_GENERATE_SCHEME` target property
+documentation to see all Xcode schema related properties.
index 1c070b8..9bd50db 100644 (file)
@@ -3,7 +3,7 @@ CPACK_CUSTOM_INSTALL_VARIABLES
 
 .. versionadded:: 3.21
 
-CPack variables (set via e.g. ``cpack -D``, ``CPackConfig.cmake`` or
+CPack variables (set via e.g. :option:`cpack -D`, ``CPackConfig.cmake`` or
 :variable:`CPACK_PROJECT_CONFIG_FILE` scripts) are not directly visible in
 installation scripts.  Instead, one can pass a list of ``varName=value``
 pairs in the ``CPACK_CUSTOM_INSTALL_VARIABLES`` variable.  At install time,
index 392845e..932f81d 100644 (file)
@@ -6,5 +6,5 @@ CTEST_CONFIGURATION_TYPE
 Specify the CTest ``DefaultCTestConfigurationType`` setting
 in a :manual:`ctest(1)` dashboard client script.
 
-If the configuration type is set via ``-C <cfg>`` from the command line
+If the configuration type is set via :option:`-C \<cfg\> <ctest -C>` from the command line
 then this variable is populated accordingly.
index 32c85ad..8cb6eaa 100644 (file)
@@ -4,4 +4,4 @@ CTEST_RUN_CURRENT_SCRIPT
 .. versionadded:: 3.11
 
 Setting this to 0 prevents :manual:`ctest(1)` from being run again when it
-reaches the end of a script run by calling ``ctest -S``.
+reaches the end of a script run by calling :option:`ctest -S`.
index 8ae6c57..aea1be8 100644 (file)
@@ -45,8 +45,8 @@
   wildcard, and optional parts are shown as ``[...]``):
 
      * ``[/path/to/]FwName[.framework]``
-     * ``[/path/to/]FwName.framework/FwName``
-     * ``[/path/to/]FwName.framework/Versions/*/FwName``
+     * ``[/path/to/]FwName.framework/FwName[suffix]``
+     * ``[/path/to/]FwName.framework/Versions/*/FwName[suffix]``
 
   Note that CMake recognizes and automatically handles framework targets,
   even without using the ``$<LINK_LIBRARY:FRAMEWORK,...>`` expression.
   ``$<LINK_LIBRARY:FRAMEWORK,...>`` for file paths so that the expected
   behavior is clear.
 
+  .. versionadded:: 3.25
+    The :prop_tgt:`FRAMEWORK_MULTI_CONFIG_POSTFIX_<CONFIG>` target property as
+    well as the ``suffix`` of the framework library name are now supported by
+    the ``FRAMEWORK`` features.
+
 ``NEEDED_FRAMEWORK``
   This is similar to the ``FRAMEWORK`` feature, except it forces the linker
   to link with the framework even if no symbols are used from it.  It uses
diff --git a/Help/variable/LINUX.rst b/Help/variable/LINUX.rst
new file mode 100644 (file)
index 0000000..26379b3
--- /dev/null
@@ -0,0 +1,6 @@
+LINUX
+-----
+
+.. versionadded:: 3.25
+
+Set to true when the target system is Linux.
index f19bc97..2643326 100644 (file)
@@ -82,6 +82,7 @@ int main(int argc, char* argv[])
   int require = 0;
   require += info_compiler[argc];
   require += info_platform[argc];
+  require += info_arch[argc];
 #ifdef COMPILER_VERSION_MAJOR
   require += info_version[argc];
 #endif
index f15974a..75d33e8 100644 (file)
@@ -70,6 +70,7 @@ function(compiler_id_detection outvar lang)
       FujitsuClang
       Fujitsu
       GHS
+      Tasking
     )
     if ("x${lang}" STREQUAL "xC")
       list(APPEND ordered_compilers
index ac0e262..9a3c940 100644 (file)
@@ -84,7 +84,7 @@ macro(CMAKE_DEPENDENT_OPTION option doc default depends force)
   else()
     set(${option} "${${option}_ISSET}")
   endif()
-  if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_; ]")
+  if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_.; ]")
     cmake_policy(GET_WARNING CMP0127 _CDO_CMP0127_WARNING)
     message(AUTHOR_WARNING "${_CDO_CMP0127_WARNING}")
   endif()
index d03cbef..5ec2972 100644 (file)
@@ -129,14 +129,13 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER_ID)
   if("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xIAR")
     # primary necessary to detect architecture, so the right archiver and linker can be picked
     # eg. "IAR Assembler V8.10.1.12857/W32 for ARM" or "IAR Assembler V4.11.1.4666 for Renesas RX"
-    # Cut out identification first, newline handling is a pain
+    # Earlier versions did not provide `--version`, so grep the full output to extract Assembler ID string
     string(REGEX MATCH "IAR Assembler[^\r\n]*" _compileid "${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT}")
     if("${_compileid}" MATCHES "V([0-9]+\\.[0-9]+\\.[0-9]+)")
       set(CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION ${CMAKE_MATCH_1})
     endif()
-    string(REGEX MATCHALL "([A-Za-z0-9-]+)" _all_compileid_matches "${_compileid}")
-    if(_all_compileid_matches)
-      list(GET _all_compileid_matches "-1" CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID)
+    if("${_compileid}" MATCHES "for.*(MSP430|8051|ARM|AVR|RH850|RISC-?V|RL78|RX|STM8|V850)")
+      set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID ${CMAKE_MATCH_1})
     endif()
   elseif("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xClang")
     # Test whether an MSVC-like command-line option works.
index da860a8..fe98469 100644 (file)
@@ -3,7 +3,7 @@
 
 if(NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
   message(FATAL_ERROR
-    "C# is currently only supported for Microsoft Visual Studio 2010 and later.")
+    "C# is currently only supported for Microsoft Visual Studio 11 2012 and later.")
 endif()
 
 include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
index 0ac06ac..f43b17b 100644 (file)
@@ -275,7 +275,7 @@ if(NOT CMAKE_CUDA_COMPILER_ID_RUN)
   if(DEFINED CMAKE_CUDA_ARCHITECTURES)
     if(CMAKE_CUDA_ARCHITECTURES STREQUAL "")
       message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES must be non-empty if set.")
-    elseif(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^([0-9]+(-real|-virtual)?(;[0-9]+(-real|-virtual)?|;)*|all|all-major|native)$")
+    elseif(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^([0-9]+a?(-real|-virtual)?(;[0-9]+a?(-real|-virtual)?|;)*|all|all-major|native)$")
       message(FATAL_ERROR
         "CMAKE_CUDA_ARCHITECTURES:\n"
         "  ${CMAKE_CUDA_ARCHITECTURES}\n"
@@ -503,7 +503,8 @@ elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
                                    CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES
                                    CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
                                    log
-                                   "${CMAKE_CUDA_IMPLICIT_OBJECT_REGEX}")
+                                   "${CMAKE_CUDA_IMPLICIT_OBJECT_REGEX}"
+                                   LANGUAGE CUDA)
 
     # Detect CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT from the compiler by looking at which
     # cudart library exists in the implicit link libraries passed to the host linker.
index a08e597..09de7b1 100644 (file)
@@ -63,6 +63,7 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_CXX17_COMPILE_FEATURES)
     set(CMAKE_CXX20_COMPILE_FEATURES)
     set(CMAKE_CXX23_COMPILE_FEATURES)
+    set(CMAKE_CXX26_COMPILE_FEATURES)
 
     include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake")
 
@@ -73,6 +74,9 @@ function(cmake_determine_compile_features lang)
       return()
     endif()
 
+    if (CMAKE_CXX23_COMPILE_FEATURES AND CMAKE_CXX26_COMPILE_FEATURES)
+      list(REMOVE_ITEM CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES})
+    endif()
     if (CMAKE_CXX20_COMPILE_FEATURES AND CMAKE_CXX23_COMPILE_FEATURES)
       list(REMOVE_ITEM CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES})
     endif()
@@ -97,6 +101,7 @@ function(cmake_determine_compile_features lang)
         ${CMAKE_CXX17_COMPILE_FEATURES}
         ${CMAKE_CXX20_COMPILE_FEATURES}
         ${CMAKE_CXX23_COMPILE_FEATURES}
+        ${CMAKE_CXX26_COMPILE_FEATURES}
       )
     endif()
 
@@ -107,6 +112,7 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_CXX17_COMPILE_FEATURES ${CMAKE_CXX17_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX20_COMPILE_FEATURES ${CMAKE_CXX20_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CXX23_COMPILE_FEATURES ${CMAKE_CXX23_COMPILE_FEATURES} PARENT_SCOPE)
+    set(CMAKE_CXX26_COMPILE_FEATURES ${CMAKE_CXX26_COMPILE_FEATURES} PARENT_SCOPE)
 
     message(CHECK_PASS "done")
 
@@ -119,6 +125,7 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_CUDA17_COMPILE_FEATURES)
     set(CMAKE_CUDA20_COMPILE_FEATURES)
     set(CMAKE_CUDA23_COMPILE_FEATURES)
+    set(CMAKE_CUDA26_COMPILE_FEATURES)
 
     include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake")
 
@@ -129,6 +136,9 @@ function(cmake_determine_compile_features lang)
       return()
     endif()
 
+    if (CMAKE_CUDA23_COMPILE_FEATURES AND CMAKE_CUDA26_COMPILE_FEATURES)
+      list(REMOVE_ITEM CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES})
+    endif()
     if (CMAKE_CUDA20_COMPILE_FEATURES AND CMAKE_CUDA23_COMPILE_FEATURES)
       list(REMOVE_ITEM CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES})
     endif()
@@ -153,6 +163,7 @@ function(cmake_determine_compile_features lang)
         ${CMAKE_CUDA17_COMPILE_FEATURES}
         ${CMAKE_CUDA20_COMPILE_FEATURES}
         ${CMAKE_CUDA23_COMPILE_FEATURES}
+        ${CMAKE_CUDA26_COMPILE_FEATURES}
       )
     endif()
 
@@ -163,6 +174,7 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_CUDA17_COMPILE_FEATURES ${CMAKE_CUDA17_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CUDA20_COMPILE_FEATURES ${CMAKE_CUDA20_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_CUDA23_COMPILE_FEATURES ${CMAKE_CUDA23_COMPILE_FEATURES} PARENT_SCOPE)
+    set(CMAKE_CUDA26_COMPILE_FEATURES ${CMAKE_CUDA26_COMPILE_FEATURES} PARENT_SCOPE)
 
     message(CHECK_PASS "done")
 
@@ -175,6 +187,8 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_HIP17_COMPILE_FEATURES)
     set(CMAKE_HIP20_COMPILE_FEATURES)
     set(CMAKE_HIP23_COMPILE_FEATURES)
+    set(CMAKE_HIP26_COMPILE_FEATURES)
+
 
     include("${CMAKE_ROOT}/Modules/Internal/FeatureTesting.cmake")
 
@@ -185,6 +199,9 @@ function(cmake_determine_compile_features lang)
       return()
     endif()
 
+    if (CMAKE_HIP23_COMPILE_FEATURES AND CMAKE_HIP26_COMPILE_FEATURES)
+      list(REMOVE_ITEM CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES})
+    endif()
     if (CMAKE_HIP20_COMPILE_FEATURES AND CMAKE_HIP23_COMPILE_FEATURES)
       list(REMOVE_ITEM CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES})
     endif()
@@ -209,6 +226,7 @@ function(cmake_determine_compile_features lang)
         ${CMAKE_HIP17_COMPILE_FEATURES}
         ${CMAKE_HIP20_COMPILE_FEATURES}
         ${CMAKE_HIP23_COMPILE_FEATURES}
+        ${CMAKE_HIP26_COMPILE_FEATURES}
       )
     endif()
 
@@ -219,6 +237,7 @@ function(cmake_determine_compile_features lang)
     set(CMAKE_HIP17_COMPILE_FEATURES ${CMAKE_HIP17_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_HIP20_COMPILE_FEATURES ${CMAKE_HIP20_COMPILE_FEATURES} PARENT_SCOPE)
     set(CMAKE_HIP23_COMPILE_FEATURES ${CMAKE_HIP23_COMPILE_FEATURES} PARENT_SCOPE)
+    set(CMAKE_HIP26_COMPILE_FEATURES ${CMAKE_HIP26_COMPILE_FEATURES} PARENT_SCOPE)
 
     message(CHECK_PASS "done")
 
index 82a6d21..053effa 100644 (file)
@@ -55,7 +55,7 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src)
     set(ENV{LANG}        C)
 
     try_compile(CMAKE_${lang}_ABI_COMPILED
-      ${CMAKE_BINARY_DIR} ${src}
+      SOURCES ${src}
       CMAKE_FLAGS ${CMAKE_FLAGS}
                   # Ignore unused flags when we are just determining the ABI.
                   "--no-warn-unused-cli"
@@ -149,7 +149,8 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src)
       if(CMAKE_${lang}_VERBOSE_FLAG)
         CMAKE_PARSE_IMPLICIT_LINK_INFO("${OUTPUT}" implicit_libs implicit_dirs implicit_fwks log
           "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
-          COMPUTE_IMPLICIT_OBJECTS implicit_objs)
+          COMPUTE_IMPLICIT_OBJECTS implicit_objs
+          LANGUAGE ${lang})
         file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
           "Parsed ${lang} implicit link information from above output:\n${log}\n\n")
       endif()
@@ -161,8 +162,9 @@ function(CMAKE_DETERMINE_COMPILER_ABI lang src)
         message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path")
         # Build a sample project which reports symbols.
         try_compile(IFORT_LIB_PATH_COMPILED
-          ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
-          ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
+          PROJECT IntelFortranImplicit
+          SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
+          BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
           IntelFortranImplicit
           CMAKE_FLAGS
           "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
index 2b19736..73c775a 100644 (file)
@@ -720,7 +720,7 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS}
      OR CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES "warning #5117: Bad # preprocessor line"
      )
     # Compilation failed.
-    set(MSG
+    string(APPEND _CMAKE_DETERMINE_COMPILER_ID_BUILD_MSG
       "Compiling the ${lang} compiler identification source file \"${src}\" failed.
 ${COMPILER_DESCRIPTION}
 The output was:
@@ -736,7 +736,11 @@ ${CMAKE_${lang}_COMPILER_ID_OUTPUT}
     # Some languages may know the correct/desired set of flags and want to fail right away if they don't work.
     # This is currently only used by CUDA.
     if(__compiler_id_require_success)
-      message(FATAL_ERROR "${MSG}")
+      message(FATAL_ERROR "${_CMAKE_DETERMINE_COMPILER_ID_BUILD_MSG}")
+    else()
+      # Build up the outputs for compiler detection attempts so that users
+      # can see all set of flags tried, instead of just last
+      set(_CMAKE_DETERMINE_COMPILER_ID_BUILD_MSG "${_CMAKE_DETERMINE_COMPILER_ID_BUILD_MSG}" PARENT_SCOPE)
     endif()
 
     # No output files should be inspected.
@@ -1123,7 +1127,7 @@ function(CMAKE_DETERMINE_MSVC_SHOWINCLUDES_PREFIX lang userflags)
     OUTPUT_VARIABLE out
     ERROR_VARIABLE err
     RESULT_VARIABLE res
-    ENCODING AUTO # cl prints in current code page
+    ENCODING AUTO # cl prints in console output code page
     )
   if(res EQUAL 0 AND "${out}" MATCHES "(^|\n)([^:\n]*:[^:\n]*:[ \t]*)")
     set(CMAKE_${lang}_CL_SHOWINCLUDES_PREFIX "${CMAKE_MATCH_2}" PARENT_SCOPE)
index 650c87a..087c0f6 100644 (file)
@@ -44,17 +44,14 @@ else()
     # finally list compilers to try
     if(NOT CMAKE_Fortran_COMPILER_INIT)
       # Known compilers:
-      #  f77/f90/f95: generic compiler names
       #  ftn: Cray fortran compiler wrapper
-      #  g77: GNU Fortran 77 compiler
       #  gfortran: putative GNU Fortran 95+ compiler (in progress)
-      #  fort77: native F77 compiler under HP-UX (and some older Crays)
-      #  frt: Fujitsu F77 compiler
+      #  frt: Fujitsu Fortran compiler
       #  pathf90/pathf95/pathf2003: PathScale Fortran compiler
-      #  pgf77/pgf90/pgf95/pgfortran: Portland Group F77/F90/F95 compilers
+      #  pgfortran: Portland Group Fortran compilers
       #  nvfortran: NVHPC Fotran compiler
       #  flang: Flang Fortran compiler
-      #  xlf/xlf90/xlf95: IBM (AIX) F77/F90/F95 compilers
+      #  xlf: IBM (AIX) Fortran compiler
       #  lf95: Lahey-Fujitsu F95 compiler
       #  fl32: Microsoft Fortran 77 "PowerStation" compiler
       #  af77: Apogee F77 compiler for Intergraph hardware running CLIX
@@ -62,31 +59,20 @@ else()
       #  fort: Compaq (now HP) Fortran 90/95 compiler for Tru64 and Linux/Alpha
       #  ifx: Intel Fortran LLVM-based compiler
       #  ifort: Intel Classic Fortran compiler
-      #  ifc: Intel Fortran 95 compiler for Linux/x86
-      #  efc: Intel Fortran 95 compiler for IA64
       #  nagfor: NAG Fortran compiler
       #
-      #  The order is 95 or newer compilers first, then 90,
-      #  then 77 or older compilers, gnu is always last in the group,
+      #  GNU is last to be searched,
       #  so if you paid for a compiler it is picked by default.
-      if(CMAKE_HOST_WIN32)
-        set(CMAKE_Fortran_COMPILER_LIST
-          ifort ifx pgf95 pgfortran nvfortran lf95 fort
-          flang gfortran gfortran-4 g95 f90 pgf90
-          pgf77 g77 f77 nag
-          )
-      else()
-        set(CMAKE_Fortran_COMPILER_LIST
-          ftn
-          ifort ifc ifx efc pgf95 pgfortran nvfortran lf95 xlf95 fort
-          flang lfortran gfortran gfortran-4 g95 f90 pgf90
-          frt pgf77 xlf g77 f77 nag
-          )
-      endif()
+      set(CMAKE_Fortran_COMPILER_LIST
+        ftn
+        ifx ifort nvfortran pgfortran lf95 xlf fort
+        flang lfortran frt nagfor
+        gfortran
+        )
 
       # Vendor-specific compiler names.
       set(_Fortran_COMPILER_NAMES_LCC       lfortran gfortran)
-      set(_Fortran_COMPILER_NAMES_GNU       gfortran gfortran-4 g95 g77)
+      set(_Fortran_COMPILER_NAMES_GNU       gfortran)
       set(_Fortran_COMPILER_NAMES_Intel     ifort ifc efc ifx)
       set(_Fortran_COMPILER_NAMES_Absoft    af95 af90 af77)
       set(_Fortran_COMPILER_NAMES_PGI       pgf95 pgfortran pgf90 pgf77)
index 7b7d7a3..6294d04 100644 (file)
@@ -86,6 +86,7 @@ if(NOT CMAKE_HIP_COMPILER_ROCM_ROOT AND CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
 
   if(_CMAKE_HIP_COMPILER_RESULT EQUAL 0 AND _CMAKE_HIP_COMPILER_STDERR MATCHES "Found HIP installation: *([^,]*)[,\n]")
     set(CMAKE_HIP_COMPILER_ROCM_ROOT "${CMAKE_MATCH_1}")
+    file(TO_CMAKE_PATH "${CMAKE_HIP_COMPILER_ROCM_ROOT}" CMAKE_HIP_COMPILER_ROCM_ROOT)
   endif()
 endif()
 if(NOT CMAKE_HIP_COMPILER_ROCM_ROOT)
index 53868d2..0d360b5 100644 (file)
@@ -105,8 +105,7 @@ function(_DetermineVSServicePack_CheckVersionWithTryCompile _SUCCESS_VAR  _VERSI
 
     try_compile(
       _CompileResult
-      "${CMAKE_BINARY_DIR}"
-      "${CMAKE_BINARY_DIR}/return0.cc"
+      SOURCES "${CMAKE_BINARY_DIR}/return0.cc"
       OUTPUT_VARIABLE _output
       COPY_FILE "${CMAKE_BINARY_DIR}/return0.cc")
 
@@ -128,8 +127,7 @@ function(_DetermineVSServicePack_CheckVersionWithTryRun _SUCCESS_VAR  _VERSION_V
     try_run(
         _RunResult
         _CompileResult
-        "${CMAKE_BINARY_DIR}"
-        "${CMAKE_BINARY_DIR}/return0.cc"
+        SOURCES "${CMAKE_BINARY_DIR}/return0.cc"
         RUN_OUTPUT_VARIABLE  _runoutput
         )
 
index a6bd0d1..2ac8879 100644 (file)
@@ -170,7 +170,7 @@ else()
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL Clang)
     if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC")
       list(PREPEND _CMAKE_LINKER_NAMES "lld-link")
-    else()
+    elseif(NOT APPLE)
       list(PREPEND _CMAKE_LINKER_NAMES "ld.lld")
     endif()
     if(APPLE)
index 8906f48..1aa3929 100644 (file)
@@ -17,12 +17,19 @@ if(NOT CMAKE_FIND_FRAMEWORKS_INCLUDED)
   macro(CMAKE_FIND_FRAMEWORKS fwk)
     set(${fwk}_FRAMEWORKS)
     if(APPLE)
+      # 'Frameworks' directory from Brew (Apple Silicon and Intel)
+      if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+        set(_brew_framework_path /opt/homebrew/Frameworks)
+      else()
+        set(_brew_framework_path /usr/local/Frameworks)
+      endif()
+
       file(TO_CMAKE_PATH "$ENV{CMAKE_FRAMEWORK_PATH}" _cmff_CMAKE_FRAMEWORK_PATH)
       set(_cmff_search_paths
             ${CMAKE_FRAMEWORK_PATH}
             ${_cmff_CMAKE_FRAMEWORK_PATH}
             ~/Library/Frameworks
-            /usr/local/Frameworks
+            ${_brew_framework_path}
             /Library/Frameworks
             /System/Library/Frameworks
             /Network/Library/Frameworks
index 4c57677..33f8697 100644 (file)
@@ -143,7 +143,7 @@ set(CMAKE_HIP_INFORMATION_LOADED 1)
 # Load the file and find the relevant HIP runtime.
 if(NOT DEFINED _CMAKE_HIP_DEVICE_RUNTIME_TARGET)
   set(hip-lang_DIR "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib/cmake/hip-lang")
-  find_package(hip-lang CONFIG QUIET NO_DEFAULT_PATH)
+  find_package(hip-lang CONFIG QUIET NO_DEFAULT_PATH REQUIRED)
 endif()
 if(DEFINED _CMAKE_HIP_DEVICE_RUNTIME_TARGET)
   list(APPEND CMAKE_HIP_RUNTIME_LIBRARIES_STATIC ${_CMAKE_HIP_DEVICE_RUNTIME_TARGET})
index 6bdefde..1773dc4 100644 (file)
@@ -22,7 +22,7 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj
   set(log "")
 
   set(keywordArgs)
-  set(oneValueArgs COMPUTE_IMPLICIT_OBJECTS)
+  set(oneValueArgs COMPUTE_IMPLICIT_OBJECTS LANGUAGE)
   set(multiValueArgs )
   cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
@@ -76,6 +76,11 @@ function(CMAKE_PARSE_IMPLICIT_LINK_INFO text lib_var dir_var fwk_var log_var obj
       endif()
     endif()
     set(is_msvc 0)
+    if(EXTRA_PARSE_LANGUAGE AND
+      ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_ID}" STREQUAL "xMSVC" OR
+       "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC"))
+      set(is_msvc 1)
+    endif()
     set(search_static 0)
     if("${cmd}" MATCHES "${linker_regex}")
       string(APPEND log "  link line: [${line}]\n")
index 06f5ecd..32b7166 100644 (file)
 # elif defined(__ADSPBLACKFIN__)
 #  define ARCHITECTURE_ID "Blackfin"
 
+#elif defined(__TASKING__)
+
+# if defined(__CTC__) || defined(__CPTC__)
+#  define ARCHITECTURE_ID "TriCore"
+
+# elif defined(__CMCS__)
+#  define ARCHITECTURE_ID "MCS"
+
+# elif defined(__CARM__)
+#  define ARCHITECTURE_ID "ARM"
+
+# elif defined(__CARC__)
+#  define ARCHITECTURE_ID "ARC"
+
+# elif defined(__C51__)
+#  define ARCHITECTURE_ID "8051"
+
+# elif defined(__CPCP__)
+#  define ARCHITECTURE_ID "PCP"
+
+# else
+#  define ARCHITECTURE_ID ""
+# endif
+
 #else
 #  define ARCHITECTURE_ID
 #endif
index ecad1d5..16726d2 100644 (file)
@@ -65,7 +65,7 @@ set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -libc MD)
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -libc MTd)
 set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -libc MDd)
 
-set(CMAKE_Swift_FLAGS_DEBUG_INIT "-g")
+set(CMAKE_Swift_FLAGS_DEBUG_INIT "-Onone -g")
 set(CMAKE_Swift_FLAGS_RELEASE_INIT "-O")
 set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
 set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
index e045932..b5dc8d3 100644 (file)
@@ -16,6 +16,8 @@ set(UNIX   )
 set(CYGWIN )
 set(MSYS )
 set(WIN32  )
+set(BSD )
+set(LINUX )
 
 function(_cmake_record_install_prefix )
   set(_CMAKE_SYSTEM_PREFIX_PATH_INSTALL_PREFIX_VALUE "${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE)
index ae5aa27..a706767 100644 (file)
@@ -38,7 +38,7 @@ endif()
 if(NOT CMAKE_C_COMPILER_WORKS)
   PrintTestCompilerStatus("C")
   __TestCompiler_setTryCompileTargetType()
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCCompiler.c
+  string(CONCAT __TestCompiler_testCCompilerSource
     "#ifdef __cplusplus\n"
     "# error \"The CMAKE_C_COMPILER is set to a C++ compiler\"\n"
     "#endif\n"
@@ -53,9 +53,10 @@ if(NOT CMAKE_C_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_C_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_C_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCCompiler.c
+  try_compile(CMAKE_C_COMPILER_WORKS
+    SOURCE_FROM_VAR testCCompiler.c __TestCompiler_testCCompilerSource
     OUTPUT_VARIABLE __CMAKE_C_COMPILER_OUTPUT)
+  unset(__TestCompiler_testCCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_C_COMPILER_WORKS ${CMAKE_C_COMPILER_WORKS})
   unset(CMAKE_C_COMPILER_WORKS CACHE)
index adea250..1c9e249 100644 (file)
@@ -12,8 +12,6 @@ include(CMakeTestCompilerCommon)
 
 unset(CMAKE_CSharp_COMPILER_WORKS CACHE)
 
-set(test_compile_file "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCSharpCompiler.cs")
-
 # This file is used by EnableLanguage in cmGlobalGenerator to
 # determine that the selected C# compiler can actually compile
 # and link the most basic of programs. If not, a fatal error
@@ -23,19 +21,21 @@ if(NOT CMAKE_CSharp_COMPILER_WORKS)
   # Don't call PrintTestCompilerStatus() because the "C#" we want to pass
   # as the LANG doesn't match with the variable name "CMAKE_CSharp_COMPILER"
   message(CHECK_START "Check for working C# compiler: ${CMAKE_CSharp_COMPILER}")
-  file(WRITE "${test_compile_file}"
-    "namespace Test {"
-    "   public class CSharp {"
-    "       static void Main(string[] args) {}"
-    "   }"
-    "}"
+  string(CONCAT __TestCompiler_testCSharpCompilerSource
+    "namespace Test {\n"
+    "   public class CSharp {\n"
+    "       static void Main(string[] args) {}\n"
+    "   }\n"
+    "}\n"
     )
   # Clear result from normal variable.
   unset(CMAKE_CSharp_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_CSharp_COMPILER_WORKS ${CMAKE_BINARY_DIR} "${test_compile_file}"
+  try_compile(CMAKE_CSharp_COMPILER_WORKS
+    SOURCE_FROM_VAR testCSharpCompiler.cs __TestCompiler_testCSharpCompilerSource
     OUTPUT_VARIABLE __CMAKE_CSharp_COMPILER_OUTPUT
     )
+  unset(__TestCompiler_testCSharpCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_CSharp_COMPILER_WORKS ${CMAKE_CSharp_COMPILER_WORKS})
   unset(CMAKE_CSharp_COMPILER_WORKS CACHE)
index 853d655..f2fa6ea 100644 (file)
@@ -76,7 +76,7 @@ endif()
 # any makefiles or projects.
 if(NOT CMAKE_CUDA_COMPILER_WORKS)
   PrintTestCompilerStatus("CUDA")
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/main.cu
+  string(CONCAT __TestCompiler_testCudaCompilerSource
     "#ifndef __CUDACC__\n"
     "# error \"The CMAKE_CUDA_COMPILER is set to an invalid CUDA compiler\"\n"
     "#endif\n"
@@ -86,9 +86,10 @@ if(NOT CMAKE_CUDA_COMPILER_WORKS)
   unset(CMAKE_CUDA_COMPILER_WORKS)
 
   # Puts test result in cache variable.
-  try_compile(CMAKE_CUDA_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/main.cu
+  try_compile(CMAKE_CUDA_COMPILER_WORKS
+    SOURCE_FROM_VAR main.cu __TestCompiler_testCudaCompilerSource
     OUTPUT_VARIABLE __CMAKE_CUDA_COMPILER_OUTPUT)
+  unset(__TestCompiler_testCudaCompilerSource)
 
   # Move result from cache to normal variable.
   set(CMAKE_CUDA_COMPILER_WORKS ${CMAKE_CUDA_COMPILER_WORKS})
index bbe3533..fa4016a 100644 (file)
@@ -38,7 +38,7 @@ endif()
 if(NOT CMAKE_CXX_COMPILER_WORKS)
   PrintTestCompilerStatus("CXX")
   __TestCompiler_setTryCompileTargetType()
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCXXCompiler.cxx
+  string(CONCAT __TestCompiler_testCXXCompilerSource
     "#ifndef __cplusplus\n"
     "# error \"The CMAKE_CXX_COMPILER is set to a C compiler\"\n"
     "#endif\n"
@@ -46,9 +46,10 @@ if(NOT CMAKE_CXX_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_CXX_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_CXX_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCXXCompiler.cxx
+  try_compile(CMAKE_CXX_COMPILER_WORKS
+    SOURCE_FROM_VAR testCXXCompiler.cxx __TestCompiler_testCXXCompilerSource
     OUTPUT_VARIABLE __CMAKE_CXX_COMPILER_OUTPUT)
+  unset(__TestCompiler_testCXXCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_CXX_COMPILER_WORKS ${CMAKE_CXX_COMPILER_WORKS})
   unset(CMAKE_CXX_COMPILER_WORKS CACHE)
@@ -56,7 +57,7 @@ if(NOT CMAKE_CXX_COMPILER_WORKS)
   if(NOT CMAKE_CXX_COMPILER_WORKS)
     PrintTestCompilerResult(CHECK_FAIL "broken")
     file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-      "Determining if the CXX compiler works failed with "
+      "Determining if the C++ compiler works failed with "
       "the following output:\n${__CMAKE_CXX_COMPILER_OUTPUT}\n\n")
     string(REPLACE "\n" "\n  " _output "${__CMAKE_CXX_COMPILER_OUTPUT}")
     message(FATAL_ERROR "The C++ compiler\n  \"${CMAKE_CXX_COMPILER}\"\n"
@@ -66,7 +67,7 @@ if(NOT CMAKE_CXX_COMPILER_WORKS)
   endif()
   PrintTestCompilerResult(CHECK_PASS "works")
   file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
-    "Determining if the CXX compiler works passed with "
+    "Determining if the C++ compiler works passed with "
     "the following output:\n${__CMAKE_CXX_COMPILER_OUTPUT}\n\n")
 endif()
 
index 579f83f..e6d1f6d 100644 (file)
@@ -38,7 +38,7 @@ endif()
 # any makefiles or projects.
 if(NOT CMAKE_Fortran_COMPILER_WORKS)
   PrintTestCompilerStatus("Fortran")
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompiler.f "
+  set(__TestCompiler_testFortranCompilerSource "
         PROGRAM TESTFortran
         PRINT *, 'Hello'
         END
@@ -46,9 +46,10 @@ if(NOT CMAKE_Fortran_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_Fortran_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_Fortran_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompiler.f
+  try_compile(CMAKE_Fortran_COMPILER_WORKS
+    SOURCE_FROM_VAR testFortranCompiler.f __TestCompiler_testFortranCompilerSource
     OUTPUT_VARIABLE OUTPUT)
+  unset(__TestCompiler_testFortranCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_Fortran_COMPILER_WORKS ${CMAKE_Fortran_COMPILER_WORKS})
   unset(CMAKE_Fortran_COMPILER_WORKS CACHE)
@@ -72,14 +73,15 @@ endif()
 # Test for Fortran 90 support by using an f90-specific construct.
 if(NOT DEFINED CMAKE_Fortran_COMPILER_SUPPORTS_F90)
   message(CHECK_START "Checking whether ${CMAKE_Fortran_COMPILER} supports Fortran 90")
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompilerF90.f90 "
+  set(__TestCompiler_testFortranCompilerSource "
     PROGRAM TESTFortran90
     integer stop ; stop = 1 ; do while ( stop .eq. 0 ) ; end do
     END PROGRAM TESTFortran90
 ")
-  try_compile(CMAKE_Fortran_COMPILER_SUPPORTS_F90 ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompilerF90.f90
+  try_compile(CMAKE_Fortran_COMPILER_SUPPORTS_F90
+    SOURCE_FROM_VAR testFortranCompilerF90.f90 __TestCompiler_testFortranCompilerF90Source
     OUTPUT_VARIABLE OUTPUT)
+  unset(__TestCompiler_testFortranCompilerF90Source)
   if(CMAKE_Fortran_COMPILER_SUPPORTS_F90)
     message(CHECK_PASS "yes")
     file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
index ecbfa7f..1da0ae4 100644 (file)
@@ -41,7 +41,7 @@ endif()
 if(NOT CMAKE_HIP_COMPILER_WORKS)
   PrintTestCompilerStatus("HIP")
   __TestCompiler_setTryCompileTargetType()
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testHIPCompiler.hip
+  string(CONCAT __TestCompiler_testHIPCompilerSource
     "#ifndef __HIP__\n"
     "# error \"The CMAKE_HIP_COMPILER is set to a C/CXX compiler\"\n"
     "#endif\n"
@@ -49,9 +49,10 @@ if(NOT CMAKE_HIP_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_HIP_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_HIP_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testHIPCompiler.hip
+  try_compile(CMAKE_HIP_COMPILER_WORKS
+    SOURCE_FROM_VAR testHIPCompiler.hip __TestCompiler_testHIPCompilerSource
     OUTPUT_VARIABLE __CMAKE_HIP_COMPILER_OUTPUT)
+  unset(__TestCompiler_testHIPCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_HIP_COMPILER_WORKS ${CMAKE_HIP_COMPILER_WORKS})
   unset(CMAKE_HIP_COMPILER_WORKS CACHE)
index 20d1f8b..bbc90a7 100644 (file)
@@ -38,7 +38,7 @@ endif()
 if(NOT CMAKE_OBJC_COMPILER_WORKS)
   PrintTestCompilerStatus("OBJC")
   __TestCompiler_setTryCompileTargetType()
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m
+  string(CONCAT __TestCompiler_testObjCCompilerSource
     "#ifdef __cplusplus\n"
     "# error \"The CMAKE_OBJC_COMPILER is set to a C++ compiler\"\n"
     "#endif\n"
@@ -50,9 +50,10 @@ if(NOT CMAKE_OBJC_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_OBJC_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCCompiler.m
+  try_compile(CMAKE_OBJC_COMPILER_WORKS
+    SOURCE_FROM_VAR testObjCCompiler.m __TestCompiler_testObjCCompilerSource
     OUTPUT_VARIABLE __CMAKE_OBJC_COMPILER_OUTPUT)
+  unset(__TestCompiler_testObjCCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_OBJC_COMPILER_WORKS ${CMAKE_OBJC_COMPILER_WORKS})
   unset(CMAKE_OBJC_COMPILER_WORKS CACHE)
index 4f7185f..aceb939 100644 (file)
@@ -38,7 +38,7 @@ endif()
 if(NOT CMAKE_OBJCXX_COMPILER_WORKS)
   PrintTestCompilerStatus("OBJCXX")
   __TestCompiler_setTryCompileTargetType()
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm
+  string(CONCAT __TestCompiler_testObjCXXCompilerSource
     "#ifndef __cplusplus\n"
     "# error \"The CMAKE_OBJCXX_COMPILER is set to a C compiler\"\n"
     "#endif\n"
@@ -49,9 +49,10 @@ if(NOT CMAKE_OBJCXX_COMPILER_WORKS)
   # Clear result from normal variable.
   unset(CMAKE_OBJCXX_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testOBJCXXCompiler.mm
+  try_compile(CMAKE_OBJCXX_COMPILER_WORKS
+    SOURCE_FROM_VAR testObjCXXCompiler.mm __TestCompiler_testObjCXXCompilerSource
     OUTPUT_VARIABLE __CMAKE_OBJCXX_COMPILER_OUTPUT)
+  unset(__TestCompiler_testObjCXXCompilerSource)
   # Move result from cache to normal variable.
   set(CMAKE_OBJCXX_COMPILER_WORKS ${CMAKE_OBJCXX_COMPILER_WORKS})
   unset(CMAKE_OBJCXX_COMPILER_WORKS CACHE)
index 2f2546f..88a864c 100644 (file)
@@ -21,13 +21,12 @@ unset(CMAKE_Swift_COMPILER_WORKS CACHE)
 # any makefiles or projects.
 if(NOT CMAKE_Swift_COMPILER_WORKS)
   PrintTestCompilerStatus("Swift")
-  file(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/main.swift
-    "print(\"CMake\")\n")
   # Clear result from normal variable.
   unset(CMAKE_Swift_COMPILER_WORKS)
   # Puts test result in cache variable.
-  try_compile(CMAKE_Swift_COMPILER_WORKS ${CMAKE_BINARY_DIR}
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/main.swift
+  set(__CMAKE_Swift_TEST_SOURCE "print(\"CMake\")\n")
+  try_compile(CMAKE_Swift_COMPILER_WORKS
+    SOURCE_FROM_VAR main.swift __CMAKE_Swift_TEST_SOURCE
     OUTPUT_VARIABLE __CMAKE_Swift_COMPILER_OUTPUT)
   # Move result from cache to normal variable.
   set(CMAKE_Swift_COMPILER_WORKS ${CMAKE_Swift_COMPILER_WORKS})
@@ -64,4 +63,5 @@ else()
   include(${CMAKE_PLATFORM_INFO_DIR}/CMakeSwiftCompiler.cmake)
 endif()
 
+unset(__CMAKE_Swift_TEST_SOURCE)
 unset(__CMAKE_Swift_COMPILER_OUTPUT)
index 4934934..7c3ad6b 100644 (file)
@@ -53,9 +53,9 @@ Here's how it works:
 
 * :manual:`cpack <cpack(1)>` runs
 * it includes ``CPackConfig.cmake``
-* it iterates over the generators given by the ``-G`` command line option,
-  or if no such option was specified, over the list of generators given by
-  the :variable:`CPACK_GENERATOR` variable set in the ``CPackConfig.cmake``
+* it iterates over the generators given by the :option:`-G <cpack -G>` command
+  line option, or if no such option was specified, over the list of generators
+  given by the :variable:`CPACK_GENERATOR` variable set in the ``CPackConfig.cmake``
   input file.
 * foreach generator, it then
 
@@ -217,7 +217,8 @@ installers.  The most commonly-used variables are:
   to the user by the produced installer (often with an explicit "Accept"
   button, for graphical installers) prior to installation.  This license
   file is NOT added to the installed files but is used by some CPack generators
-  like NSIS.  If you want to install a license file (may be the same as this
+  like NSIS.  If you want to use UTF-8 characters, the file needs to be encoded
+  in UTF-8 BOM.  If you want to install a license file (may be the same as this
   one) along with your project, you must add an appropriate CMake
   :command:`install` command in your ``CMakeLists.txt``.
 
@@ -246,9 +247,9 @@ installers.  The most commonly-used variables are:
   List of CPack generators to use.  If not specified, CPack will create a
   set of options following the naming pattern
   :variable:`CPACK_BINARY_<GENNAME>` (e.g. ``CPACK_BINARY_NSIS``) allowing
-  the user to enable/disable individual generators.  If the ``-G`` option is
-  given on the :manual:`cpack <cpack(1)>` command line, it will override this
-  variable and any ``CPACK_BINARY_<GENNAME>`` options.
+  the user to enable/disable individual generators.  If the :option:`-G <cpack -G>`
+  option is given on the :manual:`cpack <cpack(1)>` command line, it will override
+  this variable and any ``CPACK_BINARY_<GENNAME>`` options.
 
 .. variable:: CPACK_OUTPUT_CONFIG_FILE
 
@@ -469,7 +470,35 @@ The following variables are for advanced uses of CPack:
   generates (when :variable:`CPACK_GENERATOR` is not set) a set of CMake
   options (see CMake :command:`option` command) which may then be used to
   select the CPack generator(s) to be used when building the ``package``
-  target or when running :manual:`cpack <cpack(1)>` without the ``-G`` option.
+  target or when running :manual:`cpack <cpack(1)>` without the
+  :option:`-G <cpack -G>` option.
+
+.. variable:: CPACK_READELF_EXECUTABLE
+
+  .. versionadded:: 3.25
+
+  Specify the ``readelf`` executable path used by CPack.
+  The default value will be ``CMAKE_READELF`` when set.  Otherwise,
+  the default value will be empty and CPack will use :command:`find_program`
+  to determine the ``readelf`` path when needed.
+
+.. variable:: CPACK_OBJCOPY_EXECUTABLE
+
+  .. versionadded:: 3.25
+
+  Specify the ``objcopy`` executable path used by CPack.
+  The default value will be ``CMAKE_OBJCOPY`` when set.  Otherwise,
+  the default value will be empty and CPack will use :command:`find_program`
+  to determine the ``objcopy`` path when needed.
+
+.. variable:: CPACK_OBJDUMP_EXECUTABLE
+
+  .. versionadded:: 3.25
+
+  Specify the ``objdump`` executable path used by CPack.
+  The default value will be ``CMAKE_OBJDUMP`` when set.  Otherwise,
+  the default value will be empty and CPack will use :command:`find_program`
+  to determine the ``objdump`` path when needed.
 
 #]=======================================================================]
 
@@ -590,6 +619,16 @@ _cpack_set_default(CPACK_RESOURCE_FILE_WELCOME
 
 _cpack_set_default(CPACK_MODULE_PATH "${CMAKE_MODULE_PATH}")
 
+if(CMAKE_READELF)
+  _cpack_set_default(CPACK_READELF_EXECUTABLE "${CMAKE_READELF}")
+endif()
+if(CMAKE_OBJCOPY)
+  _cpack_set_default(CPACK_OBJCOPY_EXECUTABLE "${CMAKE_OBJCOPY}")
+endif()
+if(CMAKE_OBJDUMP)
+  _cpack_set_default(CPACK_OBJDUMP_EXECUTABLE "${CMAKE_OBJDUMP}")
+endif()
+
 # Set default directory creation permissions mode
 if(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)
   _cpack_set_default(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
index 8ca9f28..529f4e7 100644 (file)
@@ -301,7 +301,7 @@ be built and installed on system using macOS 10.5 or later.
 
 The site argument is a URL where the archives for downloadable
 components will reside, e.g.,
-https://cmake.org/files/2.6.1/installer/ All of the archives
+https://cmake.org/files/v3.25/ All of the archives
 produced by CPack should be uploaded to that location.
 
 UPLOAD_DIRECTORY is the local directory where CPack will create the
index a4bfe1d..d4e02f1 100644 (file)
@@ -439,37 +439,57 @@ set(_CPACK_IFW_PREFIXES
   "QtIFW-")
 
 set(_CPACK_IFW_VERSIONS
+  "4.5.0"
+  "4.5"
+  "4.4.2"
+  "4.4.1"
+  "4.4.0"
   "4.4"
+  "4.3.0"
   "4.3"
+  "4.2.0"
   "4.2"
+  "4.1.1"
+  "4.1.0"
   "4.1"
+  "4.0.1"
+  "4.0.0"
   "4.0"
-  "3.2"
+  "3.2.3"
+  "3.2.2"
+  "3.2.1"
   "3.2.0"
-  "3.1"
+  "3.2"
+  "3.1.1"
   "3.1.0"
-  "3.0"
+  "3.1"
+  "3.0.6"
+  "3.0.4"
+  "3.0.3"
+  "3.0.2"
+  "3.0.1"
   "3.0.0"
-  "2.3"
+  "3.0"
   "2.3.0"
-  "2.2"
+  "2.3"
   "2.2.0"
-  "2.1"
+  "2.2"
   "2.1.0"
-  "2.0"
+  "2.1"
   "2.0.5"
   "2.0.3"
   "2.0.2"
   "2.0.1"
   "2.0.0"
-  "1.6"
+  "2.0"
   "1.6.0"
-  "1.5"
+  "1.6"
   "1.5.0"
-  "1.4"
+  "1.5"
   "1.4.0"
-  "1.3"
-  "1.3.0")
+  "1.4"
+  "1.3.0"
+  "1.3")
 
 set(_CPACK_IFW_SUFFIXES "bin")
 foreach(_CPACK_IFW_PREFIX ${_CPACK_IFW_PREFIXES})
index 8f8ebb4..16283d6 100644 (file)
@@ -33,7 +33,7 @@ file at the top of the project with content such as::
 (the CDash server can provide the file to a project administrator who
 configures ``MyProject``).  Settings in the config file are shared by
 both this ``CTest`` module and the :manual:`ctest(1)` command-line
-:ref:`Dashboard Client` mode (``ctest -S``).
+:ref:`Dashboard Client` mode (:option:`ctest -S`).
 
 While building a project for submission to CDash, CTest scans the
 build output for errors and warnings and reports them with surrounding
index 838fbbf..b91b48e 100644 (file)
@@ -86,7 +86,7 @@ if(NOT _CTEST_TARGETS_ADDED)
   # or "RUN_TESTS" target:
   if(CTEST_TEST_TARGET_ALIAS)
     add_custom_target(${CTEST_TEST_TARGET_ALIAS}
-      ${CMAKE_CTEST_COMMAND} ${__conf_types}
+      ${CMAKE_CTEST_COMMAND} ${CMAKE_CTEST_ARGUMENTS} ${__conf_types}
       USES_TERMINAL
       )
   endif()
index 23a206b..5c544f8 100644 (file)
@@ -11,7 +11,7 @@ CTestUseLaunchers is automatically included when you include(CTest).
 However, it is split out into its own module file so projects can use
 the CTEST_USE_LAUNCHERS functionality independently.
 
-To use launchers, set CTEST_USE_LAUNCHERS to ON in a ctest -S
+To use launchers, set CTEST_USE_LAUNCHERS to ON in a :option:`ctest -S`
 dashboard script, and then also set it in the cache of the configured
 project.  Both cmake and ctest need to know the value of it for the
 launchers to work properly.  CMake needs to know in order to generate
@@ -27,9 +27,12 @@ variable initialization only occurs if CTEST_USE_LAUNCHERS is not
 already defined.
 
 .. versionadded:: 3.8
-  If CTEST_USE_LAUNCHERS is on in a ctest -S script
+  If CTEST_USE_LAUNCHERS is on in a :option:`ctest -S` script
   the ctest_configure command will add -DCTEST_USE_LAUNCHERS:BOOL=TRUE
   to the cmake command used to configure the project.
+
+.. TODO Use RST markup
+
 #]=======================================================================]
 
 if(NOT DEFINED CTEST_USE_LAUNCHERS AND DEFINED ENV{CTEST_USE_LAUNCHERS_DEFAULT})
index 7b13c3a..1fa0898 100644 (file)
@@ -74,5 +74,5 @@ include_guard(GLOBAL)
 include(CheckSymbolExists)
 
 macro(CHECK_CXX_SYMBOL_EXISTS SYMBOL FILES VARIABLE)
-  __CHECK_SYMBOL_EXISTS_IMPL("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.cxx" "${SYMBOL}" "${FILES}" "${VARIABLE}" )
+  __CHECK_SYMBOL_EXISTS_IMPL(CheckSymbolExists.cxx "${SYMBOL}" "${FILES}" "${VARIABLE}" )
 endmacro()
index 8f1ca9d..7e3a7ee 100644 (file)
@@ -58,8 +58,7 @@ macro(CHECK_FORTRAN_FUNCTION_EXISTS FUNCTION VARIABLE)
     else()
       set(CHECK_FUNCTION_EXISTS_ADD_LIBRARIES)
     endif()
-    file(WRITE
-    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompiler.f
+    set(__CheckFunction_testFortranCompilerSource
     "
       program TESTFortran
       external ${FUNCTION}
@@ -68,12 +67,12 @@ macro(CHECK_FORTRAN_FUNCTION_EXISTS FUNCTION VARIABLE)
     "
     )
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompiler.f
+      SOURCE_FROM_VAR testFortranCompiler.f __CheckFunction_testFortranCompilerSource
       ${CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS}
       ${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES}
       OUTPUT_VARIABLE OUTPUT
     )
+    unset(__CheckFunction_testFortranCompilerSource)
     if(${VARIABLE})
       set(${VARIABLE} 1 CACHE INTERNAL "Have Fortran function ${FUNCTION}")
       message(CHECK_PASS "found")
index 9efa132..60f0184 100644 (file)
@@ -81,17 +81,15 @@ macro(CHECK_FUNCTION_EXISTS FUNCTION VARIABLE)
     endif()
 
     if(CMAKE_C_COMPILER_LOADED)
-      set(_cfe_source ${CMAKE_ROOT}/Modules/CheckFunctionExists.c)
+      set(_cfe_source CheckFunctionExists.c)
     elseif(CMAKE_CXX_COMPILER_LOADED)
-      set(_cfe_source ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckFunctionExists/CheckFunctionExists.cxx)
-      configure_file(${CMAKE_ROOT}/Modules/CheckFunctionExists.c "${_cfe_source}" COPYONLY)
+      set(_cfe_source CheckFunctionExists.cxx)
     else()
       message(FATAL_ERROR "CHECK_FUNCTION_EXISTS needs either C or CXX language enabled")
     endif()
 
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${_cfe_source}
+      SOURCE_FROM_FILE "${_cfe_source}" "${CMAKE_ROOT}/Modules/CheckFunctionExists.c"
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_FUNCTION_EXISTS_ADD_LINK_OPTIONS}
       ${CHECK_FUNCTION_EXISTS_ADD_LIBRARIES}
index cca1be9..9108e34 100644 (file)
@@ -76,6 +76,23 @@ endmacro()
 
 # Run IPO/LTO test
 macro(_ipo_run_language_check language)
+  set(_C_ext "c")
+  set(_CXX_ext "cpp")
+  set(_Fortran_ext "f")
+  string(COMPARE EQUAL "${language}" "CUDA" is_cuda)
+
+  set(ext ${_${language}_ext})
+  if(NOT "${ext}" STREQUAL "")
+    set(copy_sources foo.${ext} main.${ext})
+  elseif(is_cuda)
+    if(_CMAKE_CUDA_IPO_SUPPORTED_BY_CMAKE)
+      set("${X_RESULT}" YES PARENT_SCOPE)
+    endif()
+    return()
+  else()
+    message(FATAL_ERROR "Language not supported")
+  endif()
+
   set(testdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/_CMakeLTOTest-${language}")
 
   file(REMOVE_RECURSE "${testdir}")
@@ -100,20 +117,6 @@ macro(_ipo_run_language_check language)
       @ONLY
   )
 
-  string(COMPARE EQUAL "${language}" "C" is_c)
-  string(COMPARE EQUAL "${language}" "CXX" is_cxx)
-  string(COMPARE EQUAL "${language}" "Fortran" is_fortran)
-
-  if(is_c)
-    set(copy_sources foo.c main.c)
-  elseif(is_cxx)
-    set(copy_sources foo.cpp main.cpp)
-  elseif(is_fortran)
-    set(copy_sources foo.f main.f)
-  else()
-    message(FATAL_ERROR "Language not supported")
-  endif()
-
   foreach(x ${copy_sources})
     configure_file(
         "${try_compile_src}/${x}"
@@ -134,9 +137,9 @@ macro(_ipo_run_language_check language)
 
   try_compile(
       _IPO_LANGUAGE_CHECK_RESULT
-      "${bindir}"
-      "${srcdir}"
-      "${TRY_COMPILE_PROJECT_NAME}"
+      PROJECT "${TRY_COMPILE_PROJECT_NAME}"
+      SOURCE_DIR "${srcdir}"
+      BINARY_DIR "${bindir}"
       CMAKE_FLAGS
       "-DCMAKE_VERBOSE_MAKEFILE=ON"
       "-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON"
@@ -214,6 +217,11 @@ function(check_ipo_supported)
       list(APPEND languages "C")
     endif()
 
+    list(FIND enabled_languages "CUDA" result)
+    if(NOT result EQUAL -1)
+      list(APPEND languages "CUDA")
+    endif()
+
     list(FIND enabled_languages "Fortran" result)
     if(NOT result EQUAL -1)
       list(APPEND languages "Fortran")
@@ -222,7 +230,7 @@ function(check_ipo_supported)
     string(COMPARE EQUAL "${languages}" "" no_languages)
     if(no_languages)
       _ipo_not_supported(
-          "no C/CXX/Fortran languages found in ENABLED_LANGUAGES global property"
+          "no C/CXX/CUDA/Fortran languages found in ENABLED_LANGUAGES global property"
       )
       return()
     endif()
@@ -230,7 +238,7 @@ function(check_ipo_supported)
     set(languages "${X_LANGUAGES}")
 
     set(unsupported_languages "${languages}")
-    list(REMOVE_ITEM unsupported_languages "C" "CXX" "Fortran")
+    list(REMOVE_ITEM unsupported_languages "C" "CXX" "CUDA" "Fortran")
     string(COMPARE NOTEQUAL "${unsupported_languages}" "" has_unsupported)
     if(has_unsupported)
       _ipo_not_supported(
index 71ddde7..4cba91b 100644 (file)
@@ -54,8 +54,8 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE)
     endif()
     set(MACRO_CHECK_INCLUDE_FILE_FLAGS ${CMAKE_REQUIRED_FLAGS})
     set(CHECK_INCLUDE_FILE_VAR ${INCLUDE})
-    configure_file(${CMAKE_ROOT}/Modules/CheckIncludeFile.c.in
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.c)
+    file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.c.in _CIF_SOURCE_CONTENT)
+    string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT)
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_START "Looking for ${INCLUDE}")
     endif()
@@ -93,8 +93,7 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE)
     endif()
 
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.c
+      SOURCE_FROM_VAR CheckIncludeFile.c _CIF_SOURCE_CONTENT
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${_CIF_LINK_OPTIONS}
       ${_CIF_LINK_LIBRARIES}
index 953224e..f6af036 100644 (file)
@@ -53,8 +53,8 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE)
     endif()
     set(MACRO_CHECK_INCLUDE_FILE_FLAGS ${CMAKE_REQUIRED_FLAGS})
     set(CHECK_INCLUDE_FILE_VAR ${INCLUDE})
-    configure_file(${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx)
+    file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in _CIF_SOURCE_CONTENT)
+    string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT)
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_START "Looking for C++ include ${INCLUDE}")
     endif()
@@ -92,8 +92,7 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE)
     endif()
 
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx
+      SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${_CIF_LINK_OPTIONS}
       ${_CIF_LINK_LIBRARIES}
index 1800ca8..8e82859 100644 (file)
@@ -52,7 +52,7 @@ include_guard(GLOBAL)
 
 macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE)
   if(NOT DEFINED "${VARIABLE}")
-    set(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n")
+    set(_src_content "/* */\n")
 
     if("x${ARGN}" STREQUAL "x")
        if(CMAKE_C_COMPILER_LOADED)
@@ -71,9 +71,9 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE)
     endif()
 
     if(_lang STREQUAL "C")
-      set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckIncludeFiles/${VARIABLE}.c)
+      set(src ${VARIABLE}.c)
     elseif(_lang STREQUAL "CXX")
-      set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckIncludeFiles/${VARIABLE}.cpp)
+      set(src ${VARIABLE}.cpp)
     else()
       message(FATAL_ERROR "Unknown language:\n  ${_lang}\nSupported languages: C, CXX.\n")
     endif()
@@ -86,13 +86,11 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE)
     set(CHECK_INCLUDE_FILES_CONTENT "/* */\n")
     set(MACRO_CHECK_INCLUDE_FILES_FLAGS ${CMAKE_REQUIRED_FLAGS})
     foreach(FILE ${INCLUDE})
-      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT
+      string(APPEND _src_content
         "#include <${FILE}>\n")
     endforeach()
-    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT
+    string(APPEND _src_content
       "\n\nint main(void){return 0;}\n")
-    configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
-      "${src}" @ONLY)
 
     set(_INCLUDE ${INCLUDE}) # remove empty elements
     if("${_INCLUDE}" MATCHES "^([^;]+);.+;([^;]+)$")
@@ -136,8 +134,7 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE)
       message(CHECK_START "Looking for ${_description}")
     endif()
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${src}
+      SOURCE_FROM_VAR "${src}" _src_content
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${_CIF_LINK_OPTIONS}
       ${_CIF_LINK_LIBRARIES}
@@ -164,7 +161,7 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE)
       file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
         "Determining if files ${INCLUDE} "
         "exist failed with the following output:\n"
-        "${OUTPUT}\nSource:\n${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
+        "${OUTPUT}\nSource:\n${_src_content}\n")
     endif()
   endif()
 endmacro()
index 804e0cb..56424ac 100644 (file)
@@ -61,17 +61,15 @@ macro(CHECK_LIBRARY_EXISTS LIBRARY FUNCTION LOCATION VARIABLE)
     endif()
 
     if(CMAKE_C_COMPILER_LOADED)
-      set(_cle_source ${CMAKE_ROOT}/Modules/CheckFunctionExists.c)
+      set(_cle_source CheckFunctionExists.c)
     elseif(CMAKE_CXX_COMPILER_LOADED)
-      set(_cle_source ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckLibraryExists/CheckFunctionExists.cxx)
-      configure_file(${CMAKE_ROOT}/Modules/CheckFunctionExists.c "${_cle_source}" COPYONLY)
+      set(_cle_source CheckFunctionExists.cxx)
     else()
       message(FATAL_ERROR "CHECK_FUNCTION_EXISTS needs either C or CXX language enabled")
     endif()
 
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${_cle_source}
+      SOURCE_FROM_FILE "${_cle_source}" "${CMAKE_ROOT}/Modules/CheckFunctionExists.c"
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_LIBRARY_EXISTS_LINK_OPTIONS}
       LINK_LIBRARIES ${CHECK_LIBRARY_EXISTS_LIBRARIES}
index d29c5e8..12f4e86 100644 (file)
@@ -94,14 +94,11 @@ function(check_prototype_definition _FUNCTION _PROTOTYPE _RETURN _HEADER _VARIAB
     set(CHECK_PROTOTYPE_DEFINITION_PROTO ${_PROTOTYPE})
     set(CHECK_PROTOTYPE_DEFINITION_RETURN ${_RETURN})
 
-    configure_file("${__check_proto_def_dir}/CheckPrototypeDefinition.c.in"
-      "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c" @ONLY)
-
-    file(READ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c _SOURCE)
+    file(READ ${__check_proto_def_dir}/CheckPrototypeDefinition.c.in _SOURCE)
+    string(CONFIGURE "${_SOURCE}" _SOURCE @ONLY)
 
     try_compile(${_VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckPrototypeDefinition.c
+      SOURCE_FROM_VAR CheckPrototypeDefinition.c _SOURCE
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_PROTOTYPE_DEFINITION_LINK_OPTIONS}
       ${CHECK_PROTOTYPE_DEFINITION_LIBS}
index a7139af..0d44d56 100644 (file)
@@ -68,11 +68,11 @@ cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
 macro(CHECK_SYMBOL_EXISTS SYMBOL FILES VARIABLE)
   if(CMAKE_C_COMPILER_LOADED)
     __CHECK_SYMBOL_EXISTS_FILTER_FLAGS(C)
-    __CHECK_SYMBOL_EXISTS_IMPL("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.c" "${SYMBOL}" "${FILES}" "${VARIABLE}" )
+    __CHECK_SYMBOL_EXISTS_IMPL(CheckSymbolExists.c "${SYMBOL}" "${FILES}" "${VARIABLE}" )
     __CHECK_SYMBOL_EXISTS_RESTORE_FLAGS(C)
   elseif(CMAKE_CXX_COMPILER_LOADED)
     __CHECK_SYMBOL_EXISTS_FILTER_FLAGS(CXX)
-    __CHECK_SYMBOL_EXISTS_IMPL("${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckSymbolExists.cxx" "${SYMBOL}" "${FILES}" "${VARIABLE}" )
+    __CHECK_SYMBOL_EXISTS_IMPL(CheckSymbolExists.cxx "${SYMBOL}" "${FILES}" "${VARIABLE}" )
     __CHECK_SYMBOL_EXISTS_RESTORE_FLAGS(CXX)
   else()
     message(FATAL_ERROR "CHECK_SYMBOL_EXISTS needs either C or CXX language enabled")
@@ -92,7 +92,7 @@ endmacro()
 
 macro(__CHECK_SYMBOL_EXISTS_IMPL SOURCEFILE SYMBOL FILES VARIABLE)
   if(NOT DEFINED "${VARIABLE}" OR "x${${VARIABLE}}" STREQUAL "x${VARIABLE}")
-    set(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n")
+    set(_CSE_SOURCE "/* */\n")
     set(MACRO_CHECK_SYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS})
     if(CMAKE_REQUIRED_LINK_OPTIONS)
       set(CHECK_SYMBOL_EXISTS_LINK_OPTIONS
@@ -113,17 +113,17 @@ macro(__CHECK_SYMBOL_EXISTS_IMPL SOURCEFILE SYMBOL FILES VARIABLE)
       set(CMAKE_SYMBOL_EXISTS_INCLUDES)
     endif()
     foreach(FILE ${FILES})
-      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT
+      string(APPEND _CSE_SOURCE
         "#include <${FILE}>\n")
     endforeach()
-    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+    string(APPEND _CSE_SOURCE "
 int main(int argc, char** argv)
 {
   (void)argv;")
     set(_CSE_CHECK_NON_MACRO "return ((int*)(&${SYMBOL}))[argc];")
     if("${SYMBOL}" MATCHES "^[a-zA-Z_][a-zA-Z0-9_]*$")
       # The SYMBOL has a legal macro name.  Test whether it exists as a macro.
-      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+      string(APPEND _CSE_SOURCE "
 #ifndef ${SYMBOL}
   ${_CSE_CHECK_NON_MACRO}
 #else
@@ -132,22 +132,18 @@ int main(int argc, char** argv)
 #endif")
     else()
       # The SYMBOL cannot be a macro (e.g., a template function).
-      string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+      string(APPEND _CSE_SOURCE "
   ${_CSE_CHECK_NON_MACRO}")
     endif()
-    string(APPEND CMAKE_CONFIGURABLE_FILE_CONTENT "
+    string(APPEND _CSE_SOURCE "
 }")
     unset(_CSE_CHECK_NON_MACRO)
 
-    configure_file("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in"
-      "${SOURCEFILE}" @ONLY)
-
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_START "Looking for ${SYMBOL}")
     endif()
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      "${SOURCEFILE}"
+      SOURCE_FROM_VAR "${SOURCEFILE}" _CSE_SOURCE
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_SYMBOL_EXISTS_LINK_OPTIONS}
       ${CHECK_SYMBOL_EXISTS_LIBS}
@@ -164,7 +160,7 @@ int main(int argc, char** argv)
         "Determining if the ${SYMBOL} "
         "exist passed with the following output:\n"
         "${OUTPUT}\nFile ${SOURCEFILE}:\n"
-        "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
+        "${_CSE_SOURCE}\n")
     else()
       if(NOT CMAKE_REQUIRED_QUIET)
         message(CHECK_FAIL "not found")
@@ -174,9 +170,9 @@ int main(int argc, char** argv)
         "Determining if the ${SYMBOL} "
         "exist failed with the following output:\n"
         "${OUTPUT}\nFile ${SOURCEFILE}:\n"
-        "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n")
+        "${_CSE_SOURCE}\n")
     endif()
-    unset(CMAKE_CONFIGURABLE_FILE_CONTENT)
+    unset(_CSE_SOURCE)
   endif()
 endmacro()
 
index 602170c..b14ab06 100644 (file)
@@ -104,9 +104,9 @@ function(__check_type_size_impl type var map builtin language)
 
   # Perform language check
   if(language STREQUAL "C")
-    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.c)
+    set(src ${var}.c)
   elseif(language STREQUAL "CXX")
-    set(src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.cpp)
+    set(src ${var}.cpp)
   else()
     message(FATAL_ERROR "Unknown language:\n  ${language}\nSupported languages: C, CXX.\n")
   endif()
@@ -142,8 +142,9 @@ function(__check_type_size_impl type var map builtin language)
 
   # Perform the check.
   set(bin ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CheckTypeSize/${var}.bin)
-  configure_file(${__check_type_size_dir}/CheckTypeSize.c.in ${src} @ONLY)
-  try_compile(HAVE_${var} ${CMAKE_BINARY_DIR} ${src}
+  file(READ ${__check_type_size_dir}/CheckTypeSize.c.in src_content)
+  string(CONFIGURE "${src_content}" src_content @ONLY)
+  try_compile(HAVE_${var} SOURCE_FROM_VAR "${src}" src_content
     COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
     LINK_OPTIONS ${CMAKE_REQUIRED_LINK_OPTIONS}
     LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}
@@ -209,9 +210,8 @@ function(__check_type_size_impl type var map builtin language)
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_FAIL "failed")
     endif()
-    file(READ ${src} content)
     file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-      "Determining size of ${type} failed with the following output:\n${output}\n${src}:\n${content}\n\n")
+      "Determining size of ${type} failed with the following output:\n${output}\n${src}:\n${src_content}\n\n")
     set(${var} "" CACHE INTERNAL "CHECK_TYPE_SIZE: ${type} unknown")
     file(REMOVE ${map})
   endif()
index 7420124..5dc3441 100644 (file)
@@ -62,8 +62,7 @@ macro(CHECK_VARIABLE_EXISTS VAR VARIABLE)
       set(CHECK_VARIABLE_EXISTS_ADD_LIBRARIES)
     endif()
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_ROOT}/Modules/CheckVariableExists.c
+      SOURCES ${CMAKE_ROOT}/Modules/CheckVariableExists.c
       COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_VARIABLE_EXISTS_ADD_LINK_OPTIONS}
       ${CHECK_VARIABLE_EXISTS_ADD_LIBRARIES}
index 219897e..d9929f1 100644 (file)
@@ -35,6 +35,10 @@ set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "cudadevrt;cudart_static")
 set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_SHARED "cudadevrt;cudart")
 set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_NONE   "")
 
+# Clang doesn't support CUDA device LTO
+set(_CMAKE_CUDA_IPO_SUPPORTED_BY_CMAKE NO)
+set(_CMAKE_CUDA_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO)
+
 if(UNIX)
   list(APPEND CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl")
 endif()
index df115d3..4f9af37 100644 (file)
@@ -80,7 +80,7 @@ else()
       set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-flto")
     endif()
 
-    if(ANDROID AND NOT CMAKE_ANDROID_NDK_VERSION VERSION_GREATER_EQUAL "22")
+    if(ANDROID AND CMAKE_ANDROID_NDK_VERSION VERSION_LESS "22")
       # https://github.com/android-ndk/ndk/issues/242
       set(CMAKE_${lang}_LINK_OPTIONS_IPO "-fuse-ld=gold")
     endif()
@@ -255,6 +255,7 @@ macro(__compiler_clang_cxx_standards lang)
         cxx_std_17
         cxx_std_20
         cxx_std_23
+        cxx_std_26
         )
       _record_compiler_features(${lang} "" CMAKE_${lang}_COMPILE_FEATURES)
     endmacro()
index 6c15735..3f0ef1f 100644 (file)
@@ -22,9 +22,9 @@ if(NOT CMAKE_IAR_CXX_FLAG)
   set(_CMAKE_IAR_MODERNCXX_LIST 14 17)
   if(${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} IN_LIST _CMAKE_IAR_MODERNCXX_LIST OR
      ("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" AND ${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} EQUAL 98))
-    string(PREPEND CMAKE_CXX_FLAGS "--c++ ")
+    set(CMAKE_IAR_CXX_FLAG --c++)
   else()
-    string(PREPEND CMAKE_CXX_FLAGS "--eec++ ")
+    set(CMAKE_IAR_CXX_FLAG --eec++)
   endif()
   unset(_CMAKE_IAR_MODERNCXX_LIST)
 
diff --git a/Modules/Compiler/IntelLLVM-FindBinUtils.cmake b/Modules/Compiler/IntelLLVM-FindBinUtils.cmake
new file mode 100644 (file)
index 0000000..c5b1ee6
--- /dev/null
@@ -0,0 +1,45 @@
+if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL "")
+  message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set")
+endif()
+
+# Ubuntu:
+# * /usr/bin/llvm-ar-9
+# * /usr/bin/llvm-ranlib-9
+string(REGEX MATCH "^([0-9]+)" __version_x
+    "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}")
+
+# Debian:
+# * /usr/bin/llvm-ar-4.0
+# * /usr/bin/llvm-ranlib-4.0
+string(REGEX MATCH "^([0-9]+\\.[0-9]+)" __version_x_y
+    "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_VERSION}")
+
+# Try to find tools in the IntelLLVM Clang tools directory
+get_filename_component(__intel_llvm_hint_1 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY)
+get_filename_component(__intel_llvm_hint_1 "${__intel_llvm_hint_1}/../bin-llvm" REALPATH)
+
+get_filename_component(__intel_llvm_hint_2 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY)
+
+set(__intel_llvm_hints ${__intel_llvm_hint_1} ${__intel_llvm_hint_2})
+
+# http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ar.1.html
+find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR NAMES
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x_y}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar-${__version_x}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ar"
+    HINTS ${__intel_llvm_hints}
+    NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
+    DOC "LLVM archiver"
+)
+mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
+
+# http://manpages.ubuntu.com/manpages/precise/en/man1/llvm-ranlib.1.html
+find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB NAMES
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x_y}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib-${__version_x}"
+    "${_CMAKE_TOOLCHAIN_PREFIX}llvm-ranlib"
+    HINTS ${__intel_llvm_hints}
+    NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
+    DOC "Generate index for LLVM archive"
+)
+mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_RANLIB)
index c5800dc..30de1a9 100644 (file)
@@ -15,6 +15,15 @@ set(__pch_header_CXX "c++-header")
 set(__pch_header_OBJC "objective-c-header")
 set(__pch_header_OBJCXX "objective-c++-header")
 
+# Variables that are common across front-end variants
+macro(__compiler_intel_llvm_common lang)
+  set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
+  set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
+  set(CMAKE_${lang}_ARCHIVE_CREATE_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" r <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH_IPO "\"${CMAKE_${lang}_COMPILER_RANLIB}\" <TARGET>")
+endmacro()
+
 if(CMAKE_HOST_WIN32)
   # MSVC-like
   macro(__compiler_intel_llvm lang)
@@ -24,6 +33,9 @@ if(CMAKE_HOST_WIN32)
       set(CMAKE_${lang}_COMPILE_OPTIONS_INVALID_PCH -Winvalid-pch)
       set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "-WX")
     endif()
+    __compiler_intel_llvm_common(${lang})
+    set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-Qipo")
+    set(CMAKE_${lang}_LINK_OPTIONS_IPO "-Qipo")
   endmacro()
 else()
   # GNU-like
@@ -61,12 +73,9 @@ else()
     set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
     set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP)
 
-    set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
-    set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-    set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-flto=thin")
-    set(CMAKE_${lang}_ARCHIVE_CREATE_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" cr <TARGET> <LINK_FLAGS> <OBJECTS>")
-    set(CMAKE_${lang}_ARCHIVE_APPEND_IPO "\"${CMAKE_${lang}_COMPILER_AR}\" r <TARGET> <LINK_FLAGS> <OBJECTS>")
-    set(CMAKE_${lang}_ARCHIVE_FINISH_IPO "\"${CMAKE_${lang}_COMPILER_RANLIB}\" <TARGET>")
+    __compiler_intel_llvm_common(${lang})
+    set(CMAKE_${lang}_COMPILE_OPTIONS_IPO "-ipo")
+    set(CMAKE_${lang}_LINK_OPTIONS_IPO "-ipo")
 
     set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E <SOURCE> > <PREPROCESSED_SOURCE>")
     set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -S <SOURCE> -o <ASSEMBLY_SOURCE>")
index 75165fd..212f6d1 100644 (file)
@@ -72,7 +72,20 @@ elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
       cxx_std_17
       cxx_std_20
       cxx_std_23
+      cxx_std_26
       )
     _record_compiler_features(CXX "" CMAKE_CXX_COMPILE_FEATURES)
   endmacro()
 endif()
+
+if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.34")
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+  string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+    "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE> -nologo -TP"
+    " -showIncludes"
+    " -scanDependencies <DYNDEP_FILE>"
+    " -Fo<OBJECT>")
+  set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT "msvc")
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "msvc")
+  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
+endif ()
index e07d152..c8dcd2a 100644 (file)
@@ -48,6 +48,13 @@ if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
   set(CMAKE_CUDA_DEPENDS_USE_COMPILER TRUE)
 endif()
 
+if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2)
+  set(_CMAKE_CUDA_IPO_SUPPORTED_BY_CMAKE YES)
+  set(_CMAKE_CUDA_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
+
+  set(CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO " -dlto")
+endif()
+
 if(NOT "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC")
   set(CMAKE_CUDA_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE)
   set(CMAKE_CUDA_COMPILE_OPTIONS_PIC -Xcompiler=-fPIC)
@@ -61,6 +68,7 @@ if(NOT "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC")
   string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG")
   string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG")
 endif()
+
 set(CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS -shared)
 set(CMAKE_INCLUDE_SYSTEM_FLAG_CUDA -isystem=)
 
@@ -132,15 +140,19 @@ else()
 
 endif()
 
-# FIXME: investigate use of --options-file.
-# Tell Makefile generator that nvcc does not support @<rspfile> syntax.
-set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0)
-set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
-set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0)
-
 if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0")
   set(CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ")
   set(CMAKE_CUDA_RESPONSE_FILE_FLAG "--options-file ")
 endif()
 
+if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0")
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 1)
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 1)
+else()
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0)
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
+  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0)
+endif()
+
 __compiler_check_default_language_standard(CUDA 6.0 03)
diff --git a/Modules/Compiler/Tasking-ASM.cmake b/Modules/Compiler/Tasking-ASM.cmake
new file mode 100644 (file)
index 0000000..19bce19
--- /dev/null
@@ -0,0 +1,7 @@
+include(Compiler/Tasking)
+
+set(CMAKE_ASM_OUTPUT_EXTENSION ".o")
+set(CMAKE_ASM_OUTPUT_EXTENSION_REPLACE 1)
+
+set(CMAKE_ASM_COMPILE_OBJECT       "<CMAKE_ASM_COMPILER> <INCLUDES> <FLAGS> -o <OBJECT> <SOURCE>")
+set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS S;s;asm;msa)
diff --git a/Modules/Compiler/Tasking-C.cmake b/Modules/Compiler/Tasking-C.cmake
new file mode 100644 (file)
index 0000000..0ea3cd2
--- /dev/null
@@ -0,0 +1,47 @@
+include(Compiler/Tasking)
+__compiler_tasking(C)
+
+set(CMAKE_C90_STANDARD_COMPILE_OPTION "--iso=90" "--strict")
+set(CMAKE_C90_EXTENSION_COMPILE_OPTION "--iso=90" " ")
+
+set(CMAKE_C99_STANDARD_COMPILE_OPTION "--iso=99" "--strict")
+set(CMAKE_C99_EXTENSION_COMPILE_OPTION "--iso=99" " ")
+
+set(CMAKE_C11_STANDARD_COMPILE_OPTION "--iso=11" "--strict")
+set(CMAKE_C11_EXTENSION_COMPILE_OPTION "--iso=11" " ")
+
+if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "TriCore")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(C 10.1 11)
+  else()
+    __compiler_check_default_language_standard(C 6.3 11)
+  endif()
+elseif(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(C 10.1 11)
+  elseif(CMAKE_TASKING_TOOLSET STREQUAL "TriCore")
+    __compiler_check_default_language_standard(C 6.3 11)
+  else()
+    __compiler_check_default_language_standard(C 6.0 11)
+  endif()
+elseif(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "MCS")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(C 10.1 11)
+  elseif(CMAKE_TASKING_TOOLSET STREQUAL "TriCore")
+    __compiler_check_default_language_standard(C 6.3 11)
+  else()
+    __compiler_check_default_language_standard(C 3.3 11)
+  endif()
+elseif(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARC")
+  __compiler_check_default_language_standard(C 10.1 11)
+elseif(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "8051")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(C 10.1 11)
+  elseif(CMAKE_TASKING_TOOLSET STREQUAL "TriCore")
+    __compiler_check_default_language_standard(C 6.3 11)
+  else()
+    __compiler_check_default_language_standard(C 7.2 89)
+  endif()
+elseif(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "PCP")
+  __compiler_check_default_language_standard(C 6.3 11)
+endif()
diff --git a/Modules/Compiler/Tasking-CXX.cmake b/Modules/Compiler/Tasking-CXX.cmake
new file mode 100644 (file)
index 0000000..635104c
--- /dev/null
@@ -0,0 +1,31 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+include(Compiler/Tasking)
+__compiler_tasking(CXX)
+
+set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "--c++=03" "--strict")
+set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "--iso=03" " ")
+
+set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "--c++=11" "--strict")
+set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "--c++=11" " ")
+
+set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "--c++=14" "--strict")
+set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "--c++=14" " ")
+
+if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "TriCore")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(CXX 10.1 14)
+  else()
+    __compiler_check_default_language_standard(CXX 6.3 14)
+  endif()
+elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM")
+  if(CMAKE_TASKING_TOOLSET STREQUAL "SmartCode")
+    __compiler_check_default_language_standard(CXX 10.1 14)
+  elseif(CMAKE_TASKING_TOOLSET STREQUAL "TriCore")
+    __compiler_check_default_language_standard(CXX 6.3 14)
+  else()
+    __compiler_check_default_language_standard(CXX 6.0 14)
+  endif()
+else()
+  message(FATAL_ERROR "CXX is not supported with the ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} architecture.")
+endif()
diff --git a/Modules/Compiler/Tasking-DetermineCompiler.cmake b/Modules/Compiler/Tasking-DetermineCompiler.cmake
new file mode 100644 (file)
index 0000000..a40be19
--- /dev/null
@@ -0,0 +1,10 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+set(_compiler_id_pp_test "defined(__TASKING__)")
+
+set(_compiler_id_version_compute "
+  # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__VERSION__/1000)
+  # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__VERSION__ % 100)")
+
+string(APPEND _compiler_id_version_compute "
+# define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__VERSION__)")
diff --git a/Modules/Compiler/Tasking-FindBinUtils.cmake b/Modules/Compiler/Tasking-FindBinUtils.cmake
new file mode 100644 (file)
index 0000000..eab31d7
--- /dev/null
@@ -0,0 +1,18 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Find the archiver for the compiler architecture, which is always in the same
+# directory as the compiler.
+if(NOT DEFINED _CMAKE_PROCESSING_LANGUAGE OR _CMAKE_PROCESSING_LANGUAGE STREQUAL "")
+  message(FATAL_ERROR "Internal error: _CMAKE_PROCESSING_LANGUAGE is not set")
+endif()
+
+get_filename_component(__tasking_hints "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" DIRECTORY)
+
+find_program(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR
+  NAMES artc ararm armcs ar51 ararc arpcp
+  HINTS ${__tasking_hints}
+  NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH
+  DOC "Tasking Archiver"
+)
+mark_as_advanced(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_AR)
diff --git a/Modules/Compiler/Tasking.cmake b/Modules/Compiler/Tasking.cmake
new file mode 100644 (file)
index 0000000..5bf066e
--- /dev/null
@@ -0,0 +1,54 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# This module is shared by multiple languages; use include blocker.
+if(_Tasking_CMAKE_LOADED)
+  return()
+endif()
+set(_Tasking_CMAKE_LOADED TRUE)
+include(Compiler/CMakeCommonCompilerMacros)
+
+set(CMAKE_EXECUTABLE_SUFFIX ".elf")
+set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
+set(BUILD_SHARED_LIBS FALSE CACHE BOOL "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+set(CMAKE_LINK_SEARCH_START_STATIC TRUE)
+
+if(NOT CMAKE_TASKING_TOOLSET)
+  set(CMAKE_TASKING_TOOLSET "Standalone")
+endif()
+
+macro(__compiler_tasking lang)
+
+  set(CMAKE_${lang}_VERBOSE_FLAG "-v")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "--pic")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl" " ")
+  set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "-f ")
+  set(CMAKE_DEPFILE_FLAGS_${lang} "--dep-file=<DEP_FILE>")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "--warning-as-errors")
+
+  string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
+  string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -O0 -g")
+  string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O2 -t4 -DNDEBUG")
+  string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O2 -t2 -DNDEBUG")
+  string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -t2 -g -DNDEBUG")
+
+  set(CMAKE_${lang}_ARCHIVE_CREATE "\"${CMAKE_${lang}_COMPILER_AR}\" -r <TARGET> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND "\"${CMAKE_${lang}_COMPILER_AR}\" -r <TARGET> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH "")
+
+  set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -cs <SOURCE> -o <ASSEMBLY_SOURCE>")
+  set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -Ep <SOURCE> > <PREPROCESSED_SOURCE>")
+
+  set(CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "${CMAKE_${lang}_COMPILER}")
+  if(CMAKE_${lang}_COMPILER_ARG1)
+    separate_arguments(_COMPILER_ARGS NATIVE_COMMAND "${CMAKE_${lang}_COMPILER_ARG1}")
+    list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND ${_COMPILER_ARGS})
+    unset(_COMPILER_ARGS)
+  endif()
+  list(APPEND CMAKE_${lang}_COMPILER_PREDEFINES_COMMAND "-Ep" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp")
+
+endmacro()
index 9952658..22a25bd 100644 (file)
@@ -1840,7 +1840,11 @@ function(_ep_get_build_command
       else()
         set(cmd "${CMAKE_COMMAND}")
       endif()
-      set(args --build ".")
+      if(step STREQUAL "INSTALL")
+        set(args --install ".")
+      else()
+        set(args --build ".")
+      endif()
       get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
       if(_isMultiConfig)
         if (CMAKE_CFG_INTDIR AND
@@ -1862,9 +1866,6 @@ function(_ep_get_build_command
         endif()
         list(APPEND args --config ${config})
       endif()
-      if(step STREQUAL "INSTALL")
-        list(APPEND args --target install)
-      endif()
       # But for "TEST" drive the project with corresponding "ctest".
       if("x${step}x" STREQUAL "xTESTx")
         string(REGEX REPLACE "^(.*/)cmake([^/]*)$" "\\1ctest\\2" cmd "${cmd}")
@@ -3832,6 +3833,19 @@ function(_ep_add_install_command name)
     set(uses_terminal "")
   endif()
 
+  # With BUILD_ALWAYS+BUILD_BYPRODUCTS, Ninja restats the
+  # build step outputs and may not consider this step to
+  # be out-of-date.  Explicitly mark it out-of-date too.
+  get_property(build_always
+    TARGET ${name}
+    PROPERTY _EP_BUILD_ALWAYS
+  )
+  if(build_always)
+    set(always 1)
+  else()
+    set(always 0)
+  endif()
+
   set(__cmdQuoted)
   foreach(__item IN LISTS cmd)
     string(APPEND __cmdQuoted " [==[${__item}]==]")
@@ -3842,6 +3856,7 @@ function(_ep_add_install_command name)
       COMMAND ${__cmdQuoted}
       WORKING_DIRECTORY \${binary_dir}
       DEPENDEES build
+      ALWAYS \${always}
       ${log}
       ${uses_terminal}
     )"
index 7896f62..50f0167 100644 (file)
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.5)
 
 function(get_hash_for_ref ref out_var err_var)
   execute_process(
-    COMMAND "@git_EXECUTABLE@" rev-parse "${ref}^0"
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git rev-parse "${ref}^0"
     WORKING_DIRECTORY "@work_dir@"
     RESULT_VARIABLE error_code
     OUTPUT_VARIABLE ref_hash
@@ -27,7 +27,7 @@ endif()
 
 
 execute_process(
-  COMMAND "@git_EXECUTABLE@" show-ref "@git_tag@"
+  COMMAND "@git_EXECUTABLE@" --git-dir=.git show-ref "@git_tag@"
   WORKING_DIRECTORY "@work_dir@"
   OUTPUT_VARIABLE show_ref_output
 )
@@ -95,7 +95,7 @@ endif()
 if(fetch_required)
   message(VERBOSE "Fetching latest from the remote @git_remote_name@")
   execute_process(
-    COMMAND "@git_EXECUTABLE@" fetch --tags --force "@git_remote_name@"
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git fetch --tags --force "@git_remote_name@"
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
   )
@@ -112,7 +112,7 @@ if(git_update_strategy MATCHES "^REBASE(_CHECKOUT)?$")
   # We can't if we aren't already on a branch and we shouldn't if that local
   # branch isn't tracking the one we want to checkout.
   execute_process(
-    COMMAND "@git_EXECUTABLE@" symbolic-ref -q HEAD
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git symbolic-ref -q HEAD
     WORKING_DIRECTORY "@work_dir@"
     OUTPUT_VARIABLE current_branch
     OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -128,7 +128,7 @@ if(git_update_strategy MATCHES "^REBASE(_CHECKOUT)?$")
 
   else()
     execute_process(
-      COMMAND "@git_EXECUTABLE@" for-each-ref "--format=%(upstream:short)" "${current_branch}"
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git for-each-ref "--format=%(upstream:short)" "${current_branch}"
       WORKING_DIRECTORY "@work_dir@"
       OUTPUT_VARIABLE upstream_branch
       OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -151,7 +151,7 @@ endif()
 
 # Check if stash is needed
 execute_process(
-  COMMAND "@git_EXECUTABLE@" status --porcelain
+  COMMAND "@git_EXECUTABLE@" --git-dir=.git status --porcelain
   WORKING_DIRECTORY "@work_dir@"
   RESULT_VARIABLE error_code
   OUTPUT_VARIABLE repo_status
@@ -165,7 +165,7 @@ string(LENGTH "${repo_status}" need_stash)
 # rebase or checkout without losing those changes permanently
 if(need_stash)
   execute_process(
-    COMMAND "@git_EXECUTABLE@" stash save @git_stash_save_options@
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git stash save @git_stash_save_options@
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
   )
@@ -173,13 +173,13 @@ endif()
 
 if(git_update_strategy STREQUAL "CHECKOUT")
   execute_process(
-    COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}"
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}"
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
   )
 else()
   execute_process(
-    COMMAND "@git_EXECUTABLE@" rebase "${checkout_name}"
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git rebase "${checkout_name}"
     WORKING_DIRECTORY "@work_dir@"
     RESULT_VARIABLE error_code
     OUTPUT_VARIABLE rebase_output
@@ -188,7 +188,7 @@ else()
   if(error_code)
     # Rebase failed, undo the rebase attempt before continuing
     execute_process(
-      COMMAND "@git_EXECUTABLE@" rebase --abort
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git rebase --abort
       WORKING_DIRECTORY "@work_dir@"
     )
 
@@ -196,7 +196,7 @@ else()
       # Not allowed to do a checkout as a fallback, so cannot proceed
       if(need_stash)
         execute_process(
-          COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+          COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
           WORKING_DIRECTORY "@work_dir@"
           )
       endif()
@@ -218,7 +218,7 @@ else()
     message(WARNING "Rebase failed, output has been saved to ${error_log_file}"
                     "\nFalling back to checkout, previous commit tagged as ${tag_name}")
     execute_process(
-      COMMAND "@git_EXECUTABLE@" tag -a
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git tag -a
               -m "ExternalProject attempting to move from here to ${checkout_name}"
               ${tag_name}
       WORKING_DIRECTORY "@work_dir@"
@@ -226,7 +226,7 @@ else()
     )
 
     execute_process(
-      COMMAND "@git_EXECUTABLE@" checkout "${checkout_name}"
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}"
       WORKING_DIRECTORY "@work_dir@"
       COMMAND_ERROR_IS_FATAL ANY
     )
@@ -236,29 +236,29 @@ endif()
 if(need_stash)
   # Put back the stashed changes
   execute_process(
-    COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
     WORKING_DIRECTORY "@work_dir@"
     RESULT_VARIABLE error_code
     )
   if(error_code)
     # Stash pop --index failed: Try again dropping the index
     execute_process(
-      COMMAND "@git_EXECUTABLE@" reset --hard --quiet
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet
       WORKING_DIRECTORY "@work_dir@"
     )
     execute_process(
-      COMMAND "@git_EXECUTABLE@" stash pop --quiet
+      COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --quiet
       WORKING_DIRECTORY "@work_dir@"
       RESULT_VARIABLE error_code
     )
     if(error_code)
       # Stash pop failed: Restore previous state.
       execute_process(
-        COMMAND "@git_EXECUTABLE@" reset --hard --quiet ${head_sha}
+        COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet ${head_sha}
         WORKING_DIRECTORY "@work_dir@"
       )
       execute_process(
-        COMMAND "@git_EXECUTABLE@" stash pop --index --quiet
+        COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
         WORKING_DIRECTORY "@work_dir@"
       )
       message(FATAL_ERROR "\nFailed to unstash changes in: '@work_dir@'."
@@ -270,7 +270,7 @@ endif()
 set(init_submodules "@init_submodules@")
 if(init_submodules)
   execute_process(
-    COMMAND "@git_EXECUTABLE@" submodule update @git_submodules_recurse@ --init @git_submodules@
+    COMMAND "@git_EXECUTABLE@" --git-dir=.git submodule update @git_submodules_recurse@ --init @git_submodules@
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
   )
index 995e69a..ac3918c 100644 (file)
@@ -111,6 +111,7 @@ Commands
     FetchContent_Declare(
       <name>
       <contentOptions>...
+      [SYSTEM]
       [OVERRIDE_FIND_PACKAGE |
        FIND_PACKAGE_ARGS args...]
     )
@@ -229,6 +230,16 @@ Commands
       to intercept any direct call to :command:`find_package`, except if that
       call contains the ``BYPASS_PROVIDER`` option.
 
+  .. versionadded:: 3.25
+
+    ``SYSTEM``
+      If the ``SYSTEM`` argument is provided, targets created by
+      the dependency will have their :prop_tgt:`SYSTEM` property
+      set to true when populated by :command:`FetchContent_MakeAvailable`.
+      The entries in their  :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES`
+      will be treated as ``SYSTEM`` include directories when
+      compiling consumers.
+
 .. command:: FetchContent_MakeAvailable
 
   .. versionadded:: 3.14
@@ -884,9 +895,10 @@ Overriding Where To Find CMakeLists.txt
 
 If the sub-project's ``CMakeLists.txt`` file is not at the top level of its
 source tree, the ``SOURCE_SUBDIR`` option can be used to tell ``FetchContent``
-where to find it.  The following example shows how to use that option and
+where to find it.  The following example shows how to use that option, and
 it also sets a variable which is meaningful to the subproject before pulling
-it into the main build:
+it into the main build (set as an ``INTERNAL`` cache variable to avoid
+problems with policy :policy:`CMP0077`):
 
 .. code-block:: cmake
 
@@ -897,7 +909,7 @@ it into the main build:
     GIT_TAG        ae50d9b9902526efd6c7a1907d09739f959c6297 # v3.15.0
     SOURCE_SUBDIR  cmake
   )
-  set(protobuf_BUILD_TESTS OFF)
+  set(protobuf_BUILD_TESTS OFF CACHE INTERNAL "")
   FetchContent_MakeAvailable(protobuf)
 
 Complex Dependency Hierarchies
@@ -1972,13 +1984,17 @@ macro(FetchContent_MakeAvailable)
       if("${__cmake_contentDetails}" STREQUAL "")
         message(FATAL_ERROR "No details have been set for content: ${__cmake_contentName}")
       endif()
-      cmake_parse_arguments(__cmake_arg "" "SOURCE_SUBDIR" "" ${__cmake_contentDetails})
+      cmake_parse_arguments(__cmake_arg "SYSTEM" "SOURCE_SUBDIR" "" ${__cmake_contentDetails})
       if(NOT "${__cmake_arg_SOURCE_SUBDIR}" STREQUAL "")
         string(APPEND __cmake_srcdir "/${__cmake_arg_SOURCE_SUBDIR}")
       endif()
 
       if(EXISTS ${__cmake_srcdir}/CMakeLists.txt)
-        add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR})
+        if (__cmake_arg_SYSTEM)
+          add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR} SYSTEM)
+        else()
+          add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR})
+        endif()
       endif()
 
       unset(__cmake_srcdir)
index 9655440..d63b707 100644 (file)
@@ -7,7 +7,7 @@ FindAVIFile
 
 Locate AVIFILE library and include paths
 
-AVIFILE (http://avifile.sourceforge.net/) is a set of libraries for
+AVIFILE (https://avifile.sourceforge.net/) is a set of libraries for
 i386 machines to use various AVI codecs.  Support is limited beyond
 Linux.  Windows provides native AVI support, and so doesn't need this
 library.  This module defines
index 7a381af..7af1017 100644 (file)
@@ -12,7 +12,7 @@ This module finds an installed Fortran library that implements the
 
 At least one of the ``C``, ``CXX``, or ``Fortran`` languages must be enabled.
 
-.. _`BLAS linear-algebra interface`: http://www.netlib.org/blas/
+.. _`BLAS linear-algebra interface`: https://netlib.org/blas/
 
 Input Variables
 ^^^^^^^^^^^^^^^
@@ -35,6 +35,12 @@ The following variables may be set to influence this module's behavior:
   if set ``pkg-config`` will be used to search for a BLAS library first
   and if one is found that is preferred
 
+``BLA_PKGCONFIG_BLAS``
+  .. versionadded:: 3.25
+
+  If set, the ``pkg-config`` method will look for this module name instead of
+  just ``blas``.
+
 ``BLA_SIZEOF_INTEGER``
   .. versionadded:: 3.22
 
@@ -273,8 +279,11 @@ endif()
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 
 if(BLA_PREFER_PKGCONFIG)
+  if(NOT BLA_PKGCONFIG_BLAS)
+    set(BLA_PKGCONFIG_BLAS "blas")
+  endif()
   find_package(PkgConfig QUIET)
-  pkg_check_modules(PKGC_BLAS QUIET blas)
+  pkg_check_modules(PKGC_BLAS QUIET ${BLA_PKGCONFIG_BLAS})
   if(PKGC_BLAS_FOUND)
     set(BLAS_FOUND ${PKGC_BLAS_FOUND})
     set(BLAS_LIBRARIES "${PKGC_BLAS_LINK_LIBRARIES}")
index 3d8ce88..46b62d2 100644 (file)
@@ -5,7 +5,7 @@
 FindBacktrace
 -------------
 
-Find provider for `backtrace(3) <http://man7.org/linux/man-pages/man3/backtrace.3.html>`__.
+Find provider for `backtrace(3) <https://man7.org/linux/man-pages/man3/backtrace.3.html>`__.
 
 Checks if OS supports ``backtrace(3)`` via either ``libc`` or custom library.
 This module defines the following variables:
index af5f798..470111f 100644 (file)
@@ -799,7 +799,9 @@ if(NOT "${CUDA_TOOLKIT_ROOT_DIR}" STREQUAL "${CUDA_TOOLKIT_ROOT_DIR_INTERNAL}")
   unset(CUDA_VERSION CACHE)
 endif()
 
-if(NOT "${CUDA_TOOLKIT_TARGET_DIR}" STREQUAL "${CUDA_TOOLKIT_TARGET_DIR_INTERNAL}")
+# If CUDA_TOOLKIT_TARGET_DIR exists, check if it has changed.
+if(DEFINED CUDA_TOOLKIT_TARGET_DIR
+    AND NOT "${CUDA_TOOLKIT_TARGET_DIR}" STREQUAL "${CUDA_TOOLKIT_TARGET_DIR_INTERNAL}")
   cuda_unset_include_and_libraries()
 endif()
 
index a35b3f8..5fad337 100644 (file)
@@ -135,10 +135,10 @@ function(CUDA_DETECT_INSTALLED_GPUS OUT_VARIABLE)
       "}\n")
 
     if(CMAKE_CUDA_COMPILER_LOADED) # CUDA as a language
-      try_run(run_result compile_result ${PROJECT_BINARY_DIR} ${file}
+      try_run(run_result compile_result SOURCES ${file}
               RUN_OUTPUT_VARIABLE compute_capabilities)
     else()
-      try_run(run_result compile_result ${PROJECT_BINARY_DIR} ${file}
+      try_run(run_result compile_result SOURCES ${file}
               CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${CUDA_INCLUDE_DIRS}"
               LINK_LIBRARIES ${CUDA_LIBRARIES}
               RUN_OUTPUT_VARIABLE compute_capabilities)
index 5f83ccc..94c86e9 100644 (file)
@@ -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:`cuFile<cuda_toolkit_cuFile>`
 - :ref:`cuFFT<cuda_toolkit_cuFFT>`
 - :ref:`cuRAND<cuda_toolkit_cuRAND>`
 - :ref:`cuSOLVER<cuda_toolkit_cuSOLVER>`
@@ -119,8 +120,10 @@ of the following libraries that are part of the CUDAToolkit:
 - :ref:`nvGRAPH<cuda_toolkit_nvGRAPH>`
 - :ref:`nvJPEG<cuda_toolkit_nvJPEG>`
 - :ref:`nvidia-ML<cuda_toolkit_nvML>`
+- :ref:`nvPTX Compiler<cuda_toolkit_nvptx>`
 - :ref:`nvRTC<cuda_toolkit_nvRTC>`
 - :ref:`nvToolsExt<cuda_toolkit_nvToolsExt>`
+- :ref:`nvtx3<cuda_toolkit_nvtx3>`
 - :ref:`OpenCL<cuda_toolkit_opencl>`
 - :ref:`cuLIBOS<cuda_toolkit_cuLIBOS>`
 
@@ -163,6 +166,22 @@ Targets Created:
 - ``CUDA::cublasLt`` starting in CUDA 10.1
 - ``CUDA::cublasLt_static`` starting in CUDA 10.1
 
+.. _`cuda_toolkit_cuFile`:
+
+cuFile
+""""""
+
+.. versionadded:: 3.25
+
+The NVIDIA GPUDirect Storage `cuFile <https://docs.nvidia.com/cuda/cufile-api/index.html>`_ library.
+
+Targets Created:
+
+- ``CUDA::cuFile`` starting in CUDA 11.4
+- ``CUDA::cuFile_static`` starting in CUDA 11.4
+- ``CUDA::cuFile_rdma`` starting in CUDA 11.4
+- ``CUDA::cuFile_rdma_static`` starting in CUDA 11.4
+
 .. _`cuda_toolkit_cuFFT`:
 
 cuFFT
@@ -333,6 +352,22 @@ Targets Created:
 - ``CUDA::nvjpeg``
 - ``CUDA::nvjpeg_static``
 
+.. _`cuda_toolkit_nvPTX`:
+
+nvPTX Compiler
+""""""""""""""
+
+.. versionadded:: 3.25
+
+The `nvPTX <https://docs.nvidia.com/cuda/ptx-compiler-api/index.html>`_ (PTX Compilation) library.
+The PTX Compiler APIs are a set of APIs which can be used to compile a PTX program into GPU assembly code.
+Introduced in CUDA 11.1
+This is a static library only.
+
+Targets Created:
+
+- ``CUDA::nvptxcompiler_static`` starting in CUDA 11.1
+
 .. _`cuda_toolkit_nvRTC`:
 
 nvRTC
@@ -362,6 +397,8 @@ Targets Created:
 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>`_.
 This is a shared library only.
 
@@ -369,6 +406,20 @@ Targets Created:
 
 - ``CUDA::nvToolsExt``
 
+.. _`cuda_toolkit_nvtx3`:
+
+nvtx3
+"""""
+
+.. versionadded:: 3.25
+
+The header-only `NVIDIA Tools Extension Library <https://nvidia.github.io/NVTX/doxygen/index.html>`_.
+Introduced in CUDA 10.0.
+
+Targets created:
+
+- ``CUDA::nvtx3``
+
 .. _`cuda_toolkit_opencl`:
 
 OpenCL
@@ -942,6 +993,14 @@ if(CUDAToolkit_FOUND)
     _CUDAToolkit_find_and_add_import_lib(cublas_static DEPS culibos)
   endif()
 
+  if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.4)
+    _CUDAToolkit_find_and_add_import_lib(cuFile DEPS culibos)
+    _CUDAToolkit_find_and_add_import_lib(cuFile_static DEPS culibos)
+
+    _CUDAToolkit_find_and_add_import_lib(cuFile_rdma DEPS cuFile culibos)
+    _CUDAToolkit_find_and_add_import_lib(cuFile_rdma_static DEPS cuFile_static culibos)
+  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)
@@ -998,6 +1057,12 @@ if(CUDAToolkit_FOUND)
   endif()
 
   _CUDAToolkit_find_and_add_import_lib(nvrtc DEPS cuda_driver)
+  if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 11.1.0)
+    if(NOT TARGET CUDA::nvptxcompiler_static)
+      _CUDAToolkit_find_and_add_import_lib(nvptxcompiler_static DEPS cuda_driver)
+      target_link_libraries(CUDA::nvptxcompiler_static INTERFACE Threads::Threads)
+    endif()
+  endif()
 
   _CUDAToolkit_find_and_add_import_lib(nvml ALT nvidia-ml nvml)
 
@@ -1014,6 +1079,21 @@ if(CUDAToolkit_FOUND)
   endif()
   _CUDAToolkit_find_and_add_import_lib(nvToolsExt ALT nvToolsExt64)
 
+  if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.0)
+    # nvToolsExt is deprecated since nvtx3 introduction.
+    # Warn only if the project requires a sufficiently new CMake to make migration possible.
+    if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_GREATER_EQUAL 3.25)
+      set_property(TARGET CUDA::nvToolsExt PROPERTY DEPRECATION "nvToolsExt has been superseded by nvtx3 since CUDA 10.0 and CMake 3.25. Use CUDA::nvtx3 and include <nvtx3/nvToolsExt.h> instead.")
+    endif()
+
+    # Header-only variant. Uses dlopen().
+    if(NOT TARGET CUDA::nvtx3)
+      add_library(CUDA::nvtx3 INTERFACE IMPORTED)
+      target_include_directories(CUDA::nvtx3 SYSTEM INTERFACE "${CUDAToolkit_INCLUDE_DIRS}")
+      target_link_libraries(CUDA::nvtx3 INTERFACE ${CMAKE_DL_LIBS})
+    endif()
+  endif()
+
   _CUDAToolkit_find_and_add_import_lib(OpenCL)
 endif()
 
index e37d225..acb87dc 100644 (file)
@@ -72,6 +72,8 @@ if(NOT CURL_NO_CURL_CMAKE)
   # can print what we found and return.
   if(CURL_FOUND)
     find_package_handle_standard_args(CURL HANDLE_COMPONENTS CONFIG_MODE)
+    # The upstream curl package sets CURL_VERSION, not CURL_VERSION_STRING.
+    set(CURL_VERSION_STRING "${CURL_VERSION}")
     return()
   endif()
 endif()
@@ -80,7 +82,6 @@ find_package(PkgConfig QUIET)
 if(PKG_CONFIG_FOUND)
   pkg_check_modules(PC_CURL QUIET libcurl)
   if(PC_CURL_FOUND)
-    set(CURL_VERSION_STRING ${PC_CURL_VERSION})
     pkg_get_variable(CURL_SUPPORTED_PROTOCOLS libcurl supported_protocols)
     pkg_get_variable(CURL_SUPPORTED_FEATURES libcurl supported_features)
   endif()
@@ -120,7 +121,7 @@ if(NOT CURL_LIBRARY)
   select_library_configurations(CURL)
 endif()
 
-if(CURL_INCLUDE_DIR AND NOT CURL_VERSION_STRING)
+if(CURL_INCLUDE_DIR)
   foreach(_curl_version_header curlver.h curl.h)
     if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
       file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
index 301e70b..5910ad1 100644 (file)
@@ -31,11 +31,11 @@ if (WIN32)
       "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/include"
     )
 
-    find_library(COIN3D_LIBRARY_DEBUG coin2d
+    find_library(COIN3D_LIBRARY_DEBUG NAMES coin2d coin4d
       "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib"
     )
 
-    find_library(COIN3D_LIBRARY_RELEASE coin2
+    find_library(COIN3D_LIBRARY_RELEASE NAMES coin2 coin4
       "[HKEY_LOCAL_MACHINE\\SOFTWARE\\SIM\\Coin3D\\2;Installation Path]/lib"
     )
 
index 3fc0e93..04f8b59 100644 (file)
@@ -9,7 +9,7 @@ Find CxxTest unit testing framework.
 
 Find the CxxTest suite and declare a helper macro for creating unit
 tests and integrating them with CTest.  For more details on CxxTest
-see http://cxxtest.tigris.org
+see https://cxxtest.com
 
 INPUT Variables
 
index b2e00df..0154a05 100644 (file)
@@ -18,15 +18,16 @@ Compatibility
 ^^^^^^^^^^^^^
 
 This module is able to find a version of DCMTK that does or does not export
-a *DCMTKConfig.cmake* file. It applies a two step process:
+a ``DCMTKConfig.cmake`` file. It applies a two step process:
 
-* Step 1:  Attempt to find DCMTK version providing a *DCMTKConfig.cmake* file.
-* Step 2:  If step 1 failed, rely on *FindDCMTK.cmake* to set `DCMTK_*` variables details below.
+* Step 1:  Attempt to find DCMTK version providing a ``DCMTKConfig.cmake`` file.
+* Step 2:  If step 1 failed, rely on ``FindDCMTK.cmake`` to set ``DCMTK_*``
+  variables details below.
 
 
 `Recent DCMTK
-<http://git.dcmtk.org/web?p=dcmtk.git;a=commit;h=662ae187c493c6b9a73dd5e3875372cebd0c11fe>`_
-provides a *DCMTKConfig.cmake* :manual:`package configuration file
+<https://git.dcmtk.org/?p=dcmtk.git;a=commit;h=662ae187c493c6b9a73dd5e3875372cebd0c11fe>`_
+provides a ``DCMTKConfig.cmake`` :manual:`package configuration file
 <cmake-packages(7)>`. To exclusively use the package configuration file
 (recommended when possible), pass the `NO_MODULE` option to
 :command:`find_package`. For example, `find_package(DCMTK NO_MODULE)`.
index c8e5e31..7f726ff 100644 (file)
@@ -8,7 +8,7 @@ FindDevIL
 
 
 This module locates the developer's image library.
-http://openil.sourceforge.net/
+https://openil.sourceforge.net/
 
 IMPORTED Targets
 ^^^^^^^^^^^^^^^^
index 4a16e31..ef9801e 100644 (file)
@@ -5,14 +5,14 @@
 FindDoxygen
 -----------
 
-Doxygen is a documentation generation tool (see http://www.doxygen.org).
+Doxygen is a documentation generation tool (see https://www.doxygen.nl).
 This module looks for Doxygen and some optional tools it supports:
 
 ``dot``
-  `Graphviz <http://graphviz.org>`_ ``dot`` utility used to render various
+  `Graphviz <https://graphviz.org>`_ ``dot`` utility used to render various
   graphs.
 ``mscgen``
-  `Message Chart Generator <http://www.mcternan.me.uk/mscgen/>`_ utility used
+  `Message Chart Generator <https://www.mcternan.me.uk/mscgen/>`_ utility used
   by Doxygen's ``\msc`` and ``\mscfile`` commands.
 ``dia``
   `Dia <https://wiki.gnome.org/Apps/Dia>`_ the diagram editor used by Doxygen's
@@ -91,7 +91,7 @@ Functions
   base point. Note also that Doxygen's default behavior is to strip the working
   directory from relative paths in the generated documentation (see the
   ``STRIP_FROM_PATH`` `Doxygen config option
-  <http://www.doxygen.org/manual/config.html>`_ for details).
+  <https://www.doxygen.nl/manual/config.html>`_ for details).
 
   If provided, the optional ``comment`` will be passed as the ``COMMENT`` for
   the :command:`add_custom_target` command used to create the custom target
@@ -117,7 +117,7 @@ Functions
   variables before calling ``doxygen_add_docs()``. Any variable with a name of
   the form ``DOXYGEN_<tag>`` will have its value substituted for the
   corresponding ``<tag>`` configuration option in the ``Doxyfile``. See the
-  `Doxygen documentation <http://www.doxygen.org/manual/config.html>`_ for the
+  `Doxygen documentation <https://www.doxygen.nl/manual/config.html>`_ for the
   full list of supported configuration options.
 
   Some of Doxygen's defaults are overridden to provide more appropriate
@@ -432,9 +432,44 @@ endif()
 # or use something like homebrew.
 # ============== End OSX stuff ================
 
+include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+
 #
 # Find Doxygen...
 #
+function(_Doxygen_get_version doxy_version result_var doxy_path)
+        execute_process(
+            COMMAND "${doxy_path}" --version
+            OUTPUT_VARIABLE full_doxygen_version
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+            RESULT_VARIABLE version_result
+        )
+
+        # Ignore any commit hashes, etc.
+        string(REGEX MATCH [[^[0-9]+\.[0-9]+\.[0-9]+]] sem_doxygen_version "${full_doxygen_version}")
+
+        set(${result_var} ${version_result} PARENT_SCOPE)
+        set(${doxy_version} ${sem_doxygen_version} PARENT_SCOPE)
+endfunction()
+
+function(_Doxygen_version_validator version_match doxy_path)
+    if(NOT DEFINED Doxygen_FIND_VERSION)
+        set(${is_valid_version} TRUE PARENT_SCOPE)
+    else()
+        _Doxygen_get_version(candidate_version version_result "${doxy_path}")
+
+        if(version_result)
+            message(DEBUG "Unable to determine candidate doxygen version at ${doxy_path}: ${version_result}")
+        endif()
+
+        find_package_check_version("${candidate_version}" valid_doxy_version
+            HANDLE_VERSION_RANGE
+        )
+
+        set(${version_match} "${valid_doxy_version}" PARENT_SCOPE)
+    endif()
+endfunction()
+
 macro(_Doxygen_find_doxygen)
     find_program(
         DOXYGEN_EXECUTABLE
@@ -445,17 +480,14 @@ macro(_Doxygen_find_doxygen)
             /Applications/Doxygen.app/Contents/MacOS
             /Applications/Utilities/Doxygen.app/Contents/Resources
             /Applications/Utilities/Doxygen.app/Contents/MacOS
-        DOC "Doxygen documentation generation tool (http://www.doxygen.org)"
+        DOC "Doxygen documentation generation tool (https://www.doxygen.nl)"
+        VALIDATOR _Doxygen_version_validator
     )
     mark_as_advanced(DOXYGEN_EXECUTABLE)
 
     if(DOXYGEN_EXECUTABLE)
-        execute_process(
-            COMMAND "${DOXYGEN_EXECUTABLE}" --version
-            OUTPUT_VARIABLE DOXYGEN_VERSION
-            OUTPUT_STRIP_TRAILING_WHITESPACE
-            RESULT_VARIABLE _Doxygen_version_result
-        )
+        _Doxygen_get_version(DOXYGEN_VERSION _Doxygen_version_result "${DOXYGEN_EXECUTABLE}")
+
         if(_Doxygen_version_result)
             message(WARNING "Unable to determine doxygen version: ${_Doxygen_version_result}")
         endif()
@@ -642,11 +674,11 @@ endforeach()
 unset(_comp)
 
 # Verify find results
-include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 find_package_handle_standard_args(
     Doxygen
     REQUIRED_VARS DOXYGEN_EXECUTABLE
     VERSION_VAR DOXYGEN_VERSION
+    HANDLE_VERSION_RANGE
     HANDLE_COMPONENTS
 )
 
@@ -919,7 +951,7 @@ doxygen_add_docs() for target ${targetName}")
     if(NOT DEFINED DOXYGEN_HAVE_DOT)
         # 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 (see:
-        # http://www.graphviz.org/), a graph visualization toolkit from AT&T
+        # https://www.graphviz.org/), 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.
         # Doxygen's default value is: NO.
index 5237e15..e6eac30 100644 (file)
@@ -160,7 +160,7 @@ unset(_gdal_version)
 unset(_gdal_versions)
 
 find_library(GDAL_LIBRARY
-  NAMES ${_gdal_lib} ${_gdal_libnames} gdal gdal_i gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL
+  NAMES ${_gdal_lib} ${_gdal_libnames} gdal gdald gdal_i gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL
   HINTS
      ENV GDAL_DIR
      ENV GDAL_ROOT
index b9ebe08..bfde40b 100644 (file)
@@ -63,11 +63,36 @@ This module defines the following variables:
 #]=======================================================================]
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
 
 find_package(GLEW CONFIG QUIET)
 
 if(GLEW_FOUND)
   find_package_handle_standard_args(GLEW DEFAULT_MSG GLEW_CONFIG)
+  get_target_property(GLEW_INCLUDE_DIRS GLEW::GLEW INTERFACE_INCLUDE_DIRECTORIES)
+  set(GLEW_INCLUDE_DIR ${GLEW_INCLUDE_DIRS})
+  get_target_property(_GLEW_DEFS GLEW::GLEW INTERFACE_COMPILE_DEFINITIONS)
+  if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC")
+    get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_LOCATION_DEBUG)
+    get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_LOCATION_RELEASE)
+  else()
+    get_target_property(GLEW_LIBRARY_DEBUG GLEW::GLEW IMPORTED_IMPLIB_DEBUG)
+    get_target_property(GLEW_LIBRARY_RELEASE GLEW::GLEW IMPORTED_IMPLIB_RELEASE)
+  endif()
+  get_target_property(_GLEW_LINK_INTERFACE GLEW::GLEW IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE) # same for debug and release
+  list(APPEND GLEW_LIBRARIES ${_GLEW_LINK_INTERFACE})
+  list(APPEND GLEW_LIBRARY ${_GLEW_LINK_INTERFACE})
+  select_library_configurations(GLEW)
+  if("${_GLEW_DEFS}" MATCHES "GLEW_STATIC")
+    set(GLEW_STATIC_LIBRARIES ${GLEW_LIBRARIES})
+  else()
+    set(GLEW_SHARED_LIBRARIES ${GLEW_LIBRARIES})
+  endif()
+  unset(_GLEW_DEFS)
+  unset(_GLEW_LINK_INTERFACE)
+  unset(GLEW_LIBRARY)
+  unset(GLEW_LIBRARY_DEBUG)
+  unset(GLEW_LIBRARY_RELEASE)
   return()
 endif()
 
@@ -171,8 +196,6 @@ find_library(GLEW_STATIC_LIBRARY_DEBUG
 set(CMAKE_FIND_LIBRARY_SUFFIXES ${__GLEW_CURRENT_FIND_LIBRARY_SUFFIXES})
 unset(__GLEW_CURRENT_FIND_LIBRARY_SUFFIXES)
 
-include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
-
 select_library_configurations(GLEW_SHARED)
 select_library_configurations(GLEW_STATIC)
 
index 320ddad..09403bc 100644 (file)
@@ -67,83 +67,41 @@ The following variables may also be provided, for backwards compatibility:
 include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 
-function(_add_glut_target_simple)
-  if(TARGET GLUT::GLUT)
-    return()
-  endif()
-  add_library(GLUT::GLUT INTERFACE IMPORTED)
-  if(GLUT_INCLUDE_DIRS)
-    target_include_directories(GLUT::GLUT SYSTEM
-      INTERFACE "${GLUT_INCLUDE_DIRS}")
-  endif()
-  if(GLUT_LIBRARIES)
-    target_link_libraries(GLUT::GLUT INTERFACE ${GLUT_LIBRARIES})
-  endif()
-  if(GLUT_LIBRARY_DIRS)
-    target_link_directories(GLUT::GLUT INTERFACE ${GLUT_LIBRARY_DIRS})
-  endif()
-  if(GLUT_LDFLAGS)
-    target_link_options(GLUT::GLUT INTERFACE ${GLUT_LDFLAGS})
-  endif()
-  if(GLUT_CFLAGS)
-    separate_arguments(GLUT_CFLAGS_SPLIT UNIX_COMMAND "${GLUT_CFLAGS}")
-    target_compile_options(GLUT::GLUT INTERFACE ${GLUT_CFLAGS_SPLIT})
-  endif()
-
-  set_property(TARGET GLUT::GLUT APPEND PROPERTY
-    IMPORTED_LOCATION "${GLUT_glut_LIBRARY}")
-endfunction()
-
 find_package(PkgConfig QUIET)
 if(PKG_CONFIG_FOUND)
-  # Tell pkg-config not to strip any -I flags to make sure GLUT_INCLUDE_DIRS
-  # will be defined.
-  if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS})
-    set(_pkgconfig_allow_system_cflags_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}")
-  else()
-    unset(_pkgconfig_allow_system_cflags_old)
-  endif()
-  set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} 1)
-  pkg_check_modules(GLUT QUIET glut)
-  if(DEFINED _pkgconfig_allow_system_cflags_old)
-    set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} "${_pkgconfig_allow_system_cflags_old}")
-    unset(_pkgconfig_allow_system_cflags_old)
-  else()
-    unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS})
-  endif()
-  if(NOT GLUT_FOUND)
-    pkg_check_modules(GLUT QUIET freeglut)
-  endif()
-  if(GLUT_FOUND)
-    # GLUT_INCLUDE_DIRS is now the official result variable, but
-    # older versions of CMake only provided GLUT_INCLUDE_DIR.
-    set(GLUT_INCLUDE_DIR "${GLUT_INCLUDE_DIRS}")
-    _add_glut_target_simple()
-    FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLUT REQUIRED_VARS GLUT_FOUND)
-    return()
+  pkg_check_modules(PC_GLUT QUIET glut)
+  if(NOT PC_GLUT_FOUND)
+    pkg_check_modules(PC_GLUT QUIET freeglut)
   endif()
 endif()
 
 if(WIN32)
   find_path( GLUT_INCLUDE_DIR NAMES GL/glut.h
-    PATHS  ${GLUT_ROOT_PATH}/include )
+    PATHS  ${GLUT_ROOT_PATH}/include
+    HINTS ${PC_GLUT_INCLUDE_DIRS})
   mark_as_advanced(GLUT_INCLUDE_DIR)
   find_library( GLUT_glut_LIBRARY_RELEASE NAMES freeglut glut glut32
     PATHS
     ${OPENGL_LIBRARY_DIR}
     ${GLUT_ROOT_PATH}/Release
+    HINTS
+    ${PC_GLUT_LIBRARY_DIRS}
     )
+# N.B. As the pkg-config cannot distinguish between release and debug libraries,
+# assume that their hint was the both Debug and Release library.
   find_library( GLUT_glut_LIBRARY_DEBUG NAMES freeglutd
     PATHS
     ${OPENGL_LIBRARY_DIR}
     ${GLUT_ROOT_PATH}/Debug
+    HINTS
+    ${PC_GLUT_LIBRARY_DIRS}
     )
   mark_as_advanced(GLUT_glut_LIBRARY_RELEASE GLUT_glut_LIBRARY_DEBUG)
   select_library_configurations(GLUT_glut)
 elseif(APPLE)
-  find_path(GLUT_INCLUDE_DIR glut.h ${OPENGL_LIBRARY_DIR})
+  find_path(GLUT_INCLUDE_DIR glut.h PATHS ${OPENGL_LIBRARY_DIR} HINTS ${PC_GLUT_INCLUDE_DIRS})
   mark_as_advanced(GLUT_INCLUDE_DIR)
-  find_library(GLUT_glut_LIBRARY GLUT DOC "GLUT library for OSX")
+  find_library(GLUT_glut_LIBRARY GLUT HINTS ${PC_GLUT_LIBRARY_DIRS} DOC "GLUT library for OSX")
   find_library(GLUT_cocoa_LIBRARY Cocoa DOC "Cocoa framework for OSX")
   mark_as_advanced(GLUT_glut_LIBRARY GLUT_cocoa_LIBRARY)
 
@@ -192,18 +150,24 @@ else()
   endif ()
 
   find_path( GLUT_INCLUDE_DIR GL/glut.h
+    PATHS
     /usr/include/GL
     /usr/openwin/share/include
     /usr/openwin/include
     /opt/graphics/OpenGL/include
     /opt/graphics/OpenGL/contrib/libglut
     ${_GLUT_INC_DIR}
+    HINTS
+    ${PC_GLUT_INCLUDE_DIRS}
     )
   mark_as_advanced(GLUT_INCLUDE_DIR)
 
   find_library( GLUT_glut_LIBRARY glut
+    PATHS
     /usr/openwin/lib
     ${_GLUT_glut_LIB_DIR}
+    HINTS
+    ${PC_GLUT_LIBRARY_DIRS}
     )
   mark_as_advanced(GLUT_glut_LIBRARY)
 
index 00bfc29..4634876 100644 (file)
@@ -313,6 +313,7 @@ function(_GTK2_FIND_INCLUDE_DIR _var _hdr)
             /usr/openwin/lib
             /sw/lib
             /opt/local/lib
+            /opt/homebrew/lib
             /usr/pkg/lib
             /usr/pkg/include/glib
             $ENV{GTKMM_BASEPATH}/include
index 40ed9a9..d662a7d 100644 (file)
@@ -238,7 +238,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} ${scratch_directory} ${test_file}
+    try_compile(${success} SOURCES ${test_file}
       COPY_FILE ${scratch_directory}/compiler_has_h5_c
     )
   endif()
@@ -286,7 +286,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} ${scratch_directory} ${test_file}
+    try_compile(${success} SOURCES ${test_file}
       COPY_FILE ${scratch_directory}/compiler_has_h5_cxx
     )
   endif()
@@ -323,7 +323,7 @@ function(_HDF5_test_regular_compiler_Fortran success is_parallel)
       "  call h5open_f(error)\n"
       "  call h5close_f(error)\n"
       "end\n")
-    try_compile(${success} ${scratch_directory} ${test_file})
+    try_compile(${success} SOURCES ${test_file})
     if(${success})
       execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -showconfig
         OUTPUT_VARIABLE config_output
index 91dcba5..b4f4d71 100644 (file)
@@ -254,13 +254,6 @@ function(_ICU_FIND)
       set("${component_found}" ON)
       set("${component_found_compat}" ON)
       list(APPEND ICU_LIBRARY "${${component_cache}}")
-    endif()
-    mark_as_advanced("${component_found}")
-    mark_as_advanced("${component_found_compat}")
-    set("${component_cache}" "${${component_cache}}" PARENT_SCOPE)
-    set("${component_found}" "${${component_found}}" PARENT_SCOPE)
-    set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE)
-    if(component_found OR component_found_compat)
       if (ICU_FIND_REQUIRED_${component})
         list(APPEND ICU_LIBS_FOUND "${component} (required): ${${component_cache}}")
       else()
@@ -274,6 +267,11 @@ function(_ICU_FIND)
         list(APPEND ICU_LIBS_NOTFOUND "${component} (optional)")
       endif()
     endif()
+    mark_as_advanced("${component_found}")
+    mark_as_advanced("${component_found_compat}")
+    set("${component_cache}" "${${component_cache}}" PARENT_SCOPE)
+    set("${component_found}" "${${component_found}}" PARENT_SCOPE)
+    set("${component_found_compat}" "${${component_found_compat}}" PARENT_SCOPE)
   endforeach()
   set(_ICU_REQUIRED_LIBS_FOUND "${ICU_REQUIRED_LIBS_FOUND}" PARENT_SCOPE)
   set(ICU_LIBRARY "${ICU_LIBRARY}" PARENT_SCOPE)
index b08b359..1eecb1c 100644 (file)
@@ -12,7 +12,7 @@ This module finds an installed Fortran library that implements the
 
 At least one of the ``C``, ``CXX``, or ``Fortran`` languages must be enabled.
 
-.. _`LAPACK linear-algebra interface`: http://www.netlib.org/lapack/
+.. _`LAPACK linear-algebra interface`: https://netlib.org/lapack/
 
 Input Variables
 ^^^^^^^^^^^^^^^
@@ -35,6 +35,13 @@ The following variables may be set to influence this module's behavior:
   if set ``pkg-config`` will be used to search for a LAPACK library first
   and if one is found that is preferred
 
+``BLA_PKGCONFIG_LAPACK``
+  .. versionadded:: 3.25
+
+  If set, the ``pkg-config`` method will look for this module name instead of
+  just ``lapack``.
+
+
 ``BLA_SIZEOF_INTEGER``
   .. versionadded:: 3.22
 
@@ -278,8 +285,11 @@ endif()
 
 # Search with pkg-config if specified
 if(BLA_PREFER_PKGCONFIG)
+  if(NOT BLA_PKGCONFIG_LAPACK)
+    set(BLA_PKGCONFIG_LAPACK "lapack")
+  endif()
   find_package(PkgConfig QUIET)
-  pkg_check_modules(PKGC_LAPACK QUIET lapack)
+  pkg_check_modules(PKGC_LAPACK QUIET ${BLA_PKGCONFIG_LAPACK})
   if(PKGC_LAPACK_FOUND)
     set(LAPACK_FOUND TRUE)
     set(LAPACK_LIBRARIES "${PKGC_LAPACK_LINK_LIBRARIES}")
index a70a418..eaace4f 100644 (file)
@@ -8,7 +8,7 @@ FindLTTngUST
 .. versionadded:: 3.6
 
 Find
-`Linux Trace Toolkit Next Generation (LTTng-UST) <http://lttng.org/>`__ library.
+`Linux Trace Toolkit Next Generation (LTTng-UST) <https://lttng.org/>`__ library.
 
 Imported target
 ^^^^^^^^^^^^^^^
index 08078a2..9d3ac13 100644 (file)
@@ -26,6 +26,9 @@ The module defines the following ``IMPORTED`` targets:
 .. versionadded:: 3.6
   Support for new libarchive 3.2 version string format.
 
+.. versionadded:: 3.17
+  Provides an imported target.
+
 #]=======================================================================]
 
 find_path(LibArchive_INCLUDE_DIR
index b8ca71b..2d5de89 100644 (file)
@@ -29,23 +29,19 @@ endif()
 if(MFC_ATTEMPT_TRY_COMPILE)
   if(NOT DEFINED MFC_HAVE_MFC)
     set(CHECK_INCLUDE_FILE_VAR "afxwin.h")
-    configure_file(${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx)
+    file(READ ${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in _CIF_SOURCE_CONTENT)
+    string(CONFIGURE "${_CIF_SOURCE_CONTENT}" _CIF_SOURCE_CONTENT)
     message(CHECK_START "Looking for MFC")
     # Try both shared and static as the root project may have set the /MT flag
     try_compile(MFC_HAVE_MFC
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx
+      SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT
       CMAKE_FLAGS
       -DCMAKE_MFC_FLAG:STRING=2
       -DCOMPILE_DEFINITIONS:STRING=-D_AFXDLL
       OUTPUT_VARIABLE OUTPUT)
     if(NOT MFC_HAVE_MFC)
-      configure_file(${CMAKE_ROOT}/Modules/CheckIncludeFile.cxx.in
-        ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx)
       try_compile(MFC_HAVE_MFC
-        ${CMAKE_BINARY_DIR}
-        ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx
+        SOURCE_FROM_VAR CheckIncludeFile.cxx _CIF_SOURCE_CONTENT
         CMAKE_FLAGS
         -DCMAKE_MFC_FLAG:STRING=1
         OUTPUT_VARIABLE OUTPUT)
index bca0c10..e15be91 100644 (file)
@@ -391,11 +391,11 @@ function (_MPI_check_compiler LANG QUERY_FLAG OUTPUT_VARIABLE RESULT_VARIABLE)
   # library that has invalid or missing version information there would be warning
   # messages emitted by ld.so in the compiler output. In either case, we'll treat
   # the output as invalid.
-  if("${WRAPPER_OUTPUT}" MATCHES "undefined reference|unrecognized|need to set|no version information available|command not found")
+  if(WRAPPER_OUTPUT MATCHES "undefined reference|unrecognized|need to set|no version information available|command not found")
     set(WRAPPER_RETURN 255)
   endif()
   # Ensure that no error output might be passed upwards.
-  if(NOT WRAPPER_RETURN EQUAL 0)
+  if(NOT WRAPPER_RETURN EQUAL "0")
     unset(WRAPPER_OUTPUT)
   else()
     # Strip leading whitespace
@@ -441,13 +441,13 @@ function (_MPI_interrogate_compiler LANG)
   if(MSVC)
     get_filename_component(_MPI_UNDERLAYING_COMPILER "${_MPI_UNDERLAYING_COMPILER}" NAME)
   endif()
-  if("${LANG}" STREQUAL "C")
+  if(LANG STREQUAL "C")
     _MPI_env_set_ifnot(I_MPI_CC _MPI_UNDERLAYING_COMPILER)
     _MPI_env_set_ifnot(MPICH_CC _MPI_UNDERLAYING_COMPILER)
-  elseif("${LANG}" STREQUAL "CXX")
+  elseif(LANG STREQUAL "CXX")
     _MPI_env_set_ifnot(I_MPI_CXX _MPI_UNDERLAYING_COMPILER)
     _MPI_env_set_ifnot(MPICH_CXX _MPI_UNDERLAYING_COMPILER)
-  elseif("${LANG}" STREQUAL "Fortran")
+  elseif(LANG STREQUAL "Fortran")
     _MPI_env_set_ifnot(I_MPI_FC _MPI_UNDERLAYING_COMPILER)
     _MPI_env_set_ifnot(MPICH_FC _MPI_UNDERLAYING_COMPILER)
     _MPI_env_set_ifnot(I_MPI_F77 _MPI_UNDERLAYING_COMPILER)
@@ -470,10 +470,10 @@ function (_MPI_interrogate_compiler LANG)
   # or a newer version of LAM/MPI, and implies that -showme:link will also work.
   # Open MPI also supports -show, but separates linker and compiler information
   _MPI_check_compiler(${LANG} "-showme:compile" MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
-  if (MPI_COMPILER_RETURN EQUAL 0)
+  if (MPI_COMPILER_RETURN EQUAL "0")
     _MPI_check_compiler(${LANG} "-showme:link" MPI_LINK_CMDLINE MPI_COMPILER_RETURN)
 
-    if (NOT MPI_COMPILER_RETURN EQUAL 0)
+    if (NOT MPI_COMPILER_RETURN EQUAL "0")
       unset(MPI_COMPILE_CMDLINE)
     endif()
   endif()
@@ -482,13 +482,13 @@ function (_MPI_interrogate_compiler LANG)
   # For modern versions, both do the same as -show. However, for old versions, they do differ
   # when called for mpicxx and mpif90 and it's necessary to use them over -show in order to find the
   # removed MPI C++ bindings.
-  if (NOT MPI_COMPILER_RETURN EQUAL 0)
+  if (NOT MPI_COMPILER_RETURN EQUAL "0")
     _MPI_check_compiler(${LANG} "-compile-info" MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
 
-    if (MPI_COMPILER_RETURN EQUAL 0)
+    if (MPI_COMPILER_RETURN EQUAL "0")
       _MPI_check_compiler(${LANG} "-link-info" MPI_LINK_CMDLINE MPI_COMPILER_RETURN)
 
-      if (NOT MPI_COMPILER_RETURN EQUAL 0)
+      if (NOT MPI_COMPILER_RETURN EQUAL "0")
         unset(MPI_COMPILE_CMDLINE)
       endif()
     endif()
@@ -496,18 +496,18 @@ function (_MPI_interrogate_compiler LANG)
 
   # Cray compiler wrappers come usually without a separate mpicc/c++/ftn, but offer
   # --cray-print-opts=...
-  if (NOT MPI_COMPILER_RETURN EQUAL 0)
+  if (NOT MPI_COMPILER_RETURN EQUAL "0")
     _MPI_check_compiler(${LANG} "--cray-print-opts=cflags"
                         MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
 
-    if (MPI_COMPILER_RETURN EQUAL 0)
+    if (MPI_COMPILER_RETURN EQUAL "0")
       # Pass --no-as-needed so the mpi library is always linked. Otherwise, the
       # Cray compiler wrapper puts an --as-needed flag around the mpi library,
       # and it is not linked unless code directly refers to it.
       _MPI_check_compiler(${LANG} "--no-as-needed;--cray-print-opts=libs"
                           MPI_LINK_CMDLINE MPI_COMPILER_RETURN)
 
-      if (NOT MPI_COMPILER_RETURN EQUAL 0)
+      if (NOT MPI_COMPILER_RETURN EQUAL "0")
         unset(MPI_COMPILE_CMDLINE)
         unset(MPI_LINK_CMDLINE)
       endif()
@@ -516,24 +516,24 @@ function (_MPI_interrogate_compiler LANG)
 
   # MPICH, MVAPICH2 and Intel MPI just use "-show". Open MPI also offers this, but the
   # -showme commands are more specialized.
-  if (NOT MPI_COMPILER_RETURN EQUAL 0)
+  if (NOT MPI_COMPILER_RETURN EQUAL "0")
     _MPI_check_compiler(${LANG} "-show" MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
   endif()
 
   # Older versions of LAM/MPI have "-showme". Open MPI also supports this.
   # Unknown to MPICH, MVAPICH and Intel MPI.
-  if (NOT MPI_COMPILER_RETURN EQUAL 0)
+  if (NOT MPI_COMPILER_RETURN EQUAL "0")
     _MPI_check_compiler(${LANG} "-showme" MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
   endif()
 
-  if (MPI_COMPILER_RETURN EQUAL 0 AND DEFINED MPI_COMPILE_CMDLINE)
+  if (MPI_COMPILER_RETURN EQUAL "0" AND DEFINED MPI_COMPILE_CMDLINE)
     # Intel MPI can be run with -compchk or I_MPI_CHECK_COMPILER set to 1.
     # In this case, -show will be prepended with a line to the compiler checker. This is a script that performs
     # compatibility checks and returns a non-zero exit code together with an error if something fails.
     # It has to be called as "compchk.sh <arch> <compiler>". Here, <arch> is one out of 32 (i686), 64 (ia64) or 32e (x86_64).
     # The compiler is identified by filename, and can be either the MPI compiler or the underlying compiler.
     # NOTE: It is vital to run this script while the environment variables are set up, otherwise it can check the wrong compiler.
-    if("${MPI_COMPILE_CMDLINE}" MATCHES "^([^\" ]+/compchk.sh|\"[^\"]+/compchk.sh\") +([^ ]+)")
+    if(MPI_COMPILE_CMDLINE MATCHES "^([^\" ]+/compchk.sh|\"[^\"]+/compchk.sh\") +([^ ]+)")
       # Now CMAKE_MATCH_1 contains the path to the compchk.sh file and CMAKE_MATCH_2 the architecture flag.
       unset(COMPILER_CHECKER_OUTPUT)
       execute_process(
@@ -542,7 +542,7 @@ function (_MPI_interrogate_compiler LANG)
       ERROR_VARIABLE   COMPILER_CHECKER_OUTPUT ERROR_STRIP_TRAILING_WHITESPACE
       RESULT_VARIABLE  MPI_COMPILER_RETURN)
       # If it returned a non-zero value, the check below will fail and cause the interrogation to be aborted.
-      if(NOT MPI_COMPILER_RETURN EQUAL 0)
+      if(NOT MPI_COMPILER_RETURN EQUAL "0")
         if(NOT MPI_FIND_QUIETLY)
           message(STATUS "Intel MPI compiler check failed: ${COMPILER_CHECKER_OUTPUT}")
         endif()
@@ -554,13 +554,13 @@ function (_MPI_interrogate_compiler LANG)
   endif()
 
   # Revert changes to the environment made previously
-  if("${LANG}" STREQUAL "C")
+  if(LANG STREQUAL "C")
     _MPI_env_unset_ifnot(I_MPI_CC)
     _MPI_env_unset_ifnot(MPICH_CC)
-  elseif("${LANG}" STREQUAL "CXX")
+  elseif(LANG STREQUAL "CXX")
     _MPI_env_unset_ifnot(I_MPI_CXX)
     _MPI_env_unset_ifnot(MPICH_CXX)
-  elseif("${LANG}" STREQUAL "Fortran")
+  elseif(LANG STREQUAL "Fortran")
     _MPI_env_unset_ifnot(I_MPI_FC)
     _MPI_env_unset_ifnot(MPICH_FC)
     _MPI_env_unset_ifnot(I_MPI_F77)
@@ -572,7 +572,7 @@ function (_MPI_interrogate_compiler LANG)
   _MPI_env_unset_ifnot(I_MPI_DEBUG_INFO_STRIP)
   _MPI_env_unset_ifnot(I_MPI_FORT_BIND)
 
-  if (NOT (MPI_COMPILER_RETURN EQUAL 0) OR NOT (DEFINED MPI_COMPILE_CMDLINE))
+  if (NOT (MPI_COMPILER_RETURN EQUAL "0") OR NOT (DEFINED MPI_COMPILE_CMDLINE))
     # Cannot interrogate this compiler, so exit.
     set(MPI_${LANG}_WRAPPER_FOUND FALSE PARENT_SCOPE)
     return()
@@ -613,13 +613,13 @@ function (_MPI_interrogate_compiler LANG)
   if(UNIX)
     # At this point, we obtained some output from a compiler wrapper that works.
     # We'll now try to parse it into variables with meaning to us.
-    if("${LANG}" STREQUAL "Fortran")
+    if(LANG STREQUAL "Fortran")
       # If MPICH (and derivates) didn't recognize the Fortran compiler include flag during configuration,
       # they'll return a set of three commands, consisting out of a symlink command for mpif.h,
       # the actual compiler command and deletion of the created symlink.
       # Especially with M(VA)PICH-1, this appears to happen erroneously, and therefore we should translate
       # this output into an additional include directory and then drop it from the output.
-      if("${MPI_COMPILE_CMDLINE}" MATCHES "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h")
+      if(MPI_COMPILE_CMDLINE MATCHES "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h")
         get_filename_component(MPI_INCLUDE_DIRS_WORK "${CMAKE_MATCH_1}" DIRECTORY)
         string(REGEX REPLACE "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h\n" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
         string(REGEX REPLACE "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h\n" "" MPI_LINK_CMDLINE "${MPI_LINK_CMDLINE}")
@@ -651,7 +651,7 @@ function (_MPI_interrogate_compiler LANG)
       # produce inconsistent results with the regularly flags.
       # Similarly, aliasing flags do not belong into our flag array.
       # Also strip out `-framework` flags.
-      if(NOT "${_MPI_COMPILE_OPTION}" MATCHES "^-f((no-|)(stack-protector|strict-aliasing)|PI[CE]|pi[ce]|ramework)")
+      if(NOT _MPI_COMPILE_OPTION MATCHES "^-f((no-|)(stack-protector|strict-aliasing)|PI[CE]|pi[ce]|ramework)")
         list(APPEND MPI_COMPILE_OPTIONS_WORK "${_MPI_COMPILE_OPTION}")
       endif()
     endforeach()
@@ -673,7 +673,7 @@ function (_MPI_interrogate_compiler LANG)
   foreach(_MPI_COMPILE_DEFINITION IN LISTS MPI_ALL_COMPILE_DEFINITIONS)
     string(REGEX REPLACE "^ ?${_MPI_PREPROCESSOR_FLAG_REGEX}-D *" "" _MPI_COMPILE_DEFINITION "${_MPI_COMPILE_DEFINITION}")
     string(REPLACE "\"" "" _MPI_COMPILE_DEFINITION "${_MPI_COMPILE_DEFINITION}")
-    if(NOT "${_MPI_COMPILE_DEFINITION}" MATCHES "^_FORTIFY_SOURCE.*")
+    if(NOT _MPI_COMPILE_DEFINITION MATCHES "^_FORTIFY_SOURCE.*")
       list(APPEND MPI_COMPILE_DEFINITIONS_WORK "${_MPI_COMPILE_DEFINITION}")
     endif()
   endforeach()
@@ -788,7 +788,7 @@ function (_MPI_interrogate_compiler LANG)
   # decide how to link it based on file type, not based on a prefix like 'lib'.
   set(_MPI_LIB_SUFFIX_REGEX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
   if(DEFINED CMAKE_IMPORT_LIBRARY_SUFFIX)
-    if(NOT ("${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL "${CMAKE_STATIC_LIBRARY_SUFFIX}"))
+    if(NOT (CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX))
       string(APPEND _MPI_LIB_SUFFIX_REGEX "|${CMAKE_IMPORT_LIBRARY_SUFFIX}")
     endif()
   else()
@@ -804,7 +804,7 @@ function (_MPI_interrogate_compiler LANG)
     if(_MPI_LIB_NAME_TEST STREQUAL "")
       string(REGEX REPLACE "^ +\"?|\"? +$" "" _MPI_LIB_NAME "${_MPI_LIB_NAME}")
       get_filename_component(_MPI_LIB_PATH "${_MPI_LIB_NAME}" DIRECTORY)
-      if(NOT "${_MPI_LIB_PATH}" STREQUAL "")
+      if(NOT _MPI_LIB_PATH STREQUAL "")
         list(APPEND MPI_LIB_FULLPATHS_WORK "${_MPI_LIB_NAME}")
       else()
         list(APPEND MPI_LIB_NAMES_WORK "${_MPI_LIB_NAME}")
@@ -845,7 +845,7 @@ function (_MPI_interrogate_compiler LANG)
     foreach(_MPI_LINK_DIRECTORY IN LISTS MPI_LINK_DIRECTORIES_LEFTOVER)
       file(TO_NATIVE_PATH "${_MPI_LINK_DIRECTORY}" _MPI_LINK_DIRECTORY_ACTUAL)
       string(FIND "${_MPI_LINK_DIRECTORY_ACTUAL}" " " _MPI_LINK_DIRECTORY_CONTAINS_SPACE)
-      if(NOT _MPI_LINK_DIRECTORY_CONTAINS_SPACE EQUAL -1)
+      if(NOT _MPI_LINK_DIRECTORY_CONTAINS_SPACE EQUAL "-1")
         set(_MPI_LINK_DIRECTORY_ACTUAL "\"${_MPI_LINK_DIRECTORY_ACTUAL}\"")
       endif()
       if(MPI_LINK_FLAGS_WORK)
@@ -871,7 +871,7 @@ function (_MPI_interrogate_compiler LANG)
 
   # MPI might require pthread to work. The above mechanism wouldn't detect it, but we need to
   # link it in that case. -lpthread is covered by the normal library treatment on the other hand.
-  if("${MPI_COMPILE_CMDLINE}" MATCHES "-pthread")
+  if(MPI_COMPILE_CMDLINE MATCHES "-pthread")
     list(APPEND MPI_COMPILE_OPTIONS_WORK "-pthread")
     if(MPI_LINK_FLAGS_WORK)
       string(APPEND MPI_LINK_FLAGS_WORK " -pthread")
@@ -918,10 +918,10 @@ function(_MPI_guess_settings LANG)
     # The environment variables MSMPI_INC and MSMPILIB32/64 are the only ways of locating the MSMPI_SDK,
     # which is installed separately from the runtime. Thus it's possible to have mpiexec but not MPI headers
     # or import libraries and vice versa.
-    if(NOT MPI_GUESS_LIBRARY_NAME OR "${MPI_GUESS_LIBRARY_NAME}" STREQUAL "MSMPI")
+    if(NOT MPI_GUESS_LIBRARY_NAME OR MPI_GUESS_LIBRARY_NAME STREQUAL "MSMPI")
       # We first attempt to locate the msmpi.lib. Should be find it, we'll assume that the MPI present is indeed
       # Microsoft MPI.
-      if("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
+      if(CMAKE_SIZEOF_VOID_P EQUAL "8")
         file(TO_CMAKE_PATH "$ENV{MSMPI_LIB64}" MPI_MSMPI_LIB_PATH)
         file(TO_CMAKE_PATH "$ENV{MSMPI_INC}/x64" MPI_MSMPI_INC_PATH_EXTRA)
       else()
@@ -975,9 +975,9 @@ function(_MPI_guess_settings LANG)
         # Our strategy is now to locate all libraries, but enter msmpifec into the LIB_NAMES array.
         # Should this not be adequate it's a straightforward way for a user to change the LIB_NAMES array and
         # have his library found. Still, this should not be necessary outside of exceptional cases, as reasoned.
-        if ("${LANG}" STREQUAL "Fortran")
+        if (LANG STREQUAL "Fortran")
           set(MPI_MSMPI_CALLINGCONVS c)
-          if("${CMAKE_SIZEOF_VOID_P}" EQUAL 4)
+          if(CMAKE_SIZEOF_VOID_P EQUAL "4")
             list(APPEND MPI_MSMPI_CALLINGCONVS s)
           endif()
           foreach(mpistrlenpos IN ITEMS e m)
@@ -1025,7 +1025,7 @@ function(_MPI_guess_settings LANG)
     # At this point there's not many MPIs that we could still consider.
     # OpenMPI 1.6.x and below supported Windows, but these ship compiler wrappers that still work.
     # The only other relevant MPI implementation without a wrapper is MPICH2, which had Windows support in 1.4.1p1 and older.
-    if(NOT MPI_GUESS_FOUND AND (NOT MPI_GUESS_LIBRARY_NAME OR "${MPI_GUESS_LIBRARY_NAME}" STREQUAL "MPICH2"))
+    if(NOT MPI_GUESS_FOUND AND (NOT MPI_GUESS_LIBRARY_NAME OR MPI_GUESS_LIBRARY_NAME STREQUAL "MPICH2"))
       set(MPI_MPICH_PREFIX_PATHS
         "$ENV{ProgramW6432}/MPICH2/lib"
         "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MPICH\\SMPD;binary]/../lib"
@@ -1042,7 +1042,7 @@ function(_MPI_guess_settings LANG)
         set(MPI_MPICH_LIB_NAMES "mpi")
         # If MPI-2 C++ bindings are requested, we need to locate cxx.lib as well.
         # Otherwise, MPICH_SKIP_MPICXX will be defined and these bindings aren't needed.
-        if("${LANG}" STREQUAL "CXX" AND NOT MPI_CXX_SKIP_MPICXX)
+        if(LANG STREQUAL "CXX" AND NOT MPI_CXX_SKIP_MPICXX)
           find_library(MPI_cxx_LIBRARY
             NAMES cxx
             HINTS ${MPI_MPICH_PREFIX_PATHS})
@@ -1055,7 +1055,7 @@ function(_MPI_guess_settings LANG)
         # fmpich2s.lib would be useful for Compaq Visual Fortran, fmpich2g.lib has to be used with GNU g77 and is also
         # provided in the form of an .a archive for MinGW and Cygwin. From our perspective, fmpich2.lib is the only one
         # we need to try, and if it doesn't work with the given Fortran compiler we'd find out later on during validation
-        elseif("${LANG}" STREQUAL "Fortran")
+        elseif(LANG STREQUAL "Fortran")
           find_library(MPI_fmpich2_LIBRARY
             NAMES fmpich2
             HINTS ${MPI_MPICH_PREFIX_PATHS})
@@ -1103,7 +1103,7 @@ function(_MPI_guess_settings LANG)
 endfunction()
 
 function(_MPI_adjust_compile_definitions LANG)
-  if("${LANG}" STREQUAL "CXX")
+  if(LANG STREQUAL "CXX")
     # To disable the C++ bindings, we need to pass some definitions since the mpi.h header has to deal with both C and C++
     # bindings in MPI-2.
     if(MPI_CXX_SKIP_MPICXX AND NOT MPI_${LANG}_COMPILE_DEFINITIONS MATCHES "SKIP_MPICXX")
@@ -1119,7 +1119,7 @@ endfunction()
 macro(_MPI_assemble_libraries LANG)
   set(MPI_${LANG}_LIBRARIES "")
   # Only for libraries do we need to check whether the compiler's linking stage is separate.
-  if(NOT "${MPI_${LANG}_COMPILER}" STREQUAL "${CMAKE_${LANG}_COMPILER}" OR NOT MPI_${LANG}_WORKS_IMPLICIT)
+  if(NOT MPI_${LANG}_COMPILER STREQUAL CMAKE_${LANG}_COMPILER OR NOT MPI_${LANG}_WORKS_IMPLICIT)
     foreach(mpilib IN LISTS MPI_${LANG}_LIB_NAMES)
       list(APPEND MPI_${LANG}_LIBRARIES ${MPI_${mpilib}_LIBRARY})
     endforeach()
@@ -1131,7 +1131,7 @@ macro(_MPI_assemble_include_dirs LANG)
     ${MPI_${LANG}_COMPILER_INCLUDE_DIRS}
     ${MPI_${LANG}_ADDITIONAL_INCLUDE_DIRS}
     )
-  if("${LANG}" MATCHES "(C|CXX)")
+  if(LANG MATCHES "^(C|CXX)$")
     if(MPI_${LANG}_HEADER_DIR)
       list(APPEND MPI_${LANG}_INCLUDE_DIRS "${MPI_${LANG}_HEADER_DIR}")
     endif()
@@ -1156,7 +1156,7 @@ macro(_MPI_split_include_dirs LANG)
 
   # We try to find the headers/modules among those paths (and system paths)
   # For C/C++, we just need to have a look for mpi.h.
-  if("${LANG}" MATCHES "(C|CXX)")
+  if(LANG MATCHES "^(C|CXX)$")
     find_path(MPI_${LANG}_HEADER_DIR "mpi.h"
       HINTS
         ${MPI_${LANG}_COMPILER_INCLUDE_DIRS}
@@ -1171,7 +1171,7 @@ macro(_MPI_split_include_dirs LANG)
   # any of the Fortran 77/90/2008 APIs for MPI. For example, MSMPI
   # only provides Fortran 77 and - if mpi.f90 is built - potentially
   # a Fortran 90 module.
-  elseif("${LANG}" STREQUAL "Fortran")
+  elseif(LANG STREQUAL "Fortran")
     find_path(MPI_${LANG}_F77_HEADER_DIR "mpif.h"
       HINTS
         ${MPI_${LANG}_COMPILER_INCLUDE_DIRS}
@@ -1238,28 +1238,30 @@ function(_MPI_try_staged_settings LANG MPI_TEST_FILE_NAME MODE RUN_BINARY SUPPRE
   set(SRC_DIR "${CMAKE_ROOT}/Modules/FindMPI")
   set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindMPI/${MPI_TEST_FILE_NAME}_${LANG}.bin")
   unset(MPI_TEST_COMPILE_DEFINITIONS)
-  if("${LANG}" STREQUAL "Fortran")
-    if("${MODE}" STREQUAL "F90_MODULE")
+  if(LANG STREQUAL "Fortran")
+    if(MODE STREQUAL "F90_MODULE")
       set(MPI_Fortran_INCLUDE_LINE "use mpi\n      implicit none")
-    elseif("${MODE}" STREQUAL "F08_MODULE")
+    elseif(MODE STREQUAL "F08_MODULE")
       set(MPI_Fortran_INCLUDE_LINE "use mpi_f08\n      implicit none")
     else() # F77 header
       set(MPI_Fortran_INCLUDE_LINE "implicit none\n      include 'mpif.h'")
     endif()
-    configure_file("${SRC_DIR}/${MPI_TEST_FILE_NAME}.f90.in" "${WORK_DIR}/${MPI_TEST_FILE_NAME}.f90" @ONLY)
-    set(MPI_TEST_SOURCE_FILE "${WORK_DIR}/${MPI_TEST_FILE_NAME}.f90")
-  elseif("${LANG}" STREQUAL "CXX")
-    configure_file("${SRC_DIR}/${MPI_TEST_FILE_NAME}.c" "${WORK_DIR}/${MPI_TEST_FILE_NAME}.cpp" COPYONLY)
-    set(MPI_TEST_SOURCE_FILE "${WORK_DIR}/${MPI_TEST_FILE_NAME}.cpp")
-    if("${MODE}" STREQUAL "TEST_MPICXX")
+    file(READ "${SRC_DIR}/${MPI_TEST_FILE_NAME}.f90.in" MPI_TEST_SOURCE_CONTENT)
+    string(CONFIGURE "${MPI_TEST_SOURCE_CONTENT}" MPI_TEST_SOURCE_CONTENT)
+    set(MPI_TEST_SOURCE_FILE "${MPI_TEST_FILE_NAME}.f90")
+  elseif(LANG STREQUAL "CXX")
+    file(READ "${SRC_DIR}/${MPI_TEST_FILE_NAME}.c" MPI_TEST_SOURCE_CONTENT)
+    set(MPI_TEST_SOURCE_FILE "${MPI_TEST_FILE_NAME}.cpp")
+    if(MODE STREQUAL "TEST_MPICXX")
       set(MPI_TEST_COMPILE_DEFINITIONS TEST_MPI_MPICXX)
     endif()
   else() # C
-    set(MPI_TEST_SOURCE_FILE "${SRC_DIR}/${MPI_TEST_FILE_NAME}.c")
+    file(READ "${SRC_DIR}/${MPI_TEST_FILE_NAME}.c" MPI_TEST_SOURCE_CONTENT)
+    set(MPI_TEST_SOURCE_FILE "${MPI_TEST_FILE_NAME}.c")
   endif()
   if(RUN_BINARY)
     try_run(MPI_RUN_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} MPI_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}
-     "${CMAKE_BINARY_DIR}" SOURCES "${MPI_TEST_SOURCE_FILE}"
+      SOURCE_FROM_VAR "${MPI_TEST_SOURCE_FILE}" MPI_TEST_SOURCE_CONTENT
       COMPILE_DEFINITIONS ${MPI_TEST_COMPILE_DEFINITIONS}
       LINK_LIBRARIES MPI::MPI_${LANG}
       RUN_OUTPUT_VARIABLE MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}
@@ -1267,7 +1269,7 @@ function(_MPI_try_staged_settings LANG MPI_TEST_FILE_NAME MODE RUN_BINARY SUPPRE
     set(MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} "${MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}}" PARENT_SCOPE)
   else()
     try_compile(MPI_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}
-      "${CMAKE_BINARY_DIR}" SOURCES "${MPI_TEST_SOURCE_FILE}"
+      SOURCE_FROM_VAR "${MPI_TEST_SOURCE_FILE}" MPI_TEST_SOURCE_CONTENT
       COMPILE_DEFINITIONS ${MPI_TEST_COMPILE_DEFINITIONS}
       LINK_LIBRARIES MPI::MPI_${LANG}
       COPY_FILE "${BIN_FILE}"
@@ -1290,7 +1292,7 @@ macro(_MPI_check_lang_works LANG SUPPRESS_ERRORS)
   #   - *both*, the mpi module and 'mpif.h'
   # Since older MPI standards (MPI-1) did not define anything but 'mpif.h', we need to check all three individually.
   if( NOT MPI_${LANG}_WORKS )
-    if("${LANG}" STREQUAL "Fortran")
+    if(LANG STREQUAL "Fortran")
       set(MPI_Fortran_INTEGER_LINE "(kind=MPI_INTEGER_KIND)")
       _MPI_try_staged_settings(${LANG} test_mpi F77_HEADER FALSE ${SUPPRESS_ERRORS})
       _MPI_try_staged_settings(${LANG} test_mpi F90_MODULE FALSE ${SUPPRESS_ERRORS})
@@ -1339,11 +1341,11 @@ macro(MPI_search_mpi_prefix_folder PREFIX_FOLDER)
 endmacro()
 
 set(MPI_HINT_DIRS ${MPI_HOME} $ENV{MPI_HOME} $ENV{I_MPI_ROOT})
-if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux")
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
   # SUSE Linux Enterprise Server stores its MPI implementations under /usr/lib64/mpi/gcc/<name>
   # We enumerate the subfolders and append each as a prefix
   MPI_search_mpi_prefix_folder("/usr/lib64/mpi/gcc")
-elseif("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "FreeBSD")
+elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "FreeBSD")
   # FreeBSD ships mpich under the normal system paths - but available openmpi implementations
   # will be found in /usr/local/mpi/<name>
   MPI_search_mpi_prefix_folder("/usr/local/mpi")
@@ -1410,7 +1412,7 @@ if(NOT MPI_IGNORE_LEGACY_VARIABLES)
     if(MPI_${LANG}_COMPILE_FLAGS)
       separate_arguments(MPI_SEPARATE_FLAGS NATIVE_COMMAND "${MPI_${LANG}_COMPILE_FLAGS}")
       foreach(_MPI_FLAG IN LISTS MPI_SEPARATE_FLAGS)
-        if("${_MPI_FLAG}" MATCHES "^ *-D([^ ]+)")
+        if(_MPI_FLAG MATCHES "^ *-D([^ ]+)")
           list(APPEND MPI_${LANG}_EXTRA_COMPILE_DEFINITIONS "${CMAKE_MATCH_1}")
         else()
           list(APPEND MPI_${LANG}_EXTRA_COMPILE_OPTIONS "${_MPI_FLAG}")
@@ -1459,7 +1461,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
       set(_MPI_FIND_${LANG} TRUE)
     elseif( LANG IN_LIST MPI_FIND_COMPONENTS)
       set(_MPI_FIND_${LANG} TRUE)
-    elseif( "${LANG}" STREQUAL "CXX" AND NOT MPI_CXX_SKIP_MPICXX AND MPICXX IN_LIST MPI_FIND_COMPONENTS )
+    elseif( LANG STREQUAL "CXX" AND NOT MPI_CXX_SKIP_MPICXX AND MPICXX IN_LIST MPI_FIND_COMPONENTS )
       set(_MPI_FIND_${LANG} TRUE)
     else()
       set(_MPI_FIND_${LANG} FALSE)
@@ -1471,7 +1473,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
     endif()
   endif()
   if(_MPI_FIND_${LANG})
-    if( "${LANG}" STREQUAL "CXX" AND NOT MPICXX IN_LIST MPI_FIND_COMPONENTS )
+    if( LANG STREQUAL "CXX" AND NOT MPICXX IN_LIST MPI_FIND_COMPONENTS )
       option(MPI_CXX_SKIP_MPICXX "If true, the MPI-2 C++ bindings are disabled using definitions." FALSE)
       mark_as_advanced(MPI_CXX_SKIP_MPICXX)
     endif()
@@ -1493,7 +1495,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
         set(MPI_${LANG}_TRIED_IMPLICIT TRUE)
       endif()
 
-      if(NOT "${MPI_${LANG}_COMPILER}" STREQUAL "${CMAKE_${LANG}_COMPILER}" OR NOT MPI_${LANG}_WORKS)
+      if(NOT MPI_${LANG}_COMPILER STREQUAL CMAKE_${LANG}_COMPILER OR NOT MPI_${LANG}_WORKS)
         set(MPI_${LANG}_WRAPPER_FOUND FALSE)
         set(MPI_PINNED_COMPILER FALSE)
 
@@ -1529,7 +1531,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
             DOC    "MPI compiler for ${LANG}"
           )
 
-          if("${MPI_${LANG}_COMPILER}" STREQUAL "${CMAKE_${LANG}_COMPILER}")
+          if(MPI_${LANG}_COMPILER STREQUAL CMAKE_${LANG}_COMPILER)
             set(MPI_PINNED_COMPILER TRUE)
 
             # If we haven't made the implicit compiler test yet, perform it now.
@@ -1590,7 +1592,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
           endif()
           if(_MPI_PKG AND PKG_CONFIG_FOUND)
             pkg_check_modules("MPI_${LANG}_PKG" "${_MPI_PKG}")
-            if("${MPI_${LANG}_PKG_FOUND}")
+            if(MPI_${LANG}_PKG_FOUND)
               set(MPI_${LANG}_COMPILE_OPTIONS  ${MPI_${LANG}_PKG_CFLAGS}        CACHE STRING "MPI ${LANG} compilation options"       FORCE)
               set(MPI_${LANG}_INCLUDE_PATH     ${MPI_${LANG}_PKG_INCLUDE_DIRS}  CACHE STRING "MPI ${LANG} include directories"       FORCE)
               set(MPI_${LANG}_LINK_FLAGS       ${MPI_${LANG}_PKG_LDFLAGS}       CACHE STRING "MPI ${LANG} linker flags"              FORCE)
@@ -1611,10 +1613,10 @@ foreach(LANG IN ITEMS C CXX Fortran)
             endif()
           endif()
 
-          if(NOT MPI_SKIP_GUESSING AND NOT "${MPI_${LANG}_PKG_FOUND}")
+          if(NOT MPI_SKIP_GUESSING AND NOT MPI_${LANG}_PKG_FOUND)
             # For C++, we may use the settings for C. Should a given compiler wrapper for C++ not exist, but one for C does, we copy over the
             # settings for C. An MPI distribution that is in this situation would be IBM Platform MPI.
-            if("${LANG}" STREQUAL "CXX" AND MPI_C_WRAPPER_FOUND)
+            if(LANG STREQUAL "CXX" AND MPI_C_WRAPPER_FOUND)
               set(MPI_${LANG}_COMPILE_OPTIONS          ${MPI_C_COMPILE_OPTIONS}     CACHE STRING "MPI ${LANG} compilation options"           )
               set(MPI_${LANG}_COMPILE_DEFINITIONS      ${MPI_C_COMPILE_DEFINITIONS} CACHE STRING "MPI ${LANG} compilation definitions"       )
               set(MPI_${LANG}_COMPILER_INCLUDE_DIRS    ${MPI_C_INCLUDE_DIRS}        CACHE STRING "MPI ${LANG} compiler wrapper include directories")
@@ -1628,7 +1630,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
       endif()
     endif()
 
-    if(NOT "${MPI_${LANG}_COMPILER}" STREQUAL "${CMAKE_${LANG}_COMPILER}")
+    if(NOT MPI_${LANG}_COMPILER STREQUAL CMAKE_${LANG}_COMPILER)
       _MPI_split_include_dirs(${LANG})
       _MPI_assemble_include_dirs(${LANG})
     else()
@@ -1665,7 +1667,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
     # If we've found MPI, then we'll perform additional analysis: Determine the MPI version, MPI library version, supported
     # MPI APIs (i.e. MPI-2 C++ bindings). For Fortran we also need to find specific parameters if we're under MPI-3.
     if(MPI_${LANG}_WORKS)
-      if("${LANG}" STREQUAL "CXX" AND NOT DEFINED MPI_MPICXX_FOUND)
+      if(LANG STREQUAL "CXX" AND NOT DEFINED MPI_MPICXX_FOUND)
         if(NOT MPI_CXX_SKIP_MPICXX AND NOT MPI_CXX_VALIDATE_SKIP_MPICXX)
           _MPI_try_staged_settings(${LANG} test_mpi MPICXX FALSE FALSE)
           if(MPI_RESULT_${LANG}_test_mpi_MPICXX)
@@ -1689,7 +1691,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
       # Fortran parameters, since those depend on the method of consumption.
       # For C++, we can always use the C bindings, and should do so, since the C++ bindings do not exist in MPI-3
       # whereas the C bindings do, and the C++ bindings never offered any feature advantage over their C counterparts.
-      if("${LANG}" STREQUAL "Fortran")
+      if(LANG STREQUAL "Fortran")
         if(MPI_${LANG}_HAVE_F08_MODULE)
           set(MPI_${LANG}_HIGHEST_METHOD F08_MODULE)
         elseif(MPI_${LANG}_HAVE_F90_MODULE)
@@ -1705,7 +1707,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
           _MPI_try_staged_settings(${LANG} mpiver ${MPI_${LANG}_HIGHEST_METHOD} FALSE FALSE)
           if(MPI_RESULT_${LANG}_mpiver_${MPI_${LANG}_HIGHEST_METHOD})
             file(STRINGS ${MPI_BIN_FOLDER}/mpiver_${LANG}.bin _MPI_VERSION_STRING LIMIT_COUNT 1 REGEX "INFO:MPI-VER")
-            if("${_MPI_VERSION_STRING}" MATCHES ".*INFO:MPI-VER\\[([0-9]+)\\.([0-9]+)\\].*")
+            if(_MPI_VERSION_STRING MATCHES ".*INFO:MPI-VER\\[([0-9]+)\\.([0-9]+)\\].*")
               set(MPI_${LANG}_VERSION_MAJOR "${CMAKE_MATCH_1}")
               set(MPI_${LANG}_VERSION_MINOR "${CMAKE_MATCH_2}")
               set(MPI_${LANG}_VERSION "${MPI_${LANG}_VERSION_MAJOR}.${MPI_${LANG}_VERSION_MINOR}")
@@ -1724,12 +1726,12 @@ foreach(LANG IN ITEMS C CXX Fortran)
               _MPI_try_staged_settings(${LANG} fortranparam_mpi ${mpimethod} TRUE FALSE)
               if(MPI_RESULT_${LANG}_fortranparam_mpi_${mpimethod} AND
                 NOT "${MPI_RUN_RESULT_${LANG}_fortranparam_mpi_${mpimethod}}" STREQUAL "FAILED_TO_RUN")
-                if("${MPI_RUN_OUTPUT_${LANG}_fortranparam_mpi_${mpimethod}}" MATCHES
+                if(MPI_RUN_OUTPUT_${LANG}_fortranparam_mpi_${mpimethod} MATCHES
                   ".*INFO:SUBARRAYS\\[ *([TF]) *\\]-ASYNCPROT\\[ *([TF]) *\\].*")
-                  if("${CMAKE_MATCH_1}" STREQUAL "T")
+                  if(CMAKE_MATCH_1 STREQUAL "T")
                     set(MPI_${LANG}_${mpimethod}_SUBARRAYS TRUE)
                   endif()
-                  if("${CMAKE_MATCH_2}" STREQUAL "T")
+                  if(CMAKE_MATCH_2 STREQUAL "T")
                     set(MPI_${LANG}_${mpimethod}_ASYNCPROT TRUE)
                   endif()
                 endif()
@@ -1744,7 +1746,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
         # By the MPI-2 standard, MPI_VERSION and MPI_SUBVERSION are valid for both C and C++ bindings.
         if(NOT DEFINED MPI_${LANG}_VERSION)
           file(STRINGS ${MPI_BIN_FOLDER}/test_mpi_${LANG}.bin _MPI_VERSION_STRING LIMIT_COUNT 1 REGEX "INFO:MPI-VER")
-          if("${_MPI_VERSION_STRING}" MATCHES ".*INFO:MPI-VER\\[([0-9]+)\\.([0-9]+)\\].*")
+          if(_MPI_VERSION_STRING MATCHES ".*INFO:MPI-VER\\[([0-9]+)\\.([0-9]+)\\].*")
             set(MPI_${LANG}_VERSION_MAJOR "${CMAKE_MATCH_1}")
             set(MPI_${LANG}_VERSION_MINOR "${CMAKE_MATCH_2}")
             set(MPI_${LANG}_VERSION "${MPI_${LANG}_VERSION_MAJOR}.${MPI_${LANG}_VERSION_MINOR}")
@@ -1764,7 +1766,7 @@ foreach(LANG IN ITEMS C CXX Fortran)
       if(MPI_DETERMINE_LIBRARY_VERSION AND NOT MPI_${LANG}_LIBRARY_VERSION_STRING)
         _MPI_try_staged_settings(${LANG} libver_mpi ${MPI_${LANG}_HIGHEST_METHOD} TRUE FALSE)
         if(MPI_RESULT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD} AND
-          "${MPI_RUN_RESULT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD}}" EQUAL "0")
+          MPI_RUN_RESULT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD} EQUAL "0")
           string(STRIP "${MPI_RUN_OUTPUT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD}}"
             MPI_${LANG}_LIBRARY_VERSION_STRING)
         else()
@@ -1778,12 +1780,12 @@ foreach(LANG IN ITEMS C CXX Fortran)
     set(MPI_${LANG}_FIND_VERSION_EXACT ${MPI_FIND_VERSION_EXACT})
 
     unset(MPI_${LANG}_REQUIRED_VARS)
-    if (NOT "${MPI_${LANG}_COMPILER}" STREQUAL "${CMAKE_${LANG}_COMPILER}")
+    if (NOT MPI_${LANG}_COMPILER STREQUAL CMAKE_${LANG}_COMPILER)
       foreach(mpilibname IN LISTS MPI_${LANG}_LIB_NAMES)
         list(APPEND MPI_${LANG}_REQUIRED_VARS "MPI_${mpilibname}_LIBRARY")
       endforeach()
       list(APPEND MPI_${LANG}_REQUIRED_VARS "MPI_${LANG}_LIB_NAMES")
-      if("${LANG}" STREQUAL "Fortran")
+      if(LANG STREQUAL "Fortran")
         # For Fortran we only need one of the module or header directories to have *some* support for MPI.
         if(NOT MPI_${LANG}_MODULE_DIR)
           list(APPEND MPI_${LANG}_REQUIRED_VARS "MPI_${LANG}_F77_HEADER_DIR")
@@ -1883,7 +1885,7 @@ else()
 endif()
 
 list(LENGTH MPI_LIBRARIES MPI_NUMLIBS)
-if (MPI_NUMLIBS GREATER 1)
+if (MPI_NUMLIBS GREATER "1")
   set(MPI_EXTRA_LIBRARY_WORK "${MPI_LIBRARIES}")
   list(REMOVE_AT MPI_EXTRA_LIBRARY_WORK 0)
   set(MPI_EXTRA_LIBRARY "${MPI_EXTRA_LIBRARY_WORK}")
index 470ebe0..07a9adf 100644 (file)
@@ -85,10 +85,18 @@ Module Input Variables
 Users or projects may set the following variables to configure the module
 behavior:
 
+:variable:`Matlab_ROOT <<PackageName>_ROOT>`
+  .. versionadded:: 3.25
+
+  Default value for :variable:`Matlab_ROOT_DIR`, the root of the Matlab
+  installation.
+
 :variable:`Matlab_ROOT_DIR`
-  the root of the Matlab installation.
+  The root of the Matlab installation.
+
 :variable:`MATLAB_FIND_DEBUG`
   outputs debug information
+
 :variable:`MATLAB_ADDITIONAL_VERSIONS`
   additional versions of Matlab for the automatic retrieval of the installed
   versions.
@@ -844,6 +852,15 @@ function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_ve
     endif()
   endif()
 
+  if(NOT EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
+    # last resort check as some HPC with "module load matlab" not enacted fail to catch in earlier checks
+    # and error CMake configure even if find_package(Matlab) is not REQUIRED
+    if(MATLAB_FIND_DEBUG)
+      message(WARNING "[MATLAB] Unable to determine the version of Matlab. The version log file does not exist.")
+    endif()
+    return()
+  endif()
+
   # if successful, read back the log
   file(READ "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp" _matlab_version_from_cmd)
   file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
@@ -1573,6 +1590,13 @@ endfunction()
 # this variable will get all Matlab installations found in the current system.
 set(_matlab_possible_roots)
 
+if(NOT DEFINED Matlab_ROOT AND DEFINED ENV{Matlab_ROOT})
+  set(Matlab_ROOT $ENV{Matlab_ROOT})
+endif()
+if(DEFINED Matlab_ROOT)
+  set(Matlab_ROOT_DIR ${Matlab_ROOT})
+endif()
+
 if(Matlab_ROOT_DIR)
   # if the user specifies a possible root, we keep this one
 
index cf58f3b..00e42b8 100644 (file)
@@ -27,6 +27,13 @@ The module provides :prop_tgt:`IMPORTED` targets:
 Variables
 ^^^^^^^^^
 
+The module defines the following variables:
+
+``OpenACC_FOUND``
+  .. versionadded:: 3.25
+
+  Variable indicating that OpenACC flags for at least one languages have been found.
+
 This module will set the following variables per language in your
 project, where ``<lang>`` is one of C, CXX, or Fortran:
 
@@ -121,21 +128,18 @@ set(OpenACC_Fortran_CHECK_VERSION_SOURCE
 )
 
 
-function(_OPENACC_WRITE_SOURCE_FILE LANG SRC_FILE_CONTENT_VAR SRC_FILE_NAME SRC_FILE_FULLPATH)
-  set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenACC)
+macro(_OPENACC_PREPARE_SOURCE LANG CONTENT_ID NAME_PREFIX FULLNAME_VAR CONTENT_VAR)
   if("${LANG}" STREQUAL "C")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.c")
-    file(WRITE "${SRC_FILE}" "${OpenACC_C_CXX_${SRC_FILE_CONTENT_VAR}}")
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.c")
+    set(${CONTENT_VAR} "${OpenACC_C_CXX_${CONTENT_ID}}")
   elseif("${LANG}" STREQUAL "CXX")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.cpp")
-    file(WRITE "${SRC_FILE}" "${OpenACC_C_CXX_${SRC_FILE_CONTENT_VAR}}")
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp")
+    set(${CONTENT_VAR} "${OpenACC_C_CXX_${CONTENT_ID}}")
   elseif("${LANG}" STREQUAL "Fortran")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.F90")
-    file(WRITE "${SRC_FILE}_in" "${OpenACC_Fortran_${SRC_FILE_CONTENT_VAR}}")
-    configure_file("${SRC_FILE}_in" "${SRC_FILE}" @ONLY)
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.F90")
+    set(${CONTENT_VAR} "${OpenACC_Fortran_${CONTENT_ID}}")
   endif()
-  set(${SRC_FILE_FULLPATH} "${SRC_FILE}" PARENT_SCOPE)
-endfunction()
+endmacro()
 
 
 function(_OPENACC_GET_FLAGS_CANDIDATE LANG FLAG_VAR)
@@ -177,10 +181,12 @@ endfunction()
 function(_OPENACC_GET_FLAGS LANG FLAG_VAR)
   set(FLAG_CANDIDATES "")
   _OPENACC_GET_FLAGS_CANDIDATE("${LANG}" FLAG_CANDIDATES)
-  _OPENACC_WRITE_SOURCE_FILE("${LANG}" "TEST_SOURCE" OpenACCTryFlag _OPENACC_TEST_SRC)
+  _OPENACC_PREPARE_SOURCE("${LANG}" TEST_SOURCE OpenACCTryFlag
+    _OPENACC_TEST_SRC_NAME _OPENACC_TEST_SRC_CONTENT)
 
   foreach(FLAG IN LISTS FLAG_CANDIDATES)
-    try_compile(OpenACC_FLAG_TEST_RESULT ${CMAKE_BINARY_DIR} ${_OPENACC_TEST_SRC}
+    try_compile(OpenACC_FLAG_TEST_RESULT
+      SOURCE_FROM_VAR "${_OPENACC_TEST_SRC_NAME}" _OPENACC_TEST_SRC_CONTENT
       CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${FLAG}"
       OUTPUT_VARIABLE OpenACC_TRY_COMPILE_OUTPUT
     )
@@ -205,13 +211,15 @@ endfunction()
 
 
 function(_OPENACC_GET_SPEC_DATE LANG SPEC_DATE)
-  _OPENACC_WRITE_SOURCE_FILE("${LANG}" "CHECK_VERSION_SOURCE" OpenACCCheckVersion _OPENACC_TEST_SRC)
+  _OPENACC_PREPARE_SOURCE(${LANG} CHECK_VERSION_SOURCE OpenACCCheckVersion
+    _OPENACC_TEST_SRC_NAME _OPENACC_TEST_SRC_CONTENT)
 
   set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenACC/accver_${LANG}.bin")
-  try_compile(OpenACC_SPECTEST_${LANG} "${CMAKE_BINARY_DIR}" "${_OPENACC_TEST_SRC}"
-              CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenACC_${LANG}_FLAGS}"
-              COPY_FILE ${BIN_FILE}
-              OUTPUT_VARIABLE OUTPUT)
+  try_compile(OpenACC_SPECTEST_${LANG}
+    SOURCE_FROM_VAR "${_OPENACC_TEST_SRC_NAME}" _OPENACC_TEST_SRC_CONTENT
+    CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenACC_${LANG}_FLAGS}"
+    COPY_FILE "${BIN_FILE}"
+    OUTPUT_VARIABLE OUTPUT)
 
   if(${OpenACC_SPECTEST_${LANG}})
     file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX "INFO:OpenACC-date")
@@ -270,6 +278,9 @@ foreach (LANG IN ITEMS C CXX Fortran)
       REQUIRED_VARS OpenACC_${LANG}_FLAGS
       VERSION_VAR OpenACC_${LANG}_VERSION
     )
+    if(OpenACC_${LANG}_FOUND)
+      set(OpenACC_FOUND TRUE)
+    endif()
   endif()
 endforeach()
 
index b5b92c5..53aafdc 100644 (file)
@@ -29,6 +29,16 @@ OpenAL is searched in the following order:
 5. Manually compiled framework: ``/Library/Frameworks``.
 6. Add-on package: ``/opt``.
 
+IMPORTED Targets
+^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.25
+
+This module defines the :prop_tgt:`IMPORTED` target:
+
+``OpenAL::OpenAL``
+  The OpenAL library, if found.
+
 Result Variables
 ^^^^^^^^^^^^^^^^
 
@@ -94,3 +104,19 @@ 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()
+    set_target_properties(OpenAL::OpenAL PROPERTIES
+      INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}")
+  endif()
+endif()
index d6d1c00..a9a1b2a 100644 (file)
@@ -377,7 +377,7 @@ else()
      (NOT OPENGL_USE_EGL AND
       NOT OPENGL_glx_LIBRARY AND
           OPENGL_gl_LIBRARY))
-    list(APPEND _OpenGL_REQUIRED_VARS OPENGL_gl_LIBRARY)
+    list(PREPEND _OpenGL_REQUIRED_VARS OPENGL_gl_LIBRARY)
   endif()
 
   # We always need the 'gl.h' include dir.
index ecfb7f9..9e24925 100644 (file)
@@ -178,27 +178,25 @@ set(OpenMP_Fortran_TEST_SOURCE
   "
 )
 
-function(_OPENMP_WRITE_SOURCE_FILE LANG SRC_FILE_CONTENT_VAR SRC_FILE_NAME SRC_FILE_FULLPATH)
-  set(WORK_DIR ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP)
+macro(_OPENMP_PREPARE_SOURCE LANG CONTENT_ID NAME_PREFIX FULLNAME_VAR CONTENT_VAR)
   if("${LANG}" STREQUAL "C")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.c")
-    file(WRITE "${SRC_FILE}" "${OpenMP_C_CXX_${SRC_FILE_CONTENT_VAR}}")
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.c")
+    set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}")
   elseif("${LANG}" STREQUAL "CXX")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.cpp")
-    file(WRITE "${SRC_FILE}" "${OpenMP_C_CXX_${SRC_FILE_CONTENT_VAR}}")
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.cpp")
+    set(${CONTENT_VAR} "${OpenMP_C_CXX_${CONTENT_ID}}")
   elseif("${LANG}" STREQUAL "Fortran")
-    set(SRC_FILE "${WORK_DIR}/${SRC_FILE_NAME}.f90")
-    file(WRITE "${SRC_FILE}_in" "${OpenMP_Fortran_${SRC_FILE_CONTENT_VAR}}")
-    configure_file("${SRC_FILE}_in" "${SRC_FILE}" @ONLY)
+    set(${FULLNAME_VAR} "${NAME_PREFIX}.F90")
+    string(CONFIGURE "${OpenMP_Fortran_${CONTENT_ID}}" ${CONTENT_VAR} @ONLY)
   endif()
-  set(${SRC_FILE_FULLPATH} "${SRC_FILE}" PARENT_SCOPE)
-endfunction()
+endmacro()
 
 include(${CMAKE_CURRENT_LIST_DIR}/CMakeParseImplicitLinkInfo.cmake)
 
 function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
   _OPENMP_FLAG_CANDIDATES("${LANG}")
-  _OPENMP_WRITE_SOURCE_FILE("${LANG}" "TEST_SOURCE" OpenMPTryFlag _OPENMP_TEST_SRC)
+  _OPENMP_PREPARE_SOURCE("${LANG}" TEST_SOURCE OpenMPTryFlag
+    _OPENMP_TEST_SRC_NAME _OPENMP_TEST_SRC_CONTENT)
 
   unset(OpenMP_VERBOSE_COMPILE_OPTIONS)
   separate_arguments(OpenMP_VERBOSE_OPTIONS NATIVE_COMMAND "${CMAKE_${LANG}_VERBOSE_FLAG}")
@@ -214,7 +212,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
       string(APPEND OPENMP_FLAGS_TEST " ${OpenMP_VERBOSE_COMPILE_OPTIONS}")
     endif()
     string(REGEX REPLACE "[-/=+]" "" OPENMP_PLAIN_FLAG "${OPENMP_FLAG}")
-    try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC}
+    try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
+      SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
       CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}"
       LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG}
       OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
@@ -238,11 +237,29 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
           OpenMP_${LANG}_IMPLICIT_FWK_DIRS
           OpenMP_${LANG}_LOG_VAR
           "${CMAKE_${LANG}_IMPLICIT_OBJECT_REGEX}"
+          LANGUAGE ${LANG}
         )
 
         file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
         "Parsed ${LANG} OpenMP implicit link information from above output:\n${OpenMP_${LANG}_LOG_VAR}\n\n")
 
+        # For LCC we should additionally alanyze -print-search-dirs output
+        # to check for additional implicit_dirs.
+        # Note: This won't work if CMP0129 policy is set to OLD!
+        if("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "LCC")
+          execute_process(
+            COMMAND ${CMAKE_${LANG}_COMPILER} -print-search-dirs
+            OUTPUT_VARIABLE output_lines
+            COMMAND_ERROR_IS_FATAL ANY
+            ERROR_QUIET)
+          if("${output_lines}" MATCHES ".*\nlibraries:[ \t]+(.*:)\n.*")
+            string(REPLACE ":" ";" implicit_dirs_addon "${CMAKE_MATCH_1}")
+            list(PREPEND OpenMP_${LANG}_IMPLICIT_LINK_DIRS ${implicit_dirs_addon})
+            file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+              "  Extended OpenMP library search paths: [${implicit_dirs}]\n")
+          endif()
+        endif()
+
         unset(_OPENMP_LIB_NAMES)
         foreach(_OPENMP_IMPLICIT_LIB IN LISTS OpenMP_${LANG}_IMPLICIT_LIBRARIES)
           get_filename_component(_OPENMP_IMPLICIT_LIB_DIR "${_OPENMP_IMPLICIT_LIB}" DIRECTORY)
@@ -262,7 +279,9 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
                 DOC "Path to the ${_OPENMP_IMPLICIT_LIB_PLAIN} library for OpenMP"
                 HINTS ${OpenMP_${LANG}_IMPLICIT_LINK_DIRS}
                 CMAKE_FIND_ROOT_PATH_BOTH
-                NO_DEFAULT_PATH
+                NO_PACKAGE_ROOT_PATH
+                NO_CMAKE_PATH
+                NO_CMAKE_ENVIRONMENT_PATH
               )
             endif()
             mark_as_advanced(OpenMP_${_OPENMP_IMPLICIT_LIB_PLAIN}_LIBRARY)
@@ -291,7 +310,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
         # Try without specifying include directory first. We only want to
         # explicitly add a search path if the header can't be found on the
         # default header search path already.
-        try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC}
+        try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
+          SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
           CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}"
           LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY}
           OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
@@ -301,7 +321,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
           mark_as_advanced(OpenMP_${LANG}_INCLUDE_DIR)
           set(OpenMP_${LANG}_INCLUDE_DIR "${OpenMP_${LANG}_INCLUDE_DIR}" PARENT_SCOPE)
           if(OpenMP_${LANG}_INCLUDE_DIR)
-            try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC}
+            try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
+              SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
               CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}"
                           "-DINCLUDE_DIRECTORIES:STRING=${OpenMP_${LANG}_INCLUDE_DIR}"
               LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY}
@@ -323,7 +344,8 @@ function(_OPENMP_GET_FLAGS LANG FLAG_MODE OPENMP_FLAG_VAR OPENMP_LIB_NAMES_VAR)
       )
       mark_as_advanced(OpenMP_libomp_LIBRARY)
       if(OpenMP_libomp_LIBRARY)
-        try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG} ${CMAKE_BINARY_DIR} ${_OPENMP_TEST_SRC}
+        try_compile( OpenMP_COMPILE_RESULT_${FLAG_MODE}_${OPENMP_PLAIN_FLAG}
+          SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
           CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OPENMP_FLAGS_TEST}"
           LINK_LIBRARIES ${CMAKE_${LANG}_VERBOSE_FLAG} ${OpenMP_libomp_LIBRARY}
           OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT
@@ -385,7 +407,8 @@ set(OpenMP_Fortran_CHECK_VERSION_SOURCE
 ")
 
 function(_OPENMP_GET_SPEC_DATE LANG SPEC_DATE)
-  _OPENMP_WRITE_SOURCE_FILE("${LANG}" "CHECK_VERSION_SOURCE" OpenMPCheckVersion _OPENMP_TEST_SRC)
+  _OPENMP_PREPARE_SOURCE("${LANG}" CHECK_VERSION_SOURCE OpenMPCheckVersion
+    _OPENMP_TEST_SRC_NAME _OPENMP_TEST_SRC_CONTENT)
 
   unset(_includeDirFlags)
   if(OpenMP_${LANG}_INCLUDE_DIR)
@@ -394,10 +417,11 @@ function(_OPENMP_GET_SPEC_DATE LANG SPEC_DATE)
 
   set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindOpenMP/ompver_${LANG}.bin")
   string(REGEX REPLACE "[-/=+]" "" OPENMP_PLAIN_FLAG "${OPENMP_FLAG}")
-  try_compile(OpenMP_SPECTEST_${LANG}_${OPENMP_PLAIN_FLAG} "${CMAKE_BINARY_DIR}" "${_OPENMP_TEST_SRC}"
-              CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenMP_${LANG}_FLAGS}" ${_includeDirFlags}
-              COPY_FILE ${BIN_FILE}
-              OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT)
+  try_compile(OpenMP_SPECTEST_${LANG}_${OPENMP_PLAIN_FLAG}
+    SOURCE_FROM_VAR "${_OPENMP_TEST_SRC_NAME}" _OPENMP_TEST_SRC_CONTENT
+    CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=${OpenMP_${LANG}_FLAGS}" ${_includeDirFlags}
+    COPY_FILE "${BIN_FILE}"
+    OUTPUT_VARIABLE OpenMP_TRY_COMPILE_OUTPUT)
 
   if(${OpenMP_SPECTEST_${LANG}_${OPENMP_PLAIN_FLAG}})
     file(STRINGS ${BIN_FILE} specstr LIMIT_COUNT 1 REGEX "INFO:OpenMP-date")
@@ -460,10 +484,14 @@ foreach(LANG IN ITEMS C CXX)
     if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND"
       OR NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
       _OPENMP_GET_FLAGS("${LANG}" "${LANG}" OpenMP_${LANG}_FLAGS_WORK OpenMP_${LANG}_LIB_NAMES_WORK)
-      set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}"
-        CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE)
-      set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}"
-        CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE)
+      if(NOT DEFINED OpenMP_${LANG}_FLAGS OR "${OpenMP_${LANG}_FLAGS}" STREQUAL "NOTFOUND")
+        set(OpenMP_${LANG}_FLAGS "${OpenMP_${LANG}_FLAGS_WORK}"
+          CACHE STRING "${LANG} compiler flags for OpenMP parallelization" FORCE)
+      endif()
+      if(NOT DEFINED OpenMP_${LANG}_LIB_NAMES OR "${OpenMP_${LANG}_LIB_NAMES}" STREQUAL "NOTFOUND")
+        set(OpenMP_${LANG}_LIB_NAMES "${OpenMP_${LANG}_LIB_NAMES_WORK}"
+          CACHE STRING "${LANG} compiler libraries for OpenMP parallelization" FORCE)
+      endif()
       mark_as_advanced(OpenMP_${LANG}_FLAGS OpenMP_${LANG}_LIB_NAMES)
     endif()
   endif()
@@ -479,10 +507,14 @@ if(CMAKE_Fortran_COMPILER_LOADED)
       set(OpenMP_Fortran_HAVE_OMPLIB_MODULE TRUE CACHE BOOL INTERNAL "")
     endif()
 
-    set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
-      CACHE STRING "Fortran compiler flags for OpenMP parallelization")
-    set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
-      CACHE STRING "Fortran compiler libraries for OpenMP parallelization")
+    if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND")
+      set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
+        CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE)
+    endif()
+    if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND")
+      set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
+        CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE)
+    endif()
     mark_as_advanced(OpenMP_Fortran_FLAGS OpenMP_Fortran_LIB_NAMES)
   endif()
 
@@ -495,11 +527,14 @@ if(CMAKE_Fortran_COMPILER_LOADED)
       set(OpenMP_Fortran_HAVE_OMPLIB_HEADER TRUE CACHE BOOL INTERNAL "")
     endif()
 
-    set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
-      CACHE STRING "Fortran compiler flags for OpenMP parallelization")
-
-    set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES}"
-      CACHE STRING "Fortran compiler libraries for OpenMP parallelization")
+    if(NOT DEFINED OpenMP_Fortran_FLAGS OR "${OpenMP_Fortran_FLAGS}" STREQUAL "NOTFOUND")
+      set(OpenMP_Fortran_FLAGS "${OpenMP_Fortran_FLAGS_WORK}"
+        CACHE STRING "Fortran compiler flags for OpenMP parallelization" FORCE)
+    endif()
+    if(NOT DEFINED OpenMP_Fortran_LIB_NAMES OR "${OpenMP_Fortran_LIB_NAMES}" STREQUAL "NOTFOUND")
+      set(OpenMP_Fortran_LIB_NAMES "${OpenMP_Fortran_LIB_NAMES_WORK}"
+        CACHE STRING "Fortran compiler libraries for OpenMP parallelization" FORCE)
+    endif()
   endif()
 
   if(OpenMP_Fortran_HAVE_OMPLIB_MODULE)
@@ -571,7 +606,8 @@ foreach(LANG IN LISTS OpenMP_FINDLIST)
         separate_arguments(_OpenMP_${LANG}_OPTIONS NATIVE_COMMAND "${OpenMP_${LANG}_FLAGS}")
         set_property(TARGET OpenMP::OpenMP_${LANG} PROPERTY
           INTERFACE_COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:${LANG}>:${_OpenMP_${LANG}_OPTIONS}>")
-        if(CMAKE_${LANG}_COMPILER_ID STREQUAL "Fujitsu")
+        if(CMAKE_${LANG}_COMPILER_ID STREQUAL "Fujitsu"
+          OR ${CMAKE_${LANG}_COMPILER_ID} STREQUAL "IntelLLVM")
           set_property(TARGET OpenMP::OpenMP_${LANG} PROPERTY
             INTERFACE_LINK_OPTIONS "${OpenMP_${LANG}_FLAGS}")
         endif()
diff --git a/Modules/FindOpenSP.cmake b/Modules/FindOpenSP.cmake
new file mode 100644 (file)
index 0000000..655dd65
--- /dev/null
@@ -0,0 +1,155 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FindOpenSP
+----------
+
+.. versionadded:: 3.25
+
+Try to find the OpenSP library.
+
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This will define the following variables:
+
+``OpenSP_FOUND``
+  True if (the requested version of) ``OpenSP`` is available
+
+``OpenSP_VERSION``
+  The version of ``OpenSP``
+
+``OpenSP_VERSION_MAJOR``
+  The major version of ``OpenSP``
+
+``OpenSP_VERSION_MINOR``
+  The minor version of ``OpenSP``
+
+``OpenSP_VERSION_PATCH``
+  The patch version of ``OpenSP``
+
+``OpenSP_INCLUDE_DIRS``
+  The include dirs of ``OpenSP`` with its headers
+
+``OpenSP_LIBRARIES``
+  The OpenSP library for use with target_link_libraries().
+  This can be passed to target_link_libraries() instead of
+  the :prop_tgt:`IMPORTED` ``OpenSP::OpenSP`` target
+
+``OpenSP_MULTI_BYTE``
+  True if ``SP_MULTI_BYTE`` was found to be defined in OpenSP's ``config.h``
+  header file, which indicates that the ``OpenSP`` library was compiled with
+  support for multi-byte characters. The consuming target needs to define the
+  ``SP_MULTI_BYTE`` to match this value in order to avoid issues with character
+  decoding.
+
+IMPORTED Targets
+^^^^^^^^^^^^^^^^
+
+This module defines the :prop_tgt:`IMPORTED` target ``OpenSP::OpenSP``, if
+OpenSP has been found.
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+The following cache variables may also be set:
+
+``OpenSP_INCLUDE_DIR``
+  the OpenSP include directory
+
+``OpenSP_LIBRARY``
+  the absolute path of the osp library
+
+#]=======================================================================]
+
+if (NOT OpenSP_INCLUDE_DIR AND NOT OpenSP_LIBRARY)
+  find_package(PkgConfig)
+  if (PkgConfig_FOUND)
+    pkg_check_modules(OpenSP IMPORTED_TARGET GLOBAL opensp)
+
+    if (OpenSP_FOUND)
+      add_library(OpenSP::OpenSP INTERFACE IMPORTED)
+      target_link_libraries(OpenSP::OpenSP INTERFACE PkgConfig::OpenSP)
+
+      set(OpenSP_INCLUDE_DIR ${OpenSP_INCLUDE_DIRS})
+      set(OpenSP_LIBRARY ${OpenSP_LIBRARIES})
+    endif ()
+  endif ()
+endif ()
+
+if (NOT OpenSP_INCLUDE_DIR)
+  find_path(OpenSP_INCLUDE_DIR
+    NAMES ParserEventGeneratorKit.h
+    PATH_SUFFIXES OpenSP opensp
+    DOC "The OpenSP include directory"
+    )
+endif ()
+
+if (NOT OpenSP_LIBRARY)
+  find_library(OpenSP_LIBRARY_RELEASE
+    NAMES osp libosp opensp libopensp sp133 libsp
+    )
+
+  find_library(OpenSP_LIBRARY_DEBUG
+    NAMES ospd libospd openspd libopenspd sp133d libspd
+    )
+
+  include(SelectLibraryConfigurations)
+  select_library_configurations(OpenSP)
+endif ()
+
+if (OpenSP_INCLUDE_DIR AND OpenSP_LIBRARY)
+  if (EXISTS "${OpenSP_INCLUDE_DIR}/config.h")
+    if (NOT OpenSP_VERSION)
+      file(STRINGS "${OpenSP_INCLUDE_DIR}/config.h" opensp_version_str REGEX "^#define[\t ]+SP_VERSION[\t ]+\".*\"")
+      string(REGEX REPLACE "^.*SP_VERSION[\t ]+\"([^\"]*)\".*$" "\\1" OpenSP_VERSION "${opensp_version_str}")
+      unset(opensp_version_str)
+    endif ()
+
+    if (OpenSP_VERSION MATCHES [=[([0-9]+)\.([0-9]+)\.([0-9]+)]=])
+      set(OpenSP_VERSION_MAJOR "${CMAKE_MATCH_1}")
+      set(OpenSP_VERSION_MINOR "${CMAKE_MATCH_2}")
+      set(OpenSP_VERSION_PATCH "${CMAKE_MATCH_3}")
+    endif ()
+
+    include(CheckCXXSymbolExists)
+    check_cxx_symbol_exists(SP_MULTI_BYTE "${OpenSP_INCLUDE_DIR}/config.h" OpenSP_MULTI_BYTE)
+  endif ()
+
+  if (NOT TARGET OpenSP::OpenSP)
+    set(OpenSP_INCLUDE_DIRS ${OpenSP_INCLUDE_DIR})
+
+    add_library(OpenSP::OpenSP UNKNOWN IMPORTED)
+    set_target_properties(OpenSP::OpenSP PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${OpenSP_INCLUDE_DIRS}")
+
+    if (OpenSP_LIBRARY_RELEASE)
+      set_target_properties(OpenSP::OpenSP PROPERTIES IMPORTED_LOCATION_RELEASE "${OpenSP_LIBRARY_RELEASE}")
+      set_property(TARGET OpenSP::OpenSP APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+    endif ()
+
+    if (OpenSP_LIBRARY_DEBUG)
+      set_target_properties(OpenSP::OpenSP PROPERTIES IMPORTED_LOCATION_DEBUG "${OpenSP_LIBRARY_DEBUG}")
+      set_property(TARGET OpenSP::OpenSP APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+    endif ()
+
+    if (NOT OpenSP_LIBRARY_RELEASE AND NOT OpenSP_LIBRARY_DEBUG)
+      set_property(TARGET OpenSP::OpenSP APPEND PROPERTY IMPORTED_LOCATION "${OpenSP_LIBRARY}")
+    endif ()
+  endif ()
+endif ()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(OpenSP
+  FOUND_VAR OpenSP_FOUND
+  REQUIRED_VARS OpenSP_LIBRARY OpenSP_INCLUDE_DIR
+  VERSION_VAR OpenSP_VERSION
+  )
+
+mark_as_advanced(OpenSP_INCLUDE_DIR OpenSP_LIBRARY OpenSP_MULTI_BYTE)
+
+include(FeatureSummary)
+set_package_properties(OpenSP PROPERTIES
+  URL "http://openjade.sourceforge.net/doc/index.htm"
+  DESCRIPTION "An SGML System Conforming to International Standard ISO 8879"
+  )
index 9278566..f66ffcf 100644 (file)
@@ -90,13 +90,25 @@ This module will set the following variables in your project:
 Hints
 ^^^^^
 
-Set ``OPENSSL_ROOT_DIR`` to the root directory of an OpenSSL installation.
+The following variables may be set to control search behavior:
 
-.. versionadded:: 3.4
-  Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries.
+``OPENSSL_ROOT_DIR``
+  Set to the root directory of an OpenSSL installation.
+
+``OPENSSL_USE_STATIC_LIBS``
+  .. versionadded:: 3.4
+
+  Set to ``TRUE`` to look for static libraries.
+
+``OPENSSL_MSVC_STATIC_RT``
+  .. versionadded:: 3.5
+
+  Set to ``TRUE`` to choose the MT version of the lib.
 
-.. versionadded:: 3.5
-  Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib.
+``ENV{PKG_CONFIG_PATH}``
+  On UNIX-like systems, ``pkg-config`` is used to locate the system OpenSSL.
+  Set the ``PKG_CONFIG_PATH`` environment varialbe to look in alternate
+  locations.  Useful on multi-lib systems.
 #]=======================================================================]
 
 macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library)
@@ -392,6 +404,7 @@ if(WIN32 AND NOT CYGWIN)
       PATH_SUFFIXES
         "lib/MinGW"
         "lib"
+        "lib64"
     )
 
     find_library(SSL_EAY
@@ -402,6 +415,7 @@ if(WIN32 AND NOT CYGWIN)
       PATH_SUFFIXES
         "lib/MinGW"
         "lib"
+        "lib64"
     )
 
     mark_as_advanced(SSL_EAY LIB_EAY)
index fbcf7cd..56ba1e6 100644 (file)
@@ -535,18 +535,24 @@ function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG)
   set(VERSION_MSG "")
   set(VERSION_OK TRUE)
 
-  # check with DEFINED here as the requested or found version may be "0"
+  # check that the version variable is not empty to avoid emitting a misleading
+  # message (i.e. `Found unsuitable version ""`)
   if (DEFINED ${_NAME}_FIND_VERSION)
     if(DEFINED ${FPHSA_VERSION_VAR})
-      set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}})
-      if (FPHSA_HANDLE_VERSION_RANGE)
-        set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE)
+      if(NOT "${${FPHSA_VERSION_VAR}}" STREQUAL "")
+        set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}})
+        if (FPHSA_HANDLE_VERSION_RANGE)
+          set (FPCV_HANDLE_VERSION_RANGE HANDLE_VERSION_RANGE)
+        else()
+          set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE)
+        endif()
+        find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG
+          ${FPCV_HANDLE_VERSION_RANGE})
       else()
-        set(FPCV_HANDLE_VERSION_RANGE NO_AUTHOR_WARNING_VERSION_RANGE)
+        set(VERSION_OK FALSE)
       endif()
-      find_package_check_version ("${_FOUND_VERSION}" VERSION_OK RESULT_MESSAGE_VARIABLE VERSION_MSG
-        ${FPCV_HANDLE_VERSION_RANGE})
-    else()
+    endif()
+    if("${${FPHSA_VERSION_VAR}}" STREQUAL "")
       # if the package was not found, but a version was given, add that to the output:
       if(${_NAME}_FIND_VERSION_EXACT)
         set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
index 375cb70..04f9d8c 100644 (file)
@@ -237,8 +237,8 @@ Hints
 ``Python_FIND_ABI``
   .. versionadded:: 3.16
 
-  This variable defines which ABIs, as defined in
-  `PEP 3149 <https://www.python.org/dev/peps/pep-3149/>`_, should be searched.
+  This variable defines which ABIs, as defined in :pep:`3149`, should be
+  searched.
 
   .. note::
 
@@ -363,7 +363,7 @@ Hints
     ``Anaconda`` or ``ActivePython``, rely on this implementation.
   * ``IronPython``: This implementation use the ``CSharp`` language for
     ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``).
-    See `IronPython <http://ironpython.net>`_.
+    See `IronPython <https://ironpython.net>`_.
   * ``PyPy``: This implementation use ``RPython`` language and
     ``RPython translation toolchain`` to produce the python interpreter.
     See `PyPy <https://www.pypy.org>`_.
index c4fae6b..7a127e4 100644 (file)
@@ -14,6 +14,8 @@ cmake_policy(PUSH)
 cmake_policy (SET CMP0012 NEW)
 # IN_LIST operator
 cmake_policy (SET CMP0057 NEW)
+# registry view behavior
+cmake_policy (SET CMP0134 NEW)
 
 if (NOT DEFINED _PYTHON_PREFIX)
   message (FATAL_ERROR "FindPython: INTERNAL ERROR")
@@ -175,30 +177,31 @@ function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS)
       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])
+                     [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH}/InstallPath]
+                     [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-${_${_PYTHON_PREFIX}_ARCH2}/InstallPath])
         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])
+                         [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath])
           endif()
         else()
           list (APPEND registries
-                       [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${version}\\InstallPath])
+                       [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])
+                     [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])
       endforeach()
     elseif (implementation STREQUAL "IronPython")
       foreach (version  IN LISTS _PGR_VERSION)
-        list (APPEND registries [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${version}\\InstallPath])
+        list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/IronPython/${version}/InstallPath])
       endforeach()
     endif()
   endforeach()
@@ -711,7 +714,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
 
   if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}")
     # interpreter does not exist anymore
-    set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
     return()
   endif()
@@ -732,7 +735,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
     endif()
     if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
       # incompatible ABI
-      set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+      set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
       return()
     endif()
@@ -748,7 +751,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
                      OUTPUT_STRIP_TRAILING_WHITESPACE)
     if (result)
       # interpreter is not usable
-      set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+      set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
       return()
     endif()
@@ -773,7 +776,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
 
       if (_PVI_EXACT AND NOT version VERSION_EQUAL _PVI_VERSION)
         # interpreter has wrong version
-        set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
         return()
       else()
@@ -782,7 +785,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
         string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVI_VERSION}")
         if (NOT major_version VERSION_EQUAL expected_major_version
             OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION)
-          set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
           return()
         endif()
@@ -794,7 +797,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
       find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE)
       if (NOT in_range)
         # interpreter has invalid version
-        set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
         return()
       endif()
@@ -813,9 +816,9 @@ function (_PYTHON_VALIDATE_INTERPRETER)
       if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # interpreter not usable or has wrong major version
         if (result)
-          set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
         else()
-          set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
         endif()
         set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
         return()
@@ -836,9 +839,9 @@ function (_PYTHON_VALIDATE_INTERPRETER)
     if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P)
       # interpreter not usable or has wrong architecture
       if (result)
-        set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
       else()
-        set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"" PARENT_SCOPE)
+        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()
@@ -846,6 +849,14 @@ function (_PYTHON_VALIDATE_INTERPRETER)
   endif()
 endfunction()
 
+function(_python_validate_find_interpreter status interpreter)
+  set(_${_PYTHON_PREFIX}_EXECUTABLE "${interpreter}" CACHE FILEPATH "" FORCE)
+  _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+  if (NOT _${_PYTHON_PREFIX}_EXECUTABLE)
+    set (${status} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
 
 function (_PYTHON_VALIDATE_COMPILER)
   if (NOT _${_PYTHON_PREFIX}_COMPILER)
@@ -856,7 +867,7 @@ function (_PYTHON_VALIDATE_COMPILER)
 
   if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}")
     # Compiler does not exist anymore
-    set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
     return()
   endif()
@@ -883,7 +894,7 @@ function (_PYTHON_VALIDATE_COMPILER)
   file (REMOVE_RECURSE "${working_dir}")
   if (result)
     # compiler is not usable
-    set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
     return()
   endif()
@@ -909,7 +920,7 @@ function (_PYTHON_VALIDATE_COMPILER)
 
       if (_PVC_EXACT AND NOT version VERSION_EQUAL _PVC_VERSION)
         # interpreter has wrong version
-        set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
         return()
       else()
@@ -918,7 +929,7 @@ function (_PYTHON_VALIDATE_COMPILER)
         string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVC_VERSION}")
         if (NOT major_version VERSION_EQUAL expected_major_version
             OR NOT version VERSION_GREATER_EQUAL _PVC_VERSION)
-          set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
           return()
         endif()
@@ -930,7 +941,7 @@ function (_PYTHON_VALIDATE_COMPILER)
       find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE)
       if (NOT in_range)
         # interpreter has invalid version
-        set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
         return()
       endif()
@@ -939,13 +950,21 @@ function (_PYTHON_VALIDATE_COMPILER)
     string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}")
     if (NOT major_version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
       # Compiler has wrong major version
-      set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"" PARENT_SCOPE)
+      set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND")
       return()
     endif()
   endif()
 endfunction()
 
+function(_python_validate_find_compiler status compiler)
+  set(_${_PYTHON_PREFIX}_COMPILER "${compiler}" CACHE FILEPATH "" FORCE)
+  _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+  if (NOT _${_PYTHON_PREFIX}_COMPILER)
+    set (${status} FALSE PARENT_SCOPE)
+  endif()
+endfunction()
+
 
 function (_PYTHON_VALIDATE_LIBRARY)
   if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
@@ -957,7 +976,7 @@ function (_PYTHON_VALIDATE_LIBRARY)
 
   if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
     # library does not exist anymore
-    set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
     if (WIN32)
       set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND")
@@ -971,7 +990,7 @@ function (_PYTHON_VALIDATE_LIBRARY)
 
   if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     # incompatible ABI
-    set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
   else()
     if (_PVL_VERSION OR _PVL_IN_RANGE)
@@ -980,7 +999,7 @@ function (_PYTHON_VALIDATE_LIBRARY)
         string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}")
         if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version))
           # library has wrong version
-          set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
         endif()
       endif()
@@ -990,14 +1009,14 @@ function (_PYTHON_VALIDATE_LIBRARY)
         find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE)
         if (NOT in_range)
           # library has wrong version
-          set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
         endif()
       endif()
     else()
       if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # library has wrong major version
-        set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
       endif()
     endif()
@@ -1023,7 +1042,7 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR)
 
   if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
     # include file does not exist anymore
-    set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
     return()
   endif()
@@ -1033,14 +1052,14 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR)
 
   if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     # incompatible ABI
-    set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE)
+    set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
   else()
     if (_PVID_VERSION OR _PVID_IN_RANGE)
       if (_PVID_VERSION)
         if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version))
           # include dir has wrong version
-          set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
         endif()
       endif()
@@ -1050,14 +1069,14 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR)
         find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE)
         if (NOT in_range)
           # include dir has wrong version
-          set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE)
+          set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
           set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
         endif()
       endif()
     else()
       if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR)
         # include dir has wrong major version
-        set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"" PARENT_SCOPE)
+        set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
         set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
       endif()
     endif()
@@ -1247,12 +1266,14 @@ if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY)
 endif()
 
 # Python and Anaconda distributions: define which architectures can be used
+unset (_${_PYTHON_PREFIX}_REGISTRY_VIEW)
 if (CMAKE_SIZEOF_VOID_P)
   math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8")
   if ("Development.Module" 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})
   else()
     if (_${_PYTHON_PREFIX}_ARCH EQUAL "32")
       set (_${_PYTHON_PREFIX}_ARCH2 64)
@@ -1506,9 +1527,13 @@ endfunction()
 unset (_${_PYTHON_PREFIX}_REQUIRED_VARS)
 unset (_${_PYTHON_PREFIX}_CACHED_VARS)
 unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE)
+set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE INTERNAL "Interpreter reason failure")
 unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE)
+set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE INTERNAL "Compiler reason failure")
 unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE)
+set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE INTERNAL "Development reason failure")
 unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE)
+set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE INTERNAL "NumPy reason failure")
 
 
 # preamble
@@ -1590,9 +1615,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-
-          _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1612,8 +1636,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1626,9 +1650,10 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         HINTS ${_${_PYTHON_PREFIX}_HINTS}
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1641,8 +1666,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                       HINTS ${_${_PYTHON_PREFIX}_HINTS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      NO_CMAKE_SYSTEM_PATH
+                      VALIDATOR _python_validate_find_interpreter)
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
@@ -1650,8 +1675,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
         find_program (_${_PYTHON_PREFIX}_EXECUTABLE
                       NAMES ${_${_PYTHON_PREFIX}_NAMES}
                       NAMES_PER_DIR
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
-        _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                      VALIDATOR _python_validate_find_interpreter)
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
@@ -1663,8 +1688,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1676,8 +1701,9 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_interpreter (${${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1687,9 +1713,9 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       endwhile()
     else()
       # look-up for various versions and locations
-      set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT)
+      set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT)
       if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE)
-        list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE)
+        list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE)
       endif()
 
       foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
@@ -1698,6 +1724,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
 
         _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION})
         _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION})
+        set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS})
 
         # Virtual environments handling
         if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$")
@@ -1710,8 +1737,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
           if (_${_PYTHON_PREFIX}_EXECUTABLE)
             break()
           endif()
@@ -1731,7 +1758,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
         endif()
 
         # Windows registry
@@ -1742,11 +1770,12 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         HINTS ${_${_PYTHON_PREFIX}_HINTS}
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_interpreter)
         endif()
 
-        _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
@@ -1758,21 +1787,18 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                       HINTS ${_${_PYTHON_PREFIX}_HINTS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      NO_CMAKE_SYSTEM_PATH
+                      VALIDATOR _python_validate_find_interpreter)
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
+
         # try using standard paths.
-        # NAMES_PER_DIR is not defined on purpose to have a chance to find
-        # expected version.
-        # For example, typical systems have 'python' for version 2.* and 'python3'
-        # for version 3.*. So looking for names per dir will find, potentially,
-        # systematically 'python' (i.e. version 2) even if version 3 is searched.
         find_program (_${_PYTHON_PREFIX}_EXECUTABLE
                       NAMES ${_${_PYTHON_PREFIX}_NAMES}
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
-        _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      NAMES_PER_DIR
+                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                      VALIDATOR _python_validate_find_interpreter)
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
@@ -1784,7 +1810,8 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_interpreter)
         endif()
 
         # Windows registry
@@ -1794,10 +1821,11 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_interpreter)
         endif()
 
-        _python_validate_interpreter (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
         if (_${_PYTHON_PREFIX}_EXECUTABLE)
           break()
         endif()
@@ -1806,15 +1834,12 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND
           NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY")
         # No specific version found. Retry with generic names and standard paths.
-        # NAMES_PER_DIR is not defined on purpose to have a chance to find
-        # expected version.
-        # For example, typical systems have 'python' for version 2.* and 'python3'
-        # for version 3.*. So looking for names per dir will find, potentially,
-        # systematically 'python' (i.e. version 2) even if version 3 is searched.
         _python_get_names (_${_PYTHON_PREFIX}_NAMES POSIX INTERPRETER)
+        unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS)
         find_program (_${_PYTHON_PREFIX}_EXECUTABLE
-                      NAMES ${_${_PYTHON_PREFIX}_NAMES})
-        _python_validate_interpreter ()
+                      NAMES ${_${_PYTHON_PREFIX}_NAMES}
+                      NAMES_PER_DIR
+                      VALIDATOR _python_validate_find_interpreter)
       endif()
     endif()
   endif()
@@ -1836,7 +1861,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       # Interpreter is not usable
       set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE)
       unset (${_PYTHON_PREFIX}_VERSION)
-      set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
     endif()
   endif()
 
@@ -1884,7 +1909,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       endif()
 
       if (${_PYTHON_PREFIX}_Interpreter_FOUND)
-        unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE)
+        unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE)
 
         # compute and save interpreter signature
         string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}")
@@ -2058,8 +2083,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2072,9 +2097,10 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2087,8 +2113,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                       HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      NO_CMAKE_SYSTEM_PATH
+                      VALIDATOR _python_validate_find_compiler)
         if (_${_PYTHON_PREFIX}_COMPILER)
           break()
         endif()
@@ -2097,8 +2123,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
         find_program (_${_PYTHON_PREFIX}_COMPILER
                       NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES}
                       NAMES_PER_DIR
-                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
-        _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                      VALIDATOR _python_validate_find_compiler)
         if (_${_PYTHON_PREFIX}_COMPILER)
           break()
         endif()
@@ -2110,12 +2136,13 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
         endif()
+
         # Windows registry
         if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST")
           find_program (_${_PYTHON_PREFIX}_COMPILER
@@ -2123,8 +2150,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2134,9 +2162,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       endwhile()
     else()
       # try using root dir and registry
-      set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS EXACT)
+      set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT)
       if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE)
-        list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE)
+        list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE)
       endif()
 
       foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS)
@@ -2157,6 +2185,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                                 IMPLEMENTATIONS IronPython
                                 VERSION ${_${_PYTHON_PREFIX}_VERSION})
 
+        set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS})
+
         # Apple frameworks handling
         if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST")
           find_program (_${_PYTHON_PREFIX}_COMPILER
@@ -2168,8 +2198,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NO_CMAKE_PATH
                         NO_CMAKE_ENVIRONMENT_PATH
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2182,9 +2212,10 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
                         NO_SYSTEM_ENVIRONMENT_PATH
-                        NO_CMAKE_SYSTEM_PATH)
-          _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_CMAKE_SYSTEM_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2197,8 +2228,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                       HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
                       PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
                       NO_SYSTEM_ENVIRONMENT_PATH
-                      NO_CMAKE_SYSTEM_PATH)
-        _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                      NO_CMAKE_SYSTEM_PATH
+                      VALIDATOR _python_validate_find_compiler)
         if (_${_PYTHON_PREFIX}_COMPILER)
           break()
         endif()
@@ -2210,8 +2241,8 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2223,8 +2254,9 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                         NAMES_PER_DIR
                         PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}
                         PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
-                        NO_DEFAULT_PATH)
-          _python_validate_compiler (VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS})
+                        ${_${_PYTHON_PREFIX}_REGISTRY_VIEW}
+                        NO_DEFAULT_PATH
+                        VALIDATOR _python_validate_find_compiler)
           if (_${_PYTHON_PREFIX}_COMPILER)
             break()
           endif()
@@ -2240,11 +2272,13 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
                                  IMPLEMENTATIONS IronPython
                                  VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}
                                  COMPILER)
+      unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS)
       find_program (_${_PYTHON_PREFIX}_COMPILER
                     NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES}
+                    NAMES_PER_DIR
                     HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS}
-                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES})
-      _python_validate_compiler ()
+                    PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}
+                    VALIDATOR _python_validate_find_compiler)
     endif()
   endif()
 
@@ -2285,7 +2319,7 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
     else()
       # compiler not usable
       set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE)
-      set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"")
     endif()
     file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}")
   endif()
@@ -2304,7 +2338,7 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
   endif()
 
   if (${_PYTHON_PREFIX}_Compiler_FOUND)
-    unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE)
+    unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE)
 
     # compute and save compiler signature
     string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}")
@@ -2786,7 +2820,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
     set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
 
     if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}")
-      set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND")
     endif()
 
@@ -2837,7 +2871,15 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
 
   if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS)
     while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
-      if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS
+      set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE)
+      foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module Embed)
+        string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID)
+        if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
+            AND NOT "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS)
+          set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE)
+        endif()
+      endforeach()
+      if (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED
           AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
         # Don't search for include dir if no library was founded
         break()
@@ -2939,7 +2981,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
     set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
 
     if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
-      set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
+      set_property (CACHE _${_PYTHON_PREFIX}_Development_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
     endif()
 
@@ -3033,7 +3075,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
         AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)
       OR (NOT "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
         AND ${_PYTHON_PREFIX}_Development.Module_FOUND))
-    unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE)
+    unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE)
   endif()
 
   if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
@@ -3144,7 +3186,7 @@ if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Inte
   set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
 
   if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}")
-    set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"")
+    set_property (CACHE _${_PYTHON_PREFIX}_NumPy_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND")
   endif()
 
@@ -3166,7 +3208,7 @@ if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Inte
   endif()
 
   if (${_PYTHON_PREFIX}_NumPy_FOUND)
-    unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE)
+    unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE)
 
     # compute and save numpy signature
     string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}")
@@ -3196,7 +3238,7 @@ unset (_${_PYTHON_PREFIX}_REASON_FAILURE)
 foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy)
   if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE)
     string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n        ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}")
-    unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE)
+    unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE CACHE)
   endif()
 endforeach()
 
index 0888fad..268acfe 100644 (file)
@@ -302,7 +302,7 @@ Hints
     ``Anaconda`` or ``ActivePython``, rely on this implementation.
   * ``IronPython``: This implementation use the ``CSharp`` language for
     ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``).
-    See `IronPython <http://ironpython.net>`_.
+    See `IronPython <https://ironpython.net>`_.
   * ``PyPy``: This implementation use ``RPython`` language and
     ``RPython translation toolchain`` to produce the python interpreter.
     See `PyPy <https://www.pypy.org>`_.
index 145c95e..4f198bb 100644 (file)
@@ -239,8 +239,8 @@ Hints
 ``Python3_FIND_ABI``
   .. versionadded:: 3.16
 
-  This variable defines which ABIs, as defined in
-  `PEP 3149 <https://www.python.org/dev/peps/pep-3149/>`_, should be searched.
+  This variable defines which ABIs, as defined in :pep:`3149`, should be
+  searched.
 
   .. note::
 
@@ -361,7 +361,7 @@ Hints
     ``Anaconda`` or ``ActivePython``, rely on this implementation.
   * ``IronPython``: This implementation use the ``CSharp`` language for
     ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``).
-    See `IronPython <http://ironpython.net>`_.
+    See `IronPython <https://ironpython.net>`_.
   * ``PyPy``: This implementation use ``RPython`` language and
     ``RPython translation toolchain`` to produce the python interpreter.
     See `PyPy <https://www.pypy.org>`_.
index a80758d..b14349f 100644 (file)
@@ -369,56 +369,17 @@ if(Ruby_EXECUTABLE AND NOT Ruby_VERSION_MAJOR)
     set(Ruby_VERSION_MAJOR 1)
     set(Ruby_VERSION_MINOR 9)
   endif()
-  # check whether we found 2.0.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?0")
+  # check whether we found 2.[0-7].x
+  if(${Ruby_EXECUTABLE} MATCHES "ruby2")
     set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 0)
+    string(REGEX_REPLACE ${Ruby_EXECUTABLE} "ruby2\\.?([0-7])" "\\1" Ruby_VERSION_MINOR)
   endif()
-  # check whether we found 2.1.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?1")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 1)
-  endif()
-  # check whether we found 2.2.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?2")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 2)
-  endif()
-  # check whether we found 2.3.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?3")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 3)
-  endif()
-  # check whether we found 2.4.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?4")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 4)
-  endif()
-  # check whether we found 2.5.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?5")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 5)
-  endif()
-  # check whether we found 2.6.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?6")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 6)
-  endif()
-  # check whether we found 2.7.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby2\\.?7")
-    set(Ruby_VERSION_MAJOR 2)
-    set(Ruby_VERSION_MINOR 7)
-  endif()
-  # check whether we found 3.0.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby3\\.?0")
-    set(Ruby_VERSION_MAJOR 3)
-    set(Ruby_VERSION_MINOR 0)
-  endif()
-  # check whether we found 3.1.x
-  if(${Ruby_EXECUTABLE} MATCHES "ruby3\\.?1")
+  # check whether we found 3.[0-1].x
+  if(${Ruby_EXECUTABLE} MATCHES "ruby3")
     set(Ruby_VERSION_MAJOR 3)
-    set(Ruby_VERSION_MINOR 1)
+    string(REGEX_REPLACE ${Ruby_EXECUTABLE} "ruby3\\.?([0-1])" "\\1" Ruby_VERSION_MINOR)
   endif()
+
 endif()
 
 if(Ruby_VERSION_MAJOR)
diff --git a/Modules/FindSDL_gfx.cmake b/Modules/FindSDL_gfx.cmake
new file mode 100644 (file)
index 0000000..2dd96e9
--- /dev/null
@@ -0,0 +1,86 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#[=======================================================================[.rst:
+FindSDL_gfx
+-----------
+
+.. versionadded:: 3.25
+
+Locate SDL_gfx library
+
+This module defines:
+
+::
+
+  SDL::SDL_gfx, the name of the target to use with target_*() commands
+  SDL_GFX_LIBRARIES, the name of the library to link against
+  SDL_GFX_INCLUDE_DIRS, where to find the headers
+  SDL_GFX_FOUND, if false, do not try to link against
+  SDL_GFX_VERSION_STRING - human-readable string containing the
+                             version of SDL_gfx
+
+``$SDLDIR`` is an environment variable that would correspond to the
+``./configure --prefix=$SDLDIR`` used in building SDL.
+#]=======================================================================]
+
+find_path(SDL_GFX_INCLUDE_DIRS
+  NAMES
+    SDL_framerate.h
+    SDL_gfxBlitFunc.h
+    SDL_gfxPrimitives.h
+    SDL_gfxPrimitives_font.h
+    SDL_imageFilter.h
+    SDL_rotozoom.h
+  HINTS
+    ENV SDLGFXDIR
+    ENV SDLDIR
+  PATH_SUFFIXES SDL
+                # path suffixes to search inside ENV{SDLDIR}
+                include/SDL include/SDL12 include/SDL11 include
+)
+
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+  set(VC_LIB_PATH_SUFFIX lib/x64)
+else()
+  set(VC_LIB_PATH_SUFFIX lib/x86)
+endif()
+
+find_library(SDL_GFX_LIBRARIES
+  NAMES SDL_gfx
+  HINTS
+    ENV SDLGFXDIR
+    ENV SDLDIR
+  PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX}
+)
+
+if(SDL_GFX_INCLUDE_DIRS AND EXISTS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h")
+  file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+[0-9]+$")
+  file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+[0-9]+$")
+  file(STRINGS "${SDL_GFX_INCLUDE_DIRS}/SDL_gfxPrimitives.h" SDL_GFX_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+[0-9]+$")
+  string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MAJOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MAJOR "${SDL_GFX_VERSION_MAJOR_LINE}")
+  string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MINOR[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_MINOR "${SDL_GFX_VERSION_MINOR_LINE}")
+  string(REGEX REPLACE "^#define[ \t]+SDL_GFXPRIMITIVES_MICRO[ \t]+([0-9]+)$" "\\1" SDL_GFX_VERSION_PATCH "${SDL_GFX_VERSION_PATCH_LINE}")
+  set(SDL_GFX_VERSION_STRING ${SDL_GFX_VERSION_MAJOR}.${SDL_GFX_VERSION_MINOR}.${SDL_GFX_VERSION_PATCH})
+  unset(SDL_GFX_VERSION_MAJOR_LINE)
+  unset(SDL_GFX_VERSION_MINOR_LINE)
+  unset(SDL_GFX_VERSION_PATCH_LINE)
+  unset(SDL_GFX_VERSION_MAJOR)
+  unset(SDL_GFX_VERSION_MINOR)
+  unset(SDL_GFX_VERSION_PATCH)
+endif()
+
+include(FindPackageHandleStandardArgs)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL_gfx
+                                  REQUIRED_VARS SDL_GFX_LIBRARIES SDL_GFX_INCLUDE_DIRS
+                                  VERSION_VAR SDL_GFX_VERSION_STRING)
+
+if(SDL_gfx_FOUND)
+  if(NOT TARGET SDL::SDL_gfx)
+    add_library(SDL::SDL_gfx INTERFACE IMPORTED)
+    set_target_properties(SDL::SDL_gfx PROPERTIES
+      INTERFACE_INCLUDE_DIRECTORIES "${SDL_GFX_INCLUDE_DIRS}"
+      INTERFACE_LINK_LIBRARIES "${SDL_GFX_LIBRARIES}")
+  endif()
+endif()
index e687b49..324fef5 100644 (file)
@@ -31,10 +31,6 @@ For backward compatibility the following variables are also set:
 
 $SDLDIR is an environment variable that would correspond to the
 ./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing.  This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
 #]=======================================================================]
 
 if(NOT SDL_IMAGE_INCLUDE_DIR AND SDLIMAGE_INCLUDE_DIR)
index 315400a..8ed3cb4 100644 (file)
@@ -31,10 +31,6 @@ For backward compatibility the following variables are also set:
 
 $SDLDIR is an environment variable that would correspond to the
 ./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing.  This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
 #]=======================================================================]
 
 if(NOT SDL_MIXER_INCLUDE_DIR AND SDLMIXER_INCLUDE_DIR)
index 28cb4d6..639e5bd 100644 (file)
@@ -30,10 +30,6 @@ For backward compatibility the following variables are also set:
 
 $SDLDIR is an environment variable that would correspond to the
 ./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing.  This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
 #]=======================================================================]
 
 if(NOT SDL_NET_INCLUDE_DIR AND SDLNET_INCLUDE_DIR)
index 8d2f9f8..d863e3c 100644 (file)
@@ -54,7 +54,19 @@ Typically, you should not use these variables directly, and you should
 use SDL_SOUND_LIBRARIES which contains SDL_SOUND_LIBRARY and the other
 audio libraries (if needed) to successfully compile on your system.
 
-Created by Eric Wing.  This module is a bit more complicated than the
+Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that
+would correspond to the ./configure --prefix=$SDLDIR used in building
+SDL.
+
+On OSX, this will prefer the Framework version (if found) over others.
+People will have to manually change the cache values of SDL_LIBRARY to
+override this selectionor set the CMake environment CMAKE_INCLUDE_PATH
+to modify the search paths.
+#]=======================================================================]
+
+
+#[[
+This module is a bit more complicated than the
 other FindSDL* family modules.  The reason is that SDL_sound can be
 compiled in a large variety of different ways which are independent of
 platform.  SDL_sound may dynamically link against other 3rd party
@@ -70,16 +82,7 @@ This module uses a brute force approach to create a test program that
 uses SDL_sound, and then tries to build it.  If the build fails, it
 parses the error output for known symbol names to figure out which
 libraries are needed.
-
-Responds to the $SDLDIR and $SDLSOUNDDIR environmental variable that
-would correspond to the ./configure --prefix=$SDLDIR used in building
-SDL.
-
-On OSX, this will prefer the Framework version (if found) over others.
-People will have to manually change the cache values of SDL_LIBRARY to
-override this selectionor set the CMake environment CMAKE_INCLUDE_PATH
-to modify the search paths.
-#]=======================================================================]
+#]]
 
 set(SDL_SOUND_EXTRAS "" CACHE STRING "SDL_sound extra flags")
 mark_as_advanced(SDL_SOUND_EXTRAS)
@@ -182,9 +185,9 @@ if(SDL_FOUND AND SDL_SOUND_INCLUDE_DIR AND SDL_SOUND_LIBRARY)
 
   try_compile(
     MY_RESULT
-    ${PROJECT_BINARY_DIR}/CMakeTmp
-    ${PROJECT_BINARY_DIR}/CMakeTmp
-    DetermineSoundLibs
+    PROJECT DetermineSoundLibs
+    SOURCE_DIR ${PROJECT_BINARY_DIR}/CMakeTmp
+    BINARY_DIR ${PROJECT_BINARY_DIR}/CMakeTmp
     OUTPUT_VARIABLE MY_OUTPUT
     )
 
index d5721da..d67c089 100644 (file)
@@ -30,10 +30,6 @@ For backward compatibility the following variables are also set:
 
 $SDLDIR is an environment variable that would correspond to the
 ./configure --prefix=$SDLDIR used in building SDL.
-
-Created by Eric Wing.  This was influenced by the FindSDL.cmake
-module, but with modifications to recognize OS X frameworks and
-additional Unix paths (FreeBSD, etc).
 #]=======================================================================]
 
 if(NOT SDL_TTF_INCLUDE_DIR AND SDLTTF_INCLUDE_DIR)
index 7c610d9..370fff0 100644 (file)
@@ -54,7 +54,7 @@ optional Fortran support:
      endif()
    endif()
 
-.. _`SWIG`: http://swig.org
+.. _SWIG: https://swig.org
 
 #]=======================================================================]
 
index a675fd6..7326ef9 100644 (file)
@@ -129,14 +129,12 @@ macro(_threads_check_flag_pthread)
     elseif(NOT DEFINED THREADS_HAVE_PTHREAD_ARG)
       message(CHECK_START "Check if compiler accepts -pthread")
       if(CMAKE_C_COMPILER_LOADED)
-        set(_threads_src ${CMAKE_CURRENT_LIST_DIR}/CheckForPthreads.c)
+        set(_threads_src CheckForPthreads.c)
       elseif(CMAKE_CXX_COMPILER_LOADED)
-        set(_threads_src ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindThreads/CheckForPthreads.cxx)
-        configure_file(${CMAKE_CURRENT_LIST_DIR}/CheckForPthreads.c "${_threads_src}" COPYONLY)
+        set(_threads_src CheckForPthreads.cxx)
       endif()
       try_compile(THREADS_HAVE_PTHREAD_ARG
-        ${CMAKE_BINARY_DIR}
-        ${_threads_src}
+        SOURCE_FROM_FILE "${_threads_src}" "${CMAKE_CURRENT_LIST_DIR}/CheckForPthreads.c"
         CMAKE_FLAGS -DLINK_LIBRARIES:STRING=-pthread
         OUTPUT_VARIABLE _cmake_check_pthreads_output)
 
index 7ca7d03..3817987 100644 (file)
@@ -36,6 +36,11 @@ There are corresponding imported targets for each of these.
 ``MoltenVK``
   On macOS, an additional component ``MoltenVK`` is available.
 
+``dxc``
+  .. versionadded:: 3.25
+
+  The DirectX Shader Compiler.
+
 The ``glslc`` and ``glslangValidator`` components are provided even
 if not explicitly requested (for backward compatibility).
 
@@ -89,6 +94,21 @@ This module defines :prop_tgt:`IMPORTED` targets if Vulkan has been found:
   Defined if SDK has the Khronos library which implement a subset of Vulkan API
   over Apple Metal graphics framework. (MoltenVK).
 
+``Vulkan::volk``
+  .. versionadded:: 3.25
+
+  Defined if SDK has the Vulkan meta-loader (volk).
+
+``Vulkan::dxc_lib``
+  .. versionadded:: 3.25
+
+  Defined if SDK has the DirectX shader compiler library.
+
+``Vulkan::dxc_exe``
+  .. versionadded:: 3.25
+
+  Defined if SDK has the DirectX shader compiler CLI tool.
+
 Result Variables
 ^^^^^^^^^^^^^^^^
 
@@ -128,6 +148,21 @@ This module defines the following variables:
   .. versionadded:: 3.24
 
   True, if the SDK has the MoltenVK library.
+``Vulkan_volk_FOUND``
+  .. versionadded:: 3.25
+
+  True, if the SDK has the volk library.
+
+``Vulkan_dxc_lib_FOUND``
+  .. versionadded:: 3.25
+
+  True, if the SDK has the DirectX shader compiler library.
+
+``Vulkan_dxc_exe_FOUND``
+  .. versionadded:: 3.25
+
+  True, if the SDK has the DirectX shader compiler CLI tool.
+
 
 The module will also defines these cache variables:
 
@@ -156,6 +191,21 @@ The module will also defines these cache variables:
 
   Path to the MoltenVK library.
 
+``Vulkan_volk_LIBRARY``
+  .. versionadded:: 3.25
+
+  Path to the volk library.
+
+``Vulkan_dxc_LIBRARY``
+  .. versionadded:: 3.25
+
+  Path to the DirectX shader compiler library.
+
+``Vulkan_dxc_EXECUTABLE``
+  .. versionadded:: 3.25
+
+  Path to the DirectX shader compiler CLI tool.
+
 Hints
 ^^^^^
 
@@ -403,6 +453,27 @@ if(MoltenVK IN_LIST Vulkan_FIND_COMPONENTS)
     )
   mark_as_advanced(Vulkan_MoltenVK_INCLUDE_DIR)
 endif()
+if(volk IN_LIST Vulkan_FIND_COMPONENTS)
+  find_library(Vulkan_volk_LIBRARY
+          NAMES volk
+          HINTS
+            ${_Vulkan_hint_library_search_paths})
+  mark_as_advanced(Vulkan_Volk_LIBRARY)
+endif()
+
+if (dxc IN_LIST Vulkan_FIND_COMPONENTS)
+  find_library(Vulkan_dxc_LIBRARY
+          NAMES dxcompiler
+          HINTS
+            ${_Vulkan_hint_library_search_paths})
+  mark_as_advanced(Vulkan_dxc_LIBRARY)
+
+  find_program(Vulkan_dxc_EXECUTABLE
+          NAMES dxc
+          HINTS
+            ${_Vulkan_hint_executable_search_paths})
+  mark_as_advanced(Vulkan_dxc_EXECUTABLE)
+endif()
 
 if(Vulkan_GLSLC_EXECUTABLE)
   set(Vulkan_glslc_FOUND TRUE)
@@ -416,6 +487,12 @@ else()
   set(Vulkan_glslangValidator_FOUND FALSE)
 endif()
 
+if (Vulkan_dxc_EXECUTABLE)
+  set(Vulkan_dxc_exe_FOUND TRUE)
+else()
+  set(Vulkan_dxc_exe_FOUND FALSE)
+endif()
+
 function(_Vulkan_set_library_component_found component)
   cmake_parse_arguments(PARSE_ARGV 1 _ARG
     "NO_WARNING"
@@ -466,6 +543,8 @@ _Vulkan_set_library_component_found(glslang
     glslang-genericcodegen)
 _Vulkan_set_library_component_found(shaderc_combined)
 _Vulkan_set_library_component_found(SPIRV-Tools)
+_Vulkan_set_library_component_found(volk)
+_Vulkan_set_library_component_found(dxc)
 
 if(Vulkan_MoltenVK_INCLUDE_DIR AND Vulkan_MoltenVK_LIBRARY)
   set(Vulkan_MoltenVK_FOUND TRUE)
@@ -752,6 +831,44 @@ if(Vulkan_FOUND)
           IMPORTED_LOCATION_DEBUG "${Vulkan_SPIRV-Tools_DEBUG_LIBRARY}")
     endif()
   endif()
+
+  if(Vulkan_volk_LIBRARY AND NOT TARGET Vulkan::volk)
+    add_library(Vulkan::volk STATIC IMPORTED)
+    set_property(TARGET Vulkan::volk
+            PROPERTY
+              INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
+    set_property(TARGET Vulkan::volk APPEND
+            PROPERTY
+              IMPORTED_CONFIGURATIONS Release)
+    set_property(TARGET Vulkan::volk APPEND
+            PROPERTY
+              IMPORTED_LOCATION_RELEASE "${Vulkan_volk_LIBRARY}")
+
+    if (NOT WIN32)
+      set_property(TARGET Vulkan::volk APPEND
+              PROPERTY
+                IMPORTED_LINK_INTERFACE_LIBRARIES dl)
+    endif()
+  endif()
+
+  if (Vulkan_dxc_LIBRARY AND NOT TARGET Vulkan::dxc_lib)
+    add_library(Vulkan::dxc_lib STATIC IMPORTED)
+    set_property(TARGET Vulkan::dxc_lib
+      PROPERTY
+        INTERFACE_INCLUDE_DIRECTORIES "${Vulkan_INCLUDE_DIRS}")
+    set_property(TARGET Vulkan::dxc_lib APPEND
+      PROPERTY
+        IMPORTED_CONFIGURATIONS Release)
+    set_property(TARGET Vulkan::dxc_lib APPEND
+      PROPERTY
+        IMPORTED_LOCATION_RELEASE "${Vulkan_dxc_LIBRARY}")
+  endif()
+
+  if(Vulkan_dxc_EXECUTABLE AND NOT TARGET Vulkan::dxc_exe)
+    add_executable(Vulkan::dxc_exe IMPORTED)
+    set_property(TARGET Vulkan::dxc_exe PROPERTY IMPORTED_LOCATION "${Vulkan_dxc_EXECUTABLE}")
+  endif()
+
 endif()
 
 if(Vulkan_MoltenVK_FOUND)
index 00729bc..7118df2 100644 (file)
@@ -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: http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/
+.. _Testing with Xcode: https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/testing_with_xcode/
 
 Module Functions
 ^^^^^^^^^^^^^^^^
index 07fbc1b..2d46dbd 100644 (file)
@@ -79,8 +79,7 @@ DEPRECATED
 
 
 
-AUTHOR Jan Woetzel <http://www.mip.informatik.uni-kiel.de/~jw>
-(07/2003-01/2006)
+AUTHOR Jan Woetzel (07/2003-01/2006)
 #]=======================================================================]
 
 # ------------------------------------------------------------------
index 53df01a..af025af 100644 (file)
@@ -361,10 +361,10 @@ function(FortranCInterface_VERIFY)
     # Build a sample project which reports symbols.
     set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
     try_compile(FortranCInterface_VERIFY_${lang}_COMPILED
-      ${FortranCInterface_BINARY_DIR}/Verify${lang}
-      ${FortranCInterface_SOURCE_DIR}/Verify
-      VerifyFortranC # project name
-      VerifyFortranC # target name
+      PROJECT VerifyFortranC
+      TARGET VerifyFortranC
+      SOURCE_DIR ${FortranCInterface_SOURCE_DIR}/Verify
+      BINARY_DIR ${FortranCInterface_BINARY_DIR}/Verify${lang}
       CMAKE_FLAGS -DVERIFY_CXX=${verify_cxx}
                   -DCMAKE_VERBOSE_MAKEFILE=ON
                  "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
index 567fc37..6401aed 100644 (file)
@@ -47,10 +47,10 @@ unset(_FortranCInterface_CMP0056)
 # Build a sample project which reports symbols.
 set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
 try_compile(FortranCInterface_COMPILED
-  ${FortranCInterface_BINARY_DIR}
-  ${FortranCInterface_SOURCE_DIR}
-  FortranCInterface # project name
-  FortranCInterface # target name
+  PROJECT FortranCInterface
+  TARGET FortranCInterface
+  SOURCE_DIR ${FortranCInterface_SOURCE_DIR}
+  BINARY_DIR ${FortranCInterface_BINARY_DIR}
   CMAKE_FLAGS
     "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}"
     "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
index f5f4f02..b8dc482 100644 (file)
@@ -348,7 +348,7 @@ function(gtest_add_tests)
   unset(testList)
 
   set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*")
-  set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)")
+  set(gtest_test_type_regex "(TYPED_TEST|TEST)_?[FP]?")
 
   foreach(source IN LISTS ARGS_SOURCES)
     if(NOT ARGS_SKIP_DEPENDENCY)
@@ -361,7 +361,9 @@ function(gtest_add_tests)
 
       # Parameterized tests have a different signature for the filter
       if("x${test_type}" STREQUAL "xTEST_P")
-        string(REGEX REPLACE ${gtest_case_name_regex}  "*/\\1.\\2/*" gtest_test_name ${hit})
+        string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1.\\2/*" gtest_test_name ${hit})
+      elseif("x${test_type}" STREQUAL "xTYPED_TEST_P")
+        string(REGEX REPLACE ${gtest_case_name_regex} "*/\\1/*.\\2" gtest_test_name ${hit})
       elseif("x${test_type}" STREQUAL "xTEST_F" OR "x${test_type}" STREQUAL "xTEST")
         string(REGEX REPLACE ${gtest_case_name_regex} "\\1.\\2" gtest_test_name ${hit})
       elseif("x${test_type}" STREQUAL "xTYPED_TEST")
index 958a6db..38e32c2 100644 (file)
@@ -37,8 +37,8 @@ endfunction()
 
 #extract library name and version for given shared object
 function(extract_so_info shared_object libname version)
-  if(READELF_EXECUTABLE)
-    execute_process(COMMAND "${READELF_EXECUTABLE}" -d "${shared_object}"
+  if(CPACK_READELF_EXECUTABLE)
+    execute_process(COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object}"
       WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
       RESULT_VARIABLE result
       OUTPUT_VARIABLE output
@@ -197,15 +197,15 @@ function(cpack_deb_prepare_package_vars)
     endforeach()
   endif()
 
-  find_program(READELF_EXECUTABLE NAMES readelf)
+  find_program(CPACK_READELF_EXECUTABLE NAMES readelf)
 
   if(CPACK_DEBIAN_DEBUGINFO_PACKAGE AND CPACK_DEB_UNSTRIPPED_FILES)
-    find_program(OBJCOPY_EXECUTABLE NAMES objcopy)
+    find_program(CPACK_OBJCOPY_EXECUTABLE NAMES objcopy)
 
-    if(NOT OBJCOPY_EXECUTABLE)
+    if(NOT CPACK_OBJCOPY_EXECUTABLE)
       message(FATAL_ERROR "debuginfo packages require the objcopy tool")
     endif()
-    if(NOT READELF_EXECUTABLE)
+    if(NOT CPACK_READELF_EXECUTABLE)
       message(FATAL_ERROR "debuginfo packages require the readelf tool")
     endif()
 
@@ -213,7 +213,7 @@ function(cpack_deb_prepare_package_vars)
     foreach(_FILE IN LISTS CPACK_DEB_UNSTRIPPED_FILES)
 
       # Get the file's Build ID
-      execute_process(COMMAND env LC_ALL=C ${READELF_EXECUTABLE} -n "${_FILE}"
+      execute_process(COMMAND env LC_ALL=C ${CPACK_READELF_EXECUTABLE} -n "${_FILE}"
         WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
         OUTPUT_VARIABLE READELF_OUTPUT
         RESULT_VARIABLE READELF_RESULT
@@ -221,7 +221,7 @@ function(cpack_deb_prepare_package_vars)
         OUTPUT_STRIP_TRAILING_WHITESPACE )
       if(NOT READELF_RESULT EQUAL 0)
         message(FATAL_ERROR "CPackDeb: readelf: '${READELF_ERROR}';\n"
-            "executed command: '${READELF_EXECUTABLE} -n ${_FILE}'")
+            "executed command: '${CPACK_READELF_EXECUTABLE} -n ${_FILE}'")
       endif()
       if(READELF_OUTPUT MATCHES "Build ID: ([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z]*)")
         set(_BUILD_ID_START ${CMAKE_MATCH_1})
@@ -235,7 +235,7 @@ function(cpack_deb_prepare_package_vars)
       set(_FILE_DBGSYM ${_DBGSYM_ROOT}/usr/lib/debug/.build-id/${_BUILD_ID_START}/${_BUILD_ID_REMAINING}.debug)
       get_filename_component(_OUT_DIR "${_FILE_DBGSYM}" DIRECTORY)
       file(MAKE_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}/${_OUT_DIR}")
-      execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --only-keep-debug "${_FILE}" "${_FILE_DBGSYM}"
+      execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --only-keep-debug "${_FILE}" "${_FILE_DBGSYM}"
         WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
         OUTPUT_VARIABLE OBJCOPY_OUTPUT
         RESULT_VARIABLE OBJCOPY_RESULT
@@ -243,9 +243,9 @@ function(cpack_deb_prepare_package_vars)
         OUTPUT_STRIP_TRAILING_WHITESPACE )
       if(NOT OBJCOPY_RESULT EQUAL 0)
         message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n"
-            "executed command: '${OBJCOPY_EXECUTABLE} --only-keep-debug ${_FILE} ${_FILE_DBGSYM}'")
+            "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --only-keep-debug ${_FILE} ${_FILE_DBGSYM}'")
       endif()
-      execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --strip-unneeded ${_FILE}
+      execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --strip-unneeded ${_FILE}
         WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
         OUTPUT_VARIABLE OBJCOPY_OUTPUT
         RESULT_VARIABLE OBJCOPY_RESULT
@@ -253,9 +253,9 @@ function(cpack_deb_prepare_package_vars)
         OUTPUT_STRIP_TRAILING_WHITESPACE )
       if(NOT OBJCOPY_RESULT EQUAL 0)
         message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n"
-            "executed command: '${OBJCOPY_EXECUTABLE} --strip-debug ${_FILE}'")
+            "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --strip-debug ${_FILE}'")
       endif()
-      execute_process(COMMAND ${OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}
+      execute_process(COMMAND ${CPACK_OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}
         WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
         OUTPUT_VARIABLE OBJCOPY_OUTPUT
         RESULT_VARIABLE OBJCOPY_RESULT
@@ -263,7 +263,7 @@ function(cpack_deb_prepare_package_vars)
         OUTPUT_STRIP_TRAILING_WHITESPACE )
       if(NOT OBJCOPY_RESULT EQUAL 0)
         message(FATAL_ERROR "CPackDeb: objcopy: '${OBJCOPY_ERROR}';\n"
-            "executed command: '${OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}'")
+            "executed command: '${CPACK_OBJCOPY_EXECUTABLE} --add-gnu-debuglink=${_FILE_DBGSYM} ${_FILE}'")
       endif()
     endforeach()
   endif()
@@ -652,7 +652,7 @@ function(cpack_deb_prepare_package_vars)
   unset(CPACK_DEBIAN_PACKAGE_SHLIBS_LIST)
 
   if(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS)
-    if(READELF_EXECUTABLE)
+    if(CPACK_READELF_EXECUTABLE)
       foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES)
         extract_so_info("${_FILE}" libname soversion)
         if(libname AND DEFINED soversion)
index cd631b8..7c10280 100644 (file)
@@ -625,8 +625,8 @@ function(cpack_rpm_debugsymbol_check INSTALL_FILES WORKING_DIR)
   endif()
 
   # With objdump we should check the debug symbols
-  find_program(OBJDUMP_EXECUTABLE objdump)
-  if(NOT OBJDUMP_EXECUTABLE)
+  find_program(CPACK_OBJDUMP_EXECUTABLE objdump)
+  if(NOT CPACK_OBJDUMP_EXECUTABLE)
     message(FATAL_ERROR "CPackRPM: objdump binary could not be found!"
       " Required for debuginfo packaging. See documentation of"
       " CPACK_RPM_DEBUGINFO_PACKAGE variable for details.")
@@ -649,7 +649,7 @@ function(cpack_rpm_debugsymbol_check INSTALL_FILES WORKING_DIR)
       continue()
     endif()
 
-    execute_process(COMMAND "${OBJDUMP_EXECUTABLE}" -h ${WORKING_DIR}/${F}
+    execute_process(COMMAND "${CPACK_OBJDUMP_EXECUTABLE}" -h ${WORKING_DIR}/${F}
                     WORKING_DIRECTORY "${CPACK_TOPLEVEL_DIRECTORY}"
                     RESULT_VARIABLE OBJDUMP_EXEC_RESULT
                     OUTPUT_VARIABLE OBJDUMP_OUT
index 27aa3e0..eadf3da 100644 (file)
@@ -86,15 +86,13 @@ function(CMAKE_CHECK_SOURCE_COMPILES _lang _source _var)
     else()
       set(CHECK_${LANG}_SOURCE_COMPILES_ADD_INCLUDES)
     endif()
-    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}"
-      "${_source}\n")
 
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_START "Performing Test ${_var}")
     endif()
+    string(APPEND _source "\n")
     try_compile(${_var}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}
+      SOURCE_FROM_VAR "src.${_SRC_EXT}" _source
       COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LINK_OPTIONS}
       ${CHECK_${LANG}_SOURCE_COMPILES_ADD_LIBRARIES}
index 75e9896..09c85c5 100644 (file)
@@ -85,15 +85,13 @@ function(CMAKE_CHECK_SOURCE_RUNS _lang _source _var)
     else()
       set(CHECK_${_lang}_SOURCE_COMPILES_ADD_INCLUDES)
     endif()
-    file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}"
-      "${_source}\n")
 
     if(NOT CMAKE_REQUIRED_QUIET)
       message(CHECK_START "Performing Test ${_var}")
     endif()
+    string(APPEND _source "\n")
     try_run(${_var}_EXITCODE ${_var}_COMPILED
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.${_SRC_EXT}
+      SOURCE_FROM_VAR "src.${_SRC_EXT}" _source
       COMPILE_DEFINITIONS -D${_var} ${CMAKE_REQUIRED_DEFINITIONS}
       ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LINK_OPTIONS}
       ${CHECK_${_lang}_SOURCE_COMPILES_ADD_LIBRARIES}
index b6f3c09..5c144ec 100644 (file)
@@ -4,7 +4,7 @@ macro(_record_compiler_features lang compile_flags feature_list)
 
   string(TOLOWER ${lang} lang_lc)
   file(REMOVE "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.bin")
-  file(WRITE "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.${lang_lc}" "
+  set(_content "
   const char features[] = {\"\\n\"\n")
 
   get_property(known_features GLOBAL PROPERTY CMAKE_${lang}_KNOWN_FEATURES)
@@ -16,10 +16,11 @@ macro(_record_compiler_features lang compile_flags feature_list)
       else()
         set(_feature_condition "#if ${_cmake_feature_test_${feature}}\n\"1\"\n#else\n\"0\"\n#endif\n")
       endif()
-      file(APPEND "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.${lang_lc}" "\"${lang}_FEATURE:\"\n${_feature_condition}\"${feature}\\n\"\n")
+      string(APPEND _content
+        "\"${lang}_FEATURE:\"\n${_feature_condition}\"${feature}\\n\"\n")
     endif()
   endforeach()
-  file(APPEND "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.${lang_lc}"
+  string(APPEND _content
     "\n};\n\nint main(int argc, char** argv) { (void)argv; return features[argc]; }\n")
 
   if(CMAKE_${lang}_LINK_WITH_STANDARD_COMPILE_OPTION)
@@ -31,7 +32,7 @@ macro(_record_compiler_features lang compile_flags feature_list)
   endif()
 
   try_compile(CMAKE_${lang}_FEATURE_TEST
-    ${CMAKE_BINARY_DIR} "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.${lang_lc}"
+    SOURCE_FROM_VAR "feature_tests.${lang_lc}" _content
     COMPILE_DEFINITIONS "${compile_flags}"
     LINK_LIBRARIES "${compile_flags_for_link}"
     OUTPUT_VARIABLE _output
index 5a532c7..a9aa8e0 100644 (file)
@@ -23,11 +23,11 @@ macro(__aix_compiler_gnu lang)
   # Construct the export list ourselves to pass only the object files so
   # that we export only the symbols actually provided by the sources.
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS> <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
     )
 
   set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 endmacro()
index 2a8c159..902cbb3 100644 (file)
@@ -29,12 +29,12 @@ macro(__aix_compiler_xl lang)
   # Construct the export list ourselves to pass only the object files so
   # that we export only the symbols actually provided by the sources.
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS>${_OBJECTS}"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp -c <CMAKE_${lang}_COMPILER> <AIX_EXPORTS>${_OBJECTS}"
     "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
     )
 
   set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -c <CMAKE_${lang}_COMPILER> -l . <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 
   unset(_OBJECTS)
index 891bce7..5e16fcb 100755 (executable)
@@ -5,7 +5,7 @@
 # This script is internal to CMake and meant only to be
 # invoked by CMake-generated build systems on AIX.
 
-usage='usage: ExportImportList -o <out-file> [-l <lib>] [-n] [--] <objects>...'
+usage='usage: ExportImportList -o <out-file> -c <compiler> [-l <lib>] [-n] [--] <objects>...'
 
 die() {
     echo "$@" 1>&2; exit 1
@@ -15,11 +15,13 @@ die() {
 out=''
 lib=''
 no_objects=''
+compiler=''
 while test "$#" != 0; do
     case "$1" in
     -l) shift; lib="$1" ;;
     -o) shift; out="$1" ;;
     -n) no_objects='1' ;;
+    -c) shift; compiler="$1" ;;
     --) shift; break ;;
     -*) die "$usage" ;;
     *)  break ;;
@@ -27,27 +29,47 @@ while test "$#" != 0; do
     shift
 done
 test -n "$out" || die "$usage"
+# We need the compiler executable to resolve where the ibm-llvm-nm executable is
+test -n "$compiler" || die "$usage"
 
 # Build a temporary file that atomically replaces the output later.
 out_tmp="$out.tmp$$"
 trap 'rm -f "$out_tmp"' EXIT INT TERM
 > "$out_tmp"
 
+# If IPA was enabled and a compiler from the IBMClang family is used, then
+# the object files contain LLVM bitcode[0] rather than XCOFF objects and so
+# need to be handled differently.
+#
+# [0]: https://www.ibm.com/docs/en/openxl-c-and-cpp-aix/17.1.0?topic=compatibility-link-time-optimization-lto
+NM="$(dirname "$compiler")/../libexec/ibm-llvm-nm"
+
+function IsBitcode {
+    # N4 = first 4 bytes, -tx = output in hexadecimal, -An = don't display offset
+    # cut: trim off the preceding whitespace where the offset would be
+    # 4243code is the hexadecimal magic number for LLVM bitcode
+    [ "$(od -N4 -tx -An $1 | cut -d ' ' -f 2)" == "4243c0de" ];
+}
+
 # Collect symbols exported from all object files.
 if test -z "$no_objects"; then
     for f in "$@"; do
-        dump -tov -X 32_64 "$f" |
-        awk '
-            BEGIN {
-                V["EXPORTED"]=" export"
-                V["PROTECTED"]=" protected"
-            }
-            /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / {
-                if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) {
-                    print $NF V[$(NF-1)]
+        if IsBitcode "$f"; then
+            "$NM" "$f" --defined-only --extern-only --just-symbol-name 2>/dev/null
+        else
+            dump -tov -X 32_64 "$f" |
+            awk '
+                BEGIN {
+                    V["EXPORTED"]=" export"
+                    V["PROTECTED"]=" protected"
+                }
+                /^\[[0-9]+\]\tm +[^ ]+ +\.(text|data|bss) +[^ ]+ +(extern|weak) +(EXPORTED|PROTECTED| ) / {
+                    if (!match($NF,/^(\.|__sinit|__sterm|__[0-9]+__)/)) {
+                        print $NF V[$(NF-1)]
+                    }
                 }
-            }
-        '
+            '
+        fi
     done >> "$out_tmp"
 fi
 
index 12e5f3c..994ba79 100644 (file)
@@ -3,6 +3,7 @@
 # see http://archive.netbsd.se/?ml=dfbsd-users&a=2007-07&m=4678361
 
 include(Platform/FreeBSD)
+set(BSD "DragonFlyBSD")
 
 # DragonFly BSD requires -z origin to enable $ORIGIN expansion in RPATH.
 # This is not required for FreeBSD since 10.2-RELEASE.
index bd5a786..9cd9399 100644 (file)
@@ -1,3 +1,4 @@
+set(BSD "FreeBSD")
 set(CMAKE_DL_LIBS "")
 set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC")
 set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE")
index 1363b44..992f80e 100644 (file)
@@ -8,18 +8,6 @@ if(__LINUX_COMPILER_INTEL_LLVM)
 endif()
 set(__LINUX_COMPILER_INTEL_LLVM 1)
 
-if(NOT XIAR)
-  set(_intel_xiar_hints)
-  foreach(lang C CXX Fortran)
-    if(IS_ABSOLUTE "${CMAKE_${lang}_COMPILER}")
-      get_filename_component(_hint "${CMAKE_${lang}_COMPILER}" PATH)
-      list(APPEND _intel_xiar_hints ${_hint})
-    endif()
-  endforeach()
-  find_program(XIAR NAMES xiar HINTS ${_intel_xiar_hints})
-  mark_as_advanced(XIAR)
-endif()
-
 macro(__linux_compiler_intel_llvm lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC")
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE")
@@ -37,19 +25,5 @@ macro(__linux_compiler_intel_llvm lang)
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
 
-  set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
-
-  if(XIAR)
-    # INTERPROCEDURAL_OPTIMIZATION
-    set(CMAKE_${lang}_COMPILE_OPTIONS_IPO -ipo)
-    set(CMAKE_${lang}_CREATE_STATIC_LIBRARY_IPO
-      "${XIAR} cr <TARGET> <LINK_FLAGS> <OBJECTS> "
-      "${XIAR} -s <TARGET> ")
-    set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-    set(_CMAKE_${lang}_IPO_LEGACY_BEHAVIOR YES)
-  else()
-    set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER NO)
-  endif()
-
   set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY "-fvisibility=")
 endmacro()
index aad17f1..602b417 100644 (file)
@@ -3,13 +3,15 @@
 
 
 # This module is shared by multiple languages; use include blocker.
-if(__LINUX_COMPILER_NVIDIA)
-  return()
-endif()
-set(__LINUX_COMPILER_NVIDIA 1)
-
-include(Platform/Linux-PGI)
+include_guard()
 
 macro(__linux_compiler_nvhpc lang)
-  __linux_compiler_pgi(${lang})
+  set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "-fPIC")
+  set(CMAKE_${lang}_COMPILE_OPTIONS_PIE "-fPIE")
+  set(_CMAKE_${lang}_PIE_MAY_BE_SUPPORTED_BY_LINKER YES)
+  set(CMAKE_${lang}_LINK_OPTIONS_PIE "-fPIE")
+  set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE "")
+  set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS "-fPIC")
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared")
+  set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS "")
 endmacro()
index a7e58ab..3dc3ca3 100644 (file)
@@ -1,3 +1,4 @@
+set(LINUX 1)
 set(CMAKE_DL_LIBS "dl")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "-Wl,-rpath,")
 set(CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP ":")
@@ -86,7 +87,12 @@ include(Platform/UnixPaths)
 
 # Debian has lib32 and lib64 paths only for compatibility so they should not be
 # searched.
-if(NOT CMAKE_CROSSCOMPILING AND EXISTS "/etc/debian_version")
-  set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS FALSE)
-  set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS FALSE)
+if(NOT CMAKE_CROSSCOMPILING)
+  if (EXISTS "/etc/debian_version")
+    set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB32_PATHS FALSE)
+    set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS FALSE)
+  endif()
+  if (EXISTS "/etc/arch-release")
+    set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS FALSE)
+  endif()
 endif()
index ab85923..52c6594 100644 (file)
@@ -1,3 +1,4 @@
+set(BSD "NetBSD")
 set(CMAKE_DL_LIBS "")
 set(CMAKE_C_COMPILE_OPTIONS_PIC "-fPIC")
 set(CMAKE_C_COMPILE_OPTIONS_PIE "-fPIE")
index 97e2a6a..51ea60d 100644 (file)
@@ -1,4 +1,5 @@
 include(Platform/NetBSD)
+set(BSD "OpenBSD")
 
 # On OpenBSD, the compile time linker does not share it's configuration with
 # the runtime linker.  This will extract the library search paths from the
diff --git a/Modules/Platform/SerenityOS-Clang-ASM.cmake b/Modules/Platform/SerenityOS-Clang-ASM.cmake
new file mode 100644 (file)
index 0000000..ba1e18c
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(ASM)
diff --git a/Modules/Platform/SerenityOS-Clang-C.cmake b/Modules/Platform/SerenityOS-Clang-C.cmake
new file mode 100644 (file)
index 0000000..791a197
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(C)
diff --git a/Modules/Platform/SerenityOS-Clang-CXX.cmake b/Modules/Platform/SerenityOS-Clang-CXX.cmake
new file mode 100644 (file)
index 0000000..084e319
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(CXX)
diff --git a/Modules/Platform/SerenityOS-GNU-ASM.cmake b/Modules/Platform/SerenityOS-GNU-ASM.cmake
new file mode 100644 (file)
index 0000000..ba1e18c
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(ASM)
diff --git a/Modules/Platform/SerenityOS-GNU-C.cmake b/Modules/Platform/SerenityOS-GNU-C.cmake
new file mode 100644 (file)
index 0000000..791a197
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(C)
diff --git a/Modules/Platform/SerenityOS-GNU-CXX.cmake b/Modules/Platform/SerenityOS-GNU-CXX.cmake
new file mode 100644 (file)
index 0000000..084e319
--- /dev/null
@@ -0,0 +1,2 @@
+include(Platform/SerenityOS-GNU)
+__serenity_compiler_gnu(CXX)
diff --git a/Modules/Platform/SerenityOS-GNU.cmake b/Modules/Platform/SerenityOS-GNU.cmake
new file mode 100644 (file)
index 0000000..ed39477
--- /dev/null
@@ -0,0 +1,24 @@
+# This module is shared by multiple languages; use include blocker.
+include_guard()
+
+set(CMAKE_EXE_LINKER_FLAGS_INIT "-Wl,--hash-style=gnu,-z,relro,-z,now,-z,noexecstack,-z,separate-code,-z,max-page-size=0x1000")
+
+macro(__serenity_compiler_gnu lang)
+  set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG "-Wl,-rpath,")
+  set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ":")
+  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG "-Wl,-rpath-link,")
+  set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG "-Wl,-soname,")
+  set(CMAKE_EXE_EXPORTS_${lang}_FLAG "-Wl,--export-dynamic")
+
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-shared -Wl,--hash-style=gnu,-z,relro,-z,now,-z,noexecstack,-z,separate-code")
+
+  # Initialize link type selection flags.  These flags are used when
+  # building a shared library, shared module, or executable that links
+  # to other libraries to select whether to use the static or shared
+  # versions of the libraries.
+  foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
+    set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS "-Wl,-Bstatic")
+    set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
+  endforeach()
+
+endmacro()
diff --git a/Modules/Platform/SerenityOS.cmake b/Modules/Platform/SerenityOS.cmake
new file mode 100644 (file)
index 0000000..dc4f369
--- /dev/null
@@ -0,0 +1,12 @@
+
+set(SERENITYOS 1)
+
+set(CMAKE_DL_LIBS "")
+set(CMAKE_SHARED_LIBRARY_RPATH_ORIGIN_TOKEN "\$ORIGIN")
+set(CMAKE_SHARED_LIBRARY_SUFFIX ".so")
+
+# Shared libraries with no builtin soname may not be linked safely by
+# specifying the file path.
+set(CMAKE_PLATFORM_USES_PATH_WHEN_NO_SONAME 1)
+
+include(Platform/UnixPaths)
diff --git a/Modules/Platform/Windows-Clang-HIP.cmake b/Modules/Platform/Windows-Clang-HIP.cmake
new file mode 100644 (file)
index 0000000..20879fa
--- /dev/null
@@ -0,0 +1,19 @@
+include(Platform/Windows-Clang)
+set(_COMPILE_HIP_MSVC " -TP")
+__windows_compiler_clang(HIP)
+
+if("x${CMAKE_HIP_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_HIP)
+    set(CMAKE_HIP_DEPENDS_USE_COMPILER TRUE)
+  endif()
+elseif("x${CMAKE_HIP_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+      AND CMAKE_DEPFILE_FLAGS_HIP)
+    # dependencies are computed by the compiler itself
+    set(CMAKE_HIP_DEPFILE_FORMAT gcc)
+    set(CMAKE_HIP_DEPENDS_USE_COMPILER TRUE)
+  endif()
+endif()
index 3941311..33d271d 100644 (file)
@@ -89,17 +89,27 @@ macro(__windows_compiler_clang_gnu lang)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd)
 
     if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
-      set(__ADDED_FLAGS "")
-      set(__ADDED_FLAGS_DEBUG "")
+      set(_RTL_FLAGS "")
+      set(_RTL_FLAGS_DEBUG "")
     else()
-      set(__ADDED_FLAGS_DEBUG "-D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd")
-      set(__ADDED_FLAGS "-D_DLL -D_MT -Xclang --dependent-lib=msvcrt")
+      set(_RTL_FLAGS_DEBUG " -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd")
+      set(_RTL_FLAGS " -D_DLL -D_MT -Xclang --dependent-lib=msvcrt")
     endif()
 
-    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g -Xclang -gcodeview -O0 ${__ADDED_FLAGS_DEBUG}")
-    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os -DNDEBUG ${__ADDED_FLAGS}")
-    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG ${__ADDED_FLAGS}")
-    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG -Xclang -gcodeview ${__ADDED_FLAGS}")
+    if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+      set(_DBG_FLAGS "")
+    else()
+      set(_DBG_FLAGS " -g -Xclang -gcodeview")
+    endif()
+
+    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -O0${_DBG_FLAGS}${_RTL_FLAGS_DEBUG}")
+    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os -DNDEBUG${_RTL_FLAGS}")
+    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG${_RTL_FLAGS}")
+    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -DNDEBUG${_DBG_FLAGS}${_RTL_FLAGS}")
+
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -g -Xclang -gcodeview)
+    #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase) # not supported by Clang
+    #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # not supported by Clang
   endif()
   set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
   set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
@@ -109,8 +119,9 @@ macro(__windows_compiler_clang_gnu lang)
   set(CMAKE_${lang}_COMPILE_OPTIONS_USE_PCH -Xclang -include-pch -Xclang <PCH_FILE> -Xclang -include -Xclang <PCH_HEADER>)
   set(CMAKE_${lang}_COMPILE_OPTIONS_CREATE_PCH -Xclang -emit-pch -Xclang -include -Xclang <PCH_HEADER> -x ${__pch_header_${lang}})
 
-  unset(__ADDED_FLAGS)
-  unset(__ADDED_FLAGS_DEBUG)
+  unset(_DBG_FLAGS)
+  unset(_RTL_FLAGS)
+  unset(_RTL_FLAGS_DEBUG)
   string(TOLOWER "${CMAKE_BUILD_TYPE}" BUILD_TYPE_LOWER)
   set(CMAKE_${lang}_STANDARD_LIBRARIES_INIT "-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames")
 
@@ -146,24 +157,36 @@ macro(__enable_llvm_rc_preprocessing clang_option_prefix extra_pp_flags)
   endif()
 endmacro()
 
+macro(__verify_same_language_values variable)
+  foreach(lang "C" "CXX" "HIP")
+    if(DEFINED CMAKE_${lang}_${variable})
+      list(APPEND __LANGUAGE_VALUES_${variable} "${CMAKE_${lang}_${variable}}")
+    endif()
+  endforeach()
+  list(REMOVE_DUPLICATES __LANGUAGE_VALUES_${variable})
+  list(LENGTH __LANGUAGE_VALUES_${variable} __NUM_VALUES)
+
+  if(__NUM_VALUES GREATER 1)
+    message(FATAL_ERROR ${ARGN})
+  endif()
+  unset(__NUM_VALUES)
+  unset(__LANGUAGE_VALUES_${variable})
+endmacro()
 
 if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
-    OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
+    OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC"
+    OR "x${CMAKE_HIP_SIMULATE_ID}" STREQUAL "xMSVC")
 
-  if ( DEFINED CMAKE_C_COMPILER_ID AND DEFINED CMAKE_CXX_COMPILER_ID
-       AND NOT "x${CMAKE_C_COMPILER_ID}" STREQUAL "x${CMAKE_CXX_COMPILER_ID}")
-    message(FATAL_ERROR "The current configuration mixes Clang and MSVC or "
-            "some other CL compatible compiler tool. This is not supported. "
-            "Use either clang or MSVC as both C and C++ compilers.")
-  endif()
+  __verify_same_language_values(COMPILER_ID
+                                "The current configuration mixes Clang and MSVC or "
+                                "some other CL compatible compiler tool. This is not supported. "
+                                "Use either clang or MSVC as both C, C++ and/or HIP compilers.")
 
-  if ( DEFINED CMAKE_C_COMPILER_FRONTEND_VARIANT AND DEFINED CMAKE_CXX_COMPILER_FRONTEND_VARIANT
-       AND NOT "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}")
-    message(FATAL_ERROR "The current configuration uses the Clang compiler "
-            "tool with mixed frontend variants, both the GNU and in MSVC CL "
-            "like variants. This is not supported. Use either clang/clang++ "
-            "or clang-cl as both C and C++ compilers.")
-  endif()
+  __verify_same_language_values(COMPILER_FRONTEND_VARIANT
+                                "The current configuration uses the Clang compiler "
+                                "tool with mixed frontend variants, both the GNU and in MSVC CL "
+                                "like variants. This is not supported. Use either clang/clang++ "
+                                "or clang-cl as both C, C++ and/or HIP compilers.")
 
   if(NOT CMAKE_RC_COMPILER_INIT)
     # Check if rc is already in the path
@@ -183,13 +206,17 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     unset(__RC_COMPILER_PATH CACHE)
   endif()
 
-  if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC" )
+  if ( "x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
+      OR "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"
+      OR "x${CMAKE_HIP_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
+
     include(Platform/Windows-MSVC)
     # Set the clang option forwarding prefix for clang-cl usage in the llvm-rc processing stage
     __enable_llvm_rc_preprocessing("-clang:" "")
     macro(__windows_compiler_clang_base lang)
       set(_COMPILE_${lang} "${_COMPILE_${lang}_MSVC}")
       __windows_compiler_msvc(${lang})
+      unset(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # -ZI not supported by Clang
       set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "-WX")
       set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-imsvc")
     endmacro()
@@ -202,6 +229,14 @@ if("x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC"
     endif()
     unset(__WINDOWS_CLANG_CMP0091)
 
+    cmake_policy(GET CMP0141 __WINDOWS_MSVC_CMP0141)
+    if(__WINDOWS_MSVC_CMP0141 STREQUAL "NEW")
+      set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>")
+    else()
+      set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "")
+    endif()
+    unset(__WINDOWS_MSVC_CMP0141)
+
     set(CMAKE_BUILD_TYPE_INIT Debug)
 
     __enable_llvm_rc_preprocessing("" "-x c")
index e4d9b93..8ae6852 100644 (file)
@@ -19,9 +19,8 @@ if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
   set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
 endif()
 
-if("${CMAKE_SOURCE_DIR}${CMAKE_BINARY_DIR}" MATCHES " ")
-  # The Intel compiler does not properly escape spaces in a depfile.
-  # Fall back to msvc depfile format.
-  set(CMAKE_DEPFILE_FLAGS_C "/showIncludes")
-  set(CMAKE_C_DEPFILE_FORMAT msvc)
-endif()
+# The Intel compiler does not properly escape spaces in a depfile which can
+# occur in source and binary cmake paths as well as external include paths.
+# Until Intel fixes this bug, fall back unconditionally to msvc depfile format.
+set(CMAKE_DEPFILE_FLAGS_C "/showIncludes")
+set(CMAKE_C_DEPFILE_FORMAT msvc)
index 6adbb6e..e2fa2af 100644 (file)
@@ -20,9 +20,8 @@ if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
  set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
 endif()
 
-if("${CMAKE_SOURCE_DIR}${CMAKE_BINARY_DIR}" MATCHES " ")
-  # The Intel compiler does not properly escape spaces in a depfile.
-  # Fall back to msvc depfile format.
-  set(CMAKE_DEPFILE_FLAGS_CXX "/showIncludes")
-  set(CMAKE_CXX_DEPFILE_FORMAT msvc)
-endif()
+# The Intel compiler does not properly escape spaces in a depfile which can
+# occur in source and binary cmake paths as well as external include paths.
+# Until Intel fixes this bug, fall back unconditionally to msvc depfile format.
+set(CMAKE_DEPFILE_FLAGS_CXX "/showIncludes")
+set(CMAKE_CXX_DEPFILE_FORMAT msvc)
index e3804fb..c9b70d5 100644 (file)
@@ -33,6 +33,8 @@ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -th
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll    -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
 
 # Intel Fortran for Windows supports single-threaded RTL but it is
 # not implemented by the Visual Studio integration.
index 06d0a00..202ba23 100644 (file)
@@ -33,6 +33,8 @@ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -th
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll    -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
 
 # Intel Fortran for Windows supports single-threaded RTL but it is
 # not implemented by the Visual Studio integration.
index f24dcdb..43f5874 100644 (file)
@@ -3,26 +3,61 @@
 
 
 # This module is shared by multiple languages; use include blocker.
-if(__WINDOWS_INTEL)
+if(__WINDOWS_INTEL_LLVM)
   return()
 endif()
-set(__WINDOWS_INTEL 1)
+set(__WINDOWS_INTEL_LLVM 1)
 
+# Platform/Windows-MSVC adds some linking options icx/ifx do not understand,
+# but that need to be passed to the linker.  Wrap all the linking options from
+# Platform/Windows-MSVC so that the compiler will hand them off to the linker
+# without interpreting them.
+
+# Save original CMAKE_${t}_LINKER_FLAGS_INIT
+foreach(t EXE SHARED MODULE STATIC)
+  set(_saved_cmake_${t}_linker_flags_init ${CMAKE_${t}_LINKER_FLAGS_INIT})
+  set(CMAKE_${t}_LINKER_FLAGS_INIT "")
+endforeach()
 include(Platform/Windows-MSVC)
+# Wrap linker flags from Windows-MSVC
+set(_IntelLLVM_LINKER_WRAPPER_FLAG "/Qoption,link,")
+set(_IntelLLVM_LINKER_WRAPPER_FLAG_SEP ",")
+foreach(t EXE SHARED MODULE STATIC)
+  set(_wrapped_linker_flags "")
+  foreach(flag ${CMAKE_${t}_LINKER_FLAGS_INIT})
+    string(STRIP ${flag} flag)
+    list(APPEND _wrapped_linker_flags "${_IntelLLVM_LINKER_WRAPPER_FLAG}${flag}")
+  endforeach()
+  set(CMAKE_${t}_LINKER_FLAGS_INIT "")
+  list(APPEND CMAKE_${t}_LINKER_FLAGS_INIT
+    ${_saved_cmake_${t}_linker_flags_init} ${_wrapped_linker_flags})
+endforeach()
+
 macro(__windows_compiler_intel lang)
   __windows_compiler_msvc(${lang})
 
-  # For DPCPP other offload cases, some link flags need to go to the compiler
-  # driver and others need to go to the linker.  Pass the compiler linking flags
-  # in CMAKE_${lang}_LINK_FLAGS and linker flags in LINK_FLAGS
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "${_IntelLLVM_LINKER_WRAPPER_FLAG}")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP "${_IntelLLVM_LINKER_WRAPPER_FLAG_SEP}")
+  set(CMAKE_${lang}_CREATE_WIN32_EXE "${CMAKE_${lang}_LINKER_WRAPPER_FLAG}/subsystem:windows")
+  set(CMAKE_${lang}_CREATE_CONSOLE_EXE "${CMAKE_${lang}_LINKER_WRAPPER_FLAG}/subsystem:console")
+  set(CMAKE_LINK_DEF_FILE_FLAG "${CMAKE_${lang}_LINKER_WRAPPER_FLAG}/DEF:")
+  set(CMAKE_LIBRARY_PATH_FLAG "${CMAKE_${lang}_LINKER_WRAPPER_FLAG}/LIBPATH:")
+
+  # Features for LINK_LIBRARY generator expression
+  if(MSVC_VERSION GREATER "1900")
+    ## WHOLE_ARCHIVE: Force loading all members of an archive
+    set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
+    set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+  endif()
+
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "${_CMAKE_VS_LINK_EXE}<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} /link /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES>${CMAKE_END_TEMP_FILE}")
+    "${_CMAKE_VS_LINK_EXE}<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} <LINK_FLAGS> <LINK_LIBRARIES> /link /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} ${CMAKE_END_TEMP_FILE}")
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "${_CMAKE_VS_LINK_DLL}<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -LD -link /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
+    "${_CMAKE_VS_LINK_DLL}<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -LD <LINK_FLAGS> <LINK_LIBRARIES> -link /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} ${CMAKE_END_TEMP_FILE}")
   if (NOT "${lang}" STREQUAL "Fortran" OR CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 2022.1)
     # The Fortran driver does not support -fuse-ld=llvm-lib before compiler version 2022.1
     set(CMAKE_${lang}_CREATE_STATIC_LIBRARY
-      "<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -fuse-ld=llvm-lib -o <TARGET> -link <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
+      "<CMAKE_${lang}_COMPILER> ${CMAKE_CL_NOLOGO} <CMAKE_${lang}_LINK_FLAGS> <OBJECTS> ${CMAKE_START_TEMP_FILE} -fuse-ld=llvm-lib -o <TARGET> <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
   endif()
 
   set(CMAKE_DEPFILE_FLAGS_${lang} "-QMD -QMT <DEP_TARGET> -QMF <DEP_FILE>")
index e74ec9e..8e96bf4 100644 (file)
@@ -331,6 +331,13 @@ else()
 endif()
 unset(__WINDOWS_MSVC_CMP0091)
 
+cmake_policy(GET CMP0141 __WINDOWS_MSVC_CMP0141)
+if(__WINDOWS_MSVC_CMP0141 STREQUAL "NEW")
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
+else()
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT "")
+endif()
+unset(__WINDOWS_MSVC_CMP0141)
 
 # Features for LINK_LIBRARY generator expression
 if(MSVC_VERSION GREATER "1900")
@@ -441,6 +448,12 @@ macro(__windows_compiler_msvc lang)
     endif()
     unset(_cmp0092)
 
+    if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+      set(_Zi "")
+    else()
+      set(_Zi " /Zi")
+    endif()
+
     if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       # note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
       # that include MS's own headers. CMake itself is affected project too.
@@ -451,20 +464,24 @@ macro(__windows_compiler_msvc lang)
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
     else()
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS${_W3}${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} /Zi /Ob0 /Od ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd}${_Zi} /Ob0 /Od ${_RTC1}")
       string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
-      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} /Zi /O2 /Ob1 /DNDEBUG")
+      string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD}${_Zi} /O2 /Ob1 /DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
     endif()
     unset(_Wall)
     unset(_W3)
     unset(_MDd)
     unset(_MD)
+    unset(_Zi)
 
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -MT)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -MD)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -MTd)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -MDd)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Z7)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Zi)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue -ZI)
   endif()
   set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
 
index 6c1699b..326e715 100644 (file)
@@ -57,6 +57,12 @@ else()
   set(_MD "-MD ")
 endif()
 
+if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+  set(_Zi "")
+else()
+  set(_Zi " -Zi")
+endif()
+
 cmake_policy(GET CMP0092 _cmp0092)
 if(_cmp0092 STREQUAL "NEW")
   set(_W3 "")
@@ -66,11 +72,12 @@ endif()
 unset(_cmp0092)
 
 string(APPEND CMAKE_CUDA_FLAGS_INIT " ${PLATFORM_DEFINES_CUDA} -D_WINDOWS -Xcompiler=\"${_W3}${_FLAGS_CXX}\"")
-string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}-Zi -Ob0 -Od ${_RTC1}\"")
+string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}${_Zi} -Ob0 -Od ${_RTC1}\"")
 string(APPEND CMAKE_CUDA_FLAGS_RELEASE_INIT " -Xcompiler=\"${_MD}-O2 -Ob2\" -DNDEBUG")
-string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"${_MD}-Zi -O2 -Ob1\" -DNDEBUG")
+string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"${_MD}${_Zi} -O2 -Ob1\" -DNDEBUG")
 string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -Xcompiler=\"${_MD}-O1 -Ob1\" -DNDEBUG")
 unset(_W3)
+unset(_Zi)
 unset(_MDd)
 unset(_MD)
 
@@ -78,6 +85,9 @@ set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -Xcomp
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -Xcompiler=-MD)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -Xcompiler=-MTd)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -Xcompiler=-MDd)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -Xcompiler=-Z7)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase -Xcompiler=-Zi)
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue -Xcompiler=-ZI)
 
 set(CMAKE_CUDA_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
 
index d8b3957..5263161 100644 (file)
@@ -19,8 +19,8 @@ set(CMAKE_LINK_LIBRARY_SUFFIX ".lib")
 set(CMAKE_DL_LIBS "")
 set(CMAKE_EXTRA_LINK_EXTENSIONS ".targets")
 
-set(CMAKE_FIND_LIBRARY_PREFIXES "")
-set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
+set(CMAKE_FIND_LIBRARY_PREFIXES "" "lib")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a")
 
 # for borland make long command lines are redirected to a file
 # with the following syntax, see Windows-bcc32.cmake for use
index c1db259..09c240d 100644 (file)
@@ -1,4 +1,5 @@
-# kFreeBSD looks just like Linux.
+# kFreeBSD is a Debian GNU distribution with a kernel from FreeBSD,
+# and should be marked as LINUX
 include(Platform/Linux)
 
 set(CMAKE_LIBRARY_ARCHITECTURE_REGEX "[a-z0-9_]+(-[a-z0-9_]+)?-kfreebsd-gnu[a-z0-9_]*")
index ea8ca73..096c90c 100644 (file)
@@ -77,20 +77,16 @@ macro(__TEST_BIG_ENDIAN_LEGACY_IMPL VARIABLE)
     endif()
 
     if(_test_language STREQUAL "CXX")
-      set(_test_file "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.cpp")
+      set(_test_file TestEndianess.cpp)
     else()
-      set(_test_file "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/TestEndianess.c")
+      set(_test_file TestEndianess.c)
     endif()
 
-    configure_file("${CMAKE_ROOT}/Modules/TestEndianess.c.in"
-                   ${_test_file}
-                   @ONLY)
-
-     file(READ ${_test_file} TEST_ENDIANESS_FILE_CONTENT)
+    file(READ "${CMAKE_ROOT}/Modules/TestEndianess.c.in" TEST_ENDIANESS_FILE_CONTENT)
+    string(CONFIGURE "${TEST_ENDIANESS_FILE_CONTENT}" TEST_ENDIANESS_FILE_CONTENT @ONLY)
 
      try_compile(HAVE_${VARIABLE}
-      "${CMAKE_BINARY_DIR}"
-      ${_test_file}
+      SOURCE_FROM_VAR "${_test_file}" TEST_ENDIANESS_FILE_CONTENT
       OUTPUT_VARIABLE OUTPUT
       COPY_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestEndianess.bin" )
 
@@ -131,12 +127,12 @@ macro(__TEST_BIG_ENDIAN_LEGACY_IMPL VARIABLE)
         endif()
 
         file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
-          "Determining if the system is big endian passed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n")
+          "Determining if the system is big endian passed with the following output:\n${OUTPUT}\n${_test_file}:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n")
 
       else()
         message(CHECK_FAIL "failed")
         file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
-          "Determining if the system is big endian failed with the following output:\n${OUTPUT}\nTestEndianess.c:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n")
+          "Determining if the system is big endian failed with the following output:\n${OUTPUT}\n${_test_file}:\n${TEST_ENDIANESS_FILE_CONTENT}\n\n")
         set(${VARIABLE})
       endif()
   endif()
index ce505f3..2e511ae 100644 (file)
@@ -25,8 +25,7 @@ macro(CHECK_CXX_ACCEPTS_FLAG FLAGS  VARIABLE)
   if(NOT DEFINED ${VARIABLE})
     message(CHECK_START "Checking to see if CXX compiler accepts flag ${FLAGS}")
     try_compile(${VARIABLE}
-      ${CMAKE_BINARY_DIR}
-      ${CMAKE_ROOT}/Modules/DummyCXXFile.cxx
+      SOURCES ${CMAKE_ROOT}/Modules/DummyCXXFile.cxx
       CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${FLAGS}
       OUTPUT_VARIABLE OUTPUT)
     if(${VARIABLE})
index 0f2dc01..06db586 100644 (file)
@@ -17,8 +17,8 @@ for-init-statement to the loop body.
 
 if(NOT DEFINED CMAKE_ANSI_FOR_SCOPE)
   message(CHECK_START "Check for ANSI scope")
-  try_compile(CMAKE_ANSI_FOR_SCOPE  ${CMAKE_BINARY_DIR}
-    ${CMAKE_ROOT}/Modules/TestForAnsiForScope.cxx
+  try_compile(CMAKE_ANSI_FOR_SCOPE
+    SOURCES ${CMAKE_ROOT}/Modules/TestForAnsiForScope.cxx
     OUTPUT_VARIABLE OUTPUT)
   if (CMAKE_ANSI_FOR_SCOPE)
     message(CHECK_PASS "found")
index 545b7ec..9a09ac7 100644 (file)
@@ -16,8 +16,8 @@ check if the compiler supports the standard ANSI sstream header
 
 if(NOT DEFINED CMAKE_HAS_ANSI_STRING_STREAM)
   message(CHECK_START "Check for sstream")
-  try_compile(CMAKE_HAS_ANSI_STRING_STREAM  ${CMAKE_BINARY_DIR}
-    ${CMAKE_ROOT}/Modules/TestForSSTREAM.cxx
+  try_compile(CMAKE_HAS_ANSI_STRING_STREAM
+    SOURCES ${CMAKE_ROOT}/Modules/TestForSSTREAM.cxx
     OUTPUT_VARIABLE OUTPUT)
   if (CMAKE_HAS_ANSI_STRING_STREAM)
     message(CHECK_PASS "found")
index d101c83..cd9c782 100644 (file)
@@ -16,8 +16,8 @@ check if the compiler supports std:: on stl classes
 
 if(NOT DEFINED CMAKE_STD_NAMESPACE)
   message(CHECK_START "Check for STD namespace")
-  try_compile(CMAKE_STD_NAMESPACE  ${CMAKE_BINARY_DIR}
-    ${CMAKE_ROOT}/Modules/TestForSTDNamespace.cxx
+  try_compile(CMAKE_STD_NAMESPACE
+    SOURCES ${CMAKE_ROOT}/Modules/TestForSTDNamespace.cxx
     OUTPUT_VARIABLE OUTPUT)
   if (CMAKE_STD_NAMESPACE)
     message(CHECK_PASS "found")
index ef63ac3..1511bfd 100644 (file)
@@ -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 <http://openjdk.java.net/jeps/313>`_.
+    due to the `suppression of javah tool <https://openjdk.java.net/jeps/313>`_.
     The :ref:`add_jar(GENERATE_NATIVE_HEADERS) <add_jar>` command should be
     used instead.
 
index 35b1704..fd6596b 100644 (file)
@@ -377,6 +377,7 @@ set(SWIG_EXTRA_LIBRARIES "")
 set(SWIG_PYTHON_EXTRA_FILE_EXTENSIONS ".py")
 set(SWIG_JAVA_EXTRA_FILE_EXTENSIONS ".java" "JNI.java")
 set(SWIG_CSHARP_EXTRA_FILE_EXTENSIONS ".cs" "PINVOKE.cs")
+set(SWIG_PERL_EXTRA_FILE_EXTENSIONS ".pm")
 
 set(SWIG_MANAGE_SUPPORT_FILES_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/UseSWIG/ManageSupportFiles.cmake")
 
@@ -989,6 +990,9 @@ function(SWIG_ADD_LIBRARY name)
       endif()
       set_target_properties (${target_name} PROPERTIES PREFIX "")
     endif()
+    if (APPLE)
+      set_target_properties (${target_name} PROPERTIES SUFFIX ".dylib")
+    endif ()
   else()
     # assume empty prefix because we expect the module to be dynamically loaded
     set_target_properties (${target_name} PROPERTIES PREFIX "")
index 95b07cb..c268a92 100644 (file)
@@ -4,7 +4,7 @@
 # To ensure maximum portability across various compilers and platforms
 # deactivate any compiler extensions.  Skip this for QNX, where additional
 # work is needed to build without compiler extensions.
-if (NOT CMAKE_SYSTEM_NAME STREQUAL "QNX")
+if(NOT CMAKE_SYSTEM_NAME STREQUAL "QNX")
   set(CMAKE_C_EXTENSIONS FALSE)
   set(CMAKE_CXX_EXTENSIONS FALSE)
 endif()
@@ -31,70 +31,48 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
   set(CMake_USE_XCOFF_PARSER 1)
 endif()
 
+# Watcom support
+if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+  set(CMAKE_USE_WMAKE 1)
+endif()
+
+set(CMake_STAT_HAS_ST_MTIM ${KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED})
+set(CMake_STAT_HAS_ST_MTIMESPEC ${KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED})
+
 set(EXECUTABLE_OUTPUT_PATH ${CMake_BIN_DIR})
 
 if(WIN32)
   # ensure Unicode friendly APIs are used on Windows
-  add_definitions(-DUNICODE -D_UNICODE)
+  add_compile_definitions(UNICODE _UNICODE)
 
   # minimize windows.h content
-  add_definitions(-DWIN32_LEAN_AND_MEAN)
+  add_compile_definitions(WIN32_LEAN_AND_MEAN)
 endif()
 
 # configure the .dox.in file
 if(CMake_BUILD_DEVELOPER_REFERENCE)
-  configure_file(
-    "${CMake_SOURCE_DIR}/Source/dir.dox.in"
-    "${CMake_BINARY_DIR}/Source/dir.dox"
-    @ONLY
-    )
+  configure_file(dir.dox.in dir.dox @ONLY)
 endif()
 
 # configure the .h file
-configure_file(
-  "${CMake_SOURCE_DIR}/Source/cmConfigure.cmake.h.in"
-  "${CMake_BINARY_DIR}/Source/cmConfigure.h"
-  )
-configure_file(
-  "${CMake_SOURCE_DIR}/Source/cmVersionConfig.h.in"
-  "${CMake_BINARY_DIR}/Source/cmVersionConfig.h"
-  )
-configure_file(
-  "${CMake_SOURCE_DIR}/Source/CPack/cmCPackConfigure.h.in"
-  "${CMake_BINARY_DIR}/Source/CPack/cmCPackConfigure.h"
-  )
+configure_file(cmConfigure.cmake.h.in cmConfigure.h)
+configure_file(cmVersionConfig.h.in cmVersionConfig.h)
 
 # Tell CMake executable in the build tree where to find the source tree.
 configure_file(
-  "${CMake_SOURCE_DIR}/Source/CMakeSourceDir.txt.in"
-  "${CMake_BINARY_DIR}/CMakeFiles/CMakeSourceDir.txt" @ONLY
-  )
-
-# add the include path to find the .h
-include_directories(
-  "${CMake_BINARY_DIR}/Source"
-  "${CMake_SOURCE_DIR}/Source"
-  "${CMake_SOURCE_DIR}/Source/LexerParser"
-  ${CMAKE_ZLIB_INCLUDES}
-  ${CMAKE_EXPAT_INCLUDES}
-  ${CMAKE_TAR_INCLUDES}
-  ${CMake_HAIKU_INCLUDE_DIRS}
+  CMakeSourceDir.txt.in
+  "${CMake_BINARY_DIR}/CMakeFiles/CMakeSourceDir.txt"
+  @ONLY
   )
 
-# Check if we can build the Mach-O parser.
-if(CMake_USE_MACH_PARSER)
-  set(MACH_SRCS cmMachO.h cmMachO.cxx)
-endif()
-
-# Check if we can build the XCOFF parser.
-if(CMake_USE_XCOFF_PARSER)
-  set(XCOFF_SRCS cmXCOFF.h cmXCOFF.cxx)
-endif()
+# Add a dummy library and add sources later depends on condition
+add_library(ManifestLib INTERFACE)
 
 #
-# Sources for CMakeLib
+# create a library used by the command line and the GUI
 #
-set(SRCS
+add_library(
+  CMakeLib
   # Lexers/Parsers
   LexerParser/cmCommandArgumentLexer.cxx
   LexerParser/cmCommandArgumentLexer.h
@@ -168,7 +146,9 @@ set(SRCS
   cmCMakePresetsGraphReadJSON.cxx
   cmCMakePresetsGraphReadJSONBuildPresets.cxx
   cmCMakePresetsGraphReadJSONConfigurePresets.cxx
+  cmCMakePresetsGraphReadJSONPackagePresets.cxx
   cmCMakePresetsGraphReadJSONTestPresets.cxx
+  cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommonTargetGenerator.cxx
   cmCommonTargetGenerator.h
@@ -197,6 +177,8 @@ set(SRCS
   cmCustomCommandLines.cxx
   cmCustomCommandLines.h
   cmCustomCommandTypes.h
+  cmCxxModuleMapper.cxx
+  cmCxxModuleMapper.h
   cmDefinitions.cxx
   cmDefinitions.h
   cmDependencyProvider.h
@@ -367,7 +349,6 @@ set(SRCS
   cmRulePlaceholderExpander.h
   cmLocalUnixMakefileGenerator3.cxx
   cmLocale.h
-  ${MACH_SRCS}
   cmMakefile.cxx
   cmMakefile.h
   cmMakefileTargetGenerator.cxx
@@ -467,7 +448,6 @@ set(SRCS
   cmWorkerPool.h
   cmWorkingDirectory.cxx
   cmWorkingDirectory.h
-  ${XCOFF_SRCS}
   cmXMLParser.cxx
   cmXMLParser.h
   cmXMLSafe.cxx
@@ -543,6 +523,8 @@ set(SRCS
   cmExecuteProcessCommand.h
   cmExpandedCommandArgument.cxx
   cmExpandedCommandArgument.h
+  cmExperimental.cxx
+  cmExperimental.h
   cmExportCommand.cxx
   cmExportCommand.h
   cmExportLibraryDependenciesCommand.cxx
@@ -567,6 +549,8 @@ set(SRCS
   cmFindProgramCommand.h
   cmForEachCommand.cxx
   cmForEachCommand.h
+  cmBlockCommand.cxx
+  cmBlockCommand.h
   cmFunctionBlocker.cxx
   cmFunctionBlocker.h
   cmFunctionCommand.cxx
@@ -603,6 +587,8 @@ set(SRCS
   cmInstallCommand.h
   cmInstallCommandArguments.cxx
   cmInstallCommandArguments.h
+  cmInstallCxxModuleBmiGenerator.cxx
+  cmInstallCxxModuleBmiGenerator.h
   cmInstallFilesCommand.cxx
   cmInstallFilesCommand.h
   cmInstallProgramsCommand.cxx
@@ -721,6 +707,23 @@ set(SRCS
   cmWhileCommand.h
   cmWriteFileCommand.cxx
   cmWriteFileCommand.h
+  # Ninja support
+  cmScanDepFormat.cxx
+  cmGlobalNinjaGenerator.cxx
+  cmGlobalNinjaGenerator.h
+  cmNinjaTypes.h
+  cmLocalNinjaGenerator.cxx
+  cmLocalNinjaGenerator.h
+  cmNinjaTargetGenerator.cxx
+  cmNinjaTargetGenerator.h
+  cmNinjaNormalTargetGenerator.cxx
+  cmNinjaNormalTargetGenerator.h
+  cmNinjaUtilityTargetGenerator.cxx
+  cmNinjaUtilityTargetGenerator.h
+  cmNinjaLinkLineComputer.cxx
+  cmNinjaLinkLineComputer.h
+  cmNinjaLinkLineDeviceComputer.cxx
+  cmNinjaLinkLineDeviceComputer.h
 
   cm_get_date.h
   cm_get_date.c
@@ -734,102 +737,152 @@ set(SRCS
 
   bindexplib.cxx
   )
+target_include_directories(
+  CMakeLib
+  PUBLIC
+    # add the include path to find the .h
+    "${CMAKE_CURRENT_BINARY_DIR}"
+    "${CMAKE_CURRENT_SOURCE_DIR}"
+    "${CMAKE_CURRENT_SOURCE_DIR}/LexerParser"
+    ${CMake_HAIKU_INCLUDE_DIRS}
+  )
+target_link_libraries(
+  CMakeLib
+  PUBLIC
+    cmstd
+    cmsys
+    CURL::libcurl
+    EXPAT::EXPAT
+    JsonCpp::JsonCpp
+    $<TARGET_NAME_IF_EXISTS:kwiml::kwiml>
+    LibArchive::LibArchive
+    LibRHash::LibRHash
+    LibUV::LibUV
+    Threads::Threads
+    ZLIB::ZLIB
+  )
 
-SET_PROPERTY(SOURCE cmProcessOutput.cxx cmWindowsRegistry.cxx APPEND PROPERTY COMPILE_DEFINITIONS
-  KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
+# Check if we can build the Mach-O parser.
+if(CMake_USE_MACH_PARSER)
+  target_sources(
+    CMakeLib
+    PRIVATE
+      cmMachO.h
+      cmMachO.cxx
+    )
+endif()
+
+# Check if we can build the XCOFF parser.
+if(CMake_USE_XCOFF_PARSER)
+  target_sources(
+    CMakeLib
+    PRIVATE
+      cmXCOFF.h
+      cmXCOFF.cxx
+    )
+endif()
 
 # Xcode only works on Apple
 if(APPLE)
-  set(SRCS ${SRCS}
-    cmXCodeObject.cxx
-    cmXCode21Object.cxx
-    cmXCodeScheme.cxx
-    cmGlobalXCodeGenerator.cxx
-    cmGlobalXCodeGenerator.h
-    cmLocalXCodeGenerator.cxx
-    cmLocalXCodeGenerator.h)
+  target_sources(
+    CMakeLib
+    PRIVATE
+      cmXCodeObject.cxx
+      cmXCode21Object.cxx
+      cmXCodeScheme.cxx
+      cmGlobalXCodeGenerator.cxx
+      cmGlobalXCodeGenerator.h
+      cmLocalXCodeGenerator.cxx
+      cmLocalXCodeGenerator.h
+    )
 endif()
 
-
-if (WIN32)
-  set(SRCS ${SRCS}
-    cmCallVisualStudioMacro.cxx
-    cmCallVisualStudioMacro.h
+if(WIN32)
+  target_sources(
+    CMakeLib
+    PRIVATE
+      cmCallVisualStudioMacro.cxx
+      cmCallVisualStudioMacro.h
     )
 
   if(NOT UNIX)
-    set(SRCS ${SRCS}
-      cmGlobalBorlandMakefileGenerator.cxx
-      cmGlobalBorlandMakefileGenerator.h
-      cmGlobalMSYSMakefileGenerator.cxx
-      cmGlobalMinGWMakefileGenerator.cxx
-      cmGlobalNMakeMakefileGenerator.cxx
-      cmGlobalNMakeMakefileGenerator.h
-      cmGlobalJOMMakefileGenerator.cxx
-      cmGlobalJOMMakefileGenerator.h
-      cmGlobalVisualStudio71Generator.cxx
-      cmGlobalVisualStudio71Generator.h
-      cmGlobalVisualStudio7Generator.cxx
-      cmGlobalVisualStudio7Generator.h
-      cmGlobalVisualStudio8Generator.cxx
-      cmGlobalVisualStudio8Generator.h
-      cmGlobalVisualStudio9Generator.cxx
-      cmGlobalVisualStudio9Generator.h
-      cmVisualStudioGeneratorOptions.h
-      cmVisualStudioGeneratorOptions.cxx
-      cmVsProjectType.h
-      cmVisualStudio10TargetGenerator.h
-      cmVisualStudio10TargetGenerator.cxx
-      cmLocalVisualStudio10Generator.cxx
-      cmLocalVisualStudio10Generator.h
-      cmGlobalVisualStudio10Generator.h
-      cmGlobalVisualStudio10Generator.cxx
-      cmGlobalVisualStudio11Generator.h
-      cmGlobalVisualStudio11Generator.cxx
-      cmGlobalVisualStudio12Generator.h
-      cmGlobalVisualStudio12Generator.cxx
-      cmGlobalVisualStudio14Generator.h
-      cmGlobalVisualStudio14Generator.cxx
-      cmGlobalVisualStudioGenerator.cxx
-      cmGlobalVisualStudioGenerator.h
-      cmGlobalVisualStudioVersionedGenerator.h
-      cmGlobalVisualStudioVersionedGenerator.cxx
-      cmIDEFlagTable.h
-      cmIDEOptions.cxx
-      cmIDEOptions.h
-      cmLocalVisualStudio7Generator.cxx
-      cmLocalVisualStudio7Generator.h
-      cmLocalVisualStudioGenerator.cxx
-      cmLocalVisualStudioGenerator.h
-      cmVisualStudioSlnData.h
-      cmVisualStudioSlnData.cxx
-      cmVisualStudioSlnParser.h
-      cmVisualStudioSlnParser.cxx
-      cmVisualStudioWCEPlatformParser.h
-      cmVisualStudioWCEPlatformParser.cxx
-      cmVSSetupHelper.cxx
-      cmVSSetupHelper.h
+    target_sources(
+      CMakeLib
+      PRIVATE
+        cmGlobalBorlandMakefileGenerator.cxx
+        cmGlobalBorlandMakefileGenerator.h
+        cmGlobalMSYSMakefileGenerator.cxx
+        cmGlobalMinGWMakefileGenerator.cxx
+        cmGlobalNMakeMakefileGenerator.cxx
+        cmGlobalNMakeMakefileGenerator.h
+        cmGlobalJOMMakefileGenerator.cxx
+        cmGlobalJOMMakefileGenerator.h
+        cmGlobalVisualStudio71Generator.cxx
+        cmGlobalVisualStudio71Generator.h
+        cmGlobalVisualStudio7Generator.cxx
+        cmGlobalVisualStudio7Generator.h
+        cmGlobalVisualStudio8Generator.cxx
+        cmGlobalVisualStudio8Generator.h
+        cmGlobalVisualStudio9Generator.cxx
+        cmGlobalVisualStudio9Generator.h
+        cmVisualStudioGeneratorOptions.h
+        cmVisualStudioGeneratorOptions.cxx
+        cmVsProjectType.h
+        cmVisualStudio10TargetGenerator.h
+        cmVisualStudio10TargetGenerator.cxx
+        cmLocalVisualStudio10Generator.cxx
+        cmLocalVisualStudio10Generator.h
+        cmGlobalVisualStudio10Generator.h
+        cmGlobalVisualStudio10Generator.cxx
+        cmGlobalVisualStudio11Generator.h
+        cmGlobalVisualStudio11Generator.cxx
+        cmGlobalVisualStudio12Generator.h
+        cmGlobalVisualStudio12Generator.cxx
+        cmGlobalVisualStudio14Generator.h
+        cmGlobalVisualStudio14Generator.cxx
+        cmGlobalVisualStudioGenerator.cxx
+        cmGlobalVisualStudioGenerator.h
+        cmGlobalVisualStudioVersionedGenerator.h
+        cmGlobalVisualStudioVersionedGenerator.cxx
+        cmIDEFlagTable.h
+        cmIDEOptions.cxx
+        cmIDEOptions.h
+        cmLocalVisualStudio7Generator.cxx
+        cmLocalVisualStudio7Generator.h
+        cmLocalVisualStudioGenerator.cxx
+        cmLocalVisualStudioGenerator.h
+        cmVisualStudioSlnData.h
+        cmVisualStudioSlnData.cxx
+        cmVisualStudioSlnParser.h
+        cmVisualStudioSlnParser.cxx
+        cmVisualStudioWCEPlatformParser.h
+        cmVisualStudioWCEPlatformParser.cxx
+        cmVSSetupHelper.cxx
+        cmVSSetupHelper.h
       )
 
     # Add a manifest file to executables on Windows to allow for
     # GetVersion to work properly on Windows 8 and above.
-    set(MANIFEST_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake.version.manifest)
+    target_sources(ManifestLib INTERFACE cmake.version.manifest)
   endif()
-endif ()
+endif()
 
 # Watcom support
-if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
-  set_property(SOURCE cmake.cxx APPEND PROPERTY COMPILE_DEFINITIONS CMAKE_USE_WMAKE)
-  list(APPEND SRCS
-    cmGlobalWatcomWMakeGenerator.cxx
-    cmGlobalWatcomWMakeGenerator.h
+if(CMAKE_USE_WMAKE)
+  target_sources(
+    CMakeLib
+    PRIVATE
+      cmGlobalWatcomWMakeGenerator.cxx
+      cmGlobalWatcomWMakeGenerator.h
     )
 endif()
 
 # GHS support
 # Works only for windows and linux
 if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  set(SRCS ${SRCS}
+  target_sources(
+    CMakeLib
+    PRIVATE
       cmGlobalGhsMultiGenerator.cxx
       cmGlobalGhsMultiGenerator.h
       cmLocalGhsMultiGenerator.cxx
@@ -841,104 +894,45 @@ if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
     )
 endif()
 
-
-# Ninja support
-set(SRCS ${SRCS}
-  cmScanDepFormat.cxx
-  cmGlobalNinjaGenerator.cxx
-  cmGlobalNinjaGenerator.h
-  cmNinjaTypes.h
-  cmLocalNinjaGenerator.cxx
-  cmLocalNinjaGenerator.h
-  cmNinjaTargetGenerator.cxx
-  cmNinjaTargetGenerator.h
-  cmNinjaNormalTargetGenerator.cxx
-  cmNinjaNormalTargetGenerator.h
-  cmNinjaUtilityTargetGenerator.cxx
-  cmNinjaUtilityTargetGenerator.h
-  cmNinjaLinkLineComputer.cxx
-  cmNinjaLinkLineComputer.h
-  cmNinjaLinkLineDeviceComputer.cxx
-  cmNinjaLinkLineDeviceComputer.h
-  )
-
 # Temporary variable for tools targets
 set(_tools)
 
 if(WIN32 AND NOT CYGWIN)
   set_source_files_properties(cmcldeps.cxx PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0501)
-  add_executable(cmcldeps cmcldeps.cxx ${MANIFEST_FILE})
+  add_executable(cmcldeps cmcldeps.cxx)
+  target_link_libraries(cmcldeps PRIVATE CMakeLib ManifestLib)
   list(APPEND _tools cmcldeps)
-  target_link_libraries(cmcldeps CMakeLib)
 endif()
 
-foreach(v CURL_CA_BUNDLE CURL_CA_PATH)
-  if(${v})
-    set_property(SOURCE cmCurl.cxx APPEND PROPERTY COMPILE_DEFINITIONS ${v}="${${v}}")
-  endif()
-endforeach()
-
-foreach(check
-    STAT_HAS_ST_MTIM
-    STAT_HAS_ST_MTIMESPEC
-    )
-  if(KWSYS_CXX_${check}_COMPILED) # abuse KWSys check cache entry
-    set(CMake_${check} 1)
-  else()
-    set(CMake_${check} 0)
-  endif()
-  set_property(SOURCE cmFileTime.cxx APPEND PROPERTY
-    COMPILE_DEFINITIONS CMake_${check}=${CMake_${check}})
-endforeach()
-
-# create a library used by the command line and the GUI
-add_library(CMakeLib ${SRCS})
-target_link_libraries(CMakeLib cmsys
-  ${CMAKE_STD_LIBRARY}
-  ${CMAKE_EXPAT_LIBRARIES} ${CMAKE_ZLIB_LIBRARIES}
-  ${CMAKE_TAR_LIBRARIES}
-  ${CMAKE_CURL_LIBRARIES}
-  ${CMAKE_JSONCPP_LIBRARIES}
-  ${CMAKE_LIBUV_LIBRARIES}
-  ${CMAKE_LIBRHASH_LIBRARIES}
-  ${CMake_KWIML_LIBRARIES}
-  ${CMAKE_THREAD_LIBS_INIT}
-  )
-
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "sparc")
   # the atomic instructions are implemented using libatomic on some platforms,
   # so linking to that may be required
   check_library_exists(atomic __atomic_fetch_add_4 "" LIBATOMIC_NEEDED)
   if(LIBATOMIC_NEEDED)
-    target_link_libraries(CMakeLib atomic)
+    target_link_libraries(CMakeLib PUBLIC atomic)
   endif()
 endif()
 
 # On Apple we need CoreFoundation and CoreServices
 if(APPLE)
-  target_link_libraries(CMakeLib "-framework CoreFoundation")
-  target_link_libraries(CMakeLib "-framework CoreServices")
+  target_link_libraries(CMakeLib PUBLIC "-framework CoreFoundation")
+  target_link_libraries(CMakeLib PUBLIC "-framework CoreServices")
 endif()
 
 if(WIN32 AND NOT UNIX)
   # We need the rpcrt4 library on Windows.
   # We need the crypt32 library on Windows for crypto/cert APIs.
-  target_link_libraries(CMakeLib rpcrt4 crypt32)
+  target_link_libraries(CMakeLib PUBLIC rpcrt4 crypt32)
 endif()
 
 target_compile_definitions(CMakeLib PUBLIC ${CLANG_TIDY_DEFINITIONS})
 
 #
-# CTestLib
-#
-include_directories(
-  "${CMake_SOURCE_DIR}/Source/CTest"
-  ${CMAKE_CURL_INCLUDES}
-  )
-#
-# Sources for CTestLib
+# Build CTestLib
 #
-set(CTEST_SRCS cmCTest.cxx
+add_library(
+  CTestLib
+  cmCTest.cxx
   CTest/cmProcess.cxx
   CTest/cmCTestBinPacker.cxx
   CTest/cmCTestBuildAndTestHandler.cxx
@@ -1005,21 +999,18 @@ set(CTEST_SRCS cmCTest.cxx
   LexerParser/cmCTestResourceGroupsLexer.h
   LexerParser/cmCTestResourceGroupsLexer.in.l
   )
-
-# Build CTestLib
-add_library(CTestLib ${CTEST_SRCS})
-target_link_libraries(CTestLib CMakeLib ${CMAKE_CURL_LIBRARIES})
-
-#
-# CPack
-#
-include_directories(
-  "${CMake_SOURCE_DIR}/Source/CPack"
+target_include_directories(
+  CTestLib
+  PUBLIC
+    "${CMAKE_CURRENT_SOURCE_DIR}/CTest"
   )
+target_link_libraries(CTestLib PUBLIC CMakeLib)
+
 #
-# Sources for CPack
+# Build CPackLib
 #
-set(CPACK_SRCS
+add_library(
+  CPackLib
   CPack/cmCPackArchiveGenerator.cxx
   CPack/cmCPackComponentGroup.cxx
   CPack/cmCPackDebGenerator.cxx
@@ -1030,9 +1021,7 @@ set(CPACK_SRCS
   CPack/cmCPackNSISGenerator.cxx
   CPack/cmCPackNuGetGenerator.cxx
   CPack/cmCPackSTGZGenerator.cxx
-  )
-# CPack IFW generator
-set(CPACK_SRCS ${CPACK_SRCS}
+  # CPack IFW generator
   CPack/IFW/cmCPackIFWCommon.cxx
   CPack/IFW/cmCPackIFWCommon.h
   CPack/IFW/cmCPackIFWGenerator.cxx
@@ -1044,19 +1033,20 @@ set(CPACK_SRCS ${CPACK_SRCS}
   CPack/IFW/cmCPackIFWRepository.cxx
   CPack/IFW/cmCPackIFWRepository.h
   )
-
-if(CYGWIN)
-  set(CPACK_SRCS ${CPACK_SRCS}
-    CPack/cmCPackCygwinBinaryGenerator.cxx
-    CPack/cmCPackCygwinSourceGenerator.cxx
-    )
-endif()
+target_include_directories(
+  CPackLib
+  PUBLIC
+    "${CMAKE_CURRENT_SOURCE_DIR}/CPack"
+    "${CMAKE_CURRENT_BINARY_DIR}/CPack"
+  )
+target_link_libraries(CPackLib PUBLIC CMakeLib)
 
 option(CPACK_ENABLE_FREEBSD_PKG "Add FreeBSD pkg(8) generator to CPack." OFF)
-
 if(UNIX)
-  set(CPACK_SRCS ${CPACK_SRCS}
-    CPack/cmCPackRPMGenerator.cxx
+  target_sources(
+    CPackLib
+    PRIVATE
+      CPack/cmCPackRPMGenerator.cxx
     )
 
   # Optionally, try to use pkg(8)
@@ -1072,13 +1062,14 @@ if(UNIX)
         pkg
         DOC "FreeBSD pkg(8) library")
       if(FREEBSD_PKG_LIBRARIES)
-        set(CPACK_SRCS ${CPACK_SRCS}
-          CPack/cmCPackFreeBSDGenerator.cxx
-          )
+        set(ENABLE_BUILD_FREEBSD_PKG 1)
+        target_sources(CPackLib PRIVATE CPack/cmCPackFreeBSDGenerator.cxx)
+        target_include_directories(CPackLib PUBLIC ${FREEBSD_PKG_INCLUDE_DIRS})
+        target_link_libraries(CPackLib PUBLIC ${FREEBSD_PKG_LIBRARIES})
       endif()
     endif()
 
-    if (NOT FREEBSD_PKG_INCLUDE_DIRS OR NOT FREEBSD_PKG_LIBRARIES)
+    if(NOT FREEBSD_PKG_INCLUDE_DIRS OR NOT FREEBSD_PKG_LIBRARIES)
       message(FATAL_ERROR "CPack needs libpkg(3) to produce FreeBSD packages natively.")
     endif()
   else()
@@ -1088,47 +1079,57 @@ if(UNIX)
 endif()
 
 if(CYGWIN)
+  target_sources(
+    CPackLib
+    PRIVATE
+      CPack/cmCPackCygwinBinaryGenerator.cxx
+      CPack/cmCPackCygwinSourceGenerator.cxx
+    )
   find_package(LibUUID)
 endif()
-if(WIN32 OR (CYGWIN AND LibUUID_FOUND))
-  set(CPACK_SRCS ${CPACK_SRCS}
-    CPack/WiX/cmCMakeToWixPath.cxx
-    CPack/WiX/cmCMakeToWixPath.h
-    CPack/WiX/cmCPackWIXGenerator.cxx
-    CPack/WiX/cmCPackWIXGenerator.h
-    CPack/WiX/cmWIXAccessControlList.cxx
-    CPack/WiX/cmWIXAccessControlList.h
-    CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
-    CPack/WiX/cmWIXDirectoriesSourceWriter.h
-    CPack/WiX/cmWIXFeaturesSourceWriter.cxx
-    CPack/WiX/cmWIXFeaturesSourceWriter.h
-    CPack/WiX/cmWIXFilesSourceWriter.cxx
-    CPack/WiX/cmWIXFilesSourceWriter.h
-    CPack/WiX/cmWIXPatch.cxx
-    CPack/WiX/cmWIXPatch.h
-    CPack/WiX/cmWIXPatchParser.cxx
-    CPack/WiX/cmWIXPatchParser.h
-    CPack/WiX/cmWIXRichTextFormatWriter.cxx
-    CPack/WiX/cmWIXRichTextFormatWriter.h
-    CPack/WiX/cmWIXShortcut.cxx
-    CPack/WiX/cmWIXShortcut.h
-    CPack/WiX/cmWIXSourceWriter.cxx
-    CPack/WiX/cmWIXSourceWriter.h
+
+if(WIN32 OR (CYGWIN AND TARGET LibUUID::LibUUID))
+  set(ENABLE_BUILD_WIX_GENERATOR 1)
+  target_sources(
+    CPackLib
+    PRIVATE
+      CPack/WiX/cmCMakeToWixPath.cxx
+      CPack/WiX/cmCMakeToWixPath.h
+      CPack/WiX/cmCPackWIXGenerator.cxx
+      CPack/WiX/cmCPackWIXGenerator.h
+      CPack/WiX/cmWIXAccessControlList.cxx
+      CPack/WiX/cmWIXAccessControlList.h
+      CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
+      CPack/WiX/cmWIXDirectoriesSourceWriter.h
+      CPack/WiX/cmWIXFeaturesSourceWriter.cxx
+      CPack/WiX/cmWIXFeaturesSourceWriter.h
+      CPack/WiX/cmWIXFilesSourceWriter.cxx
+      CPack/WiX/cmWIXFilesSourceWriter.h
+      CPack/WiX/cmWIXPatch.cxx
+      CPack/WiX/cmWIXPatch.h
+      CPack/WiX/cmWIXPatchParser.cxx
+      CPack/WiX/cmWIXPatchParser.h
+      CPack/WiX/cmWIXRichTextFormatWriter.cxx
+      CPack/WiX/cmWIXRichTextFormatWriter.h
+      CPack/WiX/cmWIXShortcut.cxx
+      CPack/WiX/cmWIXShortcut.h
+      CPack/WiX/cmWIXSourceWriter.cxx
+      CPack/WiX/cmWIXSourceWriter.h
     )
+  target_link_libraries(CPackLib PUBLIC $<TARGET_NAME_IF_EXISTS:LibUUID::LibUUID>)
 endif()
 
 if(APPLE)
-  set(CPACK_SRCS ${CPACK_SRCS}
-    CPack/cmCPackBundleGenerator.cxx
-    CPack/cmCPackDragNDropGenerator.cxx
-    CPack/cmCPackPKGGenerator.cxx
-    CPack/cmCPackProductBuildGenerator.cxx
+  target_sources(
+    CPackLib
+    PRIVATE
+      CPack/cmCPackBundleGenerator.cxx
+      CPack/cmCPackDragNDropGenerator.cxx
+      CPack/cmCPackPKGGenerator.cxx
+      CPack/cmCPackProductBuildGenerator.cxx
     )
 endif()
 
-# Build CPackLib
-add_library(CPackLib ${CPACK_SRCS})
-target_link_libraries(CPackLib CMakeLib)
 if(APPLE)
   # Some compilers produce errors in the CoreServices framework headers.
   # Ideally such errors should be fixed by either the compiler vendor
@@ -1136,8 +1137,7 @@ if(APPLE)
   # If it does not work, build with reduced functionality and warn.
   check_include_file("CoreServices/CoreServices.h" HAVE_CoreServices)
   if(HAVE_CoreServices)
-    set_property(SOURCE CPack/cmCPackDragNDropGenerator.cxx PROPERTY COMPILE_DEFINITIONS HAVE_CoreServices)
-    target_link_libraries(CPackLib "-framework CoreServices")
+    target_link_libraries(CPackLib PUBLIC "-framework CoreServices")
   else()
     message(WARNING "This compiler does not appear to support\n"
       "  #include <CoreServices/CoreServices.h>\n"
@@ -1145,31 +1145,25 @@ if(APPLE)
       "See CMakeFiles/CMakeError.log for details of the failure.")
   endif()
 endif()
-if(CYGWIN AND LibUUID_FOUND)
-  target_link_libraries(CPackLib ${LibUUID_LIBRARIES})
-  include_directories(CPackLib ${LibUUID_INCLUDE_DIRS})
-  set_property(SOURCE CPack/cmCPackGeneratorFactory.cxx PROPERTY COMPILE_DEFINITIONS HAVE_LIBUUID)
-endif()
-if(CPACK_ENABLE_FREEBSD_PKG AND FREEBSD_PKG_INCLUDE_DIRS AND FREEBSD_PKG_LIBRARIES)
-  target_link_libraries(CPackLib ${FREEBSD_PKG_LIBRARIES})
-  include_directories(${FREEBSD_PKG_INCLUDE_DIRS})
-  add_definitions(-DHAVE_FREEBSD_PKG)
-endif()
+
+# Render config header file for CPackLib
+configure_file(CPack/cmCPackConfigure.h.in CPack/cmCPackConfigure.h)
+
 
 # Build CMake executable
-add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h ${MANIFEST_FILE})
+add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h)
+target_link_libraries(cmake PRIVATE CMakeLib ManifestLib)
 list(APPEND _tools cmake)
-target_link_libraries(cmake CMakeLib)
 
 # Build CTest executable
-add_executable(ctest ctest.cxx ${MANIFEST_FILE})
+add_executable(ctest ctest.cxx)
+target_link_libraries(ctest PRIVATE CTestLib ManifestLib)
 list(APPEND _tools ctest)
-target_link_libraries(ctest CTestLib)
 
 # Build CPack executable
-add_executable(cpack CPack/cpack.cxx ${MANIFEST_FILE})
+add_executable(cpack CPack/cpack.cxx)
+target_link_libraries(cpack PRIVATE CPackLib ManifestLib)
 list(APPEND _tools cpack)
-target_link_libraries(cpack CPackLib)
 
 # Curses GUI
 if(BUILD_CursesDialog)
@@ -1182,8 +1176,8 @@ if(BUILD_QtDialog)
   add_subdirectory(QtDialog)
 endif()
 
-include (${CMake_BINARY_DIR}/Source/LocalUserOptions.cmake OPTIONAL)
-include (${CMake_SOURCE_DIR}/Source/LocalUserOptions.cmake OPTIONAL)
+include(${CMAKE_CURRENT_BINARY_DIR}/LocalUserOptions.cmake OPTIONAL)
+include(${CMAKE_CURRENT_SOURCE_DIR}/LocalUserOptions.cmake OPTIONAL)
 
 if(WIN32)
   # Compute the binary version that appears in the RC file. Version
@@ -1202,14 +1196,14 @@ if(WIN32)
   set(CMake_RCVERSION_STR ${CMake_VERSION})
 
   # Add Windows executable version information.
-  configure_file("CMakeVersion.rc.in" "CMakeVersion.rc" @ONLY)
+  configure_file(CMakeVersion.rc.in CMakeVersion.rc @ONLY)
 
   # We use a separate object library for this to work around a limitation of
   # MinGW's windres tool with spaces in the path to the include directories.
   add_library(CMakeVersion OBJECT "${CMAKE_CURRENT_BINARY_DIR}/CMakeVersion.rc")
   set_property(TARGET CMakeVersion PROPERTY INCLUDE_DIRECTORIES "")
-  foreach(_tool ${_tools})
-    target_sources(${_tool} PRIVATE $<TARGET_OBJECTS:CMakeVersion>)
+  foreach(_tool IN LISTS _tools)
+    target_link_libraries(${_tool} PRIVATE CMakeVersion)
   endforeach()
 endif()
 
@@ -1220,7 +1214,7 @@ endif()
 
 # Install tools
 
-foreach(_tool ${_tools})
+foreach(_tool IN LISTS _tools)
   CMake_OPTIONAL_COMPONENT(${_tool})
   install(TARGETS ${_tool} DESTINATION ${CMAKE_BIN_DIR} ${COMPONENT})
 endforeach()
index be3ae8f..e4575f4 100644 (file)
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
-set(CMake_VERSION_MINOR 24)
-set(CMake_VERSION_PATCH 3)
+set(CMake_VERSION_MINOR 25)
+set(CMake_VERSION_PATCH 0)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
@@ -9,7 +9,7 @@ set(CMake_VERSION_IS_DIRTY 0)
 set(CMake_VERSION
   "${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}.${CMake_VERSION_PATCH}")
 if(DEFINED CMake_VERSION_RC)
-  set(CMake_VERSION "${CMake_VERSION}-rc${CMake_VERSION_RC}")
+  string(APPEND CMake_VERSION "-rc${CMake_VERSION_RC}")
 endif()
 
 # Releases define a small patch level.
@@ -21,7 +21,7 @@ endif()
 
 if(NOT CMake_VERSION_NO_GIT)
   # If this source was exported by 'git archive', use its commit info.
-  set(git_info [==[c974557598 CMake 3.24.3]==])
+  set(git_info [==[13e46189c7 CMake 3.25.0]==])
 
   # Otherwise, try to identify the current development source version.
   if(NOT git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* "
@@ -53,7 +53,7 @@ if(NOT CMake_VERSION_NO_GIT)
 
     # If this is not the exact commit of a release, add dev info.
     if(NOT "${git_subject}" MATCHES "^[Cc][Mm]ake ${CMake_VERSION}$")
-      set(CMake_VERSION "${CMake_VERSION}-g${git_hash}")
+      string(APPEND CMake_VERSION "-g${git_hash}")
     endif()
 
     # If this is a work tree, check whether it is dirty.
@@ -68,7 +68,7 @@ if(NOT CMake_VERSION_NO_GIT)
     # No commit information.
     if(NOT CMake_VERSION_IS_RELEASE)
       # Generic development version.
-      set(CMake_VERSION "${CMake_VERSION}-git")
+      string(APPEND CMake_VERSION "-git")
     endif()
   endif()
 endif()
@@ -80,5 +80,5 @@ else()
   set(CMake_VERSION_SUFFIX "")
 endif()
 if(CMake_VERSION_IS_DIRTY)
-  set(CMake_VERSION ${CMake_VERSION}-dirty)
+  string(APPEND CMake_VERSION "-dirty")
 endif()
index 56e8463..894c24b 100644 (file)
@@ -94,6 +94,18 @@ std::string cmCPackArchiveGenerator::GetArchiveComponentFileName(
 int cmCPackArchiveGenerator::InitializeInternal()
 {
   this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1");
+  cmValue newExtensionValue = this->GetOption("CPACK_ARCHIVE_FILE_EXTENSION");
+  if (!newExtensionValue.IsEmpty()) {
+    std::string newExtension = *newExtensionValue;
+    if (!cmHasLiteralPrefix(newExtension, ".")) {
+      newExtension = cmStrCat('.', newExtension);
+    }
+    cmCPackLogger(cmCPackLog::LOG_DEBUG,
+                  "Using user-provided file extension "
+                    << newExtension << " instead of the default "
+                    << this->OutputExtension << std::endl);
+    this->OutputExtension = std::move(newExtension);
+  }
   return this->Superclass::InitializeInternal();
 }
 
index 8ac1661..2c1302d 100644 (file)
@@ -1,2 +1,7 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#cmakedefine01 ENABLE_BUILD_WIX_GENERATOR
+#cmakedefine01 ENABLE_BUILD_FREEBSD_PKG
+#cmakedefine01 HAVE_CoreServices
index 0f7acfb..0579066 100644 (file)
@@ -14,6 +14,7 @@
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmCPackConfigure.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmDuration.h"
@@ -23,7 +24,7 @@
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
-#ifdef HAVE_CoreServices
+#if HAVE_CoreServices
 // For the old LocaleStringToLangAndRegionCodes() function, to convert
 // to the old Script Manager RegionCode values needed for the 'LPic' data
 // structure used for generating multi-lingual SLAs.
@@ -590,7 +591,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
                            kCFStringEncodingMacRoman);
         LangCode lang = 0;
         RegionCode region = 0;
-#ifdef HAVE_CoreServices
+#if HAVE_CoreServices
         OSStatus err =
           LocaleStringToLangAndRegionCodes(iso_language_cstr, &lang, &region);
         if (err != noErr)
@@ -601,7 +602,7 @@ int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir,
                           << iso_language_cstr << std::endl);
           return 0;
         }
-#ifdef HAVE_CoreServices
+#if HAVE_CoreServices
         header_data.push_back(region);
         header_data.push_back(i);
         header_data.push_back(0);
index 607d797..162dfc7 100644 (file)
@@ -23,8 +23,6 @@
 
 // Suffix used to tell libpkg what compression to use
 static const char FreeBSDPackageCompression[] = "txz";
-// Resulting package file-suffix, for < 1.17 and >= 1.17 versions of libpkg
-static const char FreeBSDPackageSuffix_10[] = ".txz";
 static const char FreeBSDPackageSuffix_17[] = ".pkg";
 
 cmCPackFreeBSDGenerator::cmCPackFreeBSDGenerator()
@@ -83,6 +81,14 @@ public:
   {
     if (!isValid())
       return false;
+    // The API in the FreeBSD sources (the header has no documentation),
+    // is as follows:
+    //
+    // int pkg_create(struct pkg_create *pc, const char *metadata, const char
+    // *plist, bool hash)
+    //
+    // We let the plist be determined from what is installed, and all
+    // the rest comes from the manifest data.
     int r = pkg_create(d, manifest.c_str(), nullptr, false);
     return r == 0;
   }
@@ -402,7 +408,8 @@ int cmCPackFreeBSDGenerator::PackageFiles()
     return 0;
   }
 
-  std::string output_dir = cmSystemTools::CollapseFullPath("../", toplevel);
+  const std::string output_dir =
+    cmSystemTools::CollapseFullPath("../", toplevel);
   PkgCreate package(output_dir, toplevel, manifestname);
   if (package.isValid()) {
     if (!package.Create()) {
@@ -416,40 +423,33 @@ int cmCPackFreeBSDGenerator::PackageFiles()
     return 0;
   }
 
-  // Specifically looking for packages suffixed with the TAG, either extension
-  std::string broken_suffix_10 =
-    cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_10);
+  // Specifically looking for packages suffixed with the TAG
   std::string broken_suffix_17 =
     cmStrCat('-', var_lookup("CPACK_TOPLEVEL_TAG"), FreeBSDPackageSuffix_17);
   for (std::string& name : packageFileNames) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG, "Packagefile " << name << std::endl);
-    if (cmHasSuffix(name, broken_suffix_10)) {
-      name.replace(name.size() - broken_suffix_10.size(), std::string::npos,
-                   FreeBSDPackageSuffix_10);
-      break;
-    }
     if (cmHasSuffix(name, broken_suffix_17)) {
       name.replace(name.size() - broken_suffix_17.size(), std::string::npos,
                    FreeBSDPackageSuffix_17);
       break;
     }
   }
-  // If the name uses a *new* style name, which doesn't exist, but there
-  // is an *old* style name, then use that instead. This indicates we used
-  // an older libpkg, which still creates .txz instead of .pkg files.
-  for (std::string& name : packageFileNames) {
-    if (cmHasSuffix(name, FreeBSDPackageSuffix_17) &&
-        !cmSystemTools::FileExists(name)) {
-      const std::string badSuffix(FreeBSDPackageSuffix_17);
-      const std::string goodSuffix(FreeBSDPackageSuffix_10);
-      std::string repairedName(name);
-      repairedName.replace(repairedName.size() - badSuffix.size(),
-                           std::string::npos, goodSuffix);
-      if (cmSystemTools::FileExists(repairedName)) {
-        name = repairedName;
-        cmCPackLogger(cmCPackLog::LOG_DEBUG,
-                      "Repaired packagefile " << name << std::endl);
-      }
+
+  const std::string packageFileName =
+    var_lookup("CPACK_PACKAGE_FILE_NAME") + FreeBSDPackageSuffix_17;
+  if (packageFileNames.size() == 1 && !packageFileName.empty() &&
+      packageFileNames[0] != packageFileName) {
+    // Since libpkg always writes <name>-<version>.<suffix>,
+    // if there is a CPACK_PACKAGE_FILE_NAME set, we need to
+    // rename, and then re-set the name.
+    const std::string sourceFile = packageFileNames[0];
+    const std::string packageSubDirectory =
+      cmSystemTools::GetParentDirectory(sourceFile);
+    const std::string targetFileName =
+      packageSubDirectory + '/' + packageFileName;
+    if (cmSystemTools::RenameFile(sourceFile, targetFileName)) {
+      this->packageFileNames.clear();
+      this->packageFileNames.emplace_back(targetFileName);
     }
   }
 
index 725ea8a..efb94b9 100644 (file)
@@ -6,7 +6,7 @@
 #include <utility>
 
 #include "IFW/cmCPackIFWGenerator.h"
-#ifdef HAVE_FREEBSD_PKG
+#if ENABLE_BUILD_FREEBSD_PKG
 #  include "cmCPackFreeBSDGenerator.h"
 #endif
 #include "cmCPackArchiveGenerator.h"
@@ -34,7 +34,7 @@
 #  include "cmCPackRPMGenerator.h"
 #endif
 
-#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID))
+#if ENABLE_BUILD_WIX_GENERATOR
 #  include "WiX/cmCPackWIXGenerator.h"
 #endif
 
@@ -80,7 +80,7 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory()
                             cmCPackCygwinSourceGenerator::CreateGenerator);
   }
 #endif
-#if defined(_WIN32) || (defined(__CYGWIN__) && defined(HAVE_LIBUUID))
+#if ENABLE_BUILD_WIX_GENERATOR
   if (cmCPackWIXGenerator::CanGenerate()) {
     this->RegisterGenerator("WIX", "MSI file format via WiX tools",
                             cmCPackWIXGenerator::CreateGenerator);
@@ -119,7 +119,7 @@ cmCPackGeneratorFactory::cmCPackGeneratorFactory()
                             cmCPackRPMGenerator::CreateGenerator);
   }
 #endif
-#ifdef HAVE_FREEBSD_PKG
+#if ENABLE_BUILD_FREEBSD_PKG
   if (cmCPackFreeBSDGenerator::CanGenerate()) {
     this->RegisterGenerator("FREEBSD", "FreeBSD pkg(8) packages",
                             cmCPackFreeBSDGenerator::CreateGenerator);
index f3e25a6..52c1b5c 100644 (file)
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include "cmCPackConfigure.h" // IWYU pragma: keep
+
 class cmCPackGenerator;
 class cmCPackLog;
 
index 217f716..6ca5783 100644 (file)
@@ -242,6 +242,33 @@ int cmCPackNSISGenerator::PackageFiles()
     this->SetOptionIfNotSet("CPACK_NSIS_LICENSE_PAGE", licenceCode);
   }
 
+  std::string nsisPreArguments;
+  if (cmValue nsisArguments =
+        this->GetOption("CPACK_NSIS_EXECUTABLE_PRE_ARGUMENTS")) {
+    std::vector<std::string> expandedArguments;
+    cmExpandList(nsisArguments, expandedArguments);
+
+    for (auto& arg : expandedArguments) {
+      if (!cmHasPrefix(arg, NSIS_OPT)) {
+        nsisPreArguments = cmStrCat(nsisPreArguments, NSIS_OPT);
+      }
+      nsisPreArguments = cmStrCat(nsisPreArguments, arg, ' ');
+    }
+  }
+
+  std::string nsisPostArguments;
+  if (cmValue nsisArguments =
+        this->GetOption("CPACK_NSIS_EXECUTABLE_POST_ARGUMENTS")) {
+    std::vector<std::string> expandedArguments;
+    cmExpandList(nsisArguments, expandedArguments);
+    for (auto& arg : expandedArguments) {
+      if (!cmHasPrefix(arg, NSIS_OPT)) {
+        nsisPostArguments = cmStrCat(nsisPostArguments, NSIS_OPT);
+      }
+      nsisPostArguments = cmStrCat(nsisPostArguments, arg, ' ');
+    }
+  }
+
   // Setup all of the component sections
   if (this->Components.empty()) {
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
@@ -358,8 +385,11 @@ int cmCPackNSISGenerator::PackageFiles()
   this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions);
   this->ConfigureFile(nsisInFileName, nsisFileName);
   std::string nsisCmd =
-    cmStrCat('"', this->GetOption("CPACK_INSTALLER_PROGRAM"), "\" \"",
-             nsisFileName, '"');
+    cmStrCat('"', this->GetOption("CPACK_INSTALLER_PROGRAM"), "\" ",
+             nsisPreArguments, " \"", nsisFileName, '"');
+  if (!nsisPostArguments.empty()) {
+    nsisCmd = cmStrCat(nsisCmd, " ", nsisPostArguments);
+  }
   cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd << std::endl);
   std::string output;
   int retVal = 1;
index 1650368..f06946b 100644 (file)
@@ -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 <cstddef>
 #include <functional>
 #include <iostream>
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cmext/algorithm>
 
 #include "cmsys/Encoding.hxx"
 
+#include "cmCMakePresetsGraph.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackGeneratorFactory.h"
 #include "cmCPackLog.h"
@@ -50,7 +53,7 @@ const char* cmDocumentationOptions[][2] = {
   { "-C <Configuration>", "Specify the project configuration" },
   { "-D <var>=<value>", "Set a CPack variable." },
   { "--config <configFile>", "Specify the config file." },
-  { "--verbose,-V", "Enable verbose output" },
+  { "-V,--verbose", "Enable verbose output" },
   { "--trace", "Put underlying cmake scripts in trace mode." },
   { "--trace-expand", "Put underlying cmake scripts in expanded trace mode." },
   { "--debug", "Enable debug output (for CPack developers)" },
@@ -58,6 +61,8 @@ const char* cmDocumentationOptions[][2] = {
   { "-R <packageVersion>", "Override/define CPACK_PACKAGE_VERSION" },
   { "-B <packageDirectory>", "Override/define CPACK_PACKAGE_DIRECTORY" },
   { "--vendor <vendorName>", "Override/define CPACK_PACKAGE_VENDOR" },
+  { "--preset", "Read arguments from a package preset" },
+  { "--list-presets", "List available package presets" },
   { nullptr, nullptr }
 };
 
@@ -116,6 +121,9 @@ int main(int argc, char const* const* argv)
   std::string cpackProjectVendor;
   std::string cpackConfigFile;
 
+  std::string preset;
+  bool listPresets = false;
+
   std::map<std::string, std::string> definitions;
 
   auto const verboseLambda = [&log](const std::string&, cmake*,
@@ -182,6 +190,10 @@ int main(int argc, char const* const* argv)
                      CommandArgument::setToValue(cpackProjectPatch) },
     CommandArgument{ "--vendor", CommandArgument::Values::One,
                      CommandArgument::setToValue(cpackProjectVendor) },
+    CommandArgument{ "--preset", CommandArgument::Values::One,
+                     CommandArgument::setToValue(preset) },
+    CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
+                     CommandArgument::setToTrue(listPresets) },
     CommandArgument{
       "-D", CommandArgument::Values::One,
       [&log, &definitions](const std::string& arg, cmake*,
@@ -228,6 +240,160 @@ int main(int argc, char const* const* argv)
     }
   }
 
+  cmCPackGeneratorFactory generators;
+  generators.SetLogger(&log);
+
+  // Set up presets
+  if (!preset.empty() || listPresets) {
+    const auto workingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
+
+    auto const presetGeneratorsPresent =
+      [&generators](const cmCMakePresetsGraph::PackagePreset& p) {
+        return std::all_of(p.Generators.begin(), p.Generators.end(),
+                           [&generators](const std::string& gen) {
+                             return generators.GetGeneratorsList().count(
+                                      gen) != 0;
+                           });
+      };
+
+    cmCMakePresetsGraph presetsGraph;
+    auto result = presetsGraph.ReadProjectPresets(workingDirectory);
+    if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Could not read presets from "
+                    << workingDirectory << ": "
+                    << cmCMakePresetsGraph::ResultToString(result)
+                    << std::endl);
+      return 1;
+    }
+
+    if (listPresets) {
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 0;
+    }
+
+    auto presetPair = presetsGraph.PackagePresets.find(preset);
+    if (presetPair == presetsGraph.PackagePresets.end()) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "No such package preset in " << workingDirectory << ": \""
+                                               << preset << '"' << std::endl);
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 1;
+    }
+
+    if (presetPair->second.Unexpanded.Hidden) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Cannot use hidden package preset in "
+                    << workingDirectory << ": \"" << preset << '"'
+                    << std::endl);
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 1;
+    }
+
+    auto const& expandedPreset = presetPair->second.Expanded;
+    if (!expandedPreset) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Could not evaluate package preset \""
+                    << preset << "\": Invalid macro expansion" << std::endl);
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 1;
+    }
+
+    if (!expandedPreset->ConditionResult) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Cannot use disabled package preset in "
+                    << workingDirectory << ": \"" << preset << '"'
+                    << std::endl);
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 1;
+    }
+
+    if (!presetGeneratorsPresent(presetPair->second.Unexpanded)) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "Cannot use preset");
+      presetsGraph.PrintPackagePresetList(presetGeneratorsPresent);
+      return 1;
+    }
+
+    auto configurePresetPair =
+      presetsGraph.ConfigurePresets.find(expandedPreset->ConfigurePreset);
+    if (configurePresetPair == presetsGraph.ConfigurePresets.end()) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "No such configure preset in "
+                    << workingDirectory << ": \""
+                    << expandedPreset->ConfigurePreset << '"' << std::endl);
+      presetsGraph.PrintConfigurePresetList();
+      return 1;
+    }
+
+    if (configurePresetPair->second.Unexpanded.Hidden) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Cannot use hidden configure preset in "
+                    << workingDirectory << ": \""
+                    << expandedPreset->ConfigurePreset << '"' << std::endl);
+      presetsGraph.PrintConfigurePresetList();
+      return 1;
+    }
+
+    auto const& expandedConfigurePreset = configurePresetPair->second.Expanded;
+    if (!expandedConfigurePreset) {
+      cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
+                  "Could not evaluate configure preset \""
+                    << expandedPreset->ConfigurePreset
+                    << "\": Invalid macro expansion" << std::endl);
+      return 1;
+    }
+
+    cmSystemTools::ChangeDirectory(expandedConfigurePreset->BinaryDir);
+
+    auto presetEnvironment = expandedPreset->Environment;
+    for (auto const& var : presetEnvironment) {
+      if (var.second) {
+        cmSystemTools::PutEnv(cmStrCat(var.first, '=', *var.second));
+      }
+    }
+
+    if (!expandedPreset->ConfigFile.empty() && cpackConfigFile.empty()) {
+      cpackConfigFile = expandedPreset->ConfigFile;
+    }
+
+    if (!expandedPreset->Generators.empty() && generator.empty()) {
+      generator = cmJoin(expandedPreset->Generators, ";");
+    }
+
+    if (!expandedPreset->Configurations.empty() && cpackBuildConfig.empty()) {
+      cpackBuildConfig = cmJoin(expandedPreset->Configurations, ";");
+    }
+
+    definitions.insert(expandedPreset->Variables.begin(),
+                       expandedPreset->Variables.end());
+
+    if (expandedPreset->DebugOutput == true) {
+      debugLambda("", &cminst, &globalMF);
+    }
+
+    if (expandedPreset->VerboseOutput == true) {
+      verboseLambda("", &cminst, &globalMF);
+    }
+
+    if (!expandedPreset->PackageName.empty() && cpackProjectName.empty()) {
+      cpackProjectName = expandedPreset->PackageName;
+    }
+
+    if (!expandedPreset->PackageVersion.empty() &&
+        cpackProjectVersion.empty()) {
+      cpackProjectVersion = expandedPreset->PackageVersion;
+    }
+
+    if (!expandedPreset->PackageDirectory.empty() &&
+        cpackProjectDirectory.empty()) {
+      cpackProjectDirectory = expandedPreset->PackageDirectory;
+    }
+
+    if (!expandedPreset->VendorName.empty() && cpackProjectVendor.empty()) {
+      cpackProjectVendor = expandedPreset->VendorName;
+    }
+  }
+
   cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
               "Read CPack config file: " << cpackConfigFile << std::endl);
 
@@ -238,9 +404,6 @@ int main(int argc, char const* const* argv)
     cpackConfigFileSpecified = false;
   }
 
-  cmCPackGeneratorFactory generators;
-  generators.SetLogger(&log);
-
   cmDocumentation doc;
   doc.addCPackStandardDocSections();
   /* Were we invoked to display doc or to do some work ?
index 7432d08..97b0a89 100644 (file)
@@ -4,7 +4,6 @@
 
 #include <set>
 
-#include <cmext/algorithm>
 #include <cmext/string_view>
 
 #include "cmCTest.h"
@@ -18,13 +17,6 @@ void cmCTestCoverageCommand::BindArguments()
   this->Bind("LABELS"_s, this->Labels);
 }
 
-void cmCTestCoverageCommand::CheckArguments(
-  std::vector<std::string> const& keywords)
-{
-  this->LabelsMentioned =
-    !this->Labels.empty() || cm::contains(keywords, "LABELS");
-}
-
 cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler()
 {
   this->CTest->SetCTestConfigurationFromCMakeVariable(
@@ -36,9 +28,9 @@ cmCTestGenericHandler* cmCTestCoverageCommand::InitializeHandler()
   handler->Initialize();
 
   // If a LABELS option was given, select only files with the labels.
-  if (this->LabelsMentioned) {
+  if (this->Labels) {
     handler->SetLabelFilter(
-      std::set<std::string>(this->Labels.begin(), this->Labels.end()));
+      std::set<std::string>(this->Labels->begin(), this->Labels->end()));
   }
 
   handler->SetQuiet(this->Quiet);
index 9344852..55c68b2 100644 (file)
@@ -9,7 +9,9 @@
 #include <vector>
 
 #include <cm/memory>
+#include <cm/optional>
 
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
 
@@ -41,9 +43,7 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const& keywords) override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  bool LabelsMentioned;
-  std::vector<std::string> Labels;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Labels;
 };
index 5494d20..be952cd 100644 (file)
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <sstream>
 
+#include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmCTest.h"
@@ -81,15 +82,13 @@ bool cmCTestHandlerCommand::InitialPass(std::vector<std::string> const& args,
 
   // Process input arguments.
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  std::vector<std::string> parsedKeywords;
-  this->Parse(args, &unparsedArguments, &keywordsMissingValue,
-              &parsedKeywords);
-  this->CheckArguments(keywordsMissingValue);
-
-  std::sort(parsedKeywords.begin(), parsedKeywords.end());
-  auto it = std::adjacent_find(parsedKeywords.begin(), parsedKeywords.end());
-  if (it != parsedKeywords.end()) {
+  this->Parse(args, &unparsedArguments);
+  this->CheckArguments();
+
+  std::sort(this->ParsedKeywords.begin(), this->ParsedKeywords.end());
+  auto it = std::adjacent_find(this->ParsedKeywords.begin(),
+                               this->ParsedKeywords.end());
+  if (it != this->ParsedKeywords.end()) {
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       cmStrCat("Called with more than one value for ", *it));
@@ -233,6 +232,7 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*)
 
 void cmCTestHandlerCommand::BindArguments()
 {
+  this->BindParsedKeywords(this->ParsedKeywords);
   this->Bind("APPEND"_s, this->Append);
   this->Bind("QUIET"_s, this->Quiet);
   this->Bind("RETURN_VALUE"_s, this->ReturnValue);
@@ -242,6 +242,6 @@ void cmCTestHandlerCommand::BindArguments()
   this->Bind("SUBMIT_INDEX"_s, this->SubmitIndex);
 }
 
-void cmCTestHandlerCommand::CheckArguments(std::vector<std::string> const&)
+void cmCTestHandlerCommand::CheckArguments()
 {
 }
index 756952d..ed6d9af 100644 (file)
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include <cm/string_view>
+
 #include "cmArgumentParser.h"
 #include "cmCTestCommand.h"
 
@@ -42,8 +44,9 @@ protected:
 
   // Command argument handling.
   virtual void BindArguments();
-  virtual void CheckArguments(std::vector<std::string> const& keywords);
+  virtual void CheckArguments();
 
+  std::vector<cm::string_view> ParsedKeywords;
   bool Append = false;
   bool Quiet = false;
   std::string CaptureCMakeError;
index 788845b..6f6a642 100644 (file)
@@ -1177,6 +1177,13 @@ bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
     // generic error: ignore ERROR SUMMARY, CUDA-MEMCHECK and others
     "== ([A-Z][a-z].*)"
   };
+  // matchers for messages that aren't defects, but caught by above matchers
+  std::vector<cmsys::RegularExpression> false_positive_matchers{
+    "== Error: No attachable process found.*timed-out",
+    "== Default timeout can be adjusted with --launch-timeout",
+    "== Error: Target application terminated before first instrumented API",
+    "== Tracking kernels launched by child processes requires"
+  };
 
   std::vector<std::string::size_type> nonMemcheckOutput;
   auto sttime = std::chrono::steady_clock::now();
@@ -1196,11 +1203,17 @@ bool cmCTestMemCheckHandler::ProcessMemCheckCudaOutput(
       if (leakExpr.find(line)) {
         failure = static_cast<int>(this->FindOrAddWarning("Memory leak"));
       } else {
-        for (auto& matcher : matchers) {
-          if (matcher.find(line)) {
+        auto match_predicate =
+          [&line](cmsys::RegularExpression& matcher) -> bool {
+          return matcher.find(line);
+        };
+        auto const pos_matcher =
+          std::find_if(matchers.begin(), matchers.end(), match_predicate);
+        if (pos_matcher != matchers.end()) {
+          if (!std::any_of(false_positive_matchers.begin(),
+                           false_positive_matchers.end(), match_predicate)) {
             failure =
-              static_cast<int>(this->FindOrAddWarning(matcher.match(1)));
-            break;
+              static_cast<int>(this->FindOrAddWarning(pos_matcher->match(1)));
           }
         }
       }
index 2a2cb1c..5efe69f 100644 (file)
@@ -8,16 +8,12 @@
 #include <cstdint>
 #include <cstdio>
 #include <cstring>
-#include <functional>
 #include <iomanip>
 #include <ratio>
 #include <sstream>
 #include <utility>
 
 #include <cm/memory>
-#include <cm/optional>
-#include <cm/string_view>
-#include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
 
@@ -787,149 +783,31 @@ bool cmCTestRunTest::ForkProcess(
 
   this->TestProcess->SetTimeout(timeout);
 
-#ifndef CMAKE_BOOTSTRAP
   cmSystemTools::SaveRestoreEnvironment sre;
-#endif
-
   std::ostringstream envMeasurement;
+
+  // We split processing ENVIRONMENT and ENVIRONMENT_MODIFICATION into two
+  // phases to ensure that MYVAR=reset: in the latter phase resets to the
+  // former phase's settings, rather than to the original environment.
   if (environment && !environment->empty()) {
-    // Environment modification works on the assumption that the environment is
-    // actually modified here. If another strategy is used, there will need to
-    // be updates below in `apply_diff`.
-    cmSystemTools::AppendEnv(*environment);
-    for (auto const& var : *environment) {
-      envMeasurement << var << std::endl;
-    }
+    cmSystemTools::EnvDiff diff;
+    diff.AppendEnv(*environment);
+    diff.ApplyToCurrentEnv(&envMeasurement);
   }
 
   if (environment_modification && !environment_modification->empty()) {
-    std::map<std::string, cm::optional<std::string>> env_application;
-
-#ifdef _WIN32
-    char path_sep = ';';
-#else
-    char path_sep = ':';
-#endif
-
-    auto apply_diff =
-      [&env_application](const std::string& name,
-                         std::function<void(std::string&)> const& apply) {
-        cm::optional<std::string> old_value = env_application[name];
-        std::string output;
-        if (old_value) {
-          output = *old_value;
-        } else {
-          // This only works because the environment is actually modified above
-          // (`AppendEnv`). If CTest ever just creates an environment block
-          // directly, that block will need to be queried for the subprocess'
-          // value instead.
-          const char* curval = cmSystemTools::GetEnv(name);
-          if (curval) {
-            output = curval;
-          }
-        }
-        apply(output);
-        env_application[name] = output;
-      };
-
-    bool err_occurred = false;
+    cmSystemTools::EnvDiff diff;
+    bool env_ok = true;
 
     for (auto const& envmod : *environment_modification) {
-      // Split on `=`
-      auto const eq_loc = envmod.find_first_of('=');
-      if (eq_loc == std::string::npos) {
-        cmCTestLog(this->CTest, ERROR_MESSAGE,
-                   "Error: Missing `=` after the variable name in: "
-                     << envmod << std::endl);
-        err_occurred = true;
-        continue;
-      }
-      auto const name = envmod.substr(0, eq_loc);
-
-      // Split value on `:`
-      auto const op_value_start = eq_loc + 1;
-      auto const colon_loc = envmod.find_first_of(':', op_value_start);
-      if (colon_loc == std::string::npos) {
-        cmCTestLog(this->CTest, ERROR_MESSAGE,
-                   "Error: Missing `:` after the operation in: " << envmod
-                                                                 << std::endl);
-        err_occurred = true;
-        continue;
-      }
-      auto const op =
-        envmod.substr(op_value_start, colon_loc - op_value_start);
-
-      auto const value_start = colon_loc + 1;
-      auto const value = envmod.substr(value_start);
-
-      // Determine what to do with the operation.
-      if (op == "reset"_s) {
-        auto entry = env_application.find(name);
-        if (entry != env_application.end()) {
-          env_application.erase(entry);
-        }
-      } else if (op == "set"_s) {
-        env_application[name] = value;
-      } else if (op == "unset"_s) {
-        env_application[name] = {};
-      } else if (op == "string_append"_s) {
-        apply_diff(name, [&value](std::string& output) { output += value; });
-      } else if (op == "string_prepend"_s) {
-        apply_diff(name,
-                   [&value](std::string& output) { output.insert(0, value); });
-      } else if (op == "path_list_append"_s) {
-        apply_diff(name, [&value, path_sep](std::string& output) {
-          if (!output.empty()) {
-            output += path_sep;
-          }
-          output += value;
-        });
-      } else if (op == "path_list_prepend"_s) {
-        apply_diff(name, [&value, path_sep](std::string& output) {
-          if (!output.empty()) {
-            output.insert(output.begin(), path_sep);
-          }
-          output.insert(0, value);
-        });
-      } else if (op == "cmake_list_append"_s) {
-        apply_diff(name, [&value](std::string& output) {
-          if (!output.empty()) {
-            output += ';';
-          }
-          output += value;
-        });
-      } else if (op == "cmake_list_prepend"_s) {
-        apply_diff(name, [&value](std::string& output) {
-          if (!output.empty()) {
-            output.insert(output.begin(), ';');
-          }
-          output.insert(0, value);
-        });
-      } else {
-        cmCTestLog(this->CTest, ERROR_MESSAGE,
-                   "Error: Unrecognized environment manipulation argument: "
-                     << op << std::endl);
-        err_occurred = true;
-        continue;
-      }
+      env_ok &= diff.ParseOperation(envmod);
     }
 
-    if (err_occurred) {
+    if (!env_ok) {
       return false;
     }
 
-    for (auto const& env_apply : env_application) {
-      if (env_apply.second) {
-        auto const env_update =
-          cmStrCat(env_apply.first, '=', *env_apply.second);
-        cmSystemTools::PutEnv(env_update);
-        envMeasurement << env_update << std::endl;
-      } else {
-        cmSystemTools::UnsetEnv(env_apply.first.c_str());
-        // Signify that this variable is being actively unset
-        envMeasurement << "#" << env_apply.first << "=" << std::endl;
-      }
-    }
+    diff.ApplyToCurrentEnv(&envMeasurement);
   }
 
   if (this->UseAllocatedResources) {
index a2dc615..a1933cc 100644 (file)
@@ -8,7 +8,6 @@
 
 #include <cm/memory>
 #include <cm/vector>
-#include <cmext/algorithm>
 #include <cmext/string_view>
 
 #include "cmCTest.h"
@@ -87,7 +86,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
   // If FILES are given, but not PARTS, only the FILES are submitted
   // and *no* PARTS are submitted.
   //  (This is why we select the empty "noParts" set in the
-  //   FilesMentioned block below...)
+  //   if(this->Files) block below...)
   //
   // If PARTS are given, only the selected PARTS are submitted.
   //
@@ -96,7 +95,7 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
 
   // If given explicit FILES to submit, pass them to the handler.
   //
-  if (this->FilesMentioned) {
+  if (this->Files) {
     // Intentionally select *no* PARTS. (Pass an empty set.) If PARTS
     // were also explicitly mentioned, they will be selected below...
     // But FILES with no PARTS mentioned should just submit the FILES
@@ -104,14 +103,14 @@ cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
     //
     handler->SelectParts(std::set<cmCTest::Part>());
     handler->SelectFiles(
-      std::set<std::string>(this->Files.begin(), this->Files.end()));
+      std::set<std::string>(this->Files->begin(), this->Files->end()));
   }
 
   // If a PARTS option was given, select only the named parts for submission.
   //
-  if (this->PartsMentioned) {
+  if (this->Parts) {
     auto parts =
-      cmMakeRange(this->Parts).transform([this](std::string const& arg) {
+      cmMakeRange(*(this->Parts)).transform([this](std::string const& arg) {
         return this->CTest->GetPartFromName(arg);
       });
     handler->SelectParts(std::set<cmCTest::Part>(parts.begin(), parts.end()));
@@ -172,33 +171,31 @@ void cmCTestSubmitCommand::BindArguments()
   this->cmCTestHandlerCommand::BindArguments();
 }
 
-void cmCTestSubmitCommand::CheckArguments(
-  std::vector<std::string> const& keywords)
+void cmCTestSubmitCommand::CheckArguments()
 {
-  this->PartsMentioned =
-    !this->Parts.empty() || cm::contains(keywords, "PARTS");
-  this->FilesMentioned =
-    !this->Files.empty() || cm::contains(keywords, "FILES");
-
-  cm::erase_if(this->Parts, [this](std::string const& arg) -> bool {
-    cmCTest::Part p = this->CTest->GetPartFromName(arg);
-    if (p == cmCTest::PartCount) {
-      std::ostringstream e;
-      e << "Part name \"" << arg << "\" is invalid.";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return true;
-    }
-    return false;
-  });
-
-  cm::erase_if(this->Files, [this](std::string const& arg) -> bool {
-    if (!cmSystemTools::FileExists(arg)) {
-      std::ostringstream e;
-      e << "File \"" << arg << "\" does not exist. Cannot submit "
-        << "a non-existent file.";
-      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return true;
-    }
-    return false;
-  });
+  if (this->Parts) {
+    cm::erase_if(*(this->Parts), [this](std::string const& arg) -> bool {
+      cmCTest::Part p = this->CTest->GetPartFromName(arg);
+      if (p == cmCTest::PartCount) {
+        std::ostringstream e;
+        e << "Part name \"" << arg << "\" is invalid.";
+        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        return true;
+      }
+      return false;
+    });
+  }
+
+  if (this->Files) {
+    cm::erase_if(*(this->Files), [this](std::string const& arg) -> bool {
+      if (!cmSystemTools::FileExists(arg)) {
+        std::ostringstream e;
+        e << "File \"" << arg << "\" does not exist. Cannot submit "
+          << "a non-existent file.";
+        this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        return true;
+      }
+      return false;
+    });
+  }
 }
index c5d11df..b67f182 100644 (file)
@@ -8,6 +8,9 @@
 #include <string>
 #include <vector>
 
+#include <cm/optional>
+
+#include "cmArgumentParserTypes.h"
 #include "cmCTestHandlerCommand.h"
 
 class cmCommand;
@@ -35,13 +38,11 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const& keywords) override;
+  void CheckArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
 
   bool CDashUpload = false;
-  bool FilesMentioned = false;
   bool InternalTest = false;
-  bool PartsMentioned = false;
 
   std::string BuildID;
   std::string CDashUploadFile;
@@ -50,7 +51,7 @@ protected:
   std::string RetryDelay;
   std::string SubmitURL;
 
-  std::vector<std::string> Files;
-  std::vector<std::string> HttpHeaders;
-  std::vector<std::string> Parts;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> HttpHeaders;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Parts;
 };
index f86ee0d..2ed671c 100644 (file)
@@ -21,7 +21,7 @@ void cmCTestUploadCommand::BindArguments()
   this->Bind("CAPTURE_CMAKE_ERROR"_s, this->CaptureCMakeError);
 }
 
-void cmCTestUploadCommand::CheckArguments(std::vector<std::string> const&)
+void cmCTestUploadCommand::CheckArguments()
 {
   cm::erase_if(this->Files, [this](std::string const& arg) -> bool {
     if (!cmSystemTools::FileExists(arg)) {
index fe155f6..a9d1dd2 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <cm/memory>
 
+#include "cmArgumentParserTypes.h"
 #include "cmCTestHandlerCommand.h"
 #include "cmCommand.h"
 
@@ -42,8 +43,8 @@ public:
 
 protected:
   void BindArguments() override;
-  void CheckArguments(std::vector<std::string> const&) override;
+  void CheckArguments() override;
   cmCTestGenericHandler* InitializeHandler() override;
 
-  std::vector<std::string> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
 };
index b14a751..6e6f0d6 100644 (file)
@@ -85,7 +85,7 @@ void cmCursesLongMessageForm::UpdateStatusBar()
   for (size_t i = 0; i < sideSpace; i++) {
     version[i] = ' ';
   }
-  sprintf(version + sideSpace, "%s", vertmp);
+  snprintf(version + sideSpace, sizeof(version) - sideSpace, "%s", vertmp);
   version[width] = '\0';
 
   char fmt_s[] = "%s";
index 68d28c8..63214e3 100644 (file)
@@ -11,7 +11,7 @@ elseif(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
 endif()
 
-configure_file(cmFormConfigure.h.in "${CMAKE_CURRENT_BINARY_DIR}/cmFormConfigure.h")
+configure_file(cmFormConfigure.h.in cmFormConfigure.h)
 
 add_library(cmForm
              fld_arg.c
index 7107fcc..7aeb4b8 100644 (file)
@@ -117,7 +117,7 @@ static bool Check_Integer_Field(FIELD * field, const void * argp)
            {
              if (val<low || val>high) return FALSE;
            }
-         sprintf(buf,"%.*ld",(prec>0?prec:0),val);
+         snprintf(buf,sizeof(buf),"%.*ld",(prec>0?prec:0),val);
          set_field_buffer(field,0,buf);
          return TRUE;
        }
index 7809599..4109b6f 100644 (file)
@@ -140,7 +140,7 @@ static bool Check_Numeric_Field(FIELD * field, const void * argp)
            {
              if (val<low || val>high) return FALSE;
            }
-         sprintf(buf,"%.*f",(prec>0?prec:0),val);
+         snprintf(buf,sizeof(buf),"%.*f",(prec>0?prec:0),val);
          set_field_buffer(field,0,buf);
          return TRUE;
        }
index c3d0000..5703de1 100644 (file)
@@ -557,31 +557,32 @@ struct yy_trans_info
        flex_int32_t yy_verify;
        flex_int32_t yy_nxt;
        };
-static const flex_int16_t yy_accept[210] =
+static const flex_int16_t yy_accept[216] =
     {   0,
         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
        55,   49,   51,   50,   53,    1,   49,   33,    2,   47,
        48,   35,   37,   50,   39,   49,   46,   46,   46,   46,
-       46,   46,   49,   46,   51,   49,   50,   49,   46,    9,
-        8,    9,    4,    3,   49,    0,   10,    0,    0,    0,
-        0,    0,   33,   33,   34,   36,   39,   49,   46,   46,
-       46,   46,   46,   46,    0,   52,   46,    0,    0,    0,
-       12,    0,    0,    0,    0,    0,    0,   49,    0,   11,
-       46,    0,    0,    5,    0,    0,    0,    0,   29,    0,
-       33,   33,   33,   33,    0,    0,   40,   46,   46,   46,
-
-       46,   45,   12,   12,    0,    0,    0,   23,    0,    0,
-        0,    0,    0,    0,    6,    0,    0,    0,    0,    0,
-        0,    0,    0,    0,   46,   46,   46,   46,    0,    0,
-        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-        0,   30,   31,    0,    0,    0,    0,    0,   46,   46,
-       46,   46,    0,   24,   25,    0,    0,    0,    0,    0,
-        0,    0,    0,    0,   20,   32,   27,    0,    0,    0,
-       46,   46,   43,   46,    0,   26,   21,    0,    0,    0,
-       19,    0,    0,   18,   28,    0,    0,   41,   46,   46,
-       17,   22,    0,    7,   38,    7,   15,    0,   46,   46,
-
-       14,   16,   42,   44,    0,    0,    0,   13,    0
+       46,   46,   49,   46,   51,   49,   50,   51,   49,   46,
+        9,    8,    9,    9,    4,    3,   49,    0,   10,    0,
+        0,    0,    0,    0,   33,   33,   34,   36,   39,   49,
+       46,   46,   46,   46,   46,   46,    0,   52,    0,   46,
+        0,    0,    0,   12,    0,    0,    0,    0,    0,    0,
+        0,   49,    0,   11,   46,    0,    0,    0,    5,    0,
+        0,    0,    0,    0,   29,    0,   33,   33,   33,   33,
+
+        0,    0,   40,   46,   46,   46,   46,   45,   12,   12,
+        0,    0,    0,   23,    0,    0,    0,    0,    0,    0,
+        6,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       46,   46,   46,   46,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,   30,   31,    0,
+        0,    0,    0,    0,   46,   46,   46,   46,    0,   24,
+       25,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       20,   32,   27,    0,    0,    0,   46,   46,   43,   46,
+        0,   26,   21,    0,    0,    0,   19,    0,    0,   18,
+       28,    0,    0,   41,   46,   46,   17,   22,    0,    7,
+
+       38,    7,   15,    0,   46,   46,   14,   16,   42,   44,
+        0,    0,    0,   13,    0
     } ;
 
 static const YY_CHAR yy_ec[256] =
@@ -618,189 +619,197 @@ static const YY_CHAR yy_ec[256] =
 
 static const YY_CHAR yy_meta[50] =
     {   0,
-        1,    2,    2,    3,    4,    3,    3,    1,    1,    3,
-        3,    3,    3,    1,    3,    5,    3,    3,    1,    3,
+        1,    2,    2,    2,    3,    4,    4,    1,    1,    4,
+        4,    4,    4,    1,    4,    5,    4,    4,    1,    4,
         6,    1,    7,    7,    7,    7,    7,    7,    7,    7,
         7,    7,    7,    7,    7,    7,    7,    7,    1,    5,
         7,    7,    7,    7,    7,    7,    7,    7,    7
     } ;
 
-static const flex_int16_t yy_base[219] =
+static const flex_int16_t yy_base[225] =
     {   0,
-        0,   48,    0,   49,  464,   56,   52,   57,   62,   68,
-      466,    0,  468,  468,  462,  468,   74,   81,  468,  468,
-      468,  468,  447,  468,  442,  440,    0,   19,   41,  427,
-       47,   41,   90,  119,   97,  158,  455,  207,  247,  468,
-      454,  101,  468,  468,    0,  455,  468,  105,  430,  423,
-       62,   67,  119,  151,  468,  468,  468,  121,    0,   90,
-       93,  110,  431,  112,  142,  468,    0,  160,  295,    0,
-      162,  411,  123,  102,  408,  405,  446,  344,  447,  468,
-        0,  444,  170,  174,  420,  421,  132,  404,   95,  404,
-      180,  186,  192,  228,  297,  397,    0,  168,  144,   52,
-
-      411,    0,  204,  217,  397,  179,  390,  170,  389,  378,
-      364,  390,  389,  230,  468,  363,  355,  337,  337,  334,
-      335,  335,  330,  334,  187,  339,  267,  339,  327,  327,
-      327,  324,  325,  325,  318,  319,  318,  354,  352,  323,
-      327,  468,  468,  310,  307,  305,  297,  297,  275,  275,
-      277,  279,  287,  468,  468,  286,  283,  273,  196,  307,
-      200,  238,  234,  210,  468,  468,  468,  174,  171,  162,
-      279,  182,    0,  269,  150,  468,  468,  137,  109,  323,
-      468,  239,    0,  468,  468,   72,   71,    0,  283,  283,
-      468,  468,   51,  468,  468,  468,  468,   37,  283,  288,
-
-      330,  468,    0,    0,  331,    0,   52,  468,  468,  384,
-      391,  397,  400,  407,  414,  421,  428,  435
+        0,   48,    0,   49,   55,   58,   64,   66,   75,   83,
+      491,    0,  492,  492,  487,  492,   86,   92,  492,  492,
+      492,  492,  472,  492,  467,  465,    0,   56,   59,  452,
+       66,   16,  105,  131,  109,  170,  480,  481,  219,  259,
+      492,  478,  479,  116,  492,  492,    0,  478,  492,  111,
+      453,  446,   34,   78,  155,  174,  492,  492,  492,  121,
+        0,   29,  105,  101,  454,  101,  131,  492,  474,    0,
+      180,  307,    0,  146,  433,  117,   94,  430,  427,  468,
+      467,  356,  468,  492,    0,  465,  464,  187,  191,  465,
+      439,  440,  149,  423,  126,  423,  200,  240,  311,  322,
+
+      206,  416,    0,  152,  180,  176,  430,    0,  216,  224,
+      417,  186,  418,  127,  418,  411,  415,  451,  450,  247,
+      492,  423,  416,  398,  393,  373,  364,  364,  359,  353,
+      198,  358,  178,  358,  346,  346,  346,  343,  344,  344,
+      338,  340,  339,  376,  374,  343,  346,  492,  492,  329,
+      325,  324,  313,  315,  211,  211,  291,  293,  313,  492,
+      492,  314,  304,  304,  261,  328,  212,  249,  243,  203,
+      492,  492,  492,  173,  158,  150,  293,  172,    0,  273,
+      144,  492,  492,  137,  125,  335,  492,  339,    0,  492,
+      492,  112,   63,    0,  304,  300,  492,  492,   58,  492,
+
+      492,  492,  492,   30,  311,  312,  361,  492,    0,    0,
+      366,    0,   44,  492,  492,  396,  403,  409,  412,  419,
+      426,  433,  440,  447
     } ;
 
-static const flex_int16_t yy_def[219] =
+static const flex_int16_t yy_def[225] =
     {   0,
-      209,    1,    1,    1,    1,    1,  210,  210,  210,  210,
-      209,  211,  209,  209,  212,  209,  211,  209,  209,  209,
-      209,  209,  209,  209,  209,  211,  213,  213,  213,  213,
-      213,  213,  211,  213,  209,  211,  209,  214,  209,  209,
-      209,  209,  209,  209,  211,  212,  209,  209,  209,  209,
-      209,  209,  209,  215,  209,  209,  209,  211,  213,  213,
-      213,  213,  213,  213,  209,  209,   34,  209,  209,   69,
-      211,  209,  209,  209,  209,  209,  209,  214,  214,  209,
-       39,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      215,  215,  215,  215,  209,  209,  213,  213,  213,  213,
-
-      213,  213,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  213,  213,  213,  213,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  213,  213,
-      213,  213,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      213,  213,  213,  213,  209,  209,  209,  209,  209,  209,
-      209,  216,  217,  209,  209,  209,  209,  213,  213,  213,
-      209,  209,  209,  209,  209,  209,  209,  209,  213,  213,
-
-      209,  209,  213,  213,  209,  218,  218,  209,    0,  209,
-      209,  209,  209,  209,  209,  209,  209,  209
+      215,    1,    1,    1,    1,    1,  216,  216,  216,  216,
+      215,  217,  215,  215,  218,  215,  217,  215,  215,  215,
+      215,  215,  215,  215,  215,  217,  219,  219,  219,  219,
+      219,  219,  217,  219,  215,  217,  215,  215,  220,  215,
+      215,  215,  215,  215,  215,  215,  217,  218,  215,  215,
+      215,  215,  215,  215,  215,  221,  215,  215,  215,  217,
+      219,  219,  219,  219,  219,  219,  215,  215,  215,   34,
+      215,  215,   72,  217,  215,  215,  215,  215,  215,  215,
+      215,  220,  220,  215,   40,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  221,  221,  221,  221,
+
+      215,  215,  219,  219,  219,  219,  219,  219,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      219,  219,  219,  219,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  219,  219,  219,  219,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  219,  219,  219,  219,
+      215,  215,  215,  215,  215,  215,  215,  222,  223,  215,
+      215,  215,  215,  219,  219,  219,  215,  215,  215,  215,
+
+      215,  215,  215,  215,  219,  219,  215,  215,  219,  219,
+      215,  224,  224,  215,    0,  215,  215,  215,  215,  215,
+      215,  215,  215,  215
     } ;
 
-static const flex_int16_t yy_nxt[518] =
+static const flex_int16_t yy_nxt[542] =
     {   0,
        12,   13,   14,   13,   13,   15,   16,   12,   17,   18,
        19,   20,   21,   12,   22,   12,   23,   24,   12,   25,
        12,   26,   27,   27,   27,   27,   28,   27,   27,   29,
        27,   30,   27,   27,   27,   31,   27,   32,   33,   34,
        27,   27,   28,   27,   29,   27,   27,   31,   32,   35,
-       35,   60,   35,   35,   41,   36,   36,   35,   37,   41,
-       35,   42,   43,   36,   41,   60,   42,   43,   44,   38,
-       41,   42,  208,   61,   44,   48,   64,   42,   48,  202,
-       39,   39,   53,   53,   63,   53,   54,   61,   64,  127,
-       55,   65,   66,  201,   65,   63,   39,   39,   68,   49,
-
-      127,   68,   83,   84,   69,   83,   48,   87,   88,   48,
-       89,   50,  198,   90,  197,   97,   51,   98,   52,   45,
-       53,   53,   95,   53,   54,   95,   45,   45,   55,   99,
-       49,   97,   45,   98,   67,  100,  121,   45,  102,   45,
-       45,  122,   50,   65,   66,  108,   65,   51,  109,   52,
-      193,  100,   92,   53,  102,   92,   93,   45,   67,   70,
-       94,   68,   70,  104,   68,   96,  104,   69,  106,  107,
-      126,   83,   84,   71,   83,  114,  118,   71,  114,  119,
-      192,   92,   53,  115,   92,   93,  126,   92,   53,   94,
-       92,   93,  191,   92,   53,   94,   92,   93,  125,   72,
-
-       73,   94,   74,   75,  189,  104,   76,   78,  104,   80,
-      187,  133,  186,  125,   78,   78,  134,  185,  104,  103,
-       78,  104,   78,  130,  149,   78,  131,   78,   78,   92,
-       53,  114,   92,   93,  114,  149,  184,   94,  183,  115,
-      195,  195,  182,  181,  179,   78,   78,   79,   79,   80,
-       79,   79,   79,   79,   79,   79,   79,   79,   79,   79,
-       79,   79,   81,   79,   79,   79,   79,   79,   79,   81,
-       81,   81,   81,   81,   81,   81,   81,   81,   81,   81,
-       81,   81,   81,   81,   81,   79,   81,   81,   81,   81,
-       81,   81,   81,   81,   81,   81,   70,  151,   95,   70,
-
-      171,   95,  172,  173,  174,  188,  190,  199,  180,  203,
-      103,  180,  151,  200,  204,  178,  171,  190,  172,  173,
-      174,  188,  103,  199,  180,  203,  177,  180,  200,  176,
-      204,  205,  205,  175,  205,  205,   72,   73,  103,   74,
-       75,   96,  170,   76,   78,  169,   80,  168,  206,  206,
-      167,   78,   78,  166,  165,  164,  163,   78,  162,   78,
-      161,  160,   78,  159,   78,   78,  158,  157,  156,  155,
-      154,  153,  152,  150,  148,  147,  146,  145,  144,  143,
-      142,  141,   78,   78,   40,   40,   40,   40,   40,   40,
-       40,   45,  140,  139,  138,   45,   45,   46,   46,   46,
-
-       46,   46,   46,   46,   59,  137,   59,   79,   79,   79,
-       79,   79,   79,   79,   91,   91,   91,   91,   91,   91,
-       91,  194,  194,  194,  136,  194,  194,  194,  196,  135,
-      196,  132,  196,  196,  196,  207,  207,  207,  207,  207,
-      129,  207,  128,  124,  123,  120,  117,  116,  113,   80,
-      112,  111,  110,  105,  101,   86,   85,   47,   82,   77,
-       62,   58,   57,   56,   47,  209,   37,   11,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209
+       35,   66,   35,   35,  103,   36,   36,   37,   38,   35,
+       37,   38,   35,   66,  214,   36,   42,   43,   42,   43,
+      103,   39,  208,   44,   45,   44,   45,   42,   43,   93,
+       94,   46,   40,   40,   44,   42,   43,   50,   62,   46,
+       50,   63,   44,   55,   55,   55,   55,   56,   40,   40,
+
+      207,   57,   62,   65,  204,   63,   67,   68,   69,   67,
+       71,   51,   50,   71,   65,   50,   72,   88,   89,   90,
+       88,   95,  101,   52,   96,  101,  106,  108,   53,  104,
+       54,   47,   67,   68,   69,   67,   51,  114,   47,   47,
+      115,  105,  106,  108,   47,  104,   70,  110,   52,   47,
+      110,   47,   47,   53,  203,   54,   55,   55,   55,   55,
+       56,   74,  112,  113,   57,  102,  199,  127,  139,   47,
+       70,   73,  128,  140,   73,   98,   55,   98,   98,   99,
+      198,   71,  131,  100,   71,   74,  197,   72,   88,   89,
+       90,   88,  120,  124,  195,  120,  125,  131,  193,  192,
+
+      121,   98,   55,   98,   98,   99,  132,  101,  157,  100,
+      101,   75,   76,  133,   77,   78,  191,  110,   79,   82,
+      110,   84,  132,  157,  133,  110,   82,   82,  110,  190,
+      136,  109,   82,  137,   82,  155,  177,   82,  178,   82,
+       82,   98,   55,   98,   98,   99,  155,  189,  120,  100,
+      102,  120,  177,  188,  178,  187,  121,   82,   82,   83,
+       83,   84,   83,   83,   83,   83,   83,   83,   83,   83,
+       83,   83,   83,   83,   85,   83,   83,   83,   83,   83,
+       83,   85,   85,   85,   85,   85,   85,   85,   85,   85,
+       85,   85,   85,   85,   85,   85,   85,   83,   85,   85,
+
+       85,   85,   85,   85,   85,   85,   85,   85,   73,  185,
+      196,   73,   98,   55,   98,   98,   99,  179,  180,  194,
+      100,  196,  109,   98,   55,   98,   98,   99,  205,  186,
+      206,  100,  186,  179,  180,  194,  186,  209,  210,  186,
+      201,  201,  201,  109,  205,  206,  184,  183,   75,   76,
+      109,   77,   78,  209,  210,   79,   82,  182,   84,  181,
+      176,  175,  211,   82,   82,  211,  174,  211,  173,   82,
+      211,   82,  172,  171,   82,  170,   82,   82,  169,  212,
+      168,  167,  166,  165,  212,  164,  163,  162,  161,  160,
+      159,  158,  156,  154,   82,   82,   41,   41,   41,   41,
+
+       41,   41,   41,   47,  153,  152,  151,   47,   47,   48,
+       48,   48,   48,   48,   48,   48,   61,  150,   61,   83,
+       83,   83,   83,   83,   83,   83,   97,   97,   97,   97,
+       97,   97,   97,  200,  200,  149,  200,  200,  200,  200,
+      202,  148,  147,  202,  202,  202,  202,  213,  213,  213,
+      213,  213,  146,  213,  145,  144,  143,  142,  141,  138,
+      135,  134,  130,  129,  126,  123,  122,   89,   86,  119,
+       84,   80,  118,  117,  116,  111,   68,  107,   92,   91,
+       49,   87,   86,   81,   80,   64,   60,   59,   58,   49,
+      215,   11,  215,  215,  215,  215,  215,  215,  215,  215,
+
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215
     } ;
 
-static const flex_int16_t yy_chk[518] =
+static const flex_int16_t yy_chk[542] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
-        4,   28,    2,    4,    7,    2,    4,    6,    6,    8,
-        6,    7,    7,    6,    9,   28,    8,    8,    9,    6,
-       10,    9,  207,   29,   10,   17,   32,   10,   17,  198,
-        6,    6,   18,   18,   31,   18,   18,   29,   32,  100,
-       18,   33,   33,  193,   33,   31,    6,    6,   35,   17,
-
-      100,   35,   42,   42,   35,   42,   48,   51,   51,   48,
-       52,   17,  187,   52,  186,   60,   17,   61,   17,   34,
-       53,   53,   58,   53,   53,   58,   34,   34,   53,   61,
-       48,   60,   34,   61,   34,   62,   89,   34,   64,   34,
-       34,   89,   48,   65,   65,   74,   65,   48,   74,   48,
-      179,   62,   54,   54,   64,   54,   54,   34,   34,   36,
-       54,   68,   36,   71,   68,   58,   71,   68,   73,   73,
-       99,   83,   83,   36,   83,   84,   87,   71,   84,   87,
-      178,   91,   91,   84,   91,   91,   99,   92,   92,   91,
-       92,   92,  175,   93,   93,   92,   93,   93,   98,   36,
-
-       36,   93,   36,   36,  172,  103,   36,   38,  103,   38,
-      170,  108,  169,   98,   38,   38,  108,  168,  104,  103,
-       38,  104,   38,  106,  125,   38,  106,   38,   38,   94,
-       94,  114,   94,   94,  114,  125,  164,   94,  163,  114,
-      182,  182,  162,  161,  159,   38,   38,   39,   39,   39,
-       39,   39,   39,   39,   39,   39,   39,   39,   39,   39,
-       39,   39,   39,   39,   39,   39,   39,   39,   39,   39,
-       39,   39,   39,   39,   39,   39,   39,   39,   39,   39,
-       39,   39,   39,   39,   39,   39,   39,   39,   39,   39,
-       39,   39,   39,   39,   39,   39,   69,  127,   95,   69,
-
-      149,   95,  150,  151,  152,  171,  174,  189,  160,  199,
-       69,  160,  127,  190,  200,  158,  149,  174,  150,  151,
-      152,  171,  160,  189,  180,  199,  157,  180,  190,  156,
-      200,  201,  205,  153,  201,  205,   69,   69,  180,   69,
-       69,   95,  148,   69,   78,  147,   78,  146,  201,  205,
-      145,   78,   78,  144,  141,  140,  139,   78,  138,   78,
-      137,  136,   78,  135,   78,   78,  134,  133,  132,  131,
-      130,  129,  128,  126,  124,  123,  122,  121,  120,  119,
-      118,  117,   78,   78,  210,  210,  210,  210,  210,  210,
-      210,  211,  116,  113,  112,  211,  211,  212,  212,  212,
-
-      212,  212,  212,  212,  213,  111,  213,  214,  214,  214,
-      214,  214,  214,  214,  215,  215,  215,  215,  215,  215,
-      215,  216,  216,  216,  110,  216,  216,  216,  217,  109,
-      217,  107,  217,  217,  217,  218,  218,  218,  218,  218,
-      105,  218,  101,   96,   90,   88,   86,   85,   82,   79,
-       77,   76,   75,   72,   63,   50,   49,   46,   41,   37,
-       30,   26,   25,   23,   15,   11,    5,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-
-      209,  209,  209,  209,  209,  209,  209,  209,  209,  209,
-      209,  209,  209,  209,  209,  209,  209
+        4,   32,    2,    4,   62,    2,    4,    5,    5,    6,
+        6,    6,    6,   32,  213,    6,    7,    7,    8,    8,
+       62,    6,  204,    7,    7,    8,    8,    9,    9,   53,
+       53,    9,    6,    6,    9,   10,   10,   17,   28,   10,
+       17,   29,   10,   18,   18,   18,   18,   18,    6,    6,
+
+      199,   18,   28,   31,  193,   29,   33,   33,   33,   33,
+       35,   17,   50,   35,   31,   50,   35,   44,   44,   44,
+       44,   54,   60,   17,   54,   60,   64,   66,   17,   63,
+       17,   34,   67,   67,   67,   67,   50,   77,   34,   34,
+       77,   63,   64,   66,   34,   63,   34,   74,   50,   34,
+       74,   34,   34,   50,  192,   50,   55,   55,   55,   55,
+       55,   74,   76,   76,   55,   60,  185,   95,  114,   34,
+       34,   36,   95,  114,   36,   56,   56,   56,   56,   56,
+      184,   71,  104,   56,   71,   36,  181,   71,   88,   88,
+       88,   88,   89,   93,  178,   89,   93,  104,  176,  175,
+
+       89,   97,   97,   97,   97,   97,  105,  101,  133,   97,
+      101,   36,   36,  106,   36,   36,  174,  109,   36,   39,
+      109,   39,  105,  133,  106,  110,   39,   39,  110,  170,
+      112,  109,   39,  112,   39,  131,  155,   39,  156,   39,
+       39,   98,   98,   98,   98,   98,  131,  169,  120,   98,
+      101,  120,  155,  168,  156,  167,  120,   39,   39,   40,
+       40,   40,   40,   40,   40,   40,   40,   40,   40,   40,
+       40,   40,   40,   40,   40,   40,   40,   40,   40,   40,
+       40,   40,   40,   40,   40,   40,   40,   40,   40,   40,
+       40,   40,   40,   40,   40,   40,   40,   40,   40,   40,
+
+       40,   40,   40,   40,   40,   40,   40,   40,   72,  165,
+      180,   72,   99,   99,   99,   99,   99,  157,  158,  177,
+       99,  180,   72,  100,  100,  100,  100,  100,  195,  166,
+      196,  100,  166,  157,  158,  177,  186,  205,  206,  186,
+      188,  188,  188,  166,  195,  196,  164,  163,   72,   72,
+      186,   72,   72,  205,  206,   72,   82,  162,   82,  159,
+      154,  153,  207,   82,   82,  207,  152,  211,  151,   82,
+      211,   82,  150,  147,   82,  146,   82,   82,  145,  207,
+      144,  143,  142,  141,  211,  140,  139,  138,  137,  136,
+      135,  134,  132,  130,   82,   82,  216,  216,  216,  216,
+
+      216,  216,  216,  217,  129,  128,  127,  217,  217,  218,
+      218,  218,  218,  218,  218,  218,  219,  126,  219,  220,
+      220,  220,  220,  220,  220,  220,  221,  221,  221,  221,
+      221,  221,  221,  222,  222,  125,  222,  222,  222,  222,
+      223,  124,  123,  223,  223,  223,  223,  224,  224,  224,
+      224,  224,  122,  224,  119,  118,  117,  116,  115,  113,
+      111,  107,  102,   96,   94,   92,   91,   90,   87,   86,
+       83,   81,   80,   79,   78,   75,   69,   65,   52,   51,
+       48,   43,   42,   38,   37,   30,   26,   25,   23,   15,
+       11,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215,  215,  215,  215,  215,  215,  215,  215,  215,  215,
+      215
     } ;
 
 /* The intent behind this definition is that it'll catch
@@ -1139,13 +1148,13 @@ yy_match:
                        while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                                {
                                yy_current_state = (int) yy_def[yy_current_state];
-                               if ( yy_current_state >= 210 )
+                               if ( yy_current_state >= 216 )
                                        yy_c = yy_meta[yy_c];
                                }
                        yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
                        ++yy_cp;
                        }
-               while ( yy_base[yy_current_state] != 468 );
+               while ( yy_base[yy_current_state] != 492 );
 
 yy_find_action:
                yy_act = yy_accept[yy_current_state];
@@ -1733,7 +1742,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
                while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                        {
                        yy_current_state = (int) yy_def[yy_current_state];
-                       if ( yy_current_state >= 210 )
+                       if ( yy_current_state >= 216 )
                                yy_c = yy_meta[yy_c];
                        }
                yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
@@ -1762,11 +1771,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
        while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
                {
                yy_current_state = (int) yy_def[yy_current_state];
-               if ( yy_current_state >= 210 )
+               if ( yy_current_state >= 216 )
                        yy_c = yy_meta[yy_c];
                }
        yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
-       yy_is_jam = (yy_current_state == 209);
+       yy_is_jam = (yy_current_state == 215);
 
        (void)yyg;
        return yy_is_jam ? 0 : yy_current_state;
index 05769a1..fac3181 100644 (file)
@@ -75,10 +75,10 @@ Modify cmFortranLexer.cxx:
   return STRING;
 }
 
-<str_dq,str_sq>&[ \t]*\n |
-<str_dq,str_sq>&[ \t]*\n[ \t]*&  /* Ignore (continued strings, free fmt) */
+<str_dq,str_sq>&[ \t]*\r?\n |
+<str_dq,str_sq>&[ \t]*\r?\n[ \t]*&  /* Ignore (continued strings, free fmt) */
 
-<fixed_fmt,str_dq,str_sq>\n[ ]{5}[^ \t\n] {
+<fixed_fmt,str_dq,str_sq>\r?\n[ ]{5}[^ \t\r\n] {
   if (cmFortranParser_GetOldStartcond(yyextra) == fixed_fmt)
     ; /* Ignore (cont. strings, fixed fmt) */
   else
@@ -132,15 +132,15 @@ $[ \t]*else     { return F90PPR_ELSE; }
 $[ \t]*endif    { return F90PPR_ENDIF; }
 
  /* Line continuations, possible involving comments.  */
-&([ \t\n]*|!.*)*
-&([ \t\n]*|!.*)*&
+&([ \t\r\n]*|!.*)*
+&([ \t\r\n]*|!.*)*&
 
 , { return COMMA; }
 
 :: { return DCOLON; }
 : { return COLON; }
 
-<fixed_fmt>\n[ ]{5}[^ ]  { return GARBAGE; }
+<fixed_fmt>\r?\n[ ]{5}[^ ]  { return GARBAGE; }
 
 =|=>                     { return ASSIGNMENT_OP; }
 
@@ -159,13 +159,13 @@ $[ \t]*endif    { return F90PPR_ENDIF; }
 \( { return LPAREN; }
 \) { return RPAREN; }
 
-[^ \t\n\r:;,!'"a-zA-Z=&()]+ { return GARBAGE; }
+[^ \t\r\n:;,!'"a-zA-Z=&()]+ { return GARBAGE; }
 
 ;|\n { return EOSTMT; }
 
 
 [ \t\r,]         /* Ignore */
-\\[ \t]*\n       /* Ignore line-endings preceded by \ */
+\\[ \t]*\r?\n       /* Ignore line-endings preceded by \ */
 
 . { return *yytext; }
 
index b177296..5e5cc0b 100644 (file)
@@ -548,7 +548,7 @@ union yyalloc
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  2
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   594
+#define YYLAST   432
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  41
@@ -557,7 +557,7 @@ union yyalloc
 /* YYNRULES -- Number of rules.  */
 #define YYNRULES  64
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  127
+#define YYNSTATES  121
 
 /* YYMAXUTOK -- Last valid token kind.  */
 #define YYMAXUTOK   295
@@ -608,15 +608,15 @@ static const yytype_int8 yytranslate[] =
 
 #if YYDEBUG
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
-static const yytype_int16 yyrline[] =
+static const yytype_uint8 yyrline[] =
 {
-       0,   106,   106,   106,   109,   113,   118,   127,   133,   140,
-     145,   149,   154,   166,   171,   176,   181,   186,   191,   196,
-     201,   206,   210,   214,   218,   222,   223,   228,   228,   228,
-     229,   229,   230,   230,   231,   231,   232,   232,   233,   233,
-     234,   234,   235,   235,   236,   236,   237,   237,   240,   241,
-     242,   243,   244,   245,   246,   247,   248,   249,   250,   251,
-     252,   253,   254,   255,   256
+       0,   106,   106,   106,   109,   113,   118,   123,   129,   136,
+     141,   145,   150,   162,   167,   172,   177,   182,   187,   192,
+     197,   202,   206,   210,   214,   218,   219,   224,   224,   224,
+     225,   225,   226,   226,   227,   227,   228,   228,   229,   229,
+     230,   230,   231,   231,   232,   232,   233,   233,   236,   237,
+     238,   239,   240,   241,   242,   243,   244,   245,   246,   247,
+     248,   249,   250,   251,   252
 };
 #endif
 
@@ -666,19 +666,19 @@ yysymbol_name (yysymbol_kind_t yysymbol)
    STATE-NUM.  */
 static const yytype_int16 yypact[] =
 {
-     -39,    21,   -39,     1,   -39,   -20,   -39,   -39,   -39,   -39,
+     -39,    21,   -39,     5,   -39,   -23,   -39,   -39,   -39,   -39,
      -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,
-     -39,   -39,   -39,   -39,   -39,   -39,   -24,   -18,    20,    -8,
-      -3,    40,   -39,    15,    16,    17,    19,    34,   -39,   -39,
-     -39,   -39,   -39,   -39,    59,   -39,   -39,   -39,   -39,   -39,
-      36,    37,    38,   -39,   -39,   -39,   -39,   -39,   -39,    77,
-     115,   130,   168,   183,   -39,   -39,   -39,   -39,   -39,   -39,
+     -39,   -39,   -39,   -39,   -39,   -39,   -25,   -19,    20,    -8,
+     -15,   -22,   -39,    -6,    14,    15,    16,    17,   -39,   -39,
+     -39,   -39,   -39,   -39,    59,    49,    51,   -39,    63,    64,
+      35,    36,    37,   -39,   -39,   -39,   -39,   -39,   -39,    74,
+     112,   127,   165,   180,   -39,   -39,   -39,   -39,   -39,   -39,
      -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,   -39,
-     -39,   -39,   -39,   221,   236,   274,   289,   -21,    26,   -39,
-     327,   342,   380,   395,   433,   448,   -39,   -39,   -39,   -39,
-     -39,   -39,   -39,   -39,   -39,    39,    41,    42,   486,   -39,
-     -39,   -39,   -39,   -39,   -39,    18,   -39,   -39,   -39,    43,
-     501,   539,   -39,   -39,   -39,   554,   -39
+     -39,   -39,   -39,   -39,   -39,   -39,   -39,   -20,    43,   -39,
+     218,   233,   271,   286,   324,   339,   -39,   -39,   -39,   -39,
+     -39,    39,    40,    41,   377,   -39,   -39,   -39,   -39,   -39,
+     -39,    46,    78,   -39,   -39,    50,   -39,   392,    79,   -39,
+     -39
 };
 
 /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -690,15 +690,15 @@ static const yytype_int8 yydefact[] =
       30,    33,    32,    34,    36,    38,    42,    40,    44,    35,
       37,    39,    43,    41,    45,    46,     0,     0,     0,     0,
        0,     0,     3,     0,     0,     0,     0,     0,    46,    46,
-      46,    46,    26,    46,     0,    46,    46,     4,    46,    46,
+      46,    46,    26,    46,     0,     0,     0,     4,     0,     0,
        0,     0,     0,    46,    46,    46,    46,    46,    46,     0,
        0,     0,     0,     0,    15,    57,    56,    64,    62,    58,
       59,    60,    61,    63,    55,    48,    49,    50,    51,    52,
-      53,    54,    47,     0,     0,     0,     0,     0,     0,    46,
+      53,    54,    47,    10,    13,     9,     6,     0,     0,    46,
        0,     0,     0,     0,     0,     0,    21,    22,    23,    24,
-      14,    10,    13,     9,     6,     0,     0,     0,     0,     5,
-      16,    17,    18,    19,    20,     0,    46,    46,    11,     0,
-       0,     0,    46,     7,    12,     0,     8
+      14,     0,     0,     0,     0,     5,    16,    17,    18,    19,
+      20,     0,     0,    46,    11,     0,     7,     0,     0,    12,
+       8
 };
 
 /* YYPGOTO[NTERM-NUM].  */
@@ -720,81 +720,70 @@ static const yytype_int8 yydefgoto[] =
    number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int8 yytable[] =
 {
-      59,    60,    61,    62,    42,    63,   105,    83,    84,   106,
-      85,    86,    43,    45,    46,    90,    91,    92,    93,    94,
-      95,     2,     3,    47,     4,    49,    50,     5,     6,     7,
+      59,    60,    61,    62,    51,    63,    52,   101,    42,    43,
+     102,    53,    45,    46,    50,    90,    91,    92,    93,    94,
+      95,     2,     3,    47,     4,    49,    54,     5,     6,     7,
        8,     9,    10,    11,    12,    13,    14,    15,    16,    17,
-      18,    19,    20,    21,    22,    23,    24,    54,   119,    55,
-      56,   108,    57,    48,   107,    25,    26,    27,    28,    29,
-      30,    31,    64,    65,    66,    67,    51,    58,    52,    87,
-      88,    89,   115,    53,   116,   117,   122,     0,   120,   121,
-      96,    65,    66,    67,   125,    68,    69,    70,    71,    72,
+      18,    19,    20,    21,    22,    23,    24,    55,    56,    57,
+      58,   104,    83,    48,    84,    25,    26,    27,    28,    29,
+      30,    31,    64,    65,    66,    67,    85,    86,    87,    88,
+      89,   103,   111,   112,   113,   117,   115,    96,    65,    66,
+      67,   116,   120,   118,     0,    68,    69,    70,    71,    72,
       73,    74,    75,     0,    76,    77,    78,    79,    80,    81,
-       0,     0,     0,    68,    69,    70,    71,    72,    73,    74,
-      75,     0,    76,    77,    78,    79,    80,    81,    97,    65,
+      68,    69,    70,    71,    72,    73,    74,    75,     0,    76,
+      77,    78,    79,    80,    81,    97,    65,    66,    67,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      98,    65,    66,    67,     0,     0,     0,     0,    68,    69,
+      70,    71,    72,    73,    74,    75,     0,    76,    77,    78,
+      79,    80,    81,    68,    69,    70,    71,    72,    73,    74,
+      75,     0,    76,    77,    78,    79,    80,    81,    99,    65,
       66,    67,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,    98,    65,    66,    67,     0,     0,     0,
+       0,     0,     0,   100,    65,    66,    67,     0,     0,     0,
        0,    68,    69,    70,    71,    72,    73,    74,    75,     0,
       76,    77,    78,    79,    80,    81,    68,    69,    70,    71,
       72,    73,    74,    75,     0,    76,    77,    78,    79,    80,
-      81,    99,    65,    66,    67,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,   100,    65,    66,    67,
+      81,   105,    65,    66,    67,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,   106,    65,    66,    67,
        0,     0,     0,     0,    68,    69,    70,    71,    72,    73,
       74,    75,     0,    76,    77,    78,    79,    80,    81,    68,
       69,    70,    71,    72,    73,    74,    75,     0,    76,    77,
-      78,    79,    80,    81,   101,    65,    66,    67,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,   102,
+      78,    79,    80,    81,   107,    65,    66,    67,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   108,
       65,    66,    67,     0,     0,     0,     0,    68,    69,    70,
       71,    72,    73,    74,    75,     0,    76,    77,    78,    79,
       80,    81,    68,    69,    70,    71,    72,    73,    74,    75,
-       0,    76,    77,    78,    79,    80,    81,   103,    65,    66,
+       0,    76,    77,    78,    79,    80,    81,   109,    65,    66,
       67,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,   104,    65,    66,    67,     0,     0,     0,     0,
+       0,     0,   110,    65,    66,    67,     0,     0,     0,     0,
       68,    69,    70,    71,    72,    73,    74,    75,     0,    76,
       77,    78,    79,    80,    81,    68,    69,    70,    71,    72,
       73,    74,    75,     0,    76,    77,    78,    79,    80,    81,
-     109,    65,    66,    67,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,   110,    65,    66,    67,     0,
+     114,    65,    66,    67,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,   119,    65,    66,    67,     0,
        0,     0,     0,    68,    69,    70,    71,    72,    73,    74,
       75,     0,    76,    77,    78,    79,    80,    81,    68,    69,
       70,    71,    72,    73,    74,    75,     0,    76,    77,    78,
-      79,    80,    81,   111,    65,    66,    67,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,     0,   112,    65,
-      66,    67,     0,     0,     0,     0,    68,    69,    70,    71,
-      72,    73,    74,    75,     0,    76,    77,    78,    79,    80,
-      81,    68,    69,    70,    71,    72,    73,    74,    75,     0,
-      76,    77,    78,    79,    80,    81,   113,    65,    66,    67,
-       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-       0,   114,    65,    66,    67,     0,     0,     0,     0,    68,
-      69,    70,    71,    72,    73,    74,    75,     0,    76,    77,
-      78,    79,    80,    81,    68,    69,    70,    71,    72,    73,
-      74,    75,     0,    76,    77,    78,    79,    80,    81,   118,
-      65,    66,    67,     0,     0,     0,     0,     0,     0,     0,
-       0,     0,     0,     0,   123,    65,    66,    67,     0,     0,
-       0,     0,    68,    69,    70,    71,    72,    73,    74,    75,
-       0,    76,    77,    78,    79,    80,    81,    68,    69,    70,
-      71,    72,    73,    74,    75,     0,    76,    77,    78,    79,
-      80,    81,   124,    65,    66,    67,     0,     0,     0,     0,
-       0,     0,     0,     0,     0,     0,     0,   126,    65,    66,
-      67,     0,     0,     0,     0,    68,    69,    70,    71,    72,
-      73,    74,    75,     0,    76,    77,    78,    79,    80,    81,
-      68,    69,    70,    71,    72,    73,    74,    75,     0,    76,
-      77,    78,    79,    80,    81
+      79,    80,    81
 };
 
 static const yytype_int8 yycheck[] =
 {
-      38,    39,    40,    41,     3,    43,    27,    45,    46,    30,
-      48,    49,    32,    37,    32,    53,    54,    55,    56,    57,
-      58,     0,     1,     3,     3,    33,    29,     6,     7,     8,
+      38,    39,    40,    41,    26,    43,    28,    27,     3,    32,
+      30,    33,    37,    32,    29,    53,    54,    55,    56,    57,
+      58,     0,     1,     3,     3,    33,    32,     6,     7,     8,
        9,    10,    11,    12,    13,    14,    15,    16,    17,    18,
-      19,    20,    21,    22,    23,    24,    25,    32,    30,    33,
-      33,    89,    33,    33,    28,    34,    35,    36,    37,    38,
-      39,    40,     3,     4,     5,     6,    26,    33,    28,    33,
-      33,    33,    33,    33,    33,    33,    33,    -1,   116,   117,
-       3,     4,     5,     6,   122,    26,    27,    28,    29,    30,
+      19,    20,    21,    22,    23,    24,    25,    33,    33,    33,
+      33,    89,     3,    33,     3,    34,    35,    36,    37,    38,
+      39,    40,     3,     4,     5,     6,     3,     3,    33,    33,
+      33,    28,    33,    33,    33,   113,    30,     3,     4,     5,
+       6,     3,     3,    33,    -1,    26,    27,    28,    29,    30,
       31,    32,    33,    -1,    35,    36,    37,    38,    39,    40,
-      -1,    -1,    -1,    26,    27,    28,    29,    30,    31,    32,
+      26,    27,    28,    29,    30,    31,    32,    33,    -1,    35,
+      36,    37,    38,    39,    40,     3,     4,     5,     6,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+       3,     4,     5,     6,    -1,    -1,    -1,    -1,    26,    27,
+      28,    29,    30,    31,    32,    33,    -1,    35,    36,    37,
+      38,    39,    40,    26,    27,    28,    29,    30,    31,    32,
       33,    -1,    35,    36,    37,    38,    39,    40,     3,     4,
        5,     6,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
       -1,    -1,    -1,     3,     4,     5,     6,    -1,    -1,    -1,
@@ -822,28 +811,7 @@ static const yytype_int8 yycheck[] =
       -1,    -1,    -1,    26,    27,    28,    29,    30,    31,    32,
       33,    -1,    35,    36,    37,    38,    39,    40,    26,    27,
       28,    29,    30,    31,    32,    33,    -1,    35,    36,    37,
-      38,    39,    40,     3,     4,     5,     6,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,     3,     4,
-       5,     6,    -1,    -1,    -1,    -1,    26,    27,    28,    29,
-      30,    31,    32,    33,    -1,    35,    36,    37,    38,    39,
-      40,    26,    27,    28,    29,    30,    31,    32,    33,    -1,
-      35,    36,    37,    38,    39,    40,     3,     4,     5,     6,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,     3,     4,     5,     6,    -1,    -1,    -1,    -1,    26,
-      27,    28,    29,    30,    31,    32,    33,    -1,    35,    36,
-      37,    38,    39,    40,    26,    27,    28,    29,    30,    31,
-      32,    33,    -1,    35,    36,    37,    38,    39,    40,     3,
-       4,     5,     6,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,     3,     4,     5,     6,    -1,    -1,
-      -1,    -1,    26,    27,    28,    29,    30,    31,    32,    33,
-      -1,    35,    36,    37,    38,    39,    40,    26,    27,    28,
-      29,    30,    31,    32,    33,    -1,    35,    36,    37,    38,
-      39,    40,     3,     4,     5,     6,    -1,    -1,    -1,    -1,
-      -1,    -1,    -1,    -1,    -1,    -1,    -1,     3,     4,     5,
-       6,    -1,    -1,    -1,    -1,    26,    27,    28,    29,    30,
-      31,    32,    33,    -1,    35,    36,    37,    38,    39,    40,
-      26,    27,    28,    29,    30,    31,    32,    33,    -1,    35,
-      36,    37,    38,    39,    40
+      38,    39,    40
 };
 
 /* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
@@ -858,11 +826,11 @@ static const yytype_int8 yystos[] =
       29,    26,    28,    33,    32,    33,    33,    33,    33,    53,
       53,    53,    53,    53,     3,     4,     5,     6,    26,    27,
       28,    29,    30,    31,    32,    33,    35,    36,    37,    38,
-      39,    40,    54,    53,    53,    53,    53,    33,    33,    33,
+      39,    40,    54,     3,     3,     3,     3,    33,    33,    33,
       53,    53,    53,    53,    53,    53,     3,     3,     3,     3,
-       3,     3,     3,     3,     3,    27,    30,    28,    53,     3,
-       3,     3,     3,     3,     3,    33,    33,    33,     3,    30,
-      53,    53,    33,     3,     3,    53,     3
+       3,    27,    30,    28,    53,     3,     3,     3,     3,     3,
+       3,    33,    33,    33,     3,    30,     3,    53,    33,     3,
+       3
 };
 
 /* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
@@ -880,8 +848,8 @@ static const yytype_int8 yyr1[] =
 /* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
 static const yytype_int8 yyr2[] =
 {
-       0,     2,     0,     2,     2,     4,     4,     7,     9,     4,
-       4,     5,     7,     4,     4,     3,     4,     4,     4,     4,
+       0,     2,     0,     2,     2,     4,     3,     6,     8,     3,
+       3,     5,     7,     3,     4,     3,     4,     4,     4,     4,
        4,     3,     3,     3,     3,     1,     2,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
        1,     1,     1,     1,     1,     1,     0,     2,     1,     1,
@@ -1361,19 +1329,19 @@ yydestruct (const char *yymsg,
     case YYSYMBOL_STRING: /* STRING  */
 #line 100 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1365 "cmFortranParser.cxx"
+#line 1333 "cmFortranParser.cxx"
         break;
 
     case YYSYMBOL_WORD: /* WORD  */
 #line 100 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1371 "cmFortranParser.cxx"
+#line 1339 "cmFortranParser.cxx"
         break;
 
     case YYSYMBOL_CPP_INCLUDE_ANGLE: /* CPP_INCLUDE_ANGLE  */
 #line 100 "cmFortranParser.y"
             { free(((*yyvaluep).string)); }
-#line 1377 "cmFortranParser.cxx"
+#line 1345 "cmFortranParser.cxx"
         break;
 
       default:
@@ -1655,7 +1623,7 @@ yyreduce:
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
   }
-#line 1659 "cmFortranParser.cxx"
+#line 1627 "cmFortranParser.cxx"
     break;
 
   case 5: /* stmt: USE WORD other EOSTMT  */
@@ -1665,77 +1633,73 @@ yyreduce:
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1669 "cmFortranParser.cxx"
+#line 1637 "cmFortranParser.cxx"
     break;
 
-  case 6: /* stmt: MODULE WORD other EOSTMT  */
+  case 6: /* stmt: MODULE WORD EOSTMT  */
 #line 118 "cmFortranParser.y"
-                           {
+                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    if (cmsysString_strcasecmp((yyvsp[-2].string), "function") != 0 &&
-        cmsysString_strcasecmp((yyvsp[-2].string), "procedure") != 0 &&
-        cmsysString_strcasecmp((yyvsp[-2].string), "subroutine") != 0) {
-      cmFortranParser_RuleModule(parser, (yyvsp[-2].string));
-    }
-    free((yyvsp[-2].string));
+    cmFortranParser_RuleModule(parser, (yyvsp[-1].string));
+    free((yyvsp[-1].string));
   }
-#line 1683 "cmFortranParser.cxx"
+#line 1647 "cmFortranParser.cxx"
     break;
 
-  case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT  */
-#line 127 "cmFortranParser.y"
-                                                 {
+  case 7: /* stmt: SUBMODULE LPAREN WORD RPAREN WORD EOSTMT  */
+#line 123 "cmFortranParser.y"
+                                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleSubmodule(parser, (yyvsp[-4].string), (yyvsp[-2].string));
-    free((yyvsp[-4].string));
-    free((yyvsp[-2].string));
+    cmFortranParser_RuleSubmodule(parser, (yyvsp[-3].string), (yyvsp[-1].string));
+    free((yyvsp[-3].string));
+    free((yyvsp[-1].string));
   }
-#line 1694 "cmFortranParser.cxx"
+#line 1658 "cmFortranParser.cxx"
     break;
 
-  case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT  */
-#line 133 "cmFortranParser.y"
-                                                            {
+  case 8: /* stmt: SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT  */
+#line 129 "cmFortranParser.y"
+                                                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-6].string), (yyvsp[-4].string), (yyvsp[-2].string));
-    free((yyvsp[-6].string));
-    free((yyvsp[-4].string));
-    free((yyvsp[-2].string));
+    cmFortranParser_RuleSubmoduleNested(parser, (yyvsp[-5].string), (yyvsp[-3].string), (yyvsp[-1].string));
+    free((yyvsp[-5].string));
+    free((yyvsp[-3].string));
+    free((yyvsp[-1].string));
   }
-#line 1706 "cmFortranParser.cxx"
+#line 1670 "cmFortranParser.cxx"
     break;
 
-  case 9: /* stmt: INTERFACE WORD other EOSTMT  */
-#line 140 "cmFortranParser.y"
-                              {
+  case 9: /* stmt: INTERFACE WORD EOSTMT  */
+#line 136 "cmFortranParser.y"
+                        {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
-    free((yyvsp[-2].string));
+    free((yyvsp[-1].string));
   }
-#line 1716 "cmFortranParser.cxx"
+#line 1680 "cmFortranParser.cxx"
     break;
 
-  case 10: /* stmt: END INTERFACE other EOSTMT  */
-#line 145 "cmFortranParser.y"
-                             {
+  case 10: /* stmt: END INTERFACE EOSTMT  */
+#line 141 "cmFortranParser.y"
+                       {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
   }
-#line 1725 "cmFortranParser.cxx"
+#line 1689 "cmFortranParser.cxx"
     break;
 
   case 11: /* stmt: USE DCOLON WORD other EOSTMT  */
-#line 149 "cmFortranParser.y"
+#line 145 "cmFortranParser.y"
                                {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1735 "cmFortranParser.cxx"
+#line 1699 "cmFortranParser.cxx"
     break;
 
   case 12: /* stmt: USE COMMA WORD DCOLON WORD other EOSTMT  */
-#line 154 "cmFortranParser.y"
+#line 150 "cmFortranParser.y"
                                           {
     if (cmsysString_strcasecmp((yyvsp[-4].string), "non_intrinsic") == 0) {
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
@@ -1748,139 +1712,139 @@ yyreduce:
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1752 "cmFortranParser.cxx"
+#line 1716 "cmFortranParser.cxx"
     break;
 
-  case 13: /* stmt: INCLUDE STRING other EOSTMT  */
-#line 166 "cmFortranParser.y"
-                              {
+  case 13: /* stmt: INCLUDE STRING EOSTMT  */
+#line 162 "cmFortranParser.y"
+                        {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
-    free((yyvsp[-2].string));
+    cmFortranParser_RuleInclude(parser, (yyvsp[-1].string));
+    free((yyvsp[-1].string));
   }
-#line 1762 "cmFortranParser.cxx"
+#line 1726 "cmFortranParser.cxx"
     break;
 
   case 14: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT  */
-#line 171 "cmFortranParser.y"
+#line 167 "cmFortranParser.y"
                                          {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1772 "cmFortranParser.cxx"
+#line 1736 "cmFortranParser.cxx"
     break;
 
   case 15: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT  */
-#line 176 "cmFortranParser.y"
+#line 172 "cmFortranParser.y"
                                  {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1782 "cmFortranParser.cxx"
+#line 1746 "cmFortranParser.cxx"
     break;
 
   case 16: /* stmt: include STRING other EOSTMT  */
-#line 181 "cmFortranParser.y"
+#line 177 "cmFortranParser.y"
                               {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1792 "cmFortranParser.cxx"
+#line 1756 "cmFortranParser.cxx"
     break;
 
   case 17: /* stmt: define WORD other EOSTMT  */
-#line 186 "cmFortranParser.y"
+#line 182 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1802 "cmFortranParser.cxx"
+#line 1766 "cmFortranParser.cxx"
     break;
 
   case 18: /* stmt: undef WORD other EOSTMT  */
-#line 191 "cmFortranParser.y"
+#line 187 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1812 "cmFortranParser.cxx"
+#line 1776 "cmFortranParser.cxx"
     break;
 
   case 19: /* stmt: ifdef WORD other EOSTMT  */
-#line 196 "cmFortranParser.y"
+#line 192 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1822 "cmFortranParser.cxx"
+#line 1786 "cmFortranParser.cxx"
     break;
 
   case 20: /* stmt: ifndef WORD other EOSTMT  */
-#line 201 "cmFortranParser.y"
+#line 197 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1832 "cmFortranParser.cxx"
+#line 1796 "cmFortranParser.cxx"
     break;
 
   case 21: /* stmt: if other EOSTMT  */
-#line 206 "cmFortranParser.y"
+#line 202 "cmFortranParser.y"
                   {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIf(parser);
   }
-#line 1841 "cmFortranParser.cxx"
+#line 1805 "cmFortranParser.cxx"
     break;
 
   case 22: /* stmt: elif other EOSTMT  */
-#line 210 "cmFortranParser.y"
+#line 206 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElif(parser);
   }
-#line 1850 "cmFortranParser.cxx"
+#line 1814 "cmFortranParser.cxx"
     break;
 
   case 23: /* stmt: else other EOSTMT  */
-#line 214 "cmFortranParser.y"
+#line 210 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElse(parser);
   }
-#line 1859 "cmFortranParser.cxx"
+#line 1823 "cmFortranParser.cxx"
     break;
 
   case 24: /* stmt: endif other EOSTMT  */
-#line 218 "cmFortranParser.y"
+#line 214 "cmFortranParser.y"
                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleEndif(parser);
   }
-#line 1868 "cmFortranParser.cxx"
+#line 1832 "cmFortranParser.cxx"
     break;
 
   case 48: /* misc_code: WORD  */
-#line 240 "cmFortranParser.y"
+#line 236 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1874 "cmFortranParser.cxx"
+#line 1838 "cmFortranParser.cxx"
     break;
 
   case 55: /* misc_code: STRING  */
-#line 247 "cmFortranParser.y"
+#line 243 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1880 "cmFortranParser.cxx"
+#line 1844 "cmFortranParser.cxx"
     break;
 
 
-#line 1884 "cmFortranParser.cxx"
+#line 1848 "cmFortranParser.cxx"
 
       default: break;
     }
@@ -2104,6 +2068,6 @@ yyreturnlab:
   return yyresult;
 }
 
-#line 259 "cmFortranParser.y"
+#line 255 "cmFortranParser.y"
 
 /* End of grammar */
index 07ca630..a5c8976 100644 (file)
@@ -115,34 +115,30 @@ stmt:
     cmFortranParser_RuleUse(parser, $2);
     free($2);
   }
-| MODULE WORD other EOSTMT {
+| MODULE WORD EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
-    if (cmsysString_strcasecmp($2, "function") != 0 &&
-        cmsysString_strcasecmp($2, "procedure") != 0 &&
-        cmsysString_strcasecmp($2, "subroutine") != 0) {
-      cmFortranParser_RuleModule(parser, $2);
-    }
+    cmFortranParser_RuleModule(parser, $2);
     free($2);
   }
-| SUBMODULE LPAREN WORD RPAREN WORD other EOSTMT {
+| SUBMODULE LPAREN WORD RPAREN WORD EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmodule(parser, $3, $5);
     free($3);
     free($5);
   }
-| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD other EOSTMT {
+| SUBMODULE LPAREN WORD COLON WORD RPAREN WORD EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleSubmoduleNested(parser, $3, $5, $7);
     free($3);
     free($5);
     free($7);
   }
-| INTERFACE WORD other EOSTMT {
+| INTERFACE WORD EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, true);
     free($2);
   }
-| END INTERFACE other EOSTMT {
+| END INTERFACE EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_SetInInterface(parser, false);
   }
@@ -163,7 +159,7 @@ stmt:
     free($3);
     free($5);
   }
-| INCLUDE STRING other EOSTMT {
+| INCLUDE STRING EOSTMT {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, $2);
     free($2);
diff --git a/Source/Modules/CMakeBuildUtilities.cmake b/Source/Modules/CMakeBuildUtilities.cmake
new file mode 100644 (file)
index 0000000..5cfb0e7
--- /dev/null
@@ -0,0 +1,379 @@
+#-----------------------------------------------------------------------
+# Build the utilities used by CMake
+#
+# Originally it was a macro in the root `CMakeLists.txt` with the comment
+# "Simply to improve readability...".
+# However, as part of the modernization refactoring it was moved into a
+# separate file cuz adding library alises wasn't possible inside the
+# macro.
+#-----------------------------------------------------------------------
+
+# Suppress unnecessary checks in third-party code.
+include(Utilities/cmThirdPartyChecks.cmake)
+
+#---------------------------------------------------------------------
+# Create the kwsys library for CMake.
+set(KWSYS_NAMESPACE cmsys)
+set(KWSYS_USE_SystemTools 1)
+set(KWSYS_USE_Directory 1)
+set(KWSYS_USE_RegularExpression 1)
+set(KWSYS_USE_Base64 1)
+set(KWSYS_USE_MD5 1)
+set(KWSYS_USE_Process 1)
+set(KWSYS_USE_CommandLineArguments 1)
+set(KWSYS_USE_ConsoleBuf 1)
+set(KWSYS_HEADER_ROOT ${CMake_BINARY_DIR}/Source)
+set(KWSYS_INSTALL_DOC_DIR "${CMAKE_DOC_DIR}")
+if(CMake_NO_CXX_STANDARD)
+  set(KWSYS_CXX_STANDARD "")
+endif()
+if(CMake_NO_SELF_BACKTRACE)
+  set(KWSYS_NO_EXECINFO 1)
+endif()
+if(WIN32)
+  # FIXME: Teach KWSys to hard-code these checks on Windows.
+  set(KWSYS_C_HAS_CLOCK_GETTIME_MONOTONIC_COMPILED 0)
+  set(KWSYS_C_HAS_PTRDIFF_T_COMPILED 1)
+  set(KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H_COMPILED 1)
+  set(KWSYS_CXX_HAS_RLIMIT64_COMPILED 0)
+  set(KWSYS_CXX_HAS_SETENV_COMPILED 0)
+  set(KWSYS_CXX_HAS_UNSETENV_COMPILED 0)
+  set(KWSYS_CXX_HAS_UTIMENSAT_COMPILED 0)
+  set(KWSYS_CXX_HAS_UTIMES_COMPILED 0)
+  set(KWSYS_CXX_STAT_HAS_ST_MTIM_COMPILED 0)
+  set(KWSYS_CXX_STAT_HAS_ST_MTIMESPEC_COMPILED 0)
+  set(KWSYS_STL_HAS_WSTRING_COMPILED 1)
+  set(KWSYS_SYS_HAS_IFADDRS_H 0)
+endif()
+add_subdirectory(Source/kwsys)
+set(kwsys_folder "Utilities/KWSys")
+CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE} "${kwsys_folder}")
+CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}_c "${kwsys_folder}")
+if(BUILD_TESTING)
+  CMAKE_SET_TARGET_FOLDER(${KWSYS_NAMESPACE}TestDynload "${kwsys_folder}")
+  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()
+
+#---------------------------------------------------------------------
+# Setup third-party libraries.
+# Everything in the tree should be able to include files from the
+# Utilities directory.
+if((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  # using -isystem option generate error "template with C linkage"
+  include_directories("${CMake_SOURCE_DIR}/Utilities/std")
+else()
+  include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities/std")
+endif()
+
+include_directories("${CMake_BINARY_DIR}/Utilities")
+if((CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+  # using -isystem option generate error "template with C linkage"
+  include_directories("${CMake_SOURCE_DIR}/Utilities")
+else()
+  include_directories(SYSTEM "${CMake_SOURCE_DIR}/Utilities")
+endif()
+
+#---------------------------------------------------------------------
+# Build CMake std library for CMake and CTest.
+add_subdirectory(Utilities/std)
+CMAKE_SET_TARGET_FOLDER(cmstd "Utilities/std")
+
+# check for the use of system libraries versus builtin ones
+# (a macro defined in this file)
+CMAKE_HANDLE_SYSTEM_LIBRARIES()
+
+if(CMAKE_USE_SYSTEM_KWIML)
+  find_package(KWIML 1.0)
+  if(NOT KWIML_FOUND)
+    message(FATAL_ERROR "CMAKE_USE_SYSTEM_KWIML is ON but KWIML is not found!")
+  endif()
+else()
+  if(BUILD_TESTING)
+    set(KWIML_TEST_ENABLE 1)
+  endif()
+  add_subdirectory(Utilities/KWIML)
+endif()
+
+if(CMAKE_USE_SYSTEM_LIBRHASH)
+  find_package(LibRHash)
+  if(NOT LibRHash_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_LIBRHASH is ON but LibRHash is not found!")
+  endif()
+else()
+  add_subdirectory(Utilities/cmlibrhash)
+  add_library(LibRHash::LibRHash ALIAS cmlibrhash)
+  CMAKE_SET_TARGET_FOLDER(cmlibrhash "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Build zlib library for Curl, CMake, and CTest.
+if(CMAKE_USE_SYSTEM_ZLIB)
+  find_package(ZLIB)
+  if(NOT ZLIB_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_ZLIB is ON but a zlib is not found!")
+  endif()
+else()
+  if(NOT POLICY CMP0102) # CMake < 3.17
+    # Store in cache to protect from mark_as_advanced.
+    set(ZLIB_INCLUDE_DIR ${CMake_SOURCE_DIR}/Utilities CACHE PATH "")
+  else()
+    set(ZLIB_INCLUDE_DIR ${CMake_SOURCE_DIR}/Utilities)
+  endif()
+  set(ZLIB_LIBRARY cmzlib)
+  set(WITHOUT_ZLIB_DLL "")
+  set(WITHOUT_ZLIB_DLL_WITH_LIB cmzlib)
+  set(ZLIB_DLL "")
+  set(ZLIB_DLL_WITH_LIB cmzlib)
+  set(ZLIB_WINAPI "")
+  set(ZLIB_WINAPI_COMPILED 0)
+  set(ZLIB_WINAPI_WITH_LIB cmzlib)
+  add_subdirectory(Utilities/cmzlib)
+  add_library(ZLIB::ZLIB ALIAS cmzlib)
+  CMAKE_SET_TARGET_FOLDER(cmzlib "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Build Curl library for CTest.
+if(CMAKE_USE_SYSTEM_CURL)
+  find_package(CURL)
+  if(NOT CURL_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_CURL is ON but a curl is not found!")
+  endif()
+else()
+  if(CMAKE_TESTS_CDASH_SERVER)
+    set(CMAKE_CURL_TEST_URL "${CMAKE_TESTS_CDASH_SERVER}/user.php")
+  endif()
+  set(_CMAKE_USE_OPENSSL_DEFAULT OFF)
+  if(NOT DEFINED CMAKE_USE_OPENSSL AND NOT WIN32 AND NOT APPLE
+      AND CMAKE_SYSTEM_NAME MATCHES "(Linux|FreeBSD)")
+    set(_CMAKE_USE_OPENSSL_DEFAULT ON)
+  endif()
+  option(CMAKE_USE_OPENSSL "Use OpenSSL." ${_CMAKE_USE_OPENSSL_DEFAULT})
+  mark_as_advanced(CMAKE_USE_OPENSSL)
+  if(CMAKE_USE_OPENSSL)
+    set(CURL_CA_BUNDLE "" CACHE FILEPATH "Path to SSL CA Certificate Bundle")
+    set(CURL_CA_PATH "" CACHE PATH "Path to SSL CA Certificate Directory")
+    mark_as_advanced(CURL_CA_BUNDLE CURL_CA_PATH)
+  endif()
+  if(NOT CMAKE_USE_SYSTEM_NGHTTP2)
+    # Tell curl's FindNGHTTP2 module to use our library.
+    set(NGHTTP2_LIBRARY cmnghttp2)
+    set(NGHTTP2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmnghttp2/lib/includes)
+  endif()
+  add_subdirectory(Utilities/cmcurl)
+  add_library(CURL::libcurl ALIAS cmcurl)
+  CMAKE_SET_TARGET_FOLDER(cmcurl "Utilities/3rdParty")
+  CMAKE_SET_TARGET_FOLDER(LIBCURL "Utilities/3rdParty")
+  if(NOT CMAKE_USE_SYSTEM_NGHTTP2)
+    # Configure after curl to re-use some check results.
+    add_subdirectory(Utilities/cmnghttp2)
+    CMAKE_SET_TARGET_FOLDER(cmnghttp2 "Utilities/3rdParty")
+  endif()
+endif()
+
+#---------------------------------------------------------------------
+# Build expat library for CMake, CTest, and libarchive.
+if(CMAKE_USE_SYSTEM_EXPAT)
+  find_package(EXPAT)
+  if(NOT EXPAT_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_EXPAT is ON but a expat is not found!")
+  endif()
+  set(CMAKE_EXPAT_INCLUDES ${EXPAT_INCLUDE_DIRS})
+  set(CMAKE_EXPAT_LIBRARIES ${EXPAT_LIBRARIES})
+else()
+  set(CMAKE_EXPAT_INCLUDES)
+  set(CMAKE_EXPAT_LIBRARIES cmexpat)
+  add_subdirectory(Utilities/cmexpat)
+  add_library(EXPAT::EXPAT ALIAS cmexpat)
+  CMAKE_SET_TARGET_FOLDER(cmexpat "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Build or use system libbz2 for libarchive.
+if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
+  if(CMAKE_USE_SYSTEM_BZIP2)
+    find_package(BZip2)
+  else()
+    set(BZIP2_INCLUDE_DIR
+      "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmbzip2")
+    set(BZIP2_LIBRARIES cmbzip2)
+    set(BZIP2_NEED_PREFIX "")
+    set(USE_BZIP2_DLL "")
+    set(USE_BZIP2_DLL_WITH_LIB cmbzip2)
+    set(USE_BZIP2_STATIC "")
+    set(USE_BZIP2_STATIC_WITH_LIB cmbzip2)
+    add_subdirectory(Utilities/cmbzip2)
+    CMAKE_SET_TARGET_FOLDER(cmbzip2 "Utilities/3rdParty")
+  endif()
+endif()
+
+#---------------------------------------------------------------------
+# Build or use system zstd for libarchive.
+if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
+  if(NOT CMAKE_USE_SYSTEM_ZSTD)
+    set(ZSTD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmzstd")
+    set(ZSTD_LIBRARY cmzstd)
+    add_subdirectory(Utilities/cmzstd)
+    CMAKE_SET_TARGET_FOLDER(cmzstd "Utilities/3rdParty")
+  endif()
+endif()
+
+#---------------------------------------------------------------------
+# Build or use system liblzma for libarchive.
+if(NOT CMAKE_USE_SYSTEM_LIBARCHIVE)
+  if(CMAKE_USE_SYSTEM_LIBLZMA)
+    find_package(LibLZMA)
+    if(NOT LIBLZMA_FOUND)
+      message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBLZMA is ON but LibLZMA is not found!")
+    endif()
+  else()
+    add_subdirectory(Utilities/cmliblzma)
+    CMAKE_SET_TARGET_FOLDER(cmliblzma "Utilities/3rdParty")
+    set(LIBLZMA_HAS_AUTO_DECODER 1)
+    set(LIBLZMA_HAS_EASY_ENCODER 1)
+    set(LIBLZMA_HAS_LZMA_PRESET 1)
+    set(LIBLZMA_INCLUDE_DIR
+      "${CMAKE_CURRENT_SOURCE_DIR}/Utilities/cmliblzma/liblzma/api")
+    set(LIBLZMA_LIBRARY cmliblzma)
+    set(HAVE_LZMA_STREAM_ENCODER_MT 1)
+  endif()
+endif()
+
+#---------------------------------------------------------------------
+# Build or use system libarchive for CMake and CTest.
+if(CMAKE_USE_SYSTEM_LIBARCHIVE)
+  find_package(LibArchive 3.3.3)
+  if(NOT LibArchive_FOUND)
+    message(FATAL_ERROR "CMAKE_USE_SYSTEM_LIBARCHIVE is ON but LibArchive is not found!")
+  endif()
+  # NOTE `FindLibArchive` got imported targets support since 3.17
+  if (NOT TARGET LibArchive::LibArchive)
+    add_library(LibArchive::LibArchive UNKNOWN IMPORTED)
+    set_target_properties(LibArchive::LibArchive PROPERTIES
+      IMPORTED_LOCATION "${LibArchive_LIBRARIES}"
+      INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIRS}")
+  endif ()
+else()
+  set(EXPAT_INCLUDE_DIR ${CMAKE_EXPAT_INCLUDES})
+  set(EXPAT_LIBRARY ${CMAKE_EXPAT_LIBRARIES})
+  set(ENABLE_MBEDTLS OFF)
+  set(ENABLE_NETTLE OFF)
+  if(DEFINED CMAKE_USE_OPENSSL)
+    set(ENABLE_OPENSSL "${CMAKE_USE_OPENSSL}")
+  else()
+    set(ENABLE_OPENSSL OFF)
+  endif()
+  set(ENABLE_LIBB2 OFF)
+  set(ENABLE_LZ4 OFF)
+  set(ENABLE_LZO OFF)
+  set(ENABLE_LZMA ON)
+  set(ENABLE_ZSTD ON)
+  set(ENABLE_ZLIB ON)
+  set(ENABLE_BZip2 ON)
+  set(ENABLE_LIBXML2 OFF)
+  set(ENABLE_EXPAT OFF)
+  set(ENABLE_PCREPOSIX OFF)
+  set(ENABLE_LibGCC OFF)
+  set(ENABLE_CNG OFF)
+  set(ENABLE_TAR OFF)
+  set(ENABLE_TAR_SHARED OFF)
+  set(ENABLE_CPIO OFF)
+  set(ENABLE_CPIO_SHARED OFF)
+  set(ENABLE_CAT OFF)
+  set(ENABLE_CAT_SHARED OFF)
+  set(ENABLE_XATTR OFF)
+  set(ENABLE_ACL OFF)
+  set(ENABLE_ICONV OFF)
+  set(ENABLE_TEST OFF)
+  set(ENABLE_COVERAGE OFF)
+  set(ENABLE_INSTALL OFF)
+  set(POSIX_REGEX_LIB "" CACHE INTERNAL "libarchive: No POSIX regular expression support")
+  set(ENABLE_SAFESEH "" CACHE INTERNAL "libarchive: No /SAFESEH linker flag")
+  set(WINDOWS_VERSION "WIN7" CACHE INTERNAL "libarchive: Set Windows version to use (Windows only)")
+  add_subdirectory(Utilities/cmlibarchive)
+  add_library(LibArchive::LibArchive ALIAS cmlibarchive)
+  target_compile_definitions(cmlibarchive INTERFACE LIBARCHIVE_STATIC)
+  CMAKE_SET_TARGET_FOLDER(cmlibarchive "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Build jsoncpp library.
+if(CMAKE_USE_SYSTEM_JSONCPP)
+  find_package(JsonCpp 1.6.0)
+  if(NOT JsonCpp_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!")
+  endif()
+  if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC|Clang")
+    set_property(TARGET JsonCpp::JsonCpp APPEND PROPERTY
+      INTERFACE_COMPILE_OPTIONS -Wno-deprecated-declarations)
+  endif()
+else()
+  add_subdirectory(Utilities/cmjsoncpp)
+  add_library(JsonCpp::JsonCpp ALIAS cmjsoncpp)
+  CMAKE_SET_TARGET_FOLDER(cmjsoncpp "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Build libuv library.
+if(CMAKE_USE_SYSTEM_LIBUV)
+  if(WIN32)
+    find_package(LibUV 1.38.0)
+  else()
+    find_package(LibUV 1.28.0)
+  endif()
+  if(NOT LIBUV_FOUND)
+    message(FATAL_ERROR
+      "CMAKE_USE_SYSTEM_LIBUV is ON but a libuv is not found!")
+  endif()
+else()
+  add_subdirectory(Utilities/cmlibuv)
+  add_library(LibUV::LibUV ALIAS cmlibuv)
+  CMAKE_SET_TARGET_FOLDER(cmlibuv "Utilities/3rdParty")
+endif()
+
+#---------------------------------------------------------------------
+# Use curses?
+if(NOT DEFINED BUILD_CursesDialog)
+  if(UNIX)
+    include(${CMake_SOURCE_DIR}/Source/Checks/Curses.cmake)
+    set(BUILD_CursesDialog_DEFAULT "${CMakeCheckCurses_COMPILED}")
+  elseif(WIN32)
+    set(BUILD_CursesDialog_DEFAULT "OFF")
+  endif()
+  option(BUILD_CursesDialog "Build the CMake Curses Dialog ccmake" "${BUILD_CursesDialog_DEFAULT}")
+endif()
+if(BUILD_CursesDialog)
+  if(UNIX)
+    set(CURSES_NEED_NCURSES TRUE)
+    find_package(Curses)
+    if(NOT CURSES_FOUND)
+      message(WARNING
+        "'ccmake' will not be built because Curses was not found.\n"
+        "Turn off BUILD_CursesDialog to suppress this message."
+        )
+      set(BUILD_CursesDialog 0)
+    endif()
+  elseif(WIN32)
+    # FIXME: Add support for system-provided pdcurses.
+    add_subdirectory(Utilities/cmpdcurses)
+    set(CURSES_LIBRARY cmpdcurses)
+    set(CURSES_INCLUDE_PATH "") # cmpdcurses has usage requirements
+    set(CMAKE_USE_SYSTEM_FORM 0)
+    set(HAVE_CURSES_USE_DEFAULT_COLORS 1)
+  endif()
+endif()
+if(BUILD_CursesDialog)
+  if(NOT CMAKE_USE_SYSTEM_FORM)
+    add_subdirectory(Source/CursesDialog/form)
+  elseif(NOT CURSES_FORM_LIBRARY)
+    message(FATAL_ERROR "CMAKE_USE_SYSTEM_FORM in ON but CURSES_FORM_LIBRARY is not set!")
+  endif()
+endif()
index 0c263bb..c90b7ee 100644 (file)
@@ -3,7 +3,7 @@
 
 project(QtDialog)
 CMake_OPTIONAL_COMPONENT(cmake-gui)
-set (QT_COMPONENTS
+set(QT_COMPONENTS
   Core
   Widgets
   Gui
@@ -41,8 +41,8 @@ set(CMake_QT_EXTRA_LIBRARIES)
 # Try to find the package WinExtras for the task bar progress
 if(WIN32)
   find_package(Qt${INSTALLED_QT_VERSION}WinExtras QUIET)
-  if (Qt${INSTALLED_QT_VERSION}WinExtras_FOUND)
-    add_definitions(-DQT_WINEXTRAS)
+  if(Qt${INSTALLED_QT_VERSION}WinExtras_FOUND)
+    add_compile_definitions(QT_WINEXTRAS)
     list(APPEND CMake_QT_EXTRA_LIBRARIES Qt${INSTALLED_QT_VERSION}::WinExtras)
     list(APPEND QT_COMPONENTS WinExtras)
   endif()
@@ -100,14 +100,14 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32))
     endif()
   endmacro()
   macro(install_qt_plugins _comps _plugins_var)
-    foreach(_qt_comp ${${_comps}})
-      if (INSTALLED_QT_VERSION VERSION_LESS 6)
+    foreach(_qt_comp IN LISTS ${_comps})
+      if(INSTALLED_QT_VERSION VERSION_LESS 6)
         set(_qt_module_plugins ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_PLUGINS})
       else()
         get_target_property(_qt_module_plugins Qt${INSTALLED_QT_VERSION}::${_qt_comp} QT_PLUGINS)
       endif()
-      foreach(_qt_plugin ${_qt_module_plugins})
-        if (INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6)
+      foreach(_qt_plugin IN LISTS _qt_module_plugins)
+        if(INSTALLED_QT_VERSION VERSION_GREATER_EQUAL 6)
           # Qt6 provides the plugins as individual packages that need to be found.
           find_package(Qt${INSTALLED_QT_VERSION}${_qt_plugin} QUIET
             PATHS ${Qt${INSTALLED_QT_VERSION}${_qt_comp}_DIR})
@@ -117,7 +117,7 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32))
     endforeach()
   endmacro()
   if(APPLE)
-    if (INSTALLED_QT_VERSION VERSION_EQUAL 5)
+    if(INSTALLED_QT_VERSION VERSION_EQUAL 5)
       install_qt_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS)
       if(TARGET Qt5::QMacStylePlugin)
         install_qt_plugin("Qt5::QMacStylePlugin" QT_PLUGINS)
@@ -132,7 +132,7 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32))
       DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources"
       ${COMPONENT})
   elseif(WIN32 AND NOT CMake_QT_STATIC_QWindowsIntegrationPlugin_LIBRARIES)
-    if (INSTALLED_QT_VERSION VERSION_EQUAL 5)
+    if(INSTALLED_QT_VERSION VERSION_EQUAL 5)
       install_qt_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS)
     else()
       # FIXME: Minimize plugins for Qt6.
@@ -152,7 +152,10 @@ if(APPLE)
   get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH)
 endif()
 
-set(SRCS
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_library(
+  CMakeGUILib STATIC
   AddCacheEntry.cxx
   AddCacheEntry.h
   CMakeSetupDialog.cxx
@@ -179,6 +182,16 @@ set(SRCS
   WarningMessagesDialog.cxx
   WarningMessagesDialog.h
   )
+# CMake_QT_EXTRA_LIBRARIES have to come before the main libraries on the link line
+target_link_libraries(
+  CMakeGUILib
+  PUBLIC
+    CMakeLib
+    ${CMake_QT_EXTRA_LIBRARIES}
+    Qt${INSTALLED_QT_VERSION}::Core
+    Qt${INSTALLED_QT_VERSION}::Widgets
+  )
+
 set(UI_SRCS
   CMakeSetupDialog.ui
   Compilers.ui
@@ -204,7 +217,7 @@ set(MOC_SRCS
   )
 set(QRC_SRCS CMakeSetup.qrc)
 
-if (INSTALLED_QT_VERSION VERSION_LESS 6)
+if(INSTALLED_QT_VERSION VERSION_LESS 6)
   qt5_wrap_ui(UI_BUILT_SRCS ${UI_SRCS})
   qt5_wrap_cpp(MOC_BUILT_SRCS ${MOC_SRCS})
   qt5_add_resources(QRC_BUILT_SRCS ${QRC_SRCS})
@@ -215,15 +228,18 @@ else()
 endif()
 add_library(CMakeGUIQRCLib OBJECT ${QRC_BUILT_SRCS})
 
-if (FALSE) # CMake's bootstrap binary does not support automoc
+if(FALSE) # CMake's bootstrap binary does not support automoc
   set(CMAKE_AUTOMOC 1)
   set(CMAKE_AUTORCC 1)
   set(CMAKE_AUTOUIC 1)
-else ()
-  list(APPEND SRCS
-    ${UI_BUILT_SRCS}
-    ${MOC_BUILT_SRCS})
-endif ()
+else()
+  target_sources(
+    CMakeGUILib
+    PRIVATE
+      ${UI_BUILT_SRCS}
+      ${MOC_BUILT_SRCS}
+  )
+endif()
 
 if(USE_LGPL)
   install(FILES ${CMake_SOURCE_DIR}/Licenses/LGPLv${USE_LGPL}.txt
@@ -233,22 +249,25 @@ if(USE_LGPL)
     PROPERTY COMPILE_DEFINITIONS USE_LGPL="${USE_LGPL}")
 endif()
 
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-
-add_library(CMakeGUILib STATIC ${SRCS})
-# CMake_QT_EXTRA_LIBRARIES have to come before the main libraries on the link line
-target_link_libraries(CMakeGUILib PUBLIC CMakeLib ${CMake_QT_EXTRA_LIBRARIES}
-  Qt${INSTALLED_QT_VERSION}::Core Qt${INSTALLED_QT_VERSION}::Widgets)
-
 add_library(CMakeGUIMainLib STATIC CMakeSetup.cxx)
-target_link_libraries(CMakeGUIMainLib PUBLIC CMakeGUILib)
+target_link_libraries(
+  CMakeGUIMainLib
+  PUBLIC
+    CMakeGUILib
+  )
 
-add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx ${MANIFEST_FILE})
-target_link_libraries(cmake-gui CMakeGUIMainLib Qt${INSTALLED_QT_VERSION}::Core)
+add_executable(cmake-gui WIN32 MACOSX_BUNDLE CMakeGUIExec.cxx)
+target_link_libraries(cmake-gui
+  PRIVATE
+    CMakeGUIMainLib
+    CMakeGUIQRCLib
+    $<TARGET_NAME_IF_EXISTS:CMakeVersion>
+    ManifestLib
+    Qt${INSTALLED_QT_VERSION}::Core
+  )
 
-target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeGUIQRCLib>)
 if(WIN32)
-  target_sources(CMakeGUIMainLib INTERFACE $<TARGET_OBJECTS:CMakeVersion> CMakeSetup.rc)
+  target_sources(CMakeGUIMainLib INTERFACE CMakeSetup.rc)
 endif()
 if(APPLE)
   target_sources(CMakeGUIMainLib INTERFACE CMakeSetup.icns)
@@ -300,21 +319,19 @@ if(APPLE)
                    $<TARGET_FILE_DIR:cmake>/cmake-gui
     )
 endif()
-set(CMAKE_INSTALL_DESTINATION_ARGS
-  BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" ${COMPONENT})
 
 install(TARGETS cmake-gui
   RUNTIME DESTINATION bin ${COMPONENT}
-  ${CMAKE_INSTALL_DESTINATION_ARGS})
+  BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" ${COMPONENT})
 
 if(UNIX AND NOT APPLE)
-  foreach (size IN ITEMS 32 128)
+  foreach(size IN ITEMS 32 128)
     install(
       FILES       "${CMAKE_CURRENT_SOURCE_DIR}/CMakeSetup${size}.png"
       DESTINATION "${CMAKE_XDGDATA_DIR}/icons/hicolor/${size}x${size}/apps"
       ${COMPONENT}
       RENAME      "CMakeSetup.png")
-  endforeach ()
+  endforeach()
 
   # install a desktop file so CMake appears in the application start menu
   # with an icon
@@ -348,5 +365,4 @@ if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32))
 endif()
 
 set(CMAKE_PACKAGE_QTGUI TRUE)
-configure_file("${QtDialog_SOURCE_DIR}/QtDialogCPack.cmake.in"
-  "${QtDialog_BINARY_DIR}/QtDialogCPack.cmake" @ONLY)
+configure_file(QtDialogCPack.cmake.in QtDialogCPack.cmake @ONLY)
index f3c4a8b..707eaae 100644 (file)
@@ -244,7 +244,8 @@ void StartCompilerSetup::onGeneratorChanged(int index)
     if (!DefaultGeneratorPlatform.isEmpty()) {
       int platform_index = platforms.indexOf(DefaultGeneratorPlatform);
       if (platform_index != -1) {
-        this->PlatformOptions->setCurrentIndex(platform_index);
+        // The index is off-by-one due to the first empty item added above.
+        this->PlatformOptions->setCurrentIndex(platform_index + 1);
       }
     }
   } else {
index 83d6306..6a2ab0b 100644 (file)
@@ -26,6 +26,7 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
   std::string binArg;
 
   bool excludeFromAll = false;
+  bool system = false;
 
   // process the rest of the arguments looking for optional args
   for (std::string const& arg : cmMakeRange(args).advance(1)) {
@@ -33,6 +34,10 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
       excludeFromAll = true;
       continue;
     }
+    if (arg == "SYSTEM") {
+      system = true;
+      continue;
+    }
     if (binArg.empty()) {
       binArg = arg;
     } else {
@@ -40,6 +45,11 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
       return false;
     }
   }
+  // "SYSTEM" directory property should also affects targets in nested
+  // subdirectories.
+  if (mf.GetPropertyAsBool("SYSTEM")) {
+    system = true;
+  }
 
   // Compute the full path to the specified source directory.
   // Interpret a relative path with respect to the current source directory.
@@ -102,7 +112,7 @@ bool cmAddSubDirectoryCommand(std::vector<std::string> const& args,
   binPath = cmSystemTools::CollapseFullPath(binPath);
 
   // Add the subdirectory using the computed full paths.
-  mf.AddSubDirectory(srcPath, binPath, excludeFromAll, true);
+  mf.AddSubDirectory(srcPath, binPath, excludeFromAll, true, system);
 
   return true;
 }
index 4624f1c..ad57a88 100644 (file)
@@ -4,9 +4,14 @@
 
 #include <algorithm>
 
+#include "cmArgumentParserTypes.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmStringAlgorithms.h"
+
 namespace ArgumentParser {
 
-auto ActionMap::Emplace(cm::string_view name, Action action)
+auto KeywordActionMap::Emplace(cm::string_view name, KeywordAction action)
   -> std::pair<iterator, bool>
 {
   auto const it =
@@ -19,7 +24,7 @@ auto ActionMap::Emplace(cm::string_view name, Action action)
     : std::make_pair(this->emplace(it, name, std::move(action)), true);
 }
 
-auto ActionMap::Find(cm::string_view name) const -> const_iterator
+auto KeywordActionMap::Find(cm::string_view name) const -> const_iterator
 {
   auto const it =
     std::lower_bound(this->begin(), this->end(), name,
@@ -29,68 +34,171 @@ auto ActionMap::Find(cm::string_view name) const -> const_iterator
   return (it != this->end() && it->first == name) ? it : this->end();
 }
 
+auto PositionActionMap::Emplace(std::size_t pos, PositionAction action)
+  -> std::pair<iterator, bool>
+{
+  auto const it = std::lower_bound(
+    this->begin(), this->end(), pos,
+    [](value_type const& elem, std::size_t k) { return elem.first < k; });
+  return (it != this->end() && it->first == pos)
+    ? std::make_pair(it, false)
+    : std::make_pair(this->emplace(it, pos, std::move(action)), true);
+}
+
+auto PositionActionMap::Find(std::size_t pos) const -> const_iterator
+{
+  auto const it = std::lower_bound(
+    this->begin(), this->end(), pos,
+    [](value_type const& elem, std::size_t k) { return elem.first < k; });
+  return (it != this->end() && it->first == pos) ? it : this->end();
+}
+
+void Instance::Bind(std::function<Continue(cm::string_view)> f,
+                    ExpectAtLeast expect)
+{
+  this->KeywordValueFunc = std::move(f);
+  this->KeywordValuesExpected = expect.Count;
+}
+
 void Instance::Bind(bool& val)
 {
   val = true;
-  this->CurrentString = nullptr;
-  this->CurrentList = nullptr;
-  this->ExpectValue = false;
+  this->Bind(nullptr, ExpectAtLeast{ 0 });
 }
 
 void Instance::Bind(std::string& val)
 {
-  this->CurrentString = &val;
-  this->CurrentList = nullptr;
-  this->ExpectValue = true;
+  this->Bind(
+    [&val](cm::string_view arg) -> Continue {
+      val = std::string(arg);
+      return Continue::No;
+    },
+    ExpectAtLeast{ 1 });
+}
+
+void Instance::Bind(NonEmpty<std::string>& val)
+{
+  this->Bind(
+    [this, &val](cm::string_view arg) -> Continue {
+      if (arg.empty() && this->ParseResults) {
+        this->ParseResults->AddKeywordError(this->Keyword,
+                                            "  empty string not allowed\n");
+      }
+      val.assign(std::string(arg));
+      return Continue::No;
+    },
+    ExpectAtLeast{ 1 });
 }
 
-void Instance::Bind(StringList& val)
+void Instance::Bind(Maybe<std::string>& val)
 {
-  this->CurrentString = nullptr;
-  this->CurrentList = &val;
-  this->ExpectValue = true;
+  this->Bind(
+    [&val](cm::string_view arg) -> Continue {
+      static_cast<std::string&>(val) = std::string(arg);
+      return Continue::No;
+    },
+    ExpectAtLeast{ 0 });
 }
 
-void Instance::Bind(MultiStringList& val)
+void Instance::Bind(MaybeEmpty<std::vector<std::string>>& val)
 {
-  this->CurrentString = nullptr;
-  this->CurrentList = (static_cast<void>(val.emplace_back()), &val.back());
-  this->ExpectValue = false;
+  this->Bind(
+    [&val](cm::string_view arg) -> Continue {
+      val.emplace_back(arg);
+      return Continue::Yes;
+    },
+    ExpectAtLeast{ 0 });
 }
 
-void Instance::Consume(cm::string_view arg, void* result,
-                       std::vector<std::string>* unparsedArguments,
-                       std::vector<std::string>* keywordsMissingValue,
-                       std::vector<std::string>* parsedKeywords)
+void Instance::Bind(NonEmpty<std::vector<std::string>>& val)
 {
-  auto const it = this->Bindings.Find(arg);
-  if (it != this->Bindings.end()) {
-    if (parsedKeywords != nullptr) {
-      parsedKeywords->emplace_back(arg);
+  this->Bind(
+    [&val](cm::string_view arg) -> Continue {
+      val.emplace_back(arg);
+      return Continue::Yes;
+    },
+    ExpectAtLeast{ 1 });
+}
+
+void Instance::Bind(std::vector<std::vector<std::string>>& multiVal)
+{
+  multiVal.emplace_back();
+  std::vector<std::string>& val = multiVal.back();
+  this->Bind(
+    [&val](cm::string_view arg) -> Continue {
+      val.emplace_back(arg);
+      return Continue::Yes;
+    },
+    ExpectAtLeast{ 0 });
+}
+
+void Instance::Consume(std::size_t pos, cm::string_view arg)
+{
+  auto const it = this->Bindings.Keywords.Find(arg);
+  if (it != this->Bindings.Keywords.end()) {
+    this->FinishKeyword();
+    this->Keyword = it->first;
+    this->KeywordValuesSeen = 0;
+    this->DoneWithPositional = true;
+    if (this->Bindings.ParsedKeyword) {
+      this->Bindings.ParsedKeyword(*this, it->first);
     }
-    it->second(*this, result);
-    if (this->ExpectValue && keywordsMissingValue != nullptr) {
-      keywordsMissingValue->emplace_back(arg);
+    it->second(*this);
+    return;
+  }
+
+  if (this->KeywordValueFunc) {
+    switch (this->KeywordValueFunc(arg)) {
+      case Continue::Yes:
+        break;
+      case Continue::No:
+        this->KeywordValueFunc = nullptr;
+        break;
     }
+    ++this->KeywordValuesSeen;
     return;
   }
 
-  if (this->CurrentString != nullptr) {
-    this->CurrentString->assign(std::string(arg));
-    this->CurrentString = nullptr;
-    this->CurrentList = nullptr;
-  } else if (this->CurrentList != nullptr) {
-    this->CurrentList->emplace_back(arg);
-  } else if (unparsedArguments != nullptr) {
-    unparsedArguments->emplace_back(arg);
+  if (!this->DoneWithPositional) {
+    auto const pit = this->Bindings.Positions.Find(pos);
+    if (pit != this->Bindings.Positions.end()) {
+      pit->second(*this, pos, arg);
+      return;
+    }
+  }
+
+  if (this->UnparsedArguments != nullptr) {
+    this->UnparsedArguments->emplace_back(arg);
   }
+}
 
-  if (this->ExpectValue) {
-    if (keywordsMissingValue != nullptr) {
-      keywordsMissingValue->pop_back();
+void Instance::FinishKeyword()
+{
+  if (this->Keyword.empty()) {
+    return;
+  }
+  if (this->KeywordValuesSeen < this->KeywordValuesExpected) {
+    if (this->ParseResults != nullptr) {
+      this->ParseResults->AddKeywordError(this->Keyword,
+                                          "  missing required value\n");
     }
-    this->ExpectValue = false;
+    if (this->Bindings.KeywordMissingValue) {
+      this->Bindings.KeywordMissingValue(*this, this->Keyword);
+    }
+  }
+}
+
+bool ParseResult::MaybeReportError(cmMakefile& mf) const
+{
+  if (*this) {
+    return false;
+  }
+  std::string e;
+  for (auto const& ke : this->KeywordErrors) {
+    e = cmStrCat(e, "Error after keyword \"", ke.first, "\":\n", ke.second);
   }
+  mf.IssueMessage(MessageType::FATAL_ERROR, e);
+  return true;
 }
 
 } // namespace ArgumentParser
index 71ed844..fdf54fb 100644 (file)
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <cassert>
+#include <cstddef>
 #include <functional>
+#include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cm/string_view>
+#include <cm/type_traits>
 #include <cmext/string_view>
 
+#include "cmArgumentParserTypes.h" // IWYU pragma: keep
+
+template <typename Result>
+class cmArgumentParser; // IWYU pragma: keep
+
+class cmMakefile;
+
 namespace ArgumentParser {
 
-using StringList = std::vector<std::string>;
-using MultiStringList = std::vector<StringList>;
+class ParseResult
+{
+  std::map<cm::string_view, std::string> KeywordErrors;
+
+public:
+  explicit operator bool() const { return this->KeywordErrors.empty(); }
+
+  void AddKeywordError(cm::string_view key, cm::string_view text)
+
+  {
+    this->KeywordErrors[key] += text;
+  }
+
+  std::map<cm::string_view, std::string> const& GetKeywordErrors() const
+  {
+    return this->KeywordErrors;
+  }
+
+  bool MaybeReportError(cmMakefile& mf) const;
+};
+
+template <typename Result>
+typename std::enable_if<std::is_base_of<ParseResult, Result>::value,
+                        ParseResult*>::type
+AsParseResultPtr(Result& result)
+{
+  return &result;
+}
+
+template <typename Result>
+typename std::enable_if<!std::is_base_of<ParseResult, Result>::value,
+                        ParseResult*>::type
+AsParseResultPtr(Result&)
+{
+  return nullptr;
+}
+
+enum class Continue
+{
+  No,
+  Yes,
+};
+
+struct ExpectAtLeast
+{
+  std::size_t Count = 0;
+
+  ExpectAtLeast(std::size_t count)
+    : Count(count)
+  {
+  }
+};
 
 class Instance;
-using Action = std::function<void(Instance&, void*)>;
+using KeywordAction = std::function<void(Instance&)>;
+using KeywordNameAction = std::function<void(Instance&, cm::string_view)>;
+using PositionAction =
+  std::function<void(Instance&, std::size_t, cm::string_view)>;
 
-// using ActionMap = cm::flat_map<cm::string_view, Action>;
-class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
+// using KeywordActionMap = cm::flat_map<cm::string_view, KeywordAction>;
+class KeywordActionMap
+  : public std::vector<std::pair<cm::string_view, KeywordAction>>
 {
 public:
-  std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
+  std::pair<iterator, bool> Emplace(cm::string_view name,
+                                    KeywordAction action);
   const_iterator Find(cm::string_view name) const;
 };
 
+// using PositionActionMap = cm::flat_map<cm::string_view, PositionAction>;
+class PositionActionMap
+  : public std::vector<std::pair<std::size_t, PositionAction>>
+{
+public:
+  std::pair<iterator, bool> Emplace(std::size_t pos, PositionAction action);
+  const_iterator Find(std::size_t pos) const;
+};
+
+class ActionMap
+{
+public:
+  KeywordActionMap Keywords;
+  KeywordNameAction KeywordMissingValue;
+  KeywordNameAction ParsedKeyword;
+  PositionActionMap Positions;
+};
+
+class Base
+{
+public:
+  using ExpectAtLeast = ArgumentParser::ExpectAtLeast;
+  using Continue = ArgumentParser::Continue;
+  using Instance = ArgumentParser::Instance;
+  using ParseResult = ArgumentParser::ParseResult;
+
+  ArgumentParser::ActionMap Bindings;
+
+  bool MaybeBind(cm::string_view name, KeywordAction action)
+  {
+    return this->Bindings.Keywords.Emplace(name, std::move(action)).second;
+  }
+
+  void Bind(cm::string_view name, KeywordAction action)
+  {
+    bool const inserted = this->MaybeBind(name, std::move(action));
+    assert(inserted);
+    static_cast<void>(inserted);
+  }
+
+  void BindParsedKeyword(KeywordNameAction action)
+  {
+    assert(!this->Bindings.ParsedKeyword);
+    this->Bindings.ParsedKeyword = std::move(action);
+  }
+
+  void BindKeywordMissingValue(KeywordNameAction action)
+  {
+    assert(!this->Bindings.KeywordMissingValue);
+    this->Bindings.KeywordMissingValue = std::move(action);
+  }
+
+  void Bind(std::size_t pos, PositionAction action)
+  {
+    bool const inserted =
+      this->Bindings.Positions.Emplace(pos, std::move(action)).second;
+    assert(inserted);
+    static_cast<void>(inserted);
+  }
+};
+
 class Instance
 {
 public:
-  Instance(ActionMap const& bindings)
+  Instance(ActionMap const& bindings, ParseResult* parseResult,
+           std::vector<std::string>* unparsedArguments, void* result = nullptr)
     : Bindings(bindings)
+    , ParseResults(parseResult)
+    , UnparsedArguments(unparsedArguments)
+    , Result(result)
   {
   }
 
+  void Bind(std::function<Continue(cm::string_view)> f, ExpectAtLeast expect);
   void Bind(bool& val);
   void Bind(std::string& val);
-  void Bind(StringList& val);
-  void Bind(MultiStringList& val);
+  void Bind(NonEmpty<std::string>& val);
+  void Bind(Maybe<std::string>& val);
+  void Bind(MaybeEmpty<std::vector<std::string>>& val);
+  void Bind(NonEmpty<std::vector<std::string>>& val);
+  void Bind(std::vector<std::vector<std::string>>& val);
+
+  // cm::optional<> records the presence the keyword to which it binds.
+  template <typename T>
+  void Bind(cm::optional<T>& optVal)
+  {
+    if (!optVal) {
+      optVal.emplace();
+    }
+    this->Bind(*optVal);
+  }
 
-  void Consume(cm::string_view arg, void* result,
-               std::vector<std::string>* unparsedArguments,
-               std::vector<std::string>* keywordsMissingValue,
-               std::vector<std::string>* parsedKeywords);
+  template <typename Range>
+  void Parse(Range const& args, std::size_t pos = 0)
+  {
+    for (cm::string_view arg : args) {
+      this->Consume(pos++, arg);
+    }
+    this->FinishKeyword();
+  }
 
 private:
   ActionMap const& Bindings;
-  std::string* CurrentString = nullptr;
-  StringList* CurrentList = nullptr;
-  bool ExpectValue = false;
+  ParseResult* ParseResults = nullptr;
+  std::vector<std::string>* UnparsedArguments = nullptr;
+  void* Result = nullptr;
+
+  cm::string_view Keyword;
+  std::size_t KeywordValuesSeen = 0;
+  std::size_t KeywordValuesExpected = 0;
+  std::function<Continue(cm::string_view)> KeywordValueFunc;
+  bool DoneWithPositional = false;
+
+  void Consume(std::size_t pos, cm::string_view arg);
+  void FinishKeyword();
+
+  template <typename Result>
+  friend class ::cmArgumentParser;
 };
 
 } // namespace ArgumentParser
 
 template <typename Result>
-class cmArgumentParser
+class cmArgumentParser : private ArgumentParser::Base
 {
 public:
   // I *think* this function could be made `constexpr` when the code is
@@ -65,83 +226,194 @@ public:
   template <typename T>
   cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
   {
-    bool const inserted =
-      this->Bindings
-        .Emplace(name,
-                 [member](ArgumentParser::Instance& instance, void* result) {
-                   instance.Bind(static_cast<Result*>(result)->*member);
-                 })
-        .second;
-    assert(inserted), (void)inserted;
+    this->Base::Bind(name, [member](Instance& instance) {
+      instance.Bind(static_cast<Result*>(instance.Result)->*member);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(cm::static_string_view name,
+                         Continue (Result::*member)(cm::string_view),
+                         ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [member, expect](Instance& instance) {
+      Result* result = static_cast<Result*>(instance.Result);
+      instance.Bind(
+        [result, member](cm::string_view arg) -> Continue {
+          return (result->*member)(arg);
+        },
+        expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(cm::static_string_view name,
+                         Continue (Result::*member)(cm::string_view,
+                                                    cm::string_view),
+                         ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [member, expect](Instance& instance) {
+      Result* result = static_cast<Result*>(instance.Result);
+      cm::string_view keyword = instance.Keyword;
+      instance.Bind(
+        [result, member, keyword](cm::string_view arg) -> Continue {
+          return (result->*member)(keyword, arg);
+        },
+        expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(cm::static_string_view name,
+                         std::function<Continue(Result&, cm::string_view)> f,
+                         ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [f, expect](Instance& instance) {
+      Result* result = static_cast<Result*>(instance.Result);
+      instance.Bind(
+        [result, &f](cm::string_view arg) -> Continue {
+          return f(*result, arg);
+        },
+        expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(
+    cm::static_string_view name,
+    std::function<Continue(Result&, cm::string_view, cm::string_view)> f,
+    ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [f, expect](Instance& instance) {
+      Result* result = static_cast<Result*>(instance.Result);
+      cm::string_view keyword = instance.Keyword;
+      instance.Bind(
+        [result, keyword, &f](cm::string_view arg) -> Continue {
+          return f(*result, keyword, arg);
+        },
+        expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(std::size_t position,
+                         cm::optional<std::string> Result::*member)
+  {
+    this->Base::Bind(
+      position,
+      [member](Instance& instance, std::size_t, cm::string_view arg) {
+        Result* result = static_cast<Result*>(instance.Result);
+        result->*member = arg;
+      });
+    return *this;
+  }
+
+  cmArgumentParser& BindParsedKeywords(
+    std::vector<cm::string_view> Result::*member)
+  {
+    this->Base::BindParsedKeyword(
+      [member](Instance& instance, cm::string_view arg) {
+        (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
+      });
     return *this;
   }
 
   template <typename Range>
-  void Parse(Result& result, Range const& args,
-             std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr,
-             std::vector<std::string>* parsedKeywords = nullptr) const
+  bool Parse(Result& result, Range const& args,
+             std::vector<std::string>* unparsedArguments,
+             std::size_t pos = 0) const
   {
-    ArgumentParser::Instance instance(this->Bindings);
-    for (cm::string_view arg : args) {
-      instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue,
-                       parsedKeywords);
-    }
+    using ArgumentParser::AsParseResultPtr;
+    ParseResult* parseResultPtr = AsParseResultPtr(result);
+    Instance instance(this->Bindings, parseResultPtr, unparsedArguments,
+                      &result);
+    instance.Parse(args, pos);
+    return parseResultPtr ? static_cast<bool>(*parseResultPtr) : true;
   }
 
   template <typename Range>
-  Result Parse(Range const& args,
-               std::vector<std::string>* unparsedArguments = nullptr,
-               std::vector<std::string>* keywordsMissingValue = nullptr,
-               std::vector<std::string>* parsedKeywords = nullptr) const
+  Result Parse(Range const& args, std::vector<std::string>* unparsedArguments,
+               std::size_t pos = 0) const
   {
     Result result;
-    this->Parse(result, args, unparsedArguments, keywordsMissingValue,
-                parsedKeywords);
+    this->Parse(result, args, unparsedArguments, pos);
     return result;
   }
-
-private:
-  ArgumentParser::ActionMap Bindings;
 };
 
 template <>
-class cmArgumentParser<void>
+class cmArgumentParser<void> : private ArgumentParser::Base
 {
 public:
   template <typename T>
   cmArgumentParser& Bind(cm::static_string_view name, T& ref)
   {
-    bool const inserted = this->Bind(cm::string_view(name), ref);
-    assert(inserted), (void)inserted;
+    this->Base::Bind(name, [&ref](Instance& instance) { instance.Bind(ref); });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(cm::static_string_view name,
+                         std::function<Continue(cm::string_view)> f,
+                         ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [f, expect](Instance& instance) {
+      instance.Bind([&f](cm::string_view arg) -> Continue { return f(arg); },
+                    expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(
+    cm::static_string_view name,
+    std::function<Continue(cm::string_view, cm::string_view)> f,
+    ExpectAtLeast expect = { 1 })
+  {
+    this->Base::Bind(name, [f, expect](Instance& instance) {
+      cm::string_view keyword = instance.Keyword;
+      instance.Bind(
+        [keyword, &f](cm::string_view arg) -> Continue {
+          return f(keyword, arg);
+        },
+        expect);
+    });
+    return *this;
+  }
+
+  cmArgumentParser& Bind(std::size_t position, cm::optional<std::string>& ref)
+  {
+    this->Base::Bind(position,
+                     [&ref](Instance&, std::size_t, cm::string_view arg) {
+                       ref = std::string(arg);
+                     });
+    return *this;
+  }
+
+  cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
+  {
+    this->Base::BindParsedKeyword(
+      [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
     return *this;
   }
 
   template <typename Range>
-  void Parse(Range const& args,
-             std::vector<std::string>* unparsedArguments = nullptr,
-             std::vector<std::string>* keywordsMissingValue = nullptr,
-             std::vector<std::string>* parsedKeywords = nullptr) const
+  ParseResult Parse(Range const& args,
+                    std::vector<std::string>* unparsedArguments,
+                    std::size_t pos = 0) const
   {
-    ArgumentParser::Instance instance(this->Bindings);
-    for (cm::string_view arg : args) {
-      instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue,
-                       parsedKeywords);
-    }
+    ParseResult parseResult;
+    Instance instance(this->Bindings, &parseResult, unparsedArguments);
+    instance.Parse(args, pos);
+    return parseResult;
   }
 
 protected:
+  using Base::Instance;
+  using Base::BindKeywordMissingValue;
+
   template <typename T>
   bool Bind(cm::string_view name, T& ref)
   {
-    return this->Bindings
-      .Emplace(name,
-               [&ref](ArgumentParser::Instance& instance, void*) {
-                 instance.Bind(ref);
-               })
-      .second;
+    return this->MaybeBind(name,
+                           [&ref](Instance& instance) { instance.Bind(ref); });
   }
-
-private:
-  ArgumentParser::ActionMap Bindings;
 };
diff --git a/Source/cmArgumentParserTypes.h b/Source/cmArgumentParserTypes.h
new file mode 100644 (file)
index 0000000..7daae09
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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
+
+#if defined(__SUNPRO_CC)
+
+#  include <string>
+#  include <vector>
+
+namespace ArgumentParser {
+
+template <typename T>
+struct Maybe;
+template <>
+struct Maybe<std::string> : public std::string
+{
+  using std::string::basic_string;
+};
+
+template <typename T>
+struct MaybeEmpty;
+template <typename T>
+struct MaybeEmpty<std::vector<T>> : public std::vector<T>
+{
+  using std::vector<T>::vector;
+};
+
+template <typename T>
+struct NonEmpty;
+template <typename T>
+struct NonEmpty<std::vector<T>> : public std::vector<T>
+{
+  using std::vector<T>::vector;
+};
+template <>
+struct NonEmpty<std::string> : public std::string
+{
+  using std::string::basic_string;
+};
+
+} // namespace ArgumentParser
+
+#else
+
+namespace ArgumentParser {
+
+template <typename T>
+struct Maybe : public T
+{
+  using T::T;
+};
+
+template <typename T>
+struct MaybeEmpty : public T
+{
+  using T::T;
+};
+
+template <typename T>
+struct NonEmpty : public T
+{
+  using T::T;
+};
+
+} // namespace ArgumentParser
+
+#endif
diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx
new file mode 100644 (file)
index 0000000..ec79149
--- /dev/null
@@ -0,0 +1,197 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmBlockCommand.h"
+
+#include <cstdint> // IWYU pragma: keep
+#include <utility>
+
+#include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/enum_set>
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
+#include "cmExecutionStatus.h"
+#include "cmFunctionBlocker.h"
+#include "cmListFileCache.h"
+#include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+namespace {
+enum class ScopeType : std::uint8_t
+{
+  VARIABLES,
+  POLICIES
+};
+using ScopeSet = cm::enum_set<ScopeType>;
+
+class BlockScopePushPop
+{
+public:
+  BlockScopePushPop(cmMakefile* m, const ScopeSet& scopes);
+  ~BlockScopePushPop() = default;
+
+  BlockScopePushPop(const BlockScopePushPop&) = delete;
+  BlockScopePushPop& operator=(const BlockScopePushPop&) = delete;
+
+private:
+  std::unique_ptr<cmMakefile::PolicyPushPop> PolicyScope;
+  std::unique_ptr<cmMakefile::VariablePushPop> VariableScope;
+};
+
+BlockScopePushPop::BlockScopePushPop(cmMakefile* mf, const ScopeSet& scopes)
+{
+  if (scopes.contains(ScopeType::POLICIES)) {
+    this->PolicyScope = cm::make_unique<cmMakefile::PolicyPushPop>(mf);
+  }
+  if (scopes.contains(ScopeType::VARIABLES)) {
+    this->VariableScope = cm::make_unique<cmMakefile::VariablePushPop>(mf);
+  }
+}
+
+class cmBlockFunctionBlocker : public cmFunctionBlocker
+{
+public:
+  cmBlockFunctionBlocker(cmMakefile* mf, const ScopeSet& scopes,
+                         std::vector<std::string> variableNames);
+  ~cmBlockFunctionBlocker() override;
+
+  cm::string_view StartCommandName() const override { return "block"_s; }
+  cm::string_view EndCommandName() const override { return "endblock"_s; }
+
+  bool EndCommandSupportsArguments() const override { return false; }
+
+  bool ArgumentsMatch(cmListFileFunction const& lff,
+                      cmMakefile& mf) const override;
+
+  bool Replay(std::vector<cmListFileFunction> functions,
+              cmExecutionStatus& inStatus) override;
+
+private:
+  cmMakefile* Makefile;
+  ScopeSet Scopes;
+  BlockScopePushPop BlockScope;
+  std::vector<std::string> VariableNames;
+};
+
+cmBlockFunctionBlocker::cmBlockFunctionBlocker(
+  cmMakefile* const mf, const ScopeSet& scopes,
+  std::vector<std::string> variableNames)
+  : Makefile{ mf }
+  , Scopes{ scopes }
+  , BlockScope{ mf, scopes }
+  , VariableNames{ std::move(variableNames) }
+{
+}
+
+cmBlockFunctionBlocker::~cmBlockFunctionBlocker()
+{
+  if (this->Scopes.contains(ScopeType::VARIABLES)) {
+    this->Makefile->RaiseScope(this->VariableNames);
+  }
+}
+
+bool cmBlockFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
+                                            cmMakefile&) const
+{
+  // no arguments expected for endblock()
+  // but this method should not be called because EndCommandHasArguments()
+  // returns false.
+  return lff.Arguments().empty();
+}
+
+bool cmBlockFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
+                                    cmExecutionStatus& inStatus)
+{
+  auto& mf = inStatus.GetMakefile();
+
+  // Invoke all the functions that were collected in the block.
+  for (cmListFileFunction const& fn : functions) {
+    cmExecutionStatus status(mf);
+    mf.ExecuteCommand(fn, status);
+    if (status.GetReturnInvoked()) {
+      mf.RaiseScope(status.GetReturnVariables());
+      inStatus.SetReturnInvoked(status.GetReturnVariables());
+      return true;
+    }
+    if (status.GetBreakInvoked()) {
+      inStatus.SetBreakInvoked();
+      return true;
+    }
+    if (status.GetContinueInvoked()) {
+      inStatus.SetContinueInvoked();
+      return true;
+    }
+    if (cmSystemTools::GetFatalErrorOccurred()) {
+      return true;
+    }
+  }
+  return true;
+}
+
+} // anonymous namespace
+
+bool cmBlockCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status)
+{
+  struct Arguments : public ArgumentParser::ParseResult
+  {
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> ScopeFor;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Propagate;
+  };
+  static auto const parser = cmArgumentParser<Arguments>{}
+                               .Bind("SCOPE_FOR"_s, &Arguments::ScopeFor)
+                               .Bind("PROPAGATE"_s, &Arguments::Propagate);
+  std::vector<std::string> unrecognizedArguments;
+  auto parsedArgs = parser.Parse(args, &unrecognizedArguments);
+
+  if (!unrecognizedArguments.empty()) {
+    status.SetError(cmStrCat("called with unsupported argument \"",
+                             unrecognizedArguments[0], '"'));
+    cmSystemTools::SetFatalErrorOccurred();
+    return false;
+  }
+
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
+    cmSystemTools::SetFatalErrorOccurred();
+    return true;
+  }
+
+  ScopeSet scopes;
+
+  if (parsedArgs.ScopeFor) {
+    for (auto const& scope : *parsedArgs.ScopeFor) {
+      if (scope == "VARIABLES"_s) {
+        scopes.insert(ScopeType::VARIABLES);
+        continue;
+      }
+      if (scope == "POLICIES"_s) {
+        scopes.insert(ScopeType::POLICIES);
+        continue;
+      }
+      status.SetError(cmStrCat("SCOPE_FOR unsupported scope \"", scope, '"'));
+      cmSystemTools::SetFatalErrorOccurred();
+      return false;
+    }
+  } else {
+    scopes = { ScopeType::VARIABLES, ScopeType::POLICIES };
+  }
+  if (!scopes.contains(ScopeType::VARIABLES) &&
+      !parsedArgs.Propagate.empty()) {
+    status.SetError(
+      "PROPAGATE cannot be specified without a new scope for VARIABLES");
+    cmSystemTools::SetFatalErrorOccurred();
+    return false;
+  }
+
+  // create a function blocker
+  auto fb = cm::make_unique<cmBlockFunctionBlocker>(
+    &status.GetMakefile(), scopes, parsedArgs.Propagate);
+  status.GetMakefile().AddFunctionBlocker(std::move(fb));
+
+  return true;
+}
diff --git a/Source/cmBlockCommand.h b/Source/cmBlockCommand.h
new file mode 100644 (file)
index 0000000..5fd8f42
--- /dev/null
@@ -0,0 +1,14 @@
+/* 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 <string>
+#include <vector>
+
+class cmExecutionStatus;
+
+/// Starts block() ... endblock() block
+bool cmBlockCommand(std::vector<std::string> const& args,
+                    cmExecutionStatus& status);
index 0750eea..58129a0 100644 (file)
@@ -474,7 +474,7 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
   }
   std::string const& key = *args.begin();
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
     std::string ValueName;
     bool ValueNames = false;
@@ -491,19 +491,15 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
     .Bind("SEPARATOR"_s, &Arguments::Separator)
     .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable);
   std::vector<std::string> invalidArgs;
-  std::vector<std::string> keywordsMissingValue;
 
-  Arguments const arguments =
-    parser.Parse(args.advance(1), &invalidArgs, &keywordsMissingValue);
+  Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs);
   if (!invalidArgs.empty()) {
     status.SetError(cmStrCat("given invalid argument(s) \"",
                              cmJoin(invalidArgs, ", "_s), "\"."));
     return false;
   }
-  if (!keywordsMissingValue.empty()) {
-    status.SetError(cmStrCat("missing expected value for argument(s) \"",
-                             cmJoin(keywordsMissingValue, ", "_s), "\"."));
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if ((!arguments.ValueName.empty() &&
        (arguments.ValueNames || arguments.SubKeys)) ||
index a2aaa2a..68e658c 100644 (file)
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmDependencyProvider.h"
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmake.h"
 
 namespace {
 
@@ -33,13 +36,14 @@ bool FatalError(cmExecutionStatus& status, std::string const& error)
   return false;
 }
 
-std::array<cm::static_string_view, 12> InvalidCommands{
+std::array<cm::static_string_view, 14> InvalidCommands{
   { // clang-format off
   "function"_s, "endfunction"_s,
   "macro"_s, "endmacro"_s,
   "if"_s, "elseif"_s, "else"_s, "endif"_s,
   "while"_s, "endwhile"_s,
-  "foreach"_s, "endforeach"_s
+  "foreach"_s, "endforeach"_s,
+  "block"_s, "endblock"_s
   } // clang-format on
 };
 
@@ -235,7 +239,7 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
   struct SetProviderArgs
   {
     std::string Command;
-    std::vector<std::string> Methods;
+    ArgumentParser::NonEmpty<std::vector<std::string>> Methods;
   };
 
   auto const ArgsParser =
@@ -303,6 +307,27 @@ bool cmCMakeLanguageCommandSET_DEPENDENCY_PROVIDER(
 
   return true;
 }
+
+bool cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(
+  std::vector<cmListFileArgument> const& args, cmExecutionStatus& status)
+{
+  cmMakefile& makefile = status.GetMakefile();
+  std::vector<std::string> expandedArgs;
+  makefile.ExpandArguments(args, expandedArgs);
+
+  if (args.size() < 2 || expandedArgs.size() > 2) {
+    return FatalError(
+      status,
+      "sub-command GET_MESSAGE_LOG_LEVEL expects exactly one argument");
+  }
+
+  Message::LogLevel logLevel = makefile.GetCurrentLogLevel();
+  std::string outputValue = cmake::LogLevelToString(logLevel);
+
+  const std::string& outputVariable = expandedArgs[1];
+  makefile.AddDefinition(outputVariable, outputValue);
+  return true;
+}
 }
 
 bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
@@ -451,5 +476,9 @@ bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
     return cmCMakeLanguageCommandEVAL(args, status);
   }
 
+  if (expArgs[expArg] == "GET_MESSAGE_LOG_LEVEL") {
+    return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status);
+  }
+
   return FatalError(status, "called with unknown meta-operation");
 }
index bf94c2d..7755082 100644 (file)
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakePathCommand.h"
 
-#include <algorithm>
 #include <functional>
 #include <iomanip>
 #include <map>
 #include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmCMakePath.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
@@ -43,15 +44,12 @@ public:
   }
 
   template <int Advance = 2>
-  Result Parse(std::vector<std::string> const& args,
-               std::vector<std::string>* keywordsMissingValue = nullptr,
-               std::vector<std::string>* parsedKeywords = nullptr) const
+  Result Parse(std::vector<std::string> const& args) const
   {
     this->Inputs.clear();
 
     return this->cmArgumentParser<Result>::Parse(
-      cmMakeRange(args).advance(Advance), &this->Inputs, keywordsMissingValue,
-      parsedKeywords);
+      cmMakeRange(args).advance(Advance), &this->Inputs);
   }
 
   const std::vector<std::string>& GetInputs() const { return this->Inputs; }
@@ -82,52 +80,14 @@ public:
   template <int Advance = 2>
   Result Parse(std::vector<std::string> const& args) const
   {
-    this->KeywordsMissingValue.clear();
-    this->ParsedKeywords.clear();
-
     return this->CMakePathArgumentParser<Result>::template Parse<Advance>(
-      args, &this->KeywordsMissingValue, &this->ParsedKeywords);
-  }
-
-  const std::vector<std::string>& GetKeywordsMissingValue() const
-  {
-    return this->KeywordsMissingValue;
-  }
-  const std::vector<std::string>& GetParsedKeywords() const
-  {
-    return this->ParsedKeywords;
-  }
-
-  bool checkOutputVariable(const Result& arguments,
-                           cmExecutionStatus& status) const
-  {
-    if (std::find(this->GetKeywordsMissingValue().begin(),
-                  this->GetKeywordsMissingValue().end(),
-                  "OUTPUT_VARIABLE"_s) !=
-        this->GetKeywordsMissingValue().end()) {
-      status.SetError("OUTPUT_VARIABLE requires an argument.");
-      return false;
-    }
-
-    if (std::find(this->GetParsedKeywords().begin(),
-                  this->GetParsedKeywords().end(),
-                  "OUTPUT_VARIABLE"_s) != this->GetParsedKeywords().end() &&
-        arguments.Output.empty()) {
-      status.SetError("Invalid name for output variable.");
-      return false;
-    }
-
-    return true;
+      args);
   }
-
-private:
-  mutable std::vector<std::string> KeywordsMissingValue;
-  mutable std::vector<std::string> ParsedKeywords;
 };
 
-struct OutputVariable
+struct OutputVariable : public ArgumentParser::ParseResult
 {
-  std::string Output;
+  cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
 };
 // Usable when OUTPUT_VARIABLE is the only option
 class OutputVariableParser
@@ -297,8 +257,8 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
 
   const auto arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   cmCMakePath path(status.GetMakefile().GetSafeDefinition(args[1]));
@@ -307,7 +267,7 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
   }
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -319,8 +279,8 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args,
 
   const auto arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   std::string inputPath;
@@ -334,7 +294,7 @@ bool HandleAppendStringCommand(std::vector<std::string> const& args,
   }
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -346,8 +306,8 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
 
   const auto arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (!parser.GetInputs().empty()) {
@@ -364,7 +324,7 @@ bool HandleRemoveFilenameCommand(std::vector<std::string> const& args,
   path.RemoveFileName();
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -376,8 +336,8 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
 
   const auto arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (parser.GetInputs().size() > 1) {
@@ -395,7 +355,7 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
     parser.GetInputs().empty() ? "" : parser.GetInputs().front());
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -403,9 +363,9 @@ bool HandleReplaceFilenameCommand(std::vector<std::string> const& args,
 bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
                                   cmExecutionStatus& status)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string Output;
+    cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
     bool LastOnly = false;
   };
 
@@ -415,8 +375,8 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
 
   Arguments const arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (!parser.GetInputs().empty()) {
@@ -438,7 +398,7 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
   }
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -446,9 +406,9 @@ bool HandleRemoveExtensionCommand(std::vector<std::string> const& args,
 bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
                                    cmExecutionStatus& status)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string Output;
+    cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
     bool LastOnly = false;
   };
 
@@ -458,8 +418,8 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
 
   Arguments const arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (parser.GetInputs().size() > 1) {
@@ -483,7 +443,7 @@ bool HandleReplaceExtensionCommand(std::vector<std::string> const& args,
   }
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -495,8 +455,8 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args,
 
   const auto arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (!parser.GetInputs().empty()) {
@@ -512,7 +472,7 @@ bool HandleNormalPathCommand(std::vector<std::string> const& args,
   auto path = cmCMakePath(inputPath).Normal();
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
@@ -523,10 +483,10 @@ bool HandleTransformPathCommand(
                                   const std::string& base)>& transform,
   bool normalizeOption = false)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string Output;
-    std::string BaseDirectory;
+    cm::optional<ArgumentParser::NonEmpty<std::string>> Output;
+    cm::optional<std::string> BaseDirectory;
     bool Normalize = false;
   };
 
@@ -538,8 +498,8 @@ bool HandleTransformPathCommand(
 
   Arguments arguments = parser.Parse(args);
 
-  if (!parser.checkOutputVariable(arguments, status)) {
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (!parser.GetInputs().empty()) {
@@ -547,17 +507,11 @@ bool HandleTransformPathCommand(
     return false;
   }
 
-  if (std::find(parser.GetKeywordsMissingValue().begin(),
-                parser.GetKeywordsMissingValue().end(), "BASE_DIRECTORY"_s) !=
-      parser.GetKeywordsMissingValue().end()) {
-    status.SetError("BASE_DIRECTORY requires an argument.");
-    return false;
-  }
-
-  if (std::find(parser.GetParsedKeywords().begin(),
-                parser.GetParsedKeywords().end(),
-                "BASE_DIRECTORY"_s) == parser.GetParsedKeywords().end()) {
-    arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
+  std::string baseDirectory;
+  if (arguments.BaseDirectory) {
+    baseDirectory = *arguments.BaseDirectory;
+  } else {
+    baseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
   }
 
   std::string inputPath;
@@ -565,13 +519,13 @@ bool HandleTransformPathCommand(
     return false;
   }
 
-  auto path = transform(cmCMakePath(inputPath), arguments.BaseDirectory);
+  auto path = transform(cmCMakePath(inputPath), baseDirectory);
   if (arguments.Normalize) {
     path = path.Normal();
   }
 
   status.GetMakefile().AddDefinition(
-    arguments.Output.empty() ? args[1] : arguments.Output, path.String());
+    arguments.Output ? *arguments.Output : args[1], path.String());
 
   return true;
 }
index b737c1f..7325e44 100644 (file)
@@ -43,6 +43,10 @@ using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
 using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
 using BuildPreset = cmCMakePresetsGraph::BuildPreset;
 using TestPreset = cmCMakePresetsGraph::TestPreset;
+using PackagePreset = cmCMakePresetsGraph::PackagePreset;
+using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
+template <typename T>
+using PresetPair = cmCMakePresetsGraph::PresetPair<T>;
 using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult;
 using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander;
 
@@ -261,6 +265,8 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset,
   if (out->Output) {
     CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders,
                  graph.GetVersion(preset));
+    CHECK_EXPAND(out, out->Output->OutputJUnitFile, macroExpanders,
+                 graph.GetVersion(preset));
   }
 
   if (out->Filter) {
@@ -301,6 +307,36 @@ bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset,
   return true;
 }
 
+bool ExpandMacros(const cmCMakePresetsGraph& graph,
+                  const PackagePreset& preset,
+                  cm::optional<PackagePreset>& out,
+                  const std::vector<MacroExpander>& macroExpanders)
+{
+  for (auto& variable : out->Variables) {
+    CHECK_EXPAND(out, variable.second, macroExpanders,
+                 graph.GetVersion(preset));
+  }
+
+  CHECK_EXPAND(out, out->ConfigFile, macroExpanders, graph.GetVersion(preset));
+  CHECK_EXPAND(out, out->PackageName, macroExpanders,
+               graph.GetVersion(preset));
+  CHECK_EXPAND(out, out->PackageVersion, macroExpanders,
+               graph.GetVersion(preset));
+  CHECK_EXPAND(out, out->PackageDirectory, macroExpanders,
+               graph.GetVersion(preset));
+  CHECK_EXPAND(out, out->VendorName, macroExpanders, graph.GetVersion(preset));
+
+  return true;
+}
+
+bool ExpandMacros(const cmCMakePresetsGraph& /*graph*/,
+                  const WorkflowPreset& /*preset*/,
+                  cm::optional<WorkflowPreset>& /*out*/,
+                  const std::vector<MacroExpander>& /*macroExpanders*/)
+{
+  return true;
+}
+
 template <class T>
 bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
                   cm::optional<T>& out)
@@ -556,6 +592,42 @@ ExpandMacroResult ExpandMacro(std::string& out,
 
   return ExpandMacroResult::Error;
 }
+
+template <typename T>
+ReadFileResult SetupWorkflowConfigurePreset(
+  const T& preset, const ConfigurePreset*& configurePreset)
+{
+  if (preset.ConfigurePreset != configurePreset->Name) {
+    return ReadFileResult::INVALID_WORKFLOW_STEPS;
+  }
+  return ReadFileResult::READ_OK;
+}
+
+template <>
+ReadFileResult SetupWorkflowConfigurePreset<ConfigurePreset>(
+  const ConfigurePreset& preset, const ConfigurePreset*& configurePreset)
+{
+  configurePreset = &preset;
+  return ReadFileResult::READ_OK;
+}
+
+template <typename T>
+ReadFileResult TryReachPresetFromWorkflow(
+  const WorkflowPreset& origin,
+  const std::map<std::string, PresetPair<T>>& presets, const std::string& name,
+  const ConfigurePreset*& configurePreset)
+{
+  auto it = presets.find(name);
+  if (it == presets.end()) {
+    return ReadFileResult::INVALID_WORKFLOW_STEPS;
+  }
+  if (!origin.OriginFile->ReachableFiles.count(
+        it->second.Unexpanded.OriginFile)) {
+    return ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE;
+  }
+  return SetupWorkflowConfigurePreset<T>(it->second.Unexpanded,
+                                         configurePreset);
+}
 }
 
 bool cmCMakePresetsGraphInternal::EqualsCondition::Evaluate(
@@ -781,6 +853,7 @@ cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
                            parentOutput.OutputOnFailure);
       InheritOptionalValue(output.Quiet, parentOutput.Quiet);
       InheritString(output.OutputLogFile, parentOutput.OutputLogFile);
+      InheritString(output.OutputJUnitFile, parentOutput.OutputJUnitFile);
       InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary);
       InheritOptionalValue(output.SubprojectSummary,
                            parentOutput.SubprojectSummary);
@@ -868,6 +941,57 @@ cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(int /* version */)
   return ReadFileResult::READ_OK;
 }
 
+cmCMakePresetsGraph::ReadFileResult
+cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
+  const cmCMakePresetsGraph::Preset& parentPreset)
+{
+  auto& preset = *this;
+  const PackagePreset& parent =
+    static_cast<const PackagePreset&>(parentPreset);
+
+  InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
+  InheritOptionalValue(preset.InheritConfigureEnvironment,
+                       parent.InheritConfigureEnvironment);
+  InheritVector(preset.Generators, parent.Generators);
+  InheritVector(preset.Configurations, parent.Configurations);
+
+  for (auto const& v : parent.Variables) {
+    preset.Variables.insert(v);
+  }
+
+  InheritOptionalValue(preset.DebugOutput, parent.DebugOutput);
+  InheritOptionalValue(preset.VerboseOutput, parent.VerboseOutput);
+  InheritString(preset.PackageName, parent.PackageName);
+  InheritString(preset.PackageVersion, parent.PackageVersion);
+  InheritString(preset.PackageDirectory, parent.PackageDirectory);
+  InheritString(preset.VendorName, parent.VendorName);
+
+  return ReadFileResult::READ_OK;
+}
+
+cmCMakePresetsGraph::ReadFileResult
+cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(int /* version */)
+{
+  auto& preset = *this;
+  if (!preset.Hidden && preset.ConfigurePreset.empty()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+  return ReadFileResult::READ_OK;
+}
+
+cmCMakePresetsGraph::ReadFileResult
+cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit(
+  const cmCMakePresetsGraph::Preset& /*parentPreset*/)
+{
+  return ReadFileResult::READ_OK;
+}
+
+cmCMakePresetsGraph::ReadFileResult
+cmCMakePresetsGraph::WorkflowPreset::VisitPresetAfterInherit(int /* version */)
+{
+  return ReadFileResult::READ_OK;
+}
+
 std::string cmCMakePresetsGraph::GetFilename(const std::string& sourceDir)
 {
   return cmStrCat(sourceDir, "/CMakePresets.json");
@@ -901,8 +1025,9 @@ 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);
+    auto result =
+      this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
+                         inProgressFiles, file, this->errors);
     if (result != ReadFileResult::READ_OK) {
       return result;
     }
@@ -910,8 +1035,9 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
   } else {
     filename = GetFilename(this->SourceDir);
     if (cmSystemTools::FileExists(filename)) {
-      auto result = this->ReadJSONFile(
-        filename, RootType::Project, ReadReason::Root, inProgressFiles, file);
+      auto result =
+        this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
+                           inProgressFiles, file, this->errors);
       if (result != ReadFileResult::READ_OK) {
         return result;
       }
@@ -928,6 +1054,8 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
   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));
 
   for (auto& it : this->ConfigurePresets) {
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
@@ -983,6 +1111,79 @@ cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
     }
   }
 
+  for (auto& it : this->PackagePresets) {
+    if (!it.second.Unexpanded.Hidden) {
+      const auto configurePreset =
+        this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
+      if (configurePreset == this->ConfigurePresets.end()) {
+        return ReadFileResult::INVALID_CONFIGURE_PRESET;
+      }
+      if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
+            configurePreset->second.Unexpanded.OriginFile)) {
+        return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
+      }
+
+      if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
+        it.second.Unexpanded.Environment.insert(
+          configurePreset->second.Unexpanded.Environment.begin(),
+          configurePreset->second.Unexpanded.Environment.end());
+      }
+    }
+
+    if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
+      return ReadFileResult::INVALID_MACRO_EXPANSION;
+    }
+  }
+
+  for (auto& it : this->WorkflowPresets) {
+    using Type = WorkflowPreset::WorkflowStep::Type;
+
+    const ConfigurePreset* configurePreset = nullptr;
+    for (auto const& step : it.second.Unexpanded.Steps) {
+      if (configurePreset == nullptr && step.PresetType != Type::Configure) {
+        return ReadFileResult::INVALID_WORKFLOW_STEPS;
+      }
+      if (configurePreset != nullptr && step.PresetType == Type::Configure) {
+        return ReadFileResult::INVALID_WORKFLOW_STEPS;
+      }
+
+      ReadFileResult result;
+      switch (step.PresetType) {
+        case Type::Configure:
+          result = TryReachPresetFromWorkflow(
+            it.second.Unexpanded, this->ConfigurePresets, step.PresetName,
+            configurePreset);
+          break;
+        case Type::Build:
+          result = TryReachPresetFromWorkflow(
+            it.second.Unexpanded, this->BuildPresets, step.PresetName,
+            configurePreset);
+          break;
+        case Type::Test:
+          result =
+            TryReachPresetFromWorkflow(it.second.Unexpanded, this->TestPresets,
+                                       step.PresetName, configurePreset);
+          break;
+        case Type::Package:
+          result = TryReachPresetFromWorkflow(
+            it.second.Unexpanded, this->PackagePresets, step.PresetName,
+            configurePreset);
+          break;
+      }
+      if (result != ReadFileResult::READ_OK) {
+        return result;
+      }
+    }
+
+    if (configurePreset == nullptr) {
+      return ReadFileResult::INVALID_WORKFLOW_STEPS;
+    }
+
+    if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
+      return ReadFileResult::INVALID_MACRO_EXPANSION;
+    }
+  }
+
   return ReadFileResult::READ_OK;
 }
 
@@ -1026,6 +1227,10 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
     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:
@@ -1047,6 +1252,12 @@ const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
     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";
@@ -1057,14 +1268,28 @@ void cmCMakePresetsGraph::ClearPresets()
   this->ConfigurePresets.clear();
   this->BuildPresets.clear();
   this->TestPresets.clear();
+  this->PackagePresets.clear();
+  this->WorkflowPresets.clear();
 
   this->ConfigurePresetOrder.clear();
   this->BuildPresetOrder.clear();
   this->TestPresetOrder.clear();
+  this->PackagePresetOrder.clear();
+  this->WorkflowPresetOrder.clear();
 
   this->Files.clear();
 }
 
+void cmCMakePresetsGraph::printPrecedingNewline(PrintPrecedingNewline* newline)
+{
+  if (newline) {
+    if (*newline == PrintPrecedingNewline::True) {
+      std::cout << std::endl;
+    }
+    *newline = PrintPrecedingNewline::True;
+  }
+}
+
 void cmCMakePresetsGraph::PrintPresets(
   const std::vector<const cmCMakePresetsGraph::Preset*>& presets)
 {
@@ -1093,13 +1318,16 @@ void cmCMakePresetsGraph::PrintPresets(
   }
 }
 
-void cmCMakePresetsGraph::PrintConfigurePresetList() const
+void cmCMakePresetsGraph::PrintConfigurePresetList(
+  PrintPrecedingNewline* newline) const
 {
-  PrintConfigurePresetList([](const ConfigurePreset&) { return true; });
+  PrintConfigurePresetList([](const ConfigurePreset&) { return true; },
+                           newline);
 }
 
 void cmCMakePresetsGraph::PrintConfigurePresetList(
-  const std::function<bool(const ConfigurePreset&)>& filter) const
+  const std::function<bool(const ConfigurePreset&)>& filter,
+  PrintPrecedingNewline* newline) const
 {
   std::vector<const cmCMakePresetsGraph::Preset*> presets;
   for (auto const& p : this->ConfigurePresetOrder) {
@@ -1112,12 +1340,14 @@ void cmCMakePresetsGraph::PrintConfigurePresetList(
   }
 
   if (!presets.empty()) {
+    printPrecedingNewline(newline);
     std::cout << "Available configure presets:\n\n";
     cmCMakePresetsGraph::PrintPresets(presets);
   }
 }
 
-void cmCMakePresetsGraph::PrintBuildPresetList() const
+void cmCMakePresetsGraph::PrintBuildPresetList(
+  PrintPrecedingNewline* newline) const
 {
   std::vector<const cmCMakePresetsGraph::Preset*> presets;
   for (auto const& p : this->BuildPresetOrder) {
@@ -1130,12 +1360,14 @@ void cmCMakePresetsGraph::PrintBuildPresetList() const
   }
 
   if (!presets.empty()) {
+    printPrecedingNewline(newline);
     std::cout << "Available build presets:\n\n";
     cmCMakePresetsGraph::PrintPresets(presets);
   }
 }
 
-void cmCMakePresetsGraph::PrintTestPresetList() const
+void cmCMakePresetsGraph::PrintTestPresetList(
+  PrintPrecedingNewline* newline) const
 {
   std::vector<const cmCMakePresetsGraph::Preset*> presets;
   for (auto const& p : this->TestPresetOrder) {
@@ -1148,16 +1380,66 @@ void cmCMakePresetsGraph::PrintTestPresetList() const
   }
 
   if (!presets.empty()) {
+    printPrecedingNewline(newline);
     std::cout << "Available test presets:\n\n";
     cmCMakePresetsGraph::PrintPresets(presets);
   }
 }
 
+void cmCMakePresetsGraph::PrintPackagePresetList(
+  PrintPrecedingNewline* newline) const
+{
+  this->PrintPackagePresetList([](const PackagePreset&) { return true; },
+                               newline);
+}
+
+void cmCMakePresetsGraph::PrintPackagePresetList(
+  const std::function<bool(const PackagePreset&)>& filter,
+  PrintPrecedingNewline* newline) const
+{
+  std::vector<const cmCMakePresetsGraph::Preset*> presets;
+  for (auto const& p : this->PackagePresetOrder) {
+    auto const& preset = this->PackagePresets.at(p);
+    if (!preset.Unexpanded.Hidden && preset.Expanded &&
+        preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
+      presets.push_back(
+        static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
+    }
+  }
+
+  if (!presets.empty()) {
+    printPrecedingNewline(newline);
+    std::cout << "Available package presets:\n\n";
+    cmCMakePresetsGraph::PrintPresets(presets);
+  }
+}
+
+void cmCMakePresetsGraph::PrintWorkflowPresetList(
+  PrintPrecedingNewline* newline) const
+{
+  std::vector<const cmCMakePresetsGraph::Preset*> presets;
+  for (auto const& p : this->WorkflowPresetOrder) {
+    auto const& preset = this->WorkflowPresets.at(p);
+    if (!preset.Unexpanded.Hidden && preset.Expanded &&
+        preset.Expanded->ConditionResult) {
+      presets.push_back(
+        static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
+    }
+  }
+
+  if (!presets.empty()) {
+    printPrecedingNewline(newline);
+    std::cout << "Available workflow presets:\n\n";
+    cmCMakePresetsGraph::PrintPresets(presets);
+  }
+}
+
 void cmCMakePresetsGraph::PrintAllPresets() const
 {
-  this->PrintConfigurePresetList();
-  std::cout << std::endl;
-  this->PrintBuildPresetList();
-  std::cout << std::endl;
-  this->PrintTestPresetList();
+  PrintPrecedingNewline newline = PrintPrecedingNewline::False;
+  this->PrintConfigurePresetList(&newline);
+  this->PrintBuildPresetList(&newline);
+  this->PrintTestPresetList(&newline);
+  this->PrintPackagePresetList(&newline);
+  this->PrintWorkflowPresetList(&newline);
 }
index f1f8662..17c902b 100644 (file)
@@ -41,6 +41,8 @@ public:
     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,
@@ -50,8 +52,12 @@ public:
     TOOLCHAIN_FILE_UNSUPPORTED,
     CYCLIC_INCLUDE,
     TEST_OUTPUT_TRUNCATION_UNSUPPORTED,
+    INVALID_WORKFLOW_STEPS,
+    WORKFLOW_STEP_UNREACHABLE_FROM_FILE,
+    CTEST_JUNIT_UNSUPPORTED,
   };
 
+  std::string errors;
   enum class ArchToolsetStrategy
   {
     Set,
@@ -95,7 +101,7 @@ public:
 
     std::string Name;
     std::vector<std::string> Inherits;
-    bool Hidden;
+    bool Hidden = false;
     File* OriginFile;
     std::string DisplayName;
     std::string Description;
@@ -225,6 +231,7 @@ public:
       cm::optional<bool> OutputOnFailure;
       cm::optional<bool> Quiet;
       std::string OutputLogFile;
+      std::string OutputJUnitFile;
       cm::optional<bool> LabelSummary;
       cm::optional<bool> SubprojectSummary;
       cm::optional<int> MaxPassedTestOutputSize;
@@ -325,6 +332,79 @@ public:
     ReadFileResult VisitPresetAfterInherit(int /* version */) override;
   };
 
+  class PackagePreset : public Preset
+  {
+  public:
+    PackagePreset() = default;
+    PackagePreset(PackagePreset&& /*other*/) = default;
+    PackagePreset(const PackagePreset& /*other*/) = default;
+    PackagePreset& operator=(const PackagePreset& /*other*/) = default;
+    ~PackagePreset() override = default;
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+    PackagePreset& operator=(PackagePreset&& /*other*/) = default;
+#else
+    // The move assignment operators for several STL classes did not become
+    // noexcept until C++17, which causes some tools to warn about this move
+    // assignment operator throwing an exception when it shouldn't.
+    PackagePreset& operator=(PackagePreset&& /*other*/) = delete;
+#endif
+
+    std::string ConfigurePreset;
+    cm::optional<bool> InheritConfigureEnvironment;
+    std::vector<std::string> Generators;
+    std::vector<std::string> Configurations;
+    std::map<std::string, std::string> Variables;
+    std::string ConfigFile;
+
+    cm::optional<bool> DebugOutput;
+    cm::optional<bool> VerboseOutput;
+
+    std::string PackageName;
+    std::string PackageVersion;
+    std::string PackageDirectory;
+    std::string VendorName;
+
+    ReadFileResult VisitPresetInherit(const Preset& parent) override;
+    ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+  };
+
+  class WorkflowPreset : public Preset
+  {
+  public:
+    WorkflowPreset() = default;
+    WorkflowPreset(WorkflowPreset&& /*other*/) = default;
+    WorkflowPreset(const WorkflowPreset& /*other*/) = default;
+    WorkflowPreset& operator=(const WorkflowPreset& /*other*/) = default;
+    ~WorkflowPreset() override = default;
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+    WorkflowPreset& operator=(WorkflowPreset&& /*other*/) = default;
+#else
+    // The move assignment operators for several STL classes did not become
+    // noexcept until C++17, which causes some tools to warn about this move
+    // assignment operator throwing an exception when it shouldn't.
+    WorkflowPreset& operator=(WorkflowPreset&& /*other*/) = delete;
+#endif
+
+    class WorkflowStep
+    {
+    public:
+      enum class Type
+      {
+        Configure,
+        Build,
+        Test,
+        Package,
+      };
+      Type PresetType;
+      std::string PresetName;
+    };
+
+    std::vector<WorkflowStep> Steps;
+
+    ReadFileResult VisitPresetInherit(const Preset& parent) override;
+    ReadFileResult VisitPresetAfterInherit(int /* version */) override;
+  };
+
   template <class T>
   class PresetPair
   {
@@ -336,10 +416,14 @@ public:
   std::map<std::string, PresetPair<ConfigurePreset>> ConfigurePresets;
   std::map<std::string, PresetPair<BuildPreset>> BuildPresets;
   std::map<std::string, PresetPair<TestPreset>> TestPresets;
+  std::map<std::string, PresetPair<PackagePreset>> PackagePresets;
+  std::map<std::string, PresetPair<WorkflowPreset>> WorkflowPresets;
 
   std::vector<std::string> ConfigurePresetOrder;
   std::vector<std::string> BuildPresetOrder;
   std::vector<std::string> TestPresetOrder;
+  std::vector<std::string> PackagePresetOrder;
+  std::vector<std::string> WorkflowPresetOrder;
 
   std::string SourceDir;
   std::vector<std::unique_ptr<File>> Files;
@@ -382,13 +466,27 @@ public:
     return "";
   }
 
+  enum class PrintPrecedingNewline
+  {
+    False,
+    True,
+  };
+  static void printPrecedingNewline(PrintPrecedingNewline* p);
+
   static void PrintPresets(
     const std::vector<const cmCMakePresetsGraph::Preset*>& presets);
-  void PrintConfigurePresetList() const;
   void PrintConfigurePresetList(
-    const std::function<bool(const ConfigurePreset&)>& filter) const;
-  void PrintBuildPresetList() const;
-  void PrintTestPresetList() const;
+    PrintPrecedingNewline* newline = nullptr) const;
+  void PrintConfigurePresetList(
+    const std::function<bool(const ConfigurePreset&)>& filter,
+    PrintPrecedingNewline* newline = nullptr) const;
+  void PrintBuildPresetList(PrintPrecedingNewline* newline = nullptr) const;
+  void PrintTestPresetList(PrintPrecedingNewline* newline = nullptr) const;
+  void PrintPackagePresetList(PrintPrecedingNewline* newline = nullptr) const;
+  void PrintPackagePresetList(
+    const std::function<bool(const PackagePreset&)>& filter,
+    PrintPrecedingNewline* newline = nullptr) const;
+  void PrintWorkflowPresetList(PrintPrecedingNewline* newline = nullptr) const;
   void PrintAllPresets() const;
 
 private:
@@ -407,7 +505,7 @@ private:
   ReadFileResult ReadProjectPresetsInternal(bool allowNoFiles);
   ReadFileResult ReadJSONFile(const std::string& filename, RootType rootType,
                               ReadReason readReason,
-                              std::vector<File*>& inProgressFiles,
-                              File*& file);
+                              std::vector<File*>& inProgressFiles, File*& file,
+                              std::string& errMsg);
   void ClearPresets();
 };
index f7c7349..9e47a69 100644 (file)
@@ -147,6 +147,14 @@ cmCMakePresetsGraph::ReadFileResult BuildPresetsHelper(
 cmCMakePresetsGraph::ReadFileResult TestPresetsHelper(
   std::vector<cmCMakePresetsGraph::TestPreset>& out, const Json::Value* value);
 
+cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper(
+  std::vector<cmCMakePresetsGraph::PackagePreset>& out,
+  const Json::Value* value);
+
+cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper(
+  std::vector<cmCMakePresetsGraph::WorkflowPreset>& out,
+  const Json::Value* value);
+
 cmJSONHelper<std::nullptr_t, cmCMakePresetsGraph::ReadFileResult> VendorHelper(
   cmCMakePresetsGraph::ReadFileResult error);
 
index d11e839..eec53c1 100644 (file)
@@ -30,11 +30,13 @@ using CacheVariable = cmCMakePresetsGraph::CacheVariable;
 using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
 using BuildPreset = cmCMakePresetsGraph::BuildPreset;
 using TestPreset = cmCMakePresetsGraph::TestPreset;
+using PackagePreset = cmCMakePresetsGraph::PackagePreset;
+using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
 using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
 using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
 
 constexpr int MIN_VERSION = 1;
-constexpr int MAX_VERSION = 5;
+constexpr int MAX_VERSION = 6;
 
 struct CMakeVersion
 {
@@ -46,9 +48,11 @@ struct CMakeVersion
 struct RootPresets
 {
   CMakeVersion CMakeMinimumRequired;
-  std::vector<cmCMakePresetsGraph::ConfigurePreset> ConfigurePresets;
-  std::vector<cmCMakePresetsGraph::BuildPreset> BuildPresets;
-  std::vector<cmCMakePresetsGraph::TestPreset> TestPresets;
+  std::vector<ConfigurePreset> ConfigurePresets;
+  std::vector<BuildPreset> BuildPresets;
+  std::vector<TestPreset> TestPresets;
+  std::vector<PackagePreset> PackagePresets;
+  std::vector<WorkflowPreset> WorkflowPresets;
   std::vector<std::string> Include;
 };
 
@@ -281,6 +285,10 @@ auto const RootPresetsHelper =
           cmCMakePresetsGraphInternal::BuildPresetsHelper, false)
     .Bind("testPresets"_s, &RootPresets::TestPresets,
           cmCMakePresetsGraphInternal::TestPresetsHelper, false)
+    .Bind("packagePresets"_s, &RootPresets::PackagePresets,
+          cmCMakePresetsGraphInternal::PackagePresetsHelper, false)
+    .Bind("workflowPresets"_s, &RootPresets::WorkflowPresets,
+          cmCMakePresetsGraphInternal::WorkflowPresetsHelper, false)
     .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
           CMakeVersionHelper, false)
     .Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
@@ -411,7 +419,7 @@ cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper(
 
 cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
   const std::string& filename, RootType rootType, ReadReason readReason,
-  std::vector<File*>& inProgressFiles, File*& file)
+  std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg)
 {
   ReadFileResult result;
 
@@ -430,6 +438,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
 
   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.
@@ -438,7 +447,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
   Json::Value root;
   Json::CharReaderBuilder builder;
   Json::CharReaderBuilder::strictMode(&builder.settings_);
-  if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
+  if (!Json::parseFromStream(builder, fin, &root, &errMsg)) {
+    errMsg = cmStrCat(filename, ":\n", errMsg);
     return ReadFileResult::JSON_PARSE_ERROR;
   }
 
@@ -456,6 +466,16 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
     return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
   }
 
+  // Support for package presets added in version 6.
+  if (v < 6 && root.isMember("packagePresets")) {
+    return ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED;
+  }
+
+  // Support for workflow presets added in version 6.
+  if (v < 6 && root.isMember("workflowPresets")) {
+    return ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED;
+  }
+
   // Support for include added in version 4.
   if (v < 4 && root.isMember("include")) {
     return ReadFileResult::INCLUDE_UNSUPPORTED;
@@ -490,6 +510,8 @@ 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;
     }
 
@@ -523,6 +545,8 @@ 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;
     }
 
@@ -564,17 +588,61 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
       return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED;
     }
 
+    // Support for outputJUnitFile added in version 6.
+    if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
+      return ReadFileResult::CTEST_JUNIT_UNSUPPORTED;
+    }
+
     this->TestPresetOrder.push_back(preset.Name);
   }
 
+  for (auto& preset : presets.PackagePresets) {
+    preset.OriginFile = file;
+    if (preset.Name.empty()) {
+      return ReadFileResult::INVALID_PRESET;
+    }
+
+    PresetPair<PackagePreset> presetPair;
+    presetPair.Unexpanded = preset;
+    presetPair.Expanded = cm::nullopt;
+    if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
+      return ReadFileResult::DUPLICATE_PRESETS;
+    }
+
+    // Support for conditions added in version 3, but this requires version 5
+    // already, so no action needed.
+
+    this->PackagePresetOrder.push_back(preset.Name);
+  }
+
+  for (auto& preset : presets.WorkflowPresets) {
+    preset.OriginFile = file;
+    if (preset.Name.empty()) {
+      return ReadFileResult::INVALID_PRESET;
+    }
+
+    PresetPair<WorkflowPreset> presetPair;
+    presetPair.Unexpanded = preset;
+    presetPair.Expanded = cm::nullopt;
+    if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
+      return ReadFileResult::DUPLICATE_PRESETS;
+    }
+
+    // Support for conditions added in version 3, but this requires version 6
+    // already, so no action needed.
+
+    this->WorkflowPresetOrder.push_back(preset.Name);
+  }
+
   auto const includeFile = [this, &inProgressFiles, file](
                              const std::string& include, RootType rootType2,
-                             ReadReason readReason2) -> ReadFileResult {
+                             ReadReason readReason2,
+                             std::string& FailureMessage) -> ReadFileResult {
     ReadFileResult r;
     File* includedFile;
     if ((r = this->ReadJSONFile(include, rootType2, readReason2,
-                                inProgressFiles, includedFile)) !=
-        ReadFileResult::READ_OK) {
+                                inProgressFiles, includedFile,
+                                FailureMessage)) != ReadFileResult::READ_OK) {
       return r;
     }
 
@@ -589,8 +657,8 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
       include = cmStrCat(directory, '/', include);
     }
 
-    if ((result = includeFile(include, rootType, ReadReason::Included)) !=
-        ReadFileResult::READ_OK) {
+    if ((result = includeFile(include, rootType, ReadReason::Included,
+                              errMsg)) != ReadFileResult::READ_OK) {
       return result;
     }
   }
@@ -599,7 +667,7 @@ cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
     auto cmakePresetsFilename = GetFilename(this->SourceDir);
     if (cmSystemTools::FileExists(cmakePresetsFilename)) {
       if ((result = includeFile(cmakePresetsFilename, RootType::Project,
-                                ReadReason::Root)) !=
+                                ReadReason::Root, errMsg)) !=
           ReadFileResult::READ_OK) {
         return result;
       }
diff --git a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
new file mode 100644 (file)
index 0000000..4ae51b1
--- /dev/null
@@ -0,0 +1,95 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <cstddef>
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmCMakePresetsGraph.h"
+#include "cmCMakePresetsGraphInternal.h"
+#include "cmJSONHelpers.h"
+
+namespace {
+using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
+using PackagePreset = cmCMakePresetsGraph::PackagePreset;
+
+auto const OutputHelper =
+  cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, 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 VariablesHelper =
+  cmJSONHelperBuilder<ReadFileResult>::Map<std::string>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
+
+auto const PackagePresetHelper =
+  cmJSONHelperBuilder<ReadFileResult>::Object<PackagePreset>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("name"_s, &PackagePreset::Name,
+          cmCMakePresetsGraphInternal::PresetStringHelper)
+    .Bind("inherits"_s, &PackagePreset::Inherits,
+          cmCMakePresetsGraphInternal::PresetVectorOneOrMoreStringHelper,
+          false)
+    .Bind("hidden"_s, &PackagePreset::Hidden,
+          cmCMakePresetsGraphInternal::PresetBoolHelper, false)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          cmCMakePresetsGraphInternal::VendorHelper(
+                            ReadFileResult::INVALID_PRESET),
+                          false)
+    .Bind("displayName"_s, &PackagePreset::DisplayName,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("description"_s, &PackagePreset::Description,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("environment"_s, &PackagePreset::Environment,
+          cmCMakePresetsGraphInternal::EnvironmentMapHelper, false)
+    .Bind("configurePreset"_s, &PackagePreset::ConfigurePreset,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("inheritConfigureEnvironment"_s,
+          &PackagePreset::InheritConfigureEnvironment,
+          cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
+    .Bind("generators"_s, &PackagePreset::Generators,
+          cmCMakePresetsGraphInternal::PresetVectorStringHelper, false)
+    .Bind("configurations"_s, &PackagePreset::Configurations,
+          cmCMakePresetsGraphInternal::PresetVectorStringHelper, false)
+    .Bind("variables"_s, &PackagePreset::Variables, VariablesHelper, false)
+    .Bind("configFile"_s, &PackagePreset::ConfigFile,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("output"_s, OutputHelper, false)
+    .Bind("packageName"_s, &PackagePreset::PackageName,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("packageVersion"_s, &PackagePreset::PackageVersion,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("packageDirectory"_s, &PackagePreset::PackageDirectory,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("vendorName"_s, &PackagePreset::VendorName,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("condition"_s, &PackagePreset::ConditionEvaluator,
+          cmCMakePresetsGraphInternal::PresetConditionHelper, false);
+}
+
+namespace cmCMakePresetsGraphInternal {
+cmCMakePresetsGraph::ReadFileResult PackagePresetsHelper(
+  std::vector<cmCMakePresetsGraph::PackagePreset>& out,
+  const Json::Value* value)
+{
+  static auto const helper =
+    cmJSONHelperBuilder<ReadFileResult>::Vector<PackagePreset>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
+      PackagePresetHelper);
+
+  return helper(out, value);
+}
+}
index c07d380..3856f63 100644 (file)
@@ -104,6 +104,8 @@ auto const TestPresetOptionalOutputHelper =
             cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
       .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile,
             cmCMakePresetsGraphInternal::PresetStringHelper, false)
+      .Bind("outputJUnitFile"_s, &TestPreset::OutputOptions::OutputJUnitFile,
+            cmCMakePresetsGraphInternal::PresetStringHelper, false)
       .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary,
             cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
       .Bind("subprojectSummary"_s,
diff --git a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
new file mode 100644 (file)
index 0000000..33680a1
--- /dev/null
@@ -0,0 +1,95 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <cstddef>
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmCMakePresetsGraph.h"
+#include "cmCMakePresetsGraphInternal.h"
+#include "cmJSONHelpers.h"
+
+namespace {
+using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
+using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
+
+ReadFileResult WorkflowStepTypeHelper(WorkflowPreset::WorkflowStep::Type& out,
+                                      const Json::Value* value)
+{
+  if (!value) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (!value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "configure") {
+    out = WorkflowPreset::WorkflowStep::Type::Configure;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "build") {
+    out = WorkflowPreset::WorkflowStep::Type::Build;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "test") {
+    out = WorkflowPreset::WorkflowStep::Type::Test;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "package") {
+    out = WorkflowPreset::WorkflowStep::Type::Package;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const WorkflowStepHelper =
+  cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset::WorkflowStep>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, 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);
+
+auto const WorkflowPresetHelper =
+  cmJSONHelperBuilder<ReadFileResult>::Object<WorkflowPreset>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("name"_s, &WorkflowPreset::Name,
+          cmCMakePresetsGraphInternal::PresetStringHelper)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          cmCMakePresetsGraphInternal::VendorHelper(
+                            ReadFileResult::INVALID_PRESET),
+                          false)
+    .Bind("displayName"_s, &WorkflowPreset::DisplayName,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("description"_s, &WorkflowPreset::Description,
+          cmCMakePresetsGraphInternal::PresetStringHelper, false)
+    .Bind("steps"_s, &WorkflowPreset::Steps, WorkflowStepsHelper);
+}
+
+namespace cmCMakePresetsGraphInternal {
+cmCMakePresetsGraph::ReadFileResult WorkflowPresetsHelper(
+  std::vector<cmCMakePresetsGraph::WorkflowPreset>& out,
+  const Json::Value* value)
+{
+  static auto const helper =
+    cmJSONHelperBuilder<ReadFileResult>::Vector<WorkflowPreset>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
+      WorkflowPresetHelper);
+
+  return helper(out, value);
+}
+}
index 66507a7..f60a1e9 100644 (file)
@@ -2116,11 +2116,7 @@ bool cmCTest::HandleCommandLineArguments(size_t& i,
       return false;
     }
     i++;
-    this->Impl->TestHandler.SetJUnitXMLFileName(std::string(args[i]));
-    // Turn test output compression off.
-    // This makes it easier to include test output in the resulting
-    // JUnit XML report.
-    this->Impl->CompressTestOutput = false;
+    this->SetOutputJUnitFileName(std::string(args[i]));
   }
 
   cm::string_view noTestsPrefix = "--no-tests=";
@@ -2458,6 +2454,9 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName,
     if (!expandedPreset->Output->OutputLogFile.empty()) {
       this->SetOutputLogFileName(expandedPreset->Output->OutputLogFile);
     }
+    if (!expandedPreset->Output->OutputJUnitFile.empty()) {
+      this->SetOutputJUnitFileName(expandedPreset->Output->OutputJUnitFile);
+    }
 
     this->Impl->LabelSummary =
       expandedPreset->Output->LabelSummary.value_or(true);
@@ -3541,6 +3540,15 @@ void cmCTest::SetOutputLogFileName(const std::string& name)
   }
 }
 
+void cmCTest::SetOutputJUnitFileName(const std::string& name)
+{
+  this->Impl->TestHandler.SetJUnitXMLFileName(name);
+  // Turn test output compression off.
+  // This makes it easier to include test output in the resulting
+  // JUnit XML report.
+  this->Impl->CompressTestOutput = false;
+}
+
 static const char* cmCTestStringLogType[] = { "DEBUG",
                                               "OUTPUT",
                                               "HANDLER_OUTPUT",
index 551c116..0017b3e 100644 (file)
@@ -359,6 +359,9 @@ public:
   /** Set the output log file name */
   void SetOutputLogFileName(const std::string& name);
 
+  /** Set the output JUnit file name */
+  void SetOutputJUnitFileName(const std::string& name);
+
   /** Set the visual studio or Xcode config type */
   void SetConfigType(const std::string& ct);
 
index c6296f9..27f2156 100644 (file)
 #include "cmAddLibraryCommand.h"
 #include "cmAddSubDirectoryCommand.h"
 #include "cmAddTestCommand.h"
+#include "cmBlockCommand.h"
 #include "cmBreakCommand.h"
 #include "cmBuildCommand.h"
 #include "cmCMakeLanguageCommand.h"
 #include "cmCMakeMinimumRequired.h"
 #include "cmCMakePathCommand.h"
 #include "cmCMakePolicyCommand.h"
-#include "cmCommand.h"
 #include "cmConfigureFileCommand.h"
 #include "cmContinueCommand.h"
 #include "cmCreateTestSourceList.h"
@@ -127,6 +127,7 @@ void GetScriptingCommands(cmState* state)
   state->AddFlowControlCommand("macro", cmMacroCommand);
   state->AddFlowControlCommand("return", cmReturnCommand);
   state->AddFlowControlCommand("while", cmWhileCommand);
+  state->AddFlowControlCommand("block", cmBlockCommand);
 
   state->AddBuiltinCommand("cmake_language", cmCMakeLanguageCommand);
   state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired);
@@ -199,6 +200,10 @@ void GetScriptingCommands(cmState* state)
     "An ENDWHILE command was found outside of a proper "
     "WHILE ENDWHILE structure. Or its arguments did not "
     "match the opening WHILE command.");
+  state->AddUnexpectedFlowControlCommand(
+    "endblock",
+    "An ENDBLOCK command was found outside of a proper "
+    "BLOCK ENDBLOCK structure.");
 
 #if !defined(CMAKE_BOOTSTRAP)
   state->AddBuiltinCommand("cmake_host_system_information",
@@ -220,6 +225,8 @@ void GetScriptingCommands(cmState* state)
 
 void GetProjectCommands(cmState* state)
 {
+  state->AddBuiltinCommand("add_compile_definitions",
+                           cmAddCompileDefinitionsCommand);
   state->AddBuiltinCommand("add_custom_command", cmAddCustomCommandCommand);
   state->AddBuiltinCommand("add_custom_target", cmAddCustomTargetCommand);
   state->AddBuiltinCommand("add_definitions", cmAddDefinitionsCommand);
@@ -264,15 +271,12 @@ void GetProjectCommands(cmState* state)
                            cmTargetLinkLibrariesCommand);
   state->AddBuiltinCommand("target_link_options", cmTargetLinkOptionsCommand);
   state->AddBuiltinCommand("target_sources", cmTargetSourcesCommand);
-  state->AddBuiltinCommand("try_compile",
-                           cm::make_unique<cmTryCompileCommand>());
-  state->AddBuiltinCommand("try_run", cm::make_unique<cmTryRunCommand>());
+  state->AddBuiltinCommand("try_compile", cmTryCompileCommand);
+  state->AddBuiltinCommand("try_run", cmTryRunCommand);
   state->AddBuiltinCommand("target_precompile_headers",
                            cmTargetPrecompileHeadersCommand);
 
 #if !defined(CMAKE_BOOTSTRAP)
-  state->AddBuiltinCommand("add_compile_definitions",
-                           cmAddCompileDefinitionsCommand);
   state->AddBuiltinCommand("add_compile_options", cmAddCompileOptionsCommand);
   state->AddBuiltinCommand("aux_source_directory",
                            cmAuxSourceDirectoryCommand);
index ba95168..5fe6756 100644 (file)
@@ -9,6 +9,7 @@
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalCommonGenerator.h"
+#include "cmGlobalGenerator.h"
 #include "cmLocalCommonGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
@@ -175,6 +176,9 @@ std::vector<std::string> cmCommonTargetGenerator::GetLinkedTargetDirectories(
         cmLocalGenerator* lg = linkee->GetLocalGenerator();
         std::string di = cmStrCat(lg->GetCurrentBinaryDirectory(), '/',
                                   lg->GetTargetDirectory(linkee));
+        if (lg->GetGlobalGenerator()->IsMultiConfig()) {
+          di = cmStrCat(di, '/', config);
+        }
         dirs.push_back(std::move(di));
       }
     }
index cda70fc..044f69f 100644 (file)
@@ -1566,8 +1566,9 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
 
   if (target->IsFrameworkOnApple() && !this->GlobalGenerator->IsXcode()) {
     // Add the framework directory and the framework item itself
-    auto fwItems = this->GlobalGenerator->SplitFrameworkPath(item.Value, true);
-    if (!fwItems) {
+    auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath(
+      item.Value, cmGlobalGenerator::FrameworkFormat::Extended);
+    if (!fwDescriptor) {
       this->CMakeInstance->IssueMessage(
         MessageType::FATAL_ERROR,
         cmStrCat("Could not parse framework path \"", item.Value,
@@ -1575,12 +1576,13 @@ void cmComputeLinkInformation::AddTargetItem(LinkEntry const& entry)
         item.Backtrace);
       return;
     }
-    if (!fwItems->first.empty()) {
+    if (!fwDescriptor->Directory.empty()) {
       // Add the directory portion to the framework search path.
-      this->AddFrameworkPath(fwItems->first);
+      this->AddFrameworkPath(fwDescriptor->Directory);
     }
     if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) {
-      this->Items.emplace_back(fwItems->second, ItemIsPath::Yes, target,
+      this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes,
+                               target,
                                this->FindLibraryFeature(entry.Feature));
     } else {
       this->Items.emplace_back(
@@ -1851,9 +1853,11 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
   std::string const& item = entry.Item.Value;
 
   // Try to separate the framework name and path.
-  auto fwItems =
-    this->GlobalGenerator->SplitFrameworkPath(item, entry.Feature != DEFAULT);
-  if (!fwItems) {
+  auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath(
+    item,
+    entry.Feature == DEFAULT ? cmGlobalGenerator::FrameworkFormat::Relaxed
+                             : cmGlobalGenerator::FrameworkFormat::Extended);
+  if (!fwDescriptor) {
     std::ostringstream e;
     e << "Could not parse framework path \"" << item << "\" "
       << "linked by target " << this->Target->GetName() << ".";
@@ -1861,18 +1865,14 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
     return;
   }
 
-  std::string fw_path = std::move(fwItems->first);
-  std::string fw = std::move(fwItems->second);
-  std::string full_fw = cmStrCat(fw, ".framework/", fw);
-
+  const std::string& fw_path = fwDescriptor->Directory;
   if (!fw_path.empty()) {
-    full_fw = cmStrCat(fw_path, '/', full_fw);
     // Add the directory portion to the framework search path.
     this->AddFrameworkPath(fw_path);
   }
 
   // add runtime information
-  this->AddLibraryRuntimeInfo(full_fw);
+  this->AddLibraryRuntimeInfo(fwDescriptor->GetFullPath());
 
   if (entry.Feature == DEFAULT) {
     // ensure FRAMEWORK feature is loaded
@@ -1887,9 +1887,9 @@ void cmComputeLinkInformation::AddFrameworkItem(LinkEntry const& entry)
                                                         ? "FRAMEWORK"
                                                         : entry.Feature));
   } else {
-    this->Items.emplace_back(fw, ItemIsPath::Yes, nullptr,
-                             this->FindLibraryFeature(entry.Feature == DEFAULT
-                                                        ? "FRAMEWORK"
+    this->Items.emplace_back(
+      fwDescriptor->GetLinkName(), ItemIsPath::Yes, nullptr,
+      this->FindLibraryFeature(entry.Feature == DEFAULT ? "FRAMEWORK"
                                                         : entry.Feature));
   }
 }
@@ -2252,15 +2252,11 @@ void cmComputeLinkInformation::AddLibraryRuntimeInfo(
 
   // It could be an Apple framework
   if (!is_shared_library) {
-    if (fullPath.find(".framework") != std::string::npos) {
-      static cmsys::RegularExpression splitFramework(
-        "^(.*)/(.*).framework/(.*)$");
-      if (splitFramework.find(fullPath) &&
-          (std::string::npos !=
-           splitFramework.match(3).find(splitFramework.match(2)))) {
-        is_shared_library = true;
-      }
-    }
+    is_shared_library =
+      this->GlobalGenerator
+        ->SplitFrameworkPath(fullPath,
+                             cmGlobalGenerator::FrameworkFormat::Strict)
+        .has_value();
   }
 
   if (!is_shared_library) {
index 6a419f6..90f3de0 100644 (file)
 #pragma warning(disable : 1572) /* floating-point equality test */
 #endif
 
+#if defined(__LCC__) && defined(__EDG__) && (__LCC__ == 123)
+#pragma diag_suppress 2910 /* excess -Wunused-function in 1.23.x */
+#endif
+
 #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE
 #cmakedefine HAVE_UNSETENV
 #cmakedefine CMake_USE_MACH_PARSER
 #cmakedefine CMake_USE_XCOFF_PARSER
+#cmakedefine CMAKE_USE_WMAKE
 #define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
 #define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@"
 #define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@"
 #if defined(_WIN32) && !defined(NOMINMAX)
 #  define NOMINMAX
 #endif
+
+#cmakedefine CURL_CA_BUNDLE "@CURL_CA_BUNDLE@"
+#cmakedefine CURL_CA_PATH "@CURL_CA_PATH@"
+
+#cmakedefine01 CMake_STAT_HAS_ST_MTIM
+#cmakedefine01 CMake_STAT_HAS_ST_MTIMESPEC
+
+#cmakedefine KWSYS_ENCODING_DEFAULT_CODEPAGE @KWSYS_ENCODING_DEFAULT_CODEPAGE@
index b1d37a6..867f984 100644 (file)
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCoreTryCompile.h"
 
+#include <array>
 #include <cstdio>
 #include <cstring>
 #include <set>
 #include <cmext/string_view>
 
 #include "cmsys/Directory.hxx"
+#include "cmsys/FStream.hxx"
 
+#include "cmArgumentParser.h"
 #include "cmExportTryCompileFileGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
+#include "cmRange.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
 namespace {
-class LanguageStandardState
-{
-public:
-  LanguageStandardState(std::string&& lang)
-    : StandardFlag(lang + "_STANDARD")
-    , RequiredFlag(lang + "_STANDARD_REQUIRED")
-    , ExtensionFlag(lang + "_EXTENSIONS")
-  {
-  }
-
-  void Enabled(bool isEnabled) { this->IsEnabled = isEnabled; }
-
-  bool UpdateIfMatches(std::vector<std::string> const& argv, size_t& index)
-  {
-    bool updated = false;
-    if (argv[index] == this->StandardFlag) {
-      this->DidStandard = true;
-      this->StandardValue = argv[++index];
-      updated = true;
-    } else if (argv[index] == this->RequiredFlag) {
-      this->DidStandardRequired = true;
-      this->RequiredValue = argv[++index];
-      updated = true;
-    } else if (argv[index] == this->ExtensionFlag) {
-      this->DidExtensions = true;
-      this->ExtensionValue = argv[++index];
-      updated = true;
-    }
-    return updated;
-  }
-
-  bool Validate(cmMakefile* const makefile) const
-  {
-    if (this->DidStandard) {
-      makefile->IssueMessage(
-        MessageType::FATAL_ERROR,
-        cmStrCat(this->StandardFlag,
-                 " allowed only in source file signature."));
-      return false;
-    }
-    if (this->DidStandardRequired) {
-      makefile->IssueMessage(
-        MessageType::FATAL_ERROR,
-        cmStrCat(this->RequiredFlag,
-                 " allowed only in source file signature."));
-      return false;
-    }
-    if (this->DidExtensions) {
-      makefile->IssueMessage(
-        MessageType::FATAL_ERROR,
-        cmStrCat(this->ExtensionFlag,
-                 " allowed only in source file signature."));
-      return false;
-    }
-
-    return true;
-  }
-
-  bool DidNone() const
-  {
-    return !this->DidStandard && !this->DidStandardRequired &&
-      !this->DidExtensions;
-  }
-
-  void LoadUnsetPropertyValues(cmMakefile* const makefile, bool honorStandard,
-                               bool warnCMP0067,
-                               std::vector<std::string>& warnCMP0067Variables)
-  {
-    if (!this->IsEnabled) {
-      return;
-    }
-
-    auto lookupStdVar = [&](std::string const& var) -> std::string {
-      std::string value = makefile->GetSafeDefinition(var);
-      if (warnCMP0067 && !value.empty()) {
-        value.clear();
-        warnCMP0067Variables.emplace_back(var);
-      }
-      return value;
-    };
-
-    if (honorStandard || warnCMP0067) {
-      if (!this->DidStandard) {
-        this->StandardValue =
-          lookupStdVar(cmStrCat("CMAKE_", this->StandardFlag));
-      }
-      if (!this->DidStandardRequired) {
-        this->RequiredValue =
-          lookupStdVar(cmStrCat("CMAKE_", this->RequiredFlag));
-      }
-      if (!this->DidExtensions) {
-        this->ExtensionValue =
-          lookupStdVar(cmStrCat("CMAKE_", this->ExtensionFlag));
-      }
-    }
-  }
-
-  void WriteProperties(FILE* fout, std::string const& targetName) const
-  {
-    if (!this->IsEnabled) {
-      return;
-    }
-
-    auto writeProp = [&](std::string const& prop, std::string const& value) {
-      fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
-              targetName.c_str(),
-              cmOutputConverter::EscapeForCMake(prop).c_str(),
-              cmOutputConverter::EscapeForCMake(value).c_str());
-    };
-
-    if (!this->StandardValue.empty()) {
-      writeProp(this->StandardFlag, this->StandardValue);
-    }
-    if (!this->RequiredValue.empty()) {
-      writeProp(this->RequiredFlag, this->RequiredValue);
-    }
-    if (!this->ExtensionValue.empty()) {
-      writeProp(this->ExtensionFlag, this->ExtensionValue);
-    }
-  }
-
-private:
-  bool IsEnabled = false;
-  bool DidStandard = false;
-  bool DidStandardRequired = false;
-  bool DidExtensions = false;
-
-  std::string StandardFlag;
-  std::string RequiredFlag;
-  std::string ExtensionFlag;
-
-  std::string StandardValue;
-  std::string RequiredValue;
-  std::string ExtensionValue;
-};
-
+constexpr const char* unique_binary_directory = "CMAKE_BINARY_DIR_USE_MKDTEMP";
 constexpr size_t lang_property_start = 0;
 constexpr size_t lang_property_size = 4;
 constexpr size_t pie_property_start = 4;
@@ -226,6 +95,8 @@ std::string const kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES =
 std::string const kCMAKE_WARN_DEPRECATED = "CMAKE_WARN_DEPRECATED";
 std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT =
   "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT";
+std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT =
+  "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT";
 
 /* GHS Multi platform variables */
 std::set<std::string> const ghs_platform_vars{
@@ -233,114 +104,249 @@ std::set<std::string> const ghs_platform_vars{
   "GHS_OS_ROOT",         "GHS_OS_DIR",         "GHS_BSP_NAME",
   "GHS_OS_DIR_OPTION"
 };
+using Arguments = cmCoreTryCompile::Arguments;
+
+ArgumentParser::Continue TryCompileLangProp(Arguments& args,
+                                            cm::string_view key,
+                                            cm::string_view val)
+{
+  args.LangProps[std::string(key)] = std::string(val);
+  return ArgumentParser::Continue::No;
+}
+
+ArgumentParser::Continue TryCompileCompileDefs(Arguments& args,
+                                               cm::string_view val)
+{
+  cmExpandList(val, args.CompileDefs);
+  return ArgumentParser::Continue::Yes;
 }
 
-int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
-                                     bool isTryRun)
+cmArgumentParser<Arguments> makeTryCompileParser(
+  const cmArgumentParser<Arguments>& base)
+{
+  return cmArgumentParser<Arguments>{ base }.Bind("OUTPUT_VARIABLE"_s,
+                                                  &Arguments::OutputVariable);
+}
+
+cmArgumentParser<Arguments> makeTryRunParser(
+  const cmArgumentParser<Arguments>& base)
+{
+  return cmArgumentParser<Arguments>{ base }
+    .Bind("COMPILE_OUTPUT_VARIABLE"_s, &Arguments::CompileOutputVariable)
+    .Bind("RUN_OUTPUT_VARIABLE"_s, &Arguments::RunOutputVariable)
+    .Bind("RUN_OUTPUT_STDOUT_VARIABLE"_s, &Arguments::RunOutputStdOutVariable)
+    .Bind("RUN_OUTPUT_STDERR_VARIABLE"_s, &Arguments::RunOutputStdErrVariable)
+    .Bind("WORKING_DIRECTORY"_s, &Arguments::RunWorkingDirectory)
+    .Bind("ARGS"_s, &Arguments::RunArgs)
+    /* keep semicolon on own line */;
+}
+
+#define BIND_LANG_PROPS(lang)                                                 \
+  Bind(#lang "_STANDARD"_s, TryCompileLangProp)                               \
+    .Bind(#lang "_STANDARD_REQUIRED"_s, TryCompileLangProp)                   \
+    .Bind(#lang "_EXTENSIONS"_s, TryCompileLangProp)
+
+auto const TryCompileBaseArgParser =
+  cmArgumentParser<Arguments>{}
+    .Bind(0, &Arguments::CompileResultVariable)
+    .Bind("NO_CACHE"_s, &Arguments::NoCache)
+    .Bind("CMAKE_FLAGS"_s, &Arguments::CMakeFlags)
+    .Bind("__CMAKE_INTERNAL"_s, &Arguments::CMakeInternal)
+  /* keep semicolon on own line */;
+
+auto const TryCompileBaseSourcesArgParser =
+  cmArgumentParser<Arguments>{ TryCompileBaseArgParser }
+    .Bind("SOURCES"_s, &Arguments::Sources)
+    .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs,
+          ArgumentParser::ExpectAtLeast{ 0 })
+    .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries)
+    .Bind("LINK_OPTIONS"_s, &Arguments::LinkOptions)
+    .Bind("COPY_FILE"_s, &Arguments::CopyFileTo)
+    .Bind("COPY_FILE_ERROR"_s, &Arguments::CopyFileError)
+    .BIND_LANG_PROPS(C)
+    .BIND_LANG_PROPS(CUDA)
+    .BIND_LANG_PROPS(CXX)
+    .BIND_LANG_PROPS(HIP)
+    .BIND_LANG_PROPS(OBJC)
+    .BIND_LANG_PROPS(OBJCXX)
+  /* keep semicolon on own line */;
+
+auto const TryCompileBaseNewSourcesArgParser =
+  cmArgumentParser<Arguments>{ TryCompileBaseSourcesArgParser }
+    .Bind("SOURCE_FROM_CONTENT"_s, &Arguments::SourceFromContent)
+    .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar)
+    .Bind("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile)
+  /* keep semicolon on own line */;
+
+auto const TryCompileBaseProjectArgParser =
+  cmArgumentParser<Arguments>{ TryCompileBaseArgParser }
+    .Bind("PROJECT"_s, &Arguments::ProjectName)
+    .Bind("SOURCE_DIR"_s, &Arguments::SourceDirectoryOrFile)
+    .Bind("BINARY_DIR"_s, &Arguments::BinaryDirectory)
+    .Bind("TARGET"_s, &Arguments::TargetName)
+  /* keep semicolon on own line */;
+
+auto const TryCompileProjectArgParser =
+  makeTryCompileParser(TryCompileBaseProjectArgParser);
+
+auto const TryCompileSourcesArgParser =
+  makeTryCompileParser(TryCompileBaseNewSourcesArgParser);
+
+auto const TryCompileOldArgParser =
+  makeTryCompileParser(TryCompileBaseSourcesArgParser)
+    .Bind(1, &Arguments::BinaryDirectory)
+    .Bind(2, &Arguments::SourceDirectoryOrFile)
+    .Bind(3, &Arguments::ProjectName)
+    .Bind(4, &Arguments::TargetName)
+  /* keep semicolon on own line */;
+
+auto const TryRunSourcesArgParser =
+  makeTryRunParser(TryCompileBaseNewSourcesArgParser);
+
+auto const TryRunOldArgParser = makeTryRunParser(TryCompileOldArgParser);
+
+#undef BIND_LANG_PROPS
+}
+
+Arguments cmCoreTryCompile::ParseArgs(
+  const cmRange<std::vector<std::string>::const_iterator>& args,
+  const cmArgumentParser<Arguments>& parser,
+  std::vector<std::string>& unparsedArguments)
+{
+  auto arguments = parser.Parse(args, &unparsedArguments, 0);
+  if (!arguments.MaybeReportError(*(this->Makefile)) &&
+      !unparsedArguments.empty()) {
+    std::string m = "Unknown arguments:";
+    for (const auto& i : unparsedArguments) {
+      m = cmStrCat(m, "\n  \"", i, "\"");
+    }
+    this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
+  }
+  return arguments;
+}
+
+Arguments cmCoreTryCompile::ParseArgs(
+  cmRange<std::vector<std::string>::const_iterator> args, bool isTryRun)
+{
+  std::vector<std::string> unparsedArguments;
+  const auto& second = *(++args.begin());
+
+  if (!isTryRun && second == "PROJECT") {
+    // New PROJECT signature (try_compile only).
+    auto arguments =
+      this->ParseArgs(args, TryCompileProjectArgParser, unparsedArguments);
+    if (!arguments.BinaryDirectory) {
+      arguments.BinaryDirectory = unique_binary_directory;
+    }
+    return arguments;
+  }
+
+  if (cmHasLiteralPrefix(second, "SOURCE")) {
+    // New SOURCES signature.
+    auto arguments = this->ParseArgs(
+      args, isTryRun ? TryRunSourcesArgParser : TryCompileSourcesArgParser,
+      unparsedArguments);
+    arguments.BinaryDirectory = unique_binary_directory;
+    return arguments;
+  }
+
+  // Old signature.
+  auto arguments = this->ParseArgs(
+    args, isTryRun ? TryRunOldArgParser : TryCompileOldArgParser,
+    unparsedArguments);
+  // For historical reasons, treat some empty-valued keyword
+  // arguments as if they were not specified at all.
+  if (arguments.OutputVariable && arguments.OutputVariable->empty()) {
+    arguments.OutputVariable = cm::nullopt;
+  }
+  if (isTryRun) {
+    if (arguments.CompileOutputVariable &&
+        arguments.CompileOutputVariable->empty()) {
+      arguments.CompileOutputVariable = cm::nullopt;
+    }
+    if (arguments.RunOutputVariable && arguments.RunOutputVariable->empty()) {
+      arguments.RunOutputVariable = cm::nullopt;
+    }
+    if (arguments.RunOutputStdOutVariable &&
+        arguments.RunOutputStdOutVariable->empty()) {
+      arguments.RunOutputStdOutVariable = cm::nullopt;
+    }
+    if (arguments.RunOutputStdErrVariable &&
+        arguments.RunOutputStdErrVariable->empty()) {
+      arguments.RunOutputStdErrVariable = cm::nullopt;
+    }
+    if (arguments.RunWorkingDirectory &&
+        arguments.RunWorkingDirectory->empty()) {
+      arguments.RunWorkingDirectory = cm::nullopt;
+    }
+  }
+  return arguments;
+}
+
+bool cmCoreTryCompile::TryCompileCode(Arguments& arguments,
+                                      cmStateEnums::TargetType targetType)
 {
-  this->BinaryDirectory = argv[1];
   this->OutputFile.clear();
   // which signature were we called with ?
   this->SrcFileSignature = true;
 
-  cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE;
-  cmValue tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE");
-  if (!isTryRun && cmNonempty(tt)) {
-    if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) {
-      targetType = cmStateEnums::EXECUTABLE;
-    } else if (*tt ==
-               cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) {
-      targetType = cmStateEnums::STATIC_LIBRARY;
-    } else {
+  bool useUniqueBinaryDirectory = false;
+  std::string sourceDirectory;
+  std::string projectName;
+  std::string targetName;
+  if (arguments.ProjectName) {
+    this->SrcFileSignature = false;
+    if (!arguments.SourceDirectoryOrFile ||
+        arguments.SourceDirectoryOrFile->empty()) {
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                   "No <srcdir> specified.");
+      return false;
+    }
+    sourceDirectory = *arguments.SourceDirectoryOrFile;
+    projectName = *arguments.ProjectName;
+    if (arguments.TargetName) {
+      targetName = *arguments.TargetName;
+    }
+  } else {
+    projectName = "CMAKE_TRY_COMPILE";
+    /* Use a random file name to avoid rapid creation and deletion
+       of the same executable name (some filesystems fail on that).  */
+    char targetNameBuf[64];
+    snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
+             cmSystemTools::RandomSeed() & 0xFFFFF);
+    targetName = targetNameBuf;
+  }
+
+  if (!arguments.BinaryDirectory || arguments.BinaryDirectory->empty()) {
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                 "No <bindir> specified.");
+    return false;
+  }
+  if (*arguments.BinaryDirectory == unique_binary_directory) {
+    // leave empty until we're ready to create it, so we don't try to remove
+    // a non-existing directory if we abort due to e.g. bad arguments
+    this->BinaryDirectory.clear();
+    useUniqueBinaryDirectory = true;
+  } else {
+    if (!cmSystemTools::FileIsFullPath(*arguments.BinaryDirectory)) {
       this->Makefile->IssueMessage(
         MessageType::FATAL_ERROR,
-        cmStrCat("Invalid value '", *tt,
-                 "' for CMAKE_TRY_COMPILE_TARGET_TYPE.  Only '",
-                 cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE),
-                 "' and '",
-                 cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY),
-                 "' are allowed."));
-      return -1;
+        cmStrCat("<bindir> is not an absolute path:\n '",
+                 *arguments.BinaryDirectory, "'"));
+      return false;
+    }
+    this->BinaryDirectory = *arguments.BinaryDirectory;
+    // compute the binary dir when TRY_COMPILE is called with a src file
+    // signature
+    if (this->SrcFileSignature) {
+      this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
     }
   }
 
-  std::string sourceDirectory = argv[2];
-  std::string projectName;
-  std::string targetName;
-  std::vector<std::string> cmakeFlags(1, "CMAKE_FLAGS"); // fake argv[0]
-  std::vector<std::string> compileDefs;
-  std::string cmakeInternal;
-  std::string outputVariable;
-  std::string copyFile;
-  std::string copyFileError;
-  LanguageStandardState cState("C");
-  LanguageStandardState cudaState("CUDA");
-  LanguageStandardState cxxState("CXX");
-  LanguageStandardState hipState("HIP");
-  LanguageStandardState objcState("OBJC");
-  LanguageStandardState objcxxState("OBJCXX");
   std::vector<std::string> targets;
-  std::vector<std::string> linkOptions;
-  std::string libsToLink = " ";
-  bool useOldLinkLibs = true;
-  char targetNameBuf[64];
-  bool didOutputVariable = false;
-  bool didCopyFile = false;
-  bool didCopyFileError = false;
-  bool useSources = argv[2] == "SOURCES";
-  std::vector<std::string> sources;
-
-  enum Doing
-  {
-    DoingNone,
-    DoingCMakeFlags,
-    DoingCompileDefinitions,
-    DoingLinkOptions,
-    DoingLinkLibraries,
-    DoingOutputVariable,
-    DoingCopyFile,
-    DoingCopyFileError,
-    DoingSources,
-    DoingCMakeInternal
-  };
-  Doing doing = useSources ? DoingSources : DoingNone;
-  for (size_t i = 3; i < argv.size(); ++i) {
-    if (argv[i] == "CMAKE_FLAGS") {
-      doing = DoingCMakeFlags;
-    } else if (argv[i] == "COMPILE_DEFINITIONS") {
-      doing = DoingCompileDefinitions;
-    } else if (argv[i] == "LINK_OPTIONS") {
-      doing = DoingLinkOptions;
-    } else if (argv[i] == "LINK_LIBRARIES") {
-      doing = DoingLinkLibraries;
-      useOldLinkLibs = false;
-    } else if (argv[i] == "OUTPUT_VARIABLE") {
-      doing = DoingOutputVariable;
-      didOutputVariable = true;
-    } else if (argv[i] == "COPY_FILE") {
-      doing = DoingCopyFile;
-      didCopyFile = true;
-    } else if (argv[i] == "COPY_FILE_ERROR") {
-      doing = DoingCopyFileError;
-      didCopyFileError = true;
-    } else if (cState.UpdateIfMatches(argv, i) ||
-               cxxState.UpdateIfMatches(argv, i) ||
-               cudaState.UpdateIfMatches(argv, i) ||
-               hipState.UpdateIfMatches(argv, i) ||
-               objcState.UpdateIfMatches(argv, i) ||
-               objcxxState.UpdateIfMatches(argv, i)) {
-      continue;
-    } else if (argv[i] == "__CMAKE_INTERNAL") {
-      doing = DoingCMakeInternal;
-    } else if (doing == DoingCMakeFlags) {
-      cmakeFlags.emplace_back(argv[i]);
-    } else if (doing == DoingCompileDefinitions) {
-      cmExpandList(argv[i], compileDefs);
-    } else if (doing == DoingLinkOptions) {
-      linkOptions.emplace_back(argv[i]);
-    } else if (doing == DoingLinkLibraries) {
-      libsToLink += "\"" + cmTrimWhitespace(argv[i]) + "\" ";
-      if (cmTarget* tgt = this->Makefile->FindTargetToUse(argv[i])) {
+  if (arguments.LinkLibraries) {
+    for (std::string const& i : *arguments.LinkLibraries) {
+      if (cmTarget* tgt = this->Makefile->FindTargetToUse(i)) {
         switch (tgt->GetType()) {
           case cmStateEnums::SHARED_LIBRARY:
           case cmStateEnums::STATIC_LIBRARY:
@@ -359,114 +365,94 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
                        "IMPORTED LINK_LIBRARIES.  Got ",
                        tgt->GetName(), " of type ",
                        cmState::GetTargetTypeName(tgt->GetType()), "."));
-            return -1;
+            return false;
         }
         if (tgt->IsImported()) {
-          targets.emplace_back(argv[i]);
+          targets.emplace_back(i);
         }
       }
-    } else if (doing == DoingOutputVariable) {
-      outputVariable = argv[i];
-      doing = DoingNone;
-    } else if (doing == DoingCopyFile) {
-      copyFile = argv[i];
-      doing = DoingNone;
-    } else if (doing == DoingCopyFileError) {
-      copyFileError = argv[i];
-      doing = DoingNone;
-    } else if (doing == DoingSources) {
-      sources.emplace_back(argv[i]);
-    } else if (doing == DoingCMakeInternal) {
-      cmakeInternal = argv[i];
-      doing = DoingNone;
-    } else if (i == 3) {
-      this->SrcFileSignature = false;
-      projectName = argv[i];
-    } else if (i == 4 && !this->SrcFileSignature) {
-      targetName = argv[i];
-    } else {
-      std::ostringstream m;
-      m << "try_compile given unknown argument \"" << argv[i] << "\".";
-      this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m.str());
     }
   }
 
-  if (didCopyFile && copyFile.empty()) {
+  if (arguments.CopyFileTo && arguments.CopyFileTo->empty()) {
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
                                  "COPY_FILE must be followed by a file path");
-    return -1;
+    return false;
   }
 
-  if (didCopyFileError && copyFileError.empty()) {
+  if (arguments.CopyFileError && arguments.CopyFileError->empty()) {
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       "COPY_FILE_ERROR must be followed by a variable name");
-    return -1;
+    return false;
   }
 
-  if (didCopyFileError && !didCopyFile) {
+  if (arguments.CopyFileError && !arguments.CopyFileTo) {
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       "COPY_FILE_ERROR may be used only with COPY_FILE");
-    return -1;
-  }
-
-  if (didOutputVariable && outputVariable.empty()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "OUTPUT_VARIABLE must be followed by a variable name");
-    return -1;
+    return false;
   }
 
-  if (useSources && sources.empty()) {
+  if (arguments.Sources && arguments.Sources->empty()) {
     this->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
       "SOURCES must be followed by at least one source file");
-    return -1;
+    return false;
   }
 
-  if (!this->SrcFileSignature) {
-    if (!cState.Validate(this->Makefile)) {
-      return -1;
-    }
-    if (!cudaState.Validate(this->Makefile)) {
-      return -1;
-    }
-    if (!hipState.Validate(this->Makefile)) {
-      return -1;
-    }
-    if (!cxxState.Validate(this->Makefile)) {
-      return -1;
+  if (this->SrcFileSignature) {
+    if (arguments.SourceFromContent &&
+        arguments.SourceFromContent->size() % 2) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        "SOURCE_FROM_CONTENT requires exactly two arguments");
+      return false;
     }
-    if (!objcState.Validate(this->Makefile)) {
-      return -1;
+    if (arguments.SourceFromVar && arguments.SourceFromVar->size() % 2) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        "SOURCE_FROM_VAR requires exactly two arguments");
+      return false;
     }
-    if (!objcxxState.Validate(this->Makefile)) {
-      return -1;
+    if (arguments.SourceFromFile && arguments.SourceFromFile->size() % 2) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        "SOURCE_FROM_FILE requires exactly two arguments");
+      return false;
     }
-  }
-
-  // compute the binary dir when TRY_COMPILE is called with a src file
-  // signature
-  if (this->SrcFileSignature) {
-    this->BinaryDirectory += "/CMakeFiles/CMakeTmp";
   } else {
     // only valid for srcfile signatures
-    if (!compileDefs.empty()) {
+    if (!arguments.LangProps.empty()) {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(arguments.LangProps.begin()->first,
+                 " allowed only in source file signature"));
+      return false;
+    }
+    if (!arguments.CompileDefs.empty()) {
       this->Makefile->IssueMessage(
         MessageType::FATAL_ERROR,
-        "COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE");
-      return -1;
+        "COMPILE_DEFINITIONS allowed only in source file signature");
+      return false;
     }
-    if (!copyFile.empty()) {
+    if (arguments.CopyFileTo) {
       this->Makefile->IssueMessage(
         MessageType::FATAL_ERROR,
-        "COPY_FILE specified on a srcdir type TRY_COMPILE");
-      return -1;
+        "COPY_FILE allowed only in source file signature");
+      return false;
     }
   }
+
   // make sure the binary directory exists
-  cmSystemTools::MakeDirectory(this->BinaryDirectory);
+  if (useUniqueBinaryDirectory) {
+    this->BinaryDirectory =
+      cmStrCat(this->Makefile->GetHomeOutputDirectory(),
+               "/CMakeFiles/CMakeScratch/TryCompile-XXXXXX");
+    cmSystemTools::MakeTempDirectory(this->BinaryDirectory);
+  } else {
+    cmSystemTools::MakeDirectory(this->BinaryDirectory);
+  }
 
   // do not allow recursive try Compiles
   if (this->BinaryDirectory == this->Makefile->GetHomeOutputDirectory()) {
@@ -474,7 +460,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
     e << "Attempt at a recursive or nested TRY_COMPILE in directory\n"
       << "  " << this->BinaryDirectory << "\n";
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-    return -1;
+    return false;
   }
 
   std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
@@ -485,9 +471,63 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
     cmSystemTools::RemoveFile(ccFile);
 
     // Choose sources.
-    if (!useSources) {
-      sources.emplace_back(argv[2]);
+    std::vector<std::string> sources;
+    if (arguments.Sources) {
+      sources = std::move(*arguments.Sources);
+    } else if (arguments.SourceDirectoryOrFile) {
+      sources.emplace_back(*arguments.SourceDirectoryOrFile);
+    }
+    if (arguments.SourceFromContent) {
+      auto const k = arguments.SourceFromContent->size();
+      for (auto i = decltype(k){ 0 }; i < k; i += 2) {
+        const auto& name = (*arguments.SourceFromContent)[i + 0];
+        const auto& content = (*arguments.SourceFromContent)[i + 1];
+        auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT");
+        if (out.empty()) {
+          return false;
+        }
+        sources.emplace_back(std::move(out));
+      }
+    }
+    if (arguments.SourceFromVar) {
+      auto const k = arguments.SourceFromVar->size();
+      for (auto i = decltype(k){ 0 }; i < k; i += 2) {
+        const auto& name = (*arguments.SourceFromVar)[i + 0];
+        const auto& var = (*arguments.SourceFromVar)[i + 1];
+        const auto& content = this->Makefile->GetDefinition(var);
+        auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR");
+        if (out.empty()) {
+          return false;
+        }
+        sources.emplace_back(std::move(out));
+      }
     }
+    if (arguments.SourceFromFile) {
+      auto const k = arguments.SourceFromFile->size();
+      for (auto i = decltype(k){ 0 }; i < k; i += 2) {
+        const auto& dst = (*arguments.SourceFromFile)[i + 0];
+        const auto& src = (*arguments.SourceFromFile)[i + 1];
+
+        if (!cmSystemTools::GetFilenamePath(dst).empty()) {
+          const auto& msg =
+            cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
+          this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
+          return false;
+        }
+
+        auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst);
+        auto const result = cmSystemTools::CopyFileAlways(src, dstPath);
+        if (!result.IsSuccess()) {
+          const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src,
+                                     "\": ", result.GetString());
+          this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
+          return false;
+        }
+
+        sources.emplace_back(std::move(dstPath));
+      }
+    }
+    // TODO: ensure sources is not empty
 
     // Detect languages to enable.
     cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
@@ -508,7 +548,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
         err << cmJoin(langs, " ");
         err << "\nSee project() command to enable other languages.";
         this->Makefile->IssueMessage(MessageType::FATAL_ERROR, err.str());
-        return -1;
+        return false;
       }
     }
 
@@ -535,7 +575,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
         << cmSystemTools::GetLastSystemError();
       /* clang-format on */
       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
-      return -1;
+      return false;
     }
 
     cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
@@ -575,6 +615,14 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
               *cmp0123 == "NEW"_s ? "NEW" : "OLD");
     }
 
+    /* Set MSVC debug information format policy to match our selection.  */
+    if (cmValue msvcDebugInformationFormatDefault =
+          this->Makefile->GetDefinition(
+            kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)) {
+      fprintf(fout, "cmake_policy(SET CMP0141 %s)\n",
+              !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
+    }
+
     /* Set cache/normal variable policy to match outer project.
        It may affect toolchain files.  */
     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
@@ -604,13 +652,22 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
       }
     }
     fprintf(fout, "project(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
-    if (cmakeInternal == "ABI") {
+    if (arguments.CMakeInternal == "ABI") {
       // This is the ABI detection step, also used for implicit includes.
       // Erase any include_directories() calls from the toolchain file so
       // that we do not see them as implicit.  Our ABI detection source
       // does not include any system headers anyway.
       fprintf(fout,
               "set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES \"\")\n");
+
+      // The link and compile lines for ABI detection step need to not use
+      // response files so we can extract implicit includes given to
+      // the underlying host compiler
+      if (testLangs.find("CUDA") != testLangs.end()) {
+        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n");
+        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n");
+        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n");
+      }
     }
     fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
     for (std::string const& li : testLangs) {
@@ -702,18 +759,12 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
     fprintf(fout, "set(CMAKE_SUPPRESS_REGENERATION 1)\n");
     fprintf(fout, "link_directories(${LINK_DIRECTORIES})\n");
     // handle any compile flags we need to pass on
-    if (!compileDefs.empty()) {
+    if (!arguments.CompileDefs.empty()) {
       // Pass using bracket arguments to preserve content.
       fprintf(fout, "add_definitions([==[%s]==])\n",
-              cmJoin(compileDefs, "]==] [==[").c_str());
+              cmJoin(arguments.CompileDefs, "]==] [==[").c_str());
     }
 
-    /* Use a random file name to avoid rapid creation and deletion
-       of the same executable name (some filesystems fail on that).  */
-    snprintf(targetNameBuf, sizeof(targetNameBuf), "cmTC_%05x",
-             cmSystemTools::RandomSeed() & 0xFFFFF);
-    targetName = targetNameBuf;
-
     if (!targets.empty()) {
       std::string fname = "/" + std::string(targetName) + "Targets.cmake";
       cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
@@ -725,7 +776,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
         this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
                                      "could not write export file.");
         fclose(fout);
-        return -1;
+        return false;
       }
       fprintf(fout, "\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/%s\")\n\n",
               fname.c_str());
@@ -769,24 +820,27 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
       fprintf(fout, " \"%s\"", si.c_str());
 
       // Add dependencies on any non-temporary sources.
-      if (si.find("CMakeTmp") == std::string::npos) {
+      if (!IsTemporary(si)) {
         this->Makefile->AddCMakeDependFile(si);
       }
     }
     fprintf(fout, ")\n");
 
-    cState.Enabled(testLangs.find("C") != testLangs.end());
-    cxxState.Enabled(testLangs.find("CXX") != testLangs.end());
-    cudaState.Enabled(testLangs.find("CUDA") != testLangs.end());
-    hipState.Enabled(testLangs.find("HIP") != testLangs.end());
-    objcState.Enabled(testLangs.find("OBJC") != testLangs.end());
-    objcxxState.Enabled(testLangs.find("OBJCXX") != testLangs.end());
+    /* Write out the output location of the target we are building */
+    std::string perConfigGenex;
+    if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
+      perConfigGenex = "_$<UPPER_CASE:$<CONFIG>>";
+    }
+    fprintf(fout,
+            "file(GENERATE OUTPUT "
+            "\"${CMAKE_BINARY_DIR}/%s%s_loc\"\n",
+            targetName.c_str(), perConfigGenex.c_str());
+    fprintf(fout, "     CONTENT $<TARGET_FILE:%s>)\n", targetName.c_str());
 
     bool warnCMP0067 = false;
     bool honorStandard = true;
 
-    if (cState.DidNone() && cxxState.DidNone() && objcState.DidNone() &&
-        objcxxState.DidNone() && cudaState.DidNone() && hipState.DidNone()) {
+    if (arguments.LangProps.empty()) {
       switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0067)) {
         case cmPolicies::WARN:
           warnCMP0067 = this->Makefile->PolicyOptionalWarningEnabled(
@@ -811,18 +865,33 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
 
     std::vector<std::string> warnCMP0067Variables;
 
-    cState.LoadUnsetPropertyValues(this->Makefile, honorStandard, warnCMP0067,
-                                   warnCMP0067Variables);
-    cxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
-                                     warnCMP0067, warnCMP0067Variables);
-    cudaState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
-                                      warnCMP0067, warnCMP0067Variables);
-    hipState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
-                                     warnCMP0067, warnCMP0067Variables);
-    objcState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
-                                      warnCMP0067, warnCMP0067Variables);
-    objcxxState.LoadUnsetPropertyValues(this->Makefile, honorStandard,
-                                        warnCMP0067, warnCMP0067Variables);
+    if (honorStandard || warnCMP0067) {
+      static std::array<std::string, 6> const possibleLangs{
+        { "C", "CXX", "CUDA", "HIP", "OBJC", "OBJCXX" }
+      };
+      static std::array<cm::string_view, 3> const langPropSuffixes{
+        { "_STANDARD"_s, "_STANDARD_REQUIRED"_s, "_EXTENSIONS"_s }
+      };
+      for (std::string const& lang : possibleLangs) {
+        if (testLangs.find(lang) == testLangs.end()) {
+          continue;
+        }
+        for (cm::string_view propSuffix : langPropSuffixes) {
+          std::string langProp = cmStrCat(lang, propSuffix);
+          if (!arguments.LangProps.count(langProp)) {
+            std::string langPropVar = cmStrCat("CMAKE_"_s, langProp);
+            std::string value = this->Makefile->GetSafeDefinition(langPropVar);
+            if (warnCMP0067 && !value.empty()) {
+              value.clear();
+              warnCMP0067Variables.emplace_back(langPropVar);
+            }
+            if (!value.empty()) {
+              arguments.LangProps[langProp] = value;
+            }
+          }
+        }
+      }
+    }
 
     if (!warnCMP0067Variables.empty()) {
       std::ostringstream w;
@@ -838,17 +907,20 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
       this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
     }
 
-    cState.WriteProperties(fout, targetName);
-    cxxState.WriteProperties(fout, targetName);
-    cudaState.WriteProperties(fout, targetName);
-    hipState.WriteProperties(fout, targetName);
-    objcState.WriteProperties(fout, targetName);
-    objcxxState.WriteProperties(fout, targetName);
+    for (auto const& p : arguments.LangProps) {
+      if (p.second.empty()) {
+        continue;
+      }
+      fprintf(fout, "set_property(TARGET %s PROPERTY %s %s)\n",
+              targetName.c_str(),
+              cmOutputConverter::EscapeForCMake(p.first).c_str(),
+              cmOutputConverter::EscapeForCMake(p.second).c_str());
+    }
 
-    if (!linkOptions.empty()) {
+    if (!arguments.LinkOptions.empty()) {
       std::vector<std::string> options;
-      options.reserve(linkOptions.size());
-      for (const auto& option : linkOptions) {
+      options.reserve(arguments.LinkOptions.size());
+      for (const auto& option : arguments.LinkOptions) {
         options.emplace_back(cmOutputConverter::EscapeForCMake(option));
       }
 
@@ -862,15 +934,18 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
       }
     }
 
-    if (useOldLinkLibs) {
-      fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
-              targetName.c_str());
-    } else {
+    if (arguments.LinkLibraries) {
+      std::string libsToLink = " ";
+      for (std::string const& i : *arguments.LinkLibraries) {
+        libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
+      }
       fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
               libsToLink.c_str());
+    } else {
+      fprintf(fout, "target_link_libraries(%s ${LINK_LIBRARIES})\n",
+              targetName.c_str());
     }
     fclose(fout);
-    projectName = "CMAKE_TRY_COMPILE";
   }
 
   // Forward a set of variables to the inner project cache.
@@ -917,6 +992,7 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
     vars.insert(kCMAKE_WARN_DEPRECATED);
     vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
+    vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
 
     if (cmValue varListStr = this->Makefile->GetDefinition(
           kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
@@ -959,13 +1035,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
           kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
       vars.erase(kCMAKE_OSX_ARCHITECTURES);
       std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
-      cmakeFlags.emplace_back(std::move(flag));
+      arguments.CMakeFlags.emplace_back(std::move(flag));
     }
 
     for (std::string const& var : vars) {
       if (cmValue val = this->Makefile->GetDefinition(var)) {
         std::string flag = "-D" + var + "=" + *val;
-        cmakeFlags.emplace_back(std::move(flag));
+        arguments.CMakeFlags.emplace_back(std::move(flag));
       }
     }
   }
@@ -975,37 +1051,50 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
     for (std::string const& var : ghs_platform_vars) {
       if (cmValue val = this->Makefile->GetDefinition(var)) {
         std::string flag = "-D" + var + "=" + "'" + *val + "'";
-        cmakeFlags.emplace_back(std::move(flag));
+        arguments.CMakeFlags.emplace_back(std::move(flag));
       }
     }
   }
 
+  if (this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
+    auto msg =
+      cmStrCat("Executing try_compile (", *arguments.CompileResultVariable,
+               ") in:\n  ", this->BinaryDirectory);
+    this->Makefile->IssueMessage(MessageType::LOG, msg);
+  }
+
   bool erroroc = cmSystemTools::GetErrorOccurredFlag();
   cmSystemTools::ResetErrorOccurredFlag();
   std::string output;
   // actually do the try compile now that everything is setup
   int res = this->Makefile->TryCompile(
     sourceDirectory, this->BinaryDirectory, projectName, targetName,
-    this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL, &cmakeFlags,
-    output);
+    this->SrcFileSignature, cmake::NO_BUILD_PARALLEL_LEVEL,
+    &arguments.CMakeFlags, output);
   if (erroroc) {
     cmSystemTools::SetErrorOccurred();
   }
 
   // set the result var to the return value to indicate success or failure
-  this->Makefile->AddCacheDefinition(argv[0], (res == 0 ? "TRUE" : "FALSE"),
-                                     "Result of TRY_COMPILE",
-                                     cmStateEnums::INTERNAL);
+  if (arguments.NoCache) {
+    this->Makefile->AddDefinition(*arguments.CompileResultVariable,
+                                  (res == 0 ? "TRUE" : "FALSE"));
+  } else {
+    this->Makefile->AddCacheDefinition(
+      *arguments.CompileResultVariable, (res == 0 ? "TRUE" : "FALSE"),
+      "Result of TRY_COMPILE", cmStateEnums::INTERNAL);
+  }
 
-  if (!outputVariable.empty()) {
-    this->Makefile->AddDefinition(outputVariable, output);
+  if (arguments.OutputVariable) {
+    this->Makefile->AddDefinition(*arguments.OutputVariable, output);
   }
 
   if (this->SrcFileSignature) {
     std::string copyFileErrorMessage;
-    this->FindOutputFile(targetName, targetType);
+    this->FindOutputFile(targetName);
 
-    if ((res == 0) && !copyFile.empty()) {
+    if ((res == 0) && arguments.CopyFileTo) {
+      std::string const& copyFile = *arguments.CopyFileTo;
       if (this->OutputFile.empty() ||
           !cmSystemTools::CopyFileAlways(this->OutputFile, copyFile)) {
         std::ostringstream emsg;
@@ -1018,19 +1107,26 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv,
         if (!this->FindErrorMessage.empty()) {
           emsg << this->FindErrorMessage;
         }
-        if (copyFileError.empty()) {
+        if (!arguments.CopyFileError) {
           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, emsg.str());
-          return -1;
+          return false;
         }
         copyFileErrorMessage = emsg.str();
       }
     }
 
-    if (!copyFileError.empty()) {
+    if (arguments.CopyFileError) {
+      std::string const& copyFileError = *arguments.CopyFileError;
       this->Makefile->AddDefinition(copyFileError, copyFileErrorMessage);
     }
   }
-  return res;
+  return res == 0;
+}
+
+bool cmCoreTryCompile::IsTemporary(std::string const& path)
+{
+  return ((path.find("CMakeTmp") != std::string::npos) ||
+          (path.find("CMakeScratch") != std::string::npos));
 }
 
 void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
@@ -1039,11 +1135,11 @@ void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
     return;
   }
 
-  if (binDir.find("CMakeTmp") == std::string::npos) {
+  if (!IsTemporary(binDir)) {
     cmSystemTools::Error(
       "TRY_COMPILE attempt to remove -rf directory that does not contain "
-      "CMakeTmp:" +
-      binDir);
+      "CMakeTmp or CMakeScratch: \"" +
+      binDir + "\"");
     return;
   }
 
@@ -1089,63 +1185,74 @@ void cmCoreTryCompile::CleanupFiles(std::string const& binDir)
       }
     }
   }
+
+  if (binDir.find("CMakeScratch") != std::string::npos) {
+    cmSystemTools::RemoveADirectory(binDir);
+  }
 }
 
-void cmCoreTryCompile::FindOutputFile(const std::string& targetName,
-                                      cmStateEnums::TargetType targetType)
+void cmCoreTryCompile::FindOutputFile(const std::string& targetName)
 {
   this->FindErrorMessage.clear();
   this->OutputFile.clear();
   std::string tmpOutputFile = "/";
-  if (targetType == cmStateEnums::EXECUTABLE) {
-    tmpOutputFile += targetName;
-    tmpOutputFile +=
-      this->Makefile->GetSafeDefinition("CMAKE_EXECUTABLE_SUFFIX");
-  } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
-  {
-    tmpOutputFile +=
-      this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX");
-    tmpOutputFile += targetName;
-    tmpOutputFile +=
-      this->Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX");
+  tmpOutputFile += targetName;
+
+  if (this->Makefile->GetGlobalGenerator()->IsMultiConfig()) {
+    tmpOutputFile += "_DEBUG";
+  }
+  tmpOutputFile += "_loc";
+
+  std::string command = cmStrCat(this->BinaryDirectory, tmpOutputFile);
+  if (!cmSystemTools::FileExists(command)) {
+    std::ostringstream emsg;
+    emsg << "Unable to find the recorded try_compile output location:\n";
+    emsg << cmStrCat("  ", command, "\n");
+    this->FindErrorMessage = emsg.str();
+    return;
+  }
+
+  std::string outputFileLocation;
+  cmsys::ifstream ifs(command.c_str());
+  cmSystemTools::GetLineFromStream(ifs, outputFileLocation);
+  if (!cmSystemTools::FileExists(outputFileLocation)) {
+    std::ostringstream emsg;
+    emsg << "Recorded try_compile output location doesn't exist:\n";
+    emsg << cmStrCat("  ", outputFileLocation, "\n");
+    this->FindErrorMessage = emsg.str();
+    return;
   }
 
-  // a list of directories where to search for the compilation result
-  // at first directly in the binary dir
-  std::vector<std::string> searchDirs;
-  searchDirs.emplace_back();
-
-  cmValue config =
-    this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
-  // if a config was specified try that first
-  if (cmNonempty(config)) {
-    std::string tmp = cmStrCat('/', *config);
-    searchDirs.emplace_back(std::move(tmp));
+  this->OutputFile = cmSystemTools::CollapseFullPath(outputFileLocation);
+}
+
+std::string cmCoreTryCompile::WriteSource(std::string const& filename,
+                                          std::string const& content,
+                                          char const* command) const
+{
+  if (!cmSystemTools::GetFilenamePath(filename).empty()) {
+    const auto& msg =
+      cmStrCat(command, " given invalid filename \"", filename, "\"");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
+    return {};
   }
-  searchDirs.emplace_back("/Debug");
-#if defined(__APPLE__)
-  std::string app = "/" + targetName + ".app";
-  if (cmNonempty(config)) {
-    std::string tmp = cmStrCat('/', *config, app);
-    searchDirs.emplace_back(std::move(tmp));
+
+  auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
+  cmsys::ofstream file{ filepath.c_str(), std::ios::out };
+  if (!file) {
+    const auto& msg =
+      cmStrCat(command, " failed to open \"", filename, "\" for writing");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
+    return {};
   }
-  std::string tmp = "/Debug" + app;
-  searchDirs.emplace_back(std::move(tmp));
-  searchDirs.emplace_back(std::move(app));
-#endif
-  searchDirs.emplace_back("/Development");
 
-  for (std::string const& sdir : searchDirs) {
-    std::string command = cmStrCat(this->BinaryDirectory, sdir, tmpOutputFile);
-    if (cmSystemTools::FileExists(command)) {
-      this->OutputFile = cmSystemTools::CollapseFullPath(command);
-      return;
-    }
+  file << content;
+  if (!file) {
+    const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
+    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
+    return {};
   }
 
-  std::ostringstream emsg;
-  emsg << "Unable to find the executable at any of:\n";
-  emsg << cmWrap("  " + this->BinaryDirectory, searchDirs, tmpOutputFile, "\n")
-       << "\n";
-  this->FindErrorMessage = emsg.str();
+  file.close();
+  return filepath;
 }
index 594fd7f..3e1e12c 100644 (file)
@@ -4,28 +4,90 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <map>
 #include <string>
 #include <vector>
 
-#include "cmCommand.h"
+#include <cm/optional>
+
+#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmStateTypes.h"
 
+class cmMakefile;
+template <typename Iter>
+class cmRange;
+
 /** \class cmCoreTryCompile
  * \brief Base class for cmTryCompileCommand and cmTryRunCommand
  *
  * cmCoreTryCompile implements the functionality to build a program.
  * It is the base class for cmTryCompileCommand and cmTryRunCommand.
  */
-class cmCoreTryCompile : public cmCommand
+class cmCoreTryCompile
 {
 public:
-protected:
+  cmCoreTryCompile(cmMakefile* mf)
+    : Makefile(mf)
+  {
+  }
+
+  struct Arguments : public ArgumentParser::ParseResult
+  {
+    cm::optional<std::string> CompileResultVariable;
+    cm::optional<std::string> BinaryDirectory;
+    cm::optional<std::string> SourceDirectoryOrFile;
+    cm::optional<std::string> ProjectName;
+    cm::optional<std::string> TargetName;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Sources;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      SourceFromContent;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      SourceFromVar;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      SourceFromFile;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> CMakeFlags{
+      1, "CMAKE_FLAGS"
+    }; // fake argv[0]
+    std::vector<std::string> CompileDefs;
+    cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
+      LinkLibraries;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> LinkOptions;
+    std::map<std::string, std::string> LangProps;
+    std::string CMakeInternal;
+    cm::optional<std::string> OutputVariable;
+    cm::optional<std::string> CopyFileTo;
+    cm::optional<std::string> CopyFileError;
+    bool NoCache = false;
+
+    // Argument for try_run only.
+    // Keep in sync with warnings in cmCoreTryCompile::ParseArgs.
+    cm::optional<std::string> CompileOutputVariable;
+    cm::optional<std::string> RunOutputVariable;
+    cm::optional<std::string> RunOutputStdOutVariable;
+    cm::optional<std::string> RunOutputStdErrVariable;
+    cm::optional<std::string> RunWorkingDirectory;
+    cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> RunArgs;
+  };
+
+  Arguments ParseArgs(cmRange<std::vector<std::string>::const_iterator> args,
+                      bool isTryRun);
+
   /**
-   * This is the core code for try compile. It is here so that other
-   * commands, such as TryRun can access the same logic without
-   * duplication.
+   * This is the core code for try compile. It is here so that other commands,
+   * such as TryRun can access the same logic without duplication.
+   *
+   * This function requires at least two \p arguments and will crash if given
+   * fewer.
    */
-  int TryCompileCode(std::vector<std::string> const& argv, bool isTryRun);
+  bool TryCompileCode(Arguments& arguments,
+                      cmStateEnums::TargetType targetType);
+
+  /**
+   * Returns \c true if \p path resides within a CMake temporary directory,
+   * otherwise returns \c false.
+   */
+  static bool IsTemporary(std::string const& path);
 
   /**
    * This deletes all the files created by TryCompileCode.
@@ -39,11 +101,20 @@ protected:
   TryCompileCode. The result is stored in OutputFile. If nothing is found,
   the error message is stored in FindErrorMessage.
    */
-  void FindOutputFile(const std::string& targetName,
-                      cmStateEnums::TargetType targetType);
+  void FindOutputFile(const std::string& targetName);
 
   std::string BinaryDirectory;
   std::string OutputFile;
   std::string FindErrorMessage;
   bool SrcFileSignature = false;
+  cmMakefile* Makefile;
+
+private:
+  std::string WriteSource(std::string const& name, std::string const& content,
+                          char const* command) const;
+
+  Arguments ParseArgs(
+    const cmRange<std::vector<std::string>::const_iterator>& args,
+    const cmArgumentParser<Arguments>& parser,
+    std::vector<std::string>& unparsedArguments);
 };
index 2a52d1a..75c25e3 100644 (file)
@@ -87,9 +87,7 @@ bool cmCreateTestSourceList(std::vector<std::string> const& args,
       func_name = cmSystemTools::GetFilenameWithoutLastExtension(*i);
     }
     cmSystemTools::ConvertToUnixSlashes(func_name);
-    std::replace(func_name.begin(), func_name.end(), ' ', '_');
-    std::replace(func_name.begin(), func_name.end(), '/', '_');
-    std::replace(func_name.begin(), func_name.end(), ':', '_');
+    func_name = cmSystemTools::MakeCidentifier(func_name);
     bool already_declared =
       std::find(tests_func_name.begin(), tests_func_name.end(), func_name) !=
       tests_func_name.end();
index 28ee24d..fd6aee1 100644 (file)
 std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile)
 {
   std::string e;
+  std::string env_ca;
   if (!cafile.empty()) {
     ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
     check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
   }
+  /* Honor the user-configurable OpenSSL environment variables. */
+  else if (cmSystemTools::GetEnv("SSL_CERT_FILE", env_ca) &&
+           cmSystemTools::FileExists(env_ca, true)) {
+    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, env_ca.c_str());
+    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
+  } else if (cmSystemTools::GetEnv("SSL_CERT_DIR", env_ca) &&
+             cmSystemTools::FileIsDirectory(env_ca)) {
+    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAPATH, env_ca.c_str());
+    check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
+  }
 #ifdef CMAKE_FIND_CAFILE
 #  define CMAKE_CAFILE_FEDORA "/etc/pki/tls/certs/ca-bundle.crt"
   else if (cmSystemTools::FileExists(CMAKE_CAFILE_FEDORA, true)) {
diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx
new file mode 100644 (file)
index 0000000..84691c9
--- /dev/null
@@ -0,0 +1,308 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCxxModuleMapper.h"
+
+#include <cassert>
+#include <cstddef>
+#include <set>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmScanDepFormat.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+cm::optional<std::string> CxxModuleLocations::BmiGeneratorPathForModule(
+  std::string const& logical_name) const
+{
+  if (auto l = this->BmiLocationForModule(logical_name)) {
+    return this->PathForGenerator(*l);
+  }
+  return {};
+}
+
+namespace {
+
+std::string CxxModuleMapContentGcc(CxxModuleLocations const& loc,
+                                   cmScanDepInfo const& obj)
+{
+  std::stringstream mm;
+
+  // Documented in GCC's documentation. The format is a series of
+  // lines with a module name and the associated filename separated
+  // by spaces. The first line may use `$root` as the module name
+  // to specify a "repository root". That is used to anchor any
+  // relative paths present in the file (CMake should never
+  // generate any).
+
+  // Write the root directory to use for module paths.
+  mm << "$root " << loc.RootDirectory << "\n";
+
+  for (auto const& p : obj.Provides) {
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
+      mm << p.LogicalName << ' ' << *bmi_loc << '\n';
+    }
+  }
+  for (auto const& r : obj.Requires) {
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
+      mm << r.LogicalName << ' ' << *bmi_loc << '\n';
+    }
+  }
+
+  return mm.str();
+}
+
+std::string CxxModuleMapContentMsvc(CxxModuleLocations const& loc,
+                                    cmScanDepInfo const& obj,
+                                    CxxModuleUsage const& usages)
+{
+  std::stringstream mm;
+
+  // A response file of `-reference NAME=PATH` arguments.
+
+  // MSVC's command line only supports a single output. If more than one is
+  // expected, we cannot make a useful module map file.
+  if (obj.Provides.size() > 1) {
+    return {};
+  }
+
+  auto flag_for_method = [](LookupMethod method) -> cm::static_string_view {
+    switch (method) {
+      case LookupMethod::ByName:
+        return "-reference"_s;
+      case LookupMethod::IncludeAngle:
+        return "-headerUnit:angle"_s;
+      case LookupMethod::IncludeQuote:
+        return "-headerUnit:quote"_s;
+    }
+    assert(false && "unsupported lookup method");
+    return ""_s;
+  };
+
+  for (auto const& p : obj.Provides) {
+    if (p.IsInterface) {
+      mm << "-interface\n";
+    } else {
+      mm << "-internalPartition\n";
+    }
+
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
+      mm << "-ifcOutput " << *bmi_loc << '\n';
+    }
+  }
+
+  std::set<std::string> transitive_usage_directs;
+  std::set<std::string> transitive_usage_names;
+
+  for (auto const& r : obj.Requires) {
+    if (auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName)) {
+      auto flag = flag_for_method(r.Method);
+
+      mm << flag << ' ' << r.LogicalName << '=' << *bmi_loc << "\n";
+      transitive_usage_directs.insert(r.LogicalName);
+
+      // Insert transitive usages.
+      auto transitive_usages = usages.Usage.find(r.LogicalName);
+      if (transitive_usages != usages.Usage.end()) {
+        transitive_usage_names.insert(transitive_usages->second.begin(),
+                                      transitive_usages->second.end());
+      }
+    }
+  }
+
+  for (auto const& transitive_name : transitive_usage_names) {
+    if (transitive_usage_directs.count(transitive_name)) {
+      continue;
+    }
+
+    auto module_ref = usages.Reference.find(transitive_name);
+    if (module_ref != usages.Reference.end()) {
+      auto flag = flag_for_method(module_ref->second.Method);
+      mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path
+         << "\n";
+    }
+  }
+
+  return mm.str();
+}
+}
+
+bool CxxModuleUsage::AddReference(std::string const& logical,
+                                  std::string const& loc, LookupMethod method)
+{
+  auto r = this->Reference.find(logical);
+  if (r != this->Reference.end()) {
+    auto& ref = r->second;
+
+    if (ref.Path == loc && ref.Method == method) {
+      return true;
+    }
+
+    auto method_name = [](LookupMethod m) -> cm::static_string_view {
+      switch (m) {
+        case LookupMethod::ByName:
+          return "by-name"_s;
+        case LookupMethod::IncludeAngle:
+          return "include-angle"_s;
+        case LookupMethod::IncludeQuote:
+          return "include-quote"_s;
+      }
+      assert(false && "unsupported lookup method");
+      return ""_s;
+    };
+
+    cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
+                                  logical,
+                                  "' module. "
+                                  "Location A: '",
+                                  ref.Path, "' via ", method_name(ref.Method),
+                                  "; "
+                                  "Location B: '",
+                                  loc, "' via ", method_name(method), "."));
+    return false;
+  }
+
+  auto& ref = this->Reference[logical];
+  ref.Path = loc;
+  ref.Method = method;
+
+  return true;
+}
+
+cm::static_string_view CxxModuleMapExtension(
+  cm::optional<CxxModuleMapFormat> format)
+{
+  if (format) {
+    switch (*format) {
+      case CxxModuleMapFormat::Gcc:
+        return ".gcm"_s;
+      case CxxModuleMapFormat::Msvc:
+        return ".ifc"_s;
+    }
+  }
+
+  return ".bmi"_s;
+}
+
+std::set<std::string> CxxModuleUsageSeed(
+  CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
+  CxxModuleUsage& usages)
+{
+  // Track inner usages to populate usages from internal bits.
+  //
+  // This is a map of modules that required some other module that was not
+  // found to those that were not found.
+  std::map<std::string, std::set<std::string>> internal_usages;
+  std::set<std::string> unresolved;
+
+  for (cmScanDepInfo const& object : objects) {
+    // Add references for each of the provided modules.
+    for (auto const& p : object.Provides) {
+      if (auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName)) {
+        // XXX(cxx-modules): How to support header units?
+        usages.AddReference(p.LogicalName, loc.PathForGenerator(*bmi_loc),
+                            LookupMethod::ByName);
+      }
+    }
+
+    // For each requires, pull in what is required.
+    for (auto const& r : object.Requires) {
+      // Find transitive usages.
+      auto transitive_usages = usages.Usage.find(r.LogicalName);
+      // Find the required name in the current target.
+      auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
+
+      for (auto const& p : object.Provides) {
+        auto& this_usages = usages.Usage[p.LogicalName];
+
+        // Add the direct usage.
+        this_usages.insert(r.LogicalName);
+
+        // Add the transitive usage.
+        if (transitive_usages != usages.Usage.end()) {
+          this_usages.insert(transitive_usages->second.begin(),
+                             transitive_usages->second.end());
+        } else if (bmi_loc) {
+          // Mark that we need to update transitive usages later.
+          internal_usages[p.LogicalName].insert(r.LogicalName);
+        }
+      }
+
+      if (bmi_loc) {
+        usages.AddReference(r.LogicalName, loc.PathForGenerator(*bmi_loc),
+                            r.Method);
+      }
+    }
+  }
+
+  // While we have internal usages to manage.
+  while (!internal_usages.empty()) {
+    size_t starting_size = internal_usages.size();
+
+    // For each internal usage.
+    for (auto usage = internal_usages.begin(); usage != internal_usages.end();
+         /* see end of loop */) {
+      auto& this_usages = usages.Usage[usage->first];
+
+      for (auto use = usage->second.begin(); use != usage->second.end();
+           /* see end of loop */) {
+        // Check if this required module uses other internal modules; defer
+        // if so.
+        if (internal_usages.count(*use)) {
+          // Advance the iterator.
+          ++use;
+          continue;
+        }
+
+        auto transitive_usages = usages.Usage.find(*use);
+        if (transitive_usages != usages.Usage.end()) {
+          this_usages.insert(transitive_usages->second.begin(),
+                             transitive_usages->second.end());
+        }
+
+        // Remove the entry and advance the iterator.
+        use = usage->second.erase(use);
+      }
+
+      // Erase the entry if it doesn't have any remaining usages.
+      if (usage->second.empty()) {
+        usage = internal_usages.erase(usage);
+      } else {
+        ++usage;
+      }
+    }
+
+    // Check that at least one usage was resolved.
+    if (starting_size == internal_usages.size()) {
+      // Nothing could be resolved this loop; we have a cycle, so record the
+      // cycle and exit.
+      for (auto const& usage : internal_usages) {
+        unresolved.insert(usage.first);
+      }
+      break;
+    }
+  }
+
+  return unresolved;
+}
+
+std::string CxxModuleMapContent(CxxModuleMapFormat format,
+                                CxxModuleLocations const& loc,
+                                cmScanDepInfo const& obj,
+                                CxxModuleUsage const& usages)
+{
+  switch (format) {
+    case CxxModuleMapFormat::Gcc:
+      return CxxModuleMapContentGcc(loc, obj);
+    case CxxModuleMapFormat::Msvc:
+      return CxxModuleMapContentMsvc(loc, obj, usages);
+  }
+
+  assert(false);
+  return {};
+}
diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h
new file mode 100644 (file)
index 0000000..8526a07
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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 <functional>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+#include <cmext/string_view>
+
+#include "cmScanDepFormat.h"
+
+enum class CxxModuleMapFormat
+{
+  Gcc,
+  Msvc,
+};
+
+struct CxxModuleLocations
+{
+  // The path from which all relative paths should be computed. If
+  // this is relative, it is relative to the compiler's working
+  // directory.
+  std::string RootDirectory;
+
+  // A function to convert a full path to a path for the generator.
+  std::function<std::string(std::string const&)> PathForGenerator;
+
+  // Lookup the BMI location of a logical module name.
+  std::function<cm::optional<std::string>(std::string const&)>
+    BmiLocationForModule;
+
+  // Returns the generator path (if known) for the BMI given a
+  // logical module name.
+  cm::optional<std::string> BmiGeneratorPathForModule(
+    std::string const& logical_name) const;
+};
+
+struct CxxModuleReference
+{
+  // The path to the module file used.
+  std::string Path;
+  // How the module was looked up.
+  LookupMethod Method;
+};
+
+struct CxxModuleUsage
+{
+  // The usage requirements for this object.
+  std::map<std::string, std::set<std::string>> Usage;
+
+  // The references for this object.
+  std::map<std::string, CxxModuleReference> Reference;
+
+  // Add a reference to a module.
+  //
+  // Returns `true` if it matches how it was found previously, `false` if it
+  // conflicts.
+  bool AddReference(std::string const& logical, std::string const& loc,
+                    LookupMethod method);
+};
+
+// Return the extension to use for a given modulemap format.
+cm::static_string_view CxxModuleMapExtension(
+  cm::optional<CxxModuleMapFormat> format);
+
+// Fill in module usage information for internal usages.
+//
+// Returns the set of unresolved module usage requirements (these form an
+// import cycle).
+std::set<std::string> CxxModuleUsageSeed(
+  CxxModuleLocations const& loc, std::vector<cmScanDepInfo> const& objects,
+  CxxModuleUsage& usages);
+
+// Return the contents of the module map in the given format for the
+// object file.
+std::string CxxModuleMapContent(CxxModuleMapFormat format,
+                                CxxModuleLocations const& loc,
+                                cmScanDepInfo const& obj,
+                                CxxModuleUsage const& usages);
index faefcb8..31ee665 100644 (file)
@@ -8,6 +8,7 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmProperty.h"
@@ -51,8 +52,8 @@ bool cmDefinePropertyCommand(std::vector<std::string> const& args,
   // Parse remaining arguments.
   bool inherited = false;
   std::string PropertyName;
-  std::vector<std::string> BriefDocs;
-  std::vector<std::string> FullDocs;
+  ArgumentParser::NonEmpty<std::vector<std::string>> BriefDocs;
+  ArgumentParser::NonEmpty<std::vector<std::string>> FullDocs;
   std::string initializeFromVariable;
 
   cmArgumentParser<void> parser;
index 3619ade..d466a12 100644 (file)
 #include "cmVersion.h"
 
 static const char* cmDocumentationStandardOptions[][2] = {
-  { "--help,-help,-usage,-h,-H,/?", "Print usage information and exit." },
-  { "--version,-version,/V [<f>]", "Print version number and exit." },
-  { "--help-full [<f>]", "Print all help manuals and exit." },
-  { "--help-manual <man> [<f>]", "Print one help manual and exit." },
-  { "--help-manual-list [<f>]", "List help manuals available and exit." },
-  { "--help-command <cmd> [<f>]", "Print help for one command and exit." },
-  { "--help-command-list [<f>]",
+  { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." },
+  { "--version,-version,/V [<file>]", "Print version number and exit." },
+  { "--help-full [<file>]", "Print all help manuals and exit." },
+  { "--help-manual <man> [<file>]", "Print one help manual and exit." },
+  { "--help-manual-list [<file>]", "List help manuals available and exit." },
+  { "--help-command <cmd> [<file>]", "Print help for one command and exit." },
+  { "--help-command-list [<file>]",
     "List commands with help available and exit." },
-  { "--help-commands [<f>]", "Print cmake-commands manual and exit." },
-  { "--help-module <mod> [<f>]", "Print help for one module and exit." },
-  { "--help-module-list [<f>]", "List modules with help available and exit." },
-  { "--help-modules [<f>]", "Print cmake-modules manual and exit." },
-  { "--help-policy <cmp> [<f>]", "Print help for one policy and exit." },
-  { "--help-policy-list [<f>]",
+  { "--help-commands [<file>]", "Print cmake-commands manual and exit." },
+  { "--help-module <mod> [<file>]", "Print help for one module and exit." },
+  { "--help-module-list [<file>]",
+    "List modules with help available and exit." },
+  { "--help-modules [<file>]", "Print cmake-modules manual and exit." },
+  { "--help-policy <cmp> [<file>]", "Print help for one policy and exit." },
+  { "--help-policy-list [<file>]",
     "List policies with help available and exit." },
-  { "--help-policies [<f>]", "Print cmake-policies manual and exit." },
-  { "--help-property <prop> [<f>]", "Print help for one property and exit." },
-  { "--help-property-list [<f>]",
+  { "--help-policies [<file>]", "Print cmake-policies manual and exit." },
+  { "--help-property <prop> [<file>]",
+    "Print help for one property and exit." },
+  { "--help-property-list [<file>]",
     "List properties with help available and exit." },
-  { "--help-properties [<f>]", "Print cmake-properties manual and exit." },
-  { "--help-variable var [<f>]", "Print help for one variable and exit." },
-  { "--help-variable-list [<f>]",
+  { "--help-properties [<file>]", "Print cmake-properties manual and exit." },
+  { "--help-variable var [<file>]", "Print help for one variable and exit." },
+  { "--help-variable-list [<file>]",
     "List variables with help available and exit." },
-  { "--help-variables [<f>]", "Print cmake-variables manual and exit." },
+  { "--help-variables [<file>]", "Print cmake-variables manual and exit." },
   { nullptr, nullptr }
 };
 
index 222ea80..7fbd826 100644 (file)
@@ -47,7 +47,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
     std::vector<std::vector<std::string>> Commands;
     std::string OutputVariable;
@@ -95,14 +95,10 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
       .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  Arguments const arguments =
-    parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
+  Arguments const arguments = parser.Parse(args, &unparsedArguments);
 
-  if (!keywordsMissingValue.empty()) {
-    status.SetError(" called with no value for " +
-                    keywordsMissingValue.front() + ".");
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if (!unparsedArguments.empty()) {
     status.SetError(" given unknown argument \"" + unparsedArguments.front() +
index 0feaedf..ced3548 100644 (file)
@@ -5,6 +5,7 @@
 #include <cmConfigure.h> // IWYU pragma: keep
 
 #include <string>
+#include <vector>
 
 class cmMakefile;
 
@@ -27,8 +28,21 @@ public:
   void SetError(std::string const& e) { this->Error = e; }
   std::string const& GetError() const { return this->Error; }
 
-  void SetReturnInvoked() { this->ReturnInvoked = true; }
+  void SetReturnInvoked()
+  {
+    this->Variables.clear();
+    this->ReturnInvoked = true;
+  }
+  void SetReturnInvoked(std::vector<std::string> variables)
+  {
+    this->Variables = std::move(variables);
+    this->ReturnInvoked = true;
+  }
   bool GetReturnInvoked() const { return this->ReturnInvoked; }
+  const std::vector<std::string>& GetReturnVariables() const
+  {
+    return this->Variables;
+  }
 
   void SetBreakInvoked() { this->BreakInvoked = true; }
   bool GetBreakInvoked() const { return this->BreakInvoked; }
@@ -46,4 +60,5 @@ private:
   bool BreakInvoked = false;
   bool ContinueInvoked = false;
   bool NestedError = false;
+  std::vector<std::string> Variables;
 };
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx
new file mode 100644 (file)
index 0000000..922b53f
--- /dev/null
@@ -0,0 +1,63 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmExperimental.h"
+
+#include <cassert>
+#include <cstddef>
+#include <string>
+
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmValue.h"
+
+namespace {
+
+/*
+ * The `Uuid` fields of these objects should change periodically.
+ * Search for other instances to keep the documentation and test suite
+ * up-to-date.
+ */
+
+struct FeatureData
+{
+  std::string const Uuid;
+  std::string const Variable;
+  std::string const Description;
+  bool Warned;
+} LookupTable[] = {
+  // CxxModuleCMakeApi
+  { "3c375311-a3c9-4396-a187-3227ef642046",
+    "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API",
+    "CMake's C++ module support is experimental. It is meant only for "
+    "experimentation and feedback to CMake developers.",
+    false },
+};
+static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) ==
+                static_cast<size_t>(cmExperimental::Feature::Sentinel),
+              "Experimental feature lookup table mismatch");
+
+FeatureData& DataForFeature(cmExperimental::Feature f)
+{
+  assert(f != cmExperimental::Feature::Sentinel);
+  return LookupTable[static_cast<size_t>(f)];
+}
+}
+
+bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f)
+{
+  bool enabled = false;
+  auto& data = DataForFeature(f);
+
+  auto value = mf.GetDefinition(data.Variable);
+  if (value == data.Uuid) {
+    enabled = true;
+  }
+
+  if (enabled && !data.Warned) {
+    mf.IssueMessage(MessageType::AUTHOR_WARNING, data.Description);
+    data.Warned = true;
+  }
+
+  return enabled;
+}
diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h
new file mode 100644 (file)
index 0000000..26e0d17
--- /dev/null
@@ -0,0 +1,21 @@
+/* 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
+
+class cmMakefile;
+
+class cmExperimental
+{
+public:
+  enum class Feature
+  {
+    CxxModuleCMakeApi,
+
+    Sentinel,
+  };
+
+  static bool HasSupportEnabled(cmMakefile const& mf, Feature f);
+};
index 6ce0c98..ed199ea 100644 (file)
@@ -9,10 +9,13 @@
 #include <sstream>
 #include <utility>
 
+#include <cm/string_view>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmExportSet.h"
 #include "cmFileSet.h"
+#include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -23,6 +26,7 @@
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
 #include "cmValue.h"
@@ -139,11 +143,18 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->GenerateTargetFileSets(gte, os);
   }
 
+  this->GenerateCxxModuleInformation(os);
+
   // Generate import file content for each configuration.
   for (std::string const& c : this->Configurations) {
     this->GenerateImportConfig(os, c);
   }
 
+  // Generate import file content for each configuration.
+  for (std::string const& c : this->Configurations) {
+    this->GenerateImportCxxModuleConfigTargetInclusion(c);
+  }
+
   this->GenerateMissingTargetsCheckCode(os);
 
   return true;
@@ -382,6 +393,21 @@ std::string cmExportBuildFileGenerator::GetFileSetDirectories(
       std::any_of(directoryEntries.begin(), directoryEntries.end(),
                   EntryIsContextSensitive);
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base directory entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& directory : directories) {
       auto dest = cmOutputConverter::EscapeForCMake(
         directory, cmOutputConverter::WrapQuotes::NoWrap);
@@ -427,6 +453,21 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
       std::any_of(fileEntries.begin(), fileEntries.end(),
                   EntryIsContextSensitive);
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->LG->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& it : files) {
       for (auto const& filename : it.second) {
         auto escapedFile = cmOutputConverter::EscapeForCMake(
@@ -447,3 +488,60 @@ std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
 
   return cmJoin(resultVector, " ");
 }
+
+std::string cmExportBuildFileGenerator::GetCxxModulesDirectory() const
+{
+  return this->CxxModulesDirectory;
+}
+
+void cmExportBuildFileGenerator::GenerateCxxModuleConfigInformation(
+  std::ostream& os) const
+{
+  const char* opt = "";
+  if (this->Configurations.size() > 1) {
+    // With more than one configuration, each individual file is optional.
+    opt = " OPTIONAL";
+  }
+
+  // Generate import file content for each configuration.
+  for (std::string c : this->Configurations) {
+    if (c.empty()) {
+      c = "noconfig";
+    }
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << c << ".cmake\""
+       << opt << ")\n";
+  }
+}
+
+bool cmExportBuildFileGenerator::GenerateImportCxxModuleConfigTargetInclusion(
+  std::string config) const
+{
+  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
+  if (cxx_modules_dirname.empty()) {
+    return true;
+  }
+
+  if (config.empty()) {
+    config = "noconfig";
+  }
+
+  std::string fileName = cmStrCat(this->FileDir, '/', cxx_modules_dirname,
+                                  "/cxx-modules-", config, ".cmake");
+
+  cmGeneratedFileStream os(fileName, true);
+  if (!os) {
+    std::string se = cmSystemTools::GetLastSystemError();
+    std::ostringstream e;
+    e << "cannot write to file \"" << fileName << "\": " << se;
+    cmSystemTools::Error(e.str());
+    return false;
+  }
+  os.SetCopyIfDifferent(true);
+
+  for (auto const* tgt : this->ExportedTargets) {
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-" << tgt->GetExportName()
+       << '-' << config << ".cmake\")\n";
+  }
+
+  return true;
+}
index 5681e8f..4636196 100644 (file)
@@ -47,6 +47,16 @@ public:
   }
   void SetExportSet(cmExportSet*);
 
+  /** Set the name of the C++ module directory.  */
+  void SetCxxModuleDirectory(std::string cxx_module_dir)
+  {
+    this->CxxModulesDirectory = std::move(cxx_module_dir);
+  }
+  const std::string& GetCxxModuleDirectory() const
+  {
+    return this->CxxModulesDirectory;
+  }
+
   /** Set whether to append generated code to the output file.  */
   void SetAppendMode(bool append) { this->AppendMode = append; }
 
@@ -81,6 +91,10 @@ protected:
   std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
                               cmTargetExport* te) override;
 
+  std::string GetCxxModulesDirectory() const override;
+  void GenerateCxxModuleConfigInformation(std::ostream&) const override;
+  bool GenerateImportCxxModuleConfigTargetInclusion(std::string) const;
+
   std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
     cmGlobalGenerator* gg, const std::string& name);
 
@@ -88,4 +102,6 @@ protected:
   cmExportSet* ExportSet;
   std::vector<cmGeneratorTarget*> Exports;
   cmLocalGenerator* LG;
+  // The directory for C++ module information.
+  std::string CxxModulesDirectory;
 };
index 63440a3..a58f2b7 100644 (file)
@@ -7,13 +7,15 @@
 #include <utility>
 
 #include <cm/memory>
-#include <cmext/algorithm>
+#include <cm/optional>
 #include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
+#include "cmExperimental.h"
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportBuildFileGenerator.h"
 #include "cmExportSet.h"
@@ -57,10 +59,11 @@ bool cmExportCommand(std::vector<std::string> const& args,
   struct Arguments
   {
     std::string ExportSetName;
-    std::vector<std::string> Targets;
+    cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
     std::string Namespace;
     std::string Filename;
     std::string AndroidMKFile;
+    std::string CxxModulesDirectory;
     bool Append = false;
     bool ExportOld = false;
   };
@@ -69,6 +72,12 @@ bool cmExportCommand(std::vector<std::string> const& args,
                   .Bind("NAMESPACE"_s, &Arguments::Namespace)
                   .Bind("FILE"_s, &Arguments::Filename);
 
+  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
+    status.GetMakefile(), cmExperimental::Feature::CxxModuleCMakeApi);
+  if (supportCxx20FileSetTypes) {
+    parser.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory);
+  }
+
   if (args[0] == "EXPORT") {
     parser.Bind("EXPORT"_s, &Arguments::ExportSetName);
   } else {
@@ -79,9 +88,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
 
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> keywordsMissingValue;
-  Arguments const arguments =
-    parser.Parse(args, &unknownArgs, &keywordsMissingValue);
+  Arguments const arguments = parser.Parse(args, &unknownArgs);
 
   if (!unknownArgs.empty()) {
     status.SetError("Unknown argument: \"" + unknownArgs.front() + "\".");
@@ -145,9 +152,8 @@ bool cmExportCommand(std::vector<std::string> const& args,
       return false;
     }
     exportSet = &it->second;
-  } else if (!arguments.Targets.empty() ||
-             cm::contains(keywordsMissingValue, "TARGETS")) {
-    for (std::string const& currentTarget : arguments.Targets) {
+  } else if (arguments.Targets) {
+    for (std::string const& currentTarget : *arguments.Targets) {
       if (mf.IsAlias(currentTarget)) {
         std::ostringstream e;
         e << "given ALIAS target \"" << currentTarget
@@ -214,6 +220,7 @@ bool cmExportCommand(std::vector<std::string> const& args,
   }
   ebfg->SetExportFile(fname.c_str());
   ebfg->SetNamespace(arguments.Namespace);
+  ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory);
   ebfg->SetAppendMode(arguments.Append);
   if (exportSet != nullptr) {
     ebfg->SetExportSet(exportSet);
index 5a33349..50bc78c 100644 (file)
@@ -939,13 +939,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.22 (this upper limit may be reviewed
+  // policy settings for up to CMake 3.23 (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.22)\n";
+     << "cmake_policy(VERSION 2.8.3...3.23)\n";
   /* clang-format on */
 }
 
@@ -1095,6 +1095,10 @@ void cmExportFileGenerator::GenerateImportTargetCode(
        << " PROPERTY IMPORTED_NO_SYSTEM 1)\n";
   }
 
+  if (target->GetPropertyAsBool("EXPORT_NO_SYSTEM")) {
+    os << "set_property(TARGET " << targetName << " PROPERTY SYSTEM 0)\n";
+  }
+
   os << "\n";
 }
 
@@ -1308,3 +1312,28 @@ void cmExportFileGenerator::GenerateTargetFileSets(cmGeneratorTarget* gte,
     os << "  )\nendif()\n\n";
   }
 }
+
+void cmExportFileGenerator::GenerateCxxModuleInformation(std::ostream& os)
+{
+  auto const cxx_module_dirname = this->GetCxxModulesDirectory();
+  if (cxx_module_dirname.empty()) {
+    return;
+  }
+
+  // Write the include.
+  os << "# Include C++ module properties\n"
+     << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << cxx_module_dirname
+     << "/cxx-modules.cmake\")\n\n";
+
+  // Get the path to the file we're going to write.
+  std::string path = this->MainImportFile;
+  path = cmSystemTools::GetFilenamePath(path);
+  auto trampoline_path =
+    cmStrCat(path, '/', cxx_module_dirname, "/cxx-modules.cmake");
+
+  // Include all configuration-specific include files.
+  cmGeneratedFileStream ap(trampoline_path, true);
+  ap.SetCopyIfDifferent(true);
+
+  this->GenerateCxxModuleConfigInformation(ap);
+}
index d27a555..fdda878 100644 (file)
@@ -182,6 +182,8 @@ protected:
   void GenerateTargetFileSets(cmGeneratorTarget* gte, std::ostream& os,
                               cmTargetExport* te = nullptr);
 
+  void GenerateCxxModuleInformation(std::ostream& os);
+
   virtual std::string GetFileSetDirectories(cmGeneratorTarget* gte,
                                             cmFileSet* fileSet,
                                             cmTargetExport* te) = 0;
@@ -226,4 +228,7 @@ private:
 
   virtual std::string InstallNameDir(cmGeneratorTarget const* target,
                                      const std::string& config) = 0;
+
+  virtual std::string GetCxxModulesDirectory() const = 0;
+  virtual void GenerateCxxModuleConfigInformation(std::ostream& os) const = 0;
 };
index 4e4f8a1..d53254d 100644 (file)
@@ -35,8 +35,7 @@ void cmExportInstallAndroidMKGenerator::GenerateImportHeaderCode(
   for (size_t n = 0; n < numDotDot; n++) {
     path += "/..";
   }
-  os << "_IMPORT_PREFIX := "
-     << "$(LOCAL_PATH)" << path << "\n\n";
+  os << "_IMPORT_PREFIX := $(LOCAL_PATH)" << path << "\n\n";
   for (std::unique_ptr<cmTargetExport> const& te :
        this->IEGen->GetExportSet()->GetTargetExports()) {
     // Collect import properties for this target.
index adccdfe..195737b 100644 (file)
@@ -7,6 +7,9 @@
 #include <sstream>
 #include <utility>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
@@ -18,6 +21,7 @@
 #include "cmInstallTargetGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
@@ -162,10 +166,20 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
 
   this->LoadConfigFiles(os);
 
+  bool result = true;
+
+  this->GenerateCxxModuleInformation(os);
+  if (requiresConfigFiles) {
+    for (std::string const& c : this->Configurations) {
+      if (!this->GenerateImportCxxModuleConfigTargetInclusion(c)) {
+        result = false;
+      }
+    }
+  }
+
   this->CleanupTemporaryVariables(os);
   this->GenerateImportedFileCheckLoop(os);
 
-  bool result = true;
   // Generate an import file for each configuration.
   // Don't do this if we only export INTERFACE_LIBRARY targets.
   if (requiresConfigFiles) {
@@ -422,7 +436,10 @@ void cmExportInstallFileGenerator::SetImportLocationProperty(
     // Append the installed file name.
     if (target->IsAppBundleOnApple()) {
       value += cmInstallTargetGenerator::GetInstallFilename(target, config);
-      value += ".app/Contents/MacOS/";
+      value += ".app/";
+      if (!target->Makefile->PlatformIsAppleEmbedded()) {
+        value += "Contents/MacOS/";
+      }
       value += cmInstallTargetGenerator::GetInstallFilename(target, config);
     } else {
       value += cmInstallTargetGenerator::GetInstallFilename(
@@ -562,6 +579,21 @@ std::string cmExportInstallFileGenerator::GetFileSetDirectories(
                            cge->Evaluate(gte->LocalGenerator, config, gte),
                            cmOutputConverter::WrapQuotes::NoWrap));
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (cge->GetHadContextSensitiveCondition() &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) {
       resultVector.push_back(
         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
@@ -610,6 +642,21 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles(
       std::any_of(fileEntries.begin(), fileEntries.end(),
                   EntryIsContextSensitive);
 
+    auto const& type = fileSet->GetType();
+    // C++ modules do not support interface file sets which are dependent upon
+    // the configuration.
+    if (contextSensitive &&
+        (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s)) {
+      auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile();
+      std::ostringstream e;
+      e << "The \"" << gte->GetName() << "\" target's interface file set \""
+        << fileSet->GetName() << "\" of type \"" << type
+        << "\" contains context-sensitive base file entries which is not "
+           "supported.";
+      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      return std::string{};
+    }
+
     for (auto const& it : files) {
       auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
       for (auto const& filename : it.second) {
@@ -635,3 +682,65 @@ std::string cmExportInstallFileGenerator::GetFileSetFiles(
 
   return cmJoin(resultVector, " ");
 }
+
+std::string cmExportInstallFileGenerator::GetCxxModulesDirectory() const
+{
+  return IEGen->GetCxxModuleDirectory();
+}
+
+void cmExportInstallFileGenerator::GenerateCxxModuleConfigInformation(
+  std::ostream& os) const
+{
+  // Now load per-configuration properties for them.
+  /* clang-format off */
+  os << "# Load information for each installed configuration.\n"
+        "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-*.cmake\")\n"
+        "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
+        "  include(\"${_cmake_cxx_module_include}\")\n"
+        "endforeach()\n"
+        "unset(_cmake_cxx_module_include)\n"
+        "unset(_cmake_cxx_module_includes)\n";
+  /* clang-format on */
+}
+
+bool cmExportInstallFileGenerator::
+  GenerateImportCxxModuleConfigTargetInclusion(std::string const& config)
+{
+  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
+  if (cxx_modules_dirname.empty()) {
+    return true;
+  }
+
+  std::string filename_config = config;
+  if (filename_config.empty()) {
+    filename_config = "noconfig";
+  }
+
+  std::string const dest =
+    cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
+  std::string fileName =
+    cmStrCat(dest, "cxx-modules-", filename_config, ".cmake");
+
+  cmGeneratedFileStream os(fileName, true);
+  if (!os) {
+    std::string se = cmSystemTools::GetLastSystemError();
+    std::ostringstream e;
+    e << "cannot write to file \"" << fileName << "\": " << se;
+    cmSystemTools::Error(e.str());
+    return false;
+  }
+  os.SetCopyIfDifferent(true);
+
+  // Record this per-config import file.
+  this->ConfigCxxModuleFiles[config] = fileName;
+
+  auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
+  for (auto const* tgt : this->ExportedTargets) {
+    auto prop_filename = cmStrCat("target-", tgt->GetExportName(), '-',
+                                  filename_config, ".cmake");
+    prop_files.emplace_back(cmStrCat(dest, prop_filename));
+    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
+  }
+
+  return true;
+}
index 86fb505..e073a31 100644 (file)
@@ -50,6 +50,23 @@ public:
     return this->ConfigImportFiles;
   }
 
+  /** Get the per-config C++ module file generated for each configuration.
+      This maps from the configuration name to the file temporary location
+      for installation.  */
+  std::map<std::string, std::string> const& GetConfigCxxModuleFiles()
+  {
+    return this->ConfigCxxModuleFiles;
+  }
+
+  /** Get the per-config C++ module file generated for each configuration.
+      This maps from the configuration name to the file temporary location
+      for installation for each target in the export set.  */
+  std::map<std::string, std::vector<std::string>> const&
+  GetConfigCxxModuleTargetFiles()
+  {
+    return this->ConfigCxxModuleTargetFiles;
+  }
+
   /** Compute the globbing expression used to load per-config import
       files from the main file.  */
   std::string GetConfigImportFileGlob();
@@ -100,8 +117,16 @@ protected:
   std::string GetFileSetFiles(cmGeneratorTarget* gte, cmFileSet* fileSet,
                               cmTargetExport* te) override;
 
+  std::string GetCxxModulesDirectory() const override;
+  void GenerateCxxModuleConfigInformation(std::ostream&) const override;
+  bool GenerateImportCxxModuleConfigTargetInclusion(std::string const&);
+
   cmInstallExportGenerator* IEGen;
 
   // The import file generated for each configuration.
   std::map<std::string, std::string> ConfigImportFiles;
+  // The C++ module property file generated for each configuration.
+  std::map<std::string, std::string> ConfigCxxModuleFiles;
+  // The C++ module property target files generated for each configuration.
+  std::map<std::string, std::vector<std::string>> ConfigCxxModuleTargetFiles;
 };
index 1dd8a20..5c34fad 100644 (file)
@@ -55,6 +55,9 @@ protected:
   std::string GetFileSetFiles(cmGeneratorTarget* target, cmFileSet* fileSet,
                               cmTargetExport* te) override;
 
+  std::string GetCxxModulesDirectory() const override { return {}; }
+  void GenerateCxxModuleConfigInformation(std::ostream&) const override {}
+
 private:
   std::string FindTargets(const std::string& prop,
                           const cmGeneratorTarget* tgt,
index dd0540c..0581802 100644 (file)
@@ -27,6 +27,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstallCxxModuleBmiGenerator.h"
 #include "cmInstallDirectoryGenerator.h"
 #include "cmInstallExportGenerator.h"
 #include "cmInstallFileSetGenerator.h"
@@ -1092,6 +1093,21 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen)
     if (installFileSet->GetOptional()) {
       installer["isOptional"] = true;
     }
+  } else if (auto* cxxModuleBmi =
+               dynamic_cast<cmInstallCxxModuleBmiGenerator*>(gen)) {
+    installer["type"] = "cxxModuleBmi";
+    installer["destination"] = cxxModuleBmi->GetDestination(this->Config);
+
+    auto const* target = cxxModuleBmi->GetTarget();
+    installer["cxxModuleBmiTarget"] = Json::objectValue;
+    installer["cxxModuleBmiTarget"]["id"] = TargetId(target, this->TopBuild);
+    installer["cxxModuleBmiTarget"]["index"] = this->TargetIndexMap[target];
+
+    // FIXME: Parse FilePermissions.
+    // FIXME: Parse MessageLevel.
+    if (cxxModuleBmi->GetOptional()) {
+      installer["isOptional"] = true;
+    }
   }
 
   // Add fields common to all install generators.
index 3c234a6..fe38db5 100644 (file)
@@ -28,8 +28,8 @@
 
 #include "cm_sys_stat.h"
 
-#include "cmAlgorithms.h"
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmCMakePath.h"
 #include "cmCryptoHash.h"
 #include "cmELF.h"
@@ -172,7 +172,8 @@ bool HandleReadCommand(std::vector<std::string> const& args,
                                .Bind("LIMIT"_s, &Arguments::Limit)
                                .Bind("HEX"_s, &Arguments::Hex);
 
-  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3));
+  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(3),
+                                           /*unparsedArguments=*/nullptr);
 
   std::string fileName = fileNameArg;
   if (!cmsys::SystemTools::FileIsFullPath(fileName)) {
@@ -959,42 +960,34 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args,
 {
   // Evaluate arguments.
   std::string file;
-  std::string oldRPath;
-  std::string newRPath;
+  cm::optional<std::string> oldRPath;
+  cm::optional<std::string> newRPath;
   bool removeEnvironmentRPath = false;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
   parser.Bind("FILE"_s, file)
     .Bind("OLD_RPATH"_s, oldRPath)
     .Bind("NEW_RPATH"_s, newRPath)
     .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath);
-  parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
-               &parsedArgs);
+  ArgumentParser::ParseResult parseResult =
+    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
   if (!unknownArgs.empty()) {
     status.SetError(
       cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front()));
     return false;
   }
-  if (!missingArgs.empty()) {
-    status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(),
-                             "\" argument not given value."));
-    return false;
+  if (parseResult.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if (file.empty()) {
     status.SetError("RPATH_CHANGE not given FILE option.");
     return false;
   }
-  if (oldRPath.empty() &&
-      std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") ==
-        parsedArgs.end()) {
+  if (!oldRPath) {
     status.SetError("RPATH_CHANGE not given OLD_RPATH option.");
     return false;
   }
-  if (newRPath.empty() &&
-      std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") ==
-        parsedArgs.end()) {
+  if (!newRPath) {
     status.SetError("RPATH_CHANGE not given NEW_RPATH option.");
     return false;
   }
@@ -1008,17 +1001,17 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args,
   std::string emsg;
   bool changed;
 
-  if (!cmSystemTools::ChangeRPath(file, oldRPath, newRPath,
+  if (!cmSystemTools::ChangeRPath(file, *oldRPath, *newRPath,
                                   removeEnvironmentRPath, &emsg, &changed)) {
     status.SetError(cmStrCat("RPATH_CHANGE could not write new RPATH:\n  ",
-                             newRPath, "\nto the file:\n  ", file, "\n",
+                             *newRPath, "\nto the file:\n  ", file, "\n",
                              emsg));
     success = false;
   }
   if (success) {
     if (changed) {
       std::string message =
-        cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"');
+        cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
       status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
@@ -1031,31 +1024,25 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args,
 {
   // Evaluate arguments.
   std::string file;
-  std::string newRPath;
+  cm::optional<std::string> newRPath;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
   parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath);
-  parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
-               &parsedArgs);
+  ArgumentParser::ParseResult parseResult =
+    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
   if (!unknownArgs.empty()) {
     status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"",
                              unknownArgs.front(), "\"."));
     return false;
   }
-  if (!missingArgs.empty()) {
-    status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(),
-                             "\" argument not given value."));
-    return false;
+  if (parseResult.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if (file.empty()) {
     status.SetError("RPATH_SET not given FILE option.");
     return false;
   }
-  if (newRPath.empty() &&
-      std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") ==
-        parsedArgs.end()) {
+  if (!newRPath) {
     status.SetError("RPATH_SET not given NEW_RPATH option.");
     return false;
   }
@@ -1069,16 +1056,16 @@ bool HandleRPathSetCommand(std::vector<std::string> const& args,
   std::string emsg;
   bool changed;
 
-  if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) {
+  if (!cmSystemTools::SetRPath(file, *newRPath, &emsg, &changed)) {
     status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n  ",
-                             newRPath, "\nto the file:\n  ", file, "\n",
+                             *newRPath, "\nto the file:\n  ", file, "\n",
                              emsg));
     success = false;
   }
   if (success) {
     if (changed) {
       std::string message =
-        cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"');
+        cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
       status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
@@ -1093,18 +1080,16 @@ bool HandleRPathRemoveCommand(std::vector<std::string> const& args,
   std::string file;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
   parser.Bind("FILE"_s, file);
-  parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs);
+  ArgumentParser::ParseResult parseResult =
+    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
   if (!unknownArgs.empty()) {
     status.SetError(
       cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front()));
     return false;
   }
-  if (!missingArgs.empty()) {
-    status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(),
-                             "\" argument not given value."));
-    return false;
+  if (parseResult.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if (file.empty()) {
     status.SetError("RPATH_REMOVE not given FILE option.");
@@ -1141,31 +1126,25 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args,
 {
   // Evaluate arguments.
   std::string file;
-  std::string rpath;
+  cm::optional<std::string> rpath;
   cmArgumentParser<void> parser;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> missingArgs;
-  std::vector<std::string> parsedArgs;
   parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath);
-  parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs,
-               &parsedArgs);
+  ArgumentParser::ParseResult parseResult =
+    parser.Parse(cmMakeRange(args).advance(1), &unknownArgs);
   if (!unknownArgs.empty()) {
     status.SetError(
       cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front()));
     return false;
   }
-  if (!missingArgs.empty()) {
-    status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(),
-                             "\" argument not given value."));
-    return false;
+  if (parseResult.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
   if (file.empty()) {
     status.SetError("RPATH_CHECK not given FILE option.");
     return false;
   }
-  if (rpath.empty() &&
-      std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") ==
-        parsedArgs.end()) {
+  if (!rpath) {
     status.SetError("RPATH_CHECK not given RPATH option.");
     return false;
   }
@@ -1174,7 +1153,7 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args,
   // delete it.  This is used during installation to re-install a file
   // if its RPath will change.
   if (cmSystemTools::FileExists(file, true) &&
-      !cmSystemTools::CheckRPath(file, rpath)) {
+      !cmSystemTools::CheckRPath(file, *rpath)) {
     cmSystemTools::RemoveFile(file);
   }
 
@@ -1203,7 +1182,8 @@ bool HandleReadElfCommand(std::vector<std::string> const& args,
                                .Bind("RPATH"_s, &Arguments::RPath)
                                .Bind("RUNPATH"_s, &Arguments::RunPath)
                                .Bind("CAPTURE_ERROR"_s, &Arguments::Error);
-  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2));
+  Arguments const arguments = parser.Parse(cmMakeRange(args).advance(2),
+                                           /*unparsedArguments=*/nullptr);
 
   if (!cmSystemTools::FileExists(fileNameArg, true)) {
     status.SetError(cmStrCat("READ_ELF given FILE \"", fileNameArg,
@@ -1256,9 +1236,9 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string BaseDirectory;
+    cm::optional<std::string> BaseDirectory;
     bool ExpandTilde = false;
   };
   static auto const parser =
@@ -1267,22 +1247,18 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
       .Bind("EXPAND_TILDE"_s, &Arguments::ExpandTilde);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  std::vector<std::string> parsedKeywords;
   auto arguments =
-    parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments,
-                 &keywordsMissingValue, &parsedKeywords);
+    parser.Parse(cmMakeRange(args).advance(3), &unparsedArguments);
 
   if (!unparsedArguments.empty()) {
     status.SetError("REAL_PATH called with unexpected arguments");
     return false;
   }
-  if (!keywordsMissingValue.empty()) {
-    status.SetError("BASE_DIRECTORY requires a value");
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
-  if (parsedKeywords.empty()) {
+  if (!arguments.BaseDirectory) {
     arguments.BaseDirectory = status.GetMakefile().GetCurrentSourceDirectory();
   }
 
@@ -1301,7 +1277,7 @@ bool HandleRealPathCommand(std::vector<std::string> const& args,
   }
 
   cmCMakePath path(input, cmCMakePath::auto_format);
-  path = path.Absolute(arguments.BaseDirectory).Normal();
+  path = path.Absolute(*arguments.BaseDirectory).Normal();
   auto realPath = cmSystemTools::GetRealPath(path.GenericString());
 
   status.GetMakefile().AddDefinition(args[2], realPath);
@@ -1944,7 +1920,7 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
     std::string msg;
     std::string actualHash = hash->HashFile(file);
     if (actualHash == expectedHash) {
-      msg = cmStrCat("returning early; file already exists with expected ",
+      msg = cmStrCat("skipping download as file already exists with expected ",
                      hashMatchMSG, '"');
       if (!statusVar.empty()) {
         status.GetMakefile().AddDefinition(statusVar, cmStrCat(0, ";\"", msg));
@@ -2114,6 +2090,13 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
 
   ::curl_global_cleanup();
 
+  // Ensure requested curl logs are returned (especially in case of failure)
+  //
+  if (!logVar.empty()) {
+    chunkDebug.push_back(0);
+    status.GetMakefile().AddDefinition(logVar, chunkDebug.data());
+  }
+
   // Explicitly flush/close so we can measure the md5 accurately.
   //
   if (!file.empty()) {
@@ -2156,11 +2139,6 @@ bool HandleDownloadCommand(std::vector<std::string> const& args,
     }
   }
 
-  if (!logVar.empty()) {
-    chunkDebug.push_back(0);
-    status.GetMakefile().AddDefinition(logVar, chunkDebug.data());
-  }
-
   return true;
 #else
   status.SetError("DOWNLOAD not supported by bootstrap cmake.");
@@ -2501,17 +2479,18 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string Output;
-    std::string Input;
-    std::string Content;
-    std::string Condition;
-    std::string Target;
-    std::string NewLineStyle;
+    cm::optional<std::string> Output;
+    cm::optional<std::string> Input;
+    cm::optional<std::string> Content;
+    cm::optional<std::string> Condition;
+    cm::optional<std::string> Target;
+    cm::optional<std::string> NewLineStyle;
     bool NoSourcePermissions = false;
     bool UseSourcePermissions = false;
-    std::vector<std::string> FilePermissions;
+    ArgumentParser::NonEmpty<std::vector<std::string>> FilePermissions;
+    std::vector<cm::string_view> ParsedKeywords;
   };
 
   static auto const parser =
@@ -2524,18 +2503,15 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
       .Bind("NO_SOURCE_PERMISSIONS"_s, &Arguments::NoSourcePermissions)
       .Bind("USE_SOURCE_PERMISSIONS"_s, &Arguments::UseSourcePermissions)
       .Bind("FILE_PERMISSIONS"_s, &Arguments::FilePermissions)
-      .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle);
+      .Bind("NEWLINE_STYLE"_s, &Arguments::NewLineStyle)
+      .BindParsedKeywords(&Arguments::ParsedKeywords);
 
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValues;
-  std::vector<std::string> parsedKeywords;
   Arguments const arguments =
-    parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments,
-                 &keywordsMissingValues, &parsedKeywords);
+    parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
 
-  if (!keywordsMissingValues.empty()) {
-    status.SetError("Incorrect arguments to GENERATE subcommand.");
-    return false;
+  if (arguments.MaybeReportError(status.GetMakefile())) {
+    return true;
   }
 
   if (!unparsedArguments.empty()) {
@@ -2543,56 +2519,41 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  bool mandatoryOptionsSpecified = false;
-  if (parsedKeywords.size() > 1) {
-    const bool outputOprionSpecified = parsedKeywords[0] == "OUTPUT"_s;
-    const bool inputOrContentSpecified =
-      parsedKeywords[1] == "INPUT"_s || parsedKeywords[1] == "CONTENT"_s;
-    if (outputOprionSpecified && inputOrContentSpecified) {
-      mandatoryOptionsSpecified = true;
-    }
+  if (!arguments.Output || arguments.ParsedKeywords[0] != "OUTPUT"_s) {
+    status.SetError("GENERATE requires OUTPUT as first option.");
+    return false;
   }
-  if (!mandatoryOptionsSpecified) {
-    status.SetError("Incorrect arguments to GENERATE subcommand.");
+  std::string const& output = *arguments.Output;
+
+  if (!arguments.Input && !arguments.Content) {
+    status.SetError("GENERATE requires INPUT or CONTENT option.");
     return false;
   }
+  const bool inputIsContent = arguments.ParsedKeywords[1] == "CONTENT"_s;
+  if (!inputIsContent && arguments.ParsedKeywords[1] == "INPUT") {
+    status.SetError("Unknown argument to GENERATE subcommand.");
+  }
+  std::string const& input =
+    inputIsContent ? *arguments.Content : *arguments.Input;
 
-  const bool conditionOptionSpecified =
-    std::find(parsedKeywords.begin(), parsedKeywords.end(), "CONDITION"_s) !=
-    parsedKeywords.end();
-  if (conditionOptionSpecified && arguments.Condition.empty()) {
+  if (arguments.Condition && arguments.Condition->empty()) {
     status.SetError("CONDITION of sub-command GENERATE must not be empty "
                     "if specified.");
     return false;
   }
+  std::string const& condition =
+    arguments.Condition ? *arguments.Condition : std::string();
 
-  const bool targetOptionSpecified =
-    std::find(parsedKeywords.begin(), parsedKeywords.end(), "TARGET"_s) !=
-    parsedKeywords.end();
-  if (targetOptionSpecified && arguments.Target.empty()) {
+  if (arguments.Target && arguments.Target->empty()) {
     status.SetError("TARGET of sub-command GENERATE must not be empty "
                     "if specified.");
     return false;
   }
+  std::string const& target =
+    arguments.Target ? *arguments.Target : std::string();
 
-  const bool outputOptionSpecified =
-    std::find(parsedKeywords.begin(), parsedKeywords.end(), "OUTPUT"_s) !=
-    parsedKeywords.end();
-  if (outputOptionSpecified && parsedKeywords[0] != "OUTPUT"_s) {
-    status.SetError("Incorrect arguments to GENERATE subcommand.");
-    return false;
-  }
-
-  const bool inputIsContent = parsedKeywords[1] != "INPUT"_s;
-  if (inputIsContent && parsedKeywords[1] != "CONTENT") {
-    status.SetError("Unknown argument to GENERATE subcommand.");
-  }
-
-  const bool newLineStyleSpecified =
-    std::find(parsedKeywords.begin(), parsedKeywords.end(),
-              "NEWLINE_STYLE"_s) != parsedKeywords.end();
   cmNewLineStyle newLineStyle;
-  if (newLineStyleSpecified) {
+  if (arguments.NewLineStyle) {
     std::string errorMessage;
     if (!newLineStyle.ReadFromArguments(args, errorMessage)) {
       status.SetError(cmStrCat("GENERATE ", errorMessage));
@@ -2600,11 +2561,6 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     }
   }
 
-  std::string input = arguments.Input;
-  if (inputIsContent) {
-    input = arguments.Content;
-  }
-
   if (arguments.NoSourcePermissions && arguments.UseSourcePermissions) {
     status.SetError("given both NO_SOURCE_PERMISSIONS and "
                     "USE_SOURCE_PERMISSIONS. Only one option allowed.");
@@ -2662,8 +2618,7 @@ bool HandleGenerateCommand(std::vector<std::string> const& args,
     }
   }
 
-  AddEvaluationFile(input, arguments.Target, arguments.Output,
-                    arguments.Condition, inputIsContent,
+  AddEvaluationFile(input, target, output, condition, inputIsContent,
                     newLineStyle.GetCharacters(), permissions, status);
   return true;
 }
@@ -2842,7 +2797,11 @@ bool HandleTimestampCommand(std::vector<std::string> const& args,
 
   unsigned int argsIndex = 1;
 
-  const std::string& filename = args[argsIndex++];
+  std::string filename = args[argsIndex++];
+  if (!cmsys::SystemTools::FileIsFullPath(filename)) {
+    filename = cmStrCat(status.GetMakefile().GetCurrentSourceDirectory(), '/',
+                        filename);
+  }
 
   const std::string& outputVariable = args[argsIndex++];
 
@@ -3003,11 +2962,23 @@ bool HandleCreateLinkCommand(std::vector<std::string> const& args,
 
   // Check if the command requires a symbolic link.
   if (arguments.Symbolic) {
-    completed = static_cast<bool>(
-      cmSystemTools::CreateSymlink(fileName, newFileName, &result));
+    cmsys::Status linked =
+      cmSystemTools::CreateSymlinkQuietly(fileName, newFileName);
+    if (linked) {
+      completed = true;
+    } else {
+      result = cmStrCat("failed to create symbolic link '", newFileName,
+                        "': ", linked.GetString());
+    }
   } else {
-    completed = static_cast<bool>(
-      cmSystemTools::CreateLink(fileName, newFileName, &result));
+    cmsys::Status linked =
+      cmSystemTools::CreateLinkQuietly(fileName, newFileName);
+    if (linked) {
+      completed = true;
+    } else {
+      result = cmStrCat("failed to create link '", newFileName,
+                        "': ", linked.GetString());
+    }
   }
 
   // Check if copy-on-error is enabled in the arguments.
@@ -3066,24 +3037,25 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
       "\n    ]])");
   }
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
     std::string ResolvedDependenciesVar;
     std::string UnresolvedDependenciesVar;
     std::string ConflictingDependenciesPrefix;
     std::string RPathPrefix;
     std::string BundleExecutable;
-    std::vector<std::string> Executables;
-    std::vector<std::string> Libraries;
-    std::vector<std::string> Directories;
-    std::vector<std::string> Modules;
-    std::vector<std::string> PreIncludeRegexes;
-    std::vector<std::string> PreExcludeRegexes;
-    std::vector<std::string> PostIncludeRegexes;
-    std::vector<std::string> PostExcludeRegexes;
-    std::vector<std::string> PostIncludeFiles;
-    std::vector<std::string> PostExcludeFiles;
-    std::vector<std::string> PostExcludeFilesStrict;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Executables;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Libraries;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Modules;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>>
+      PostExcludeFilesStrict;
   };
 
   static auto const parser =
@@ -3108,10 +3080,8 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
       .Bind("POST_EXCLUDE_FILES_STRICT"_s, &Arguments::PostExcludeFilesStrict);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
   auto parsedArgs =
-    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
-                 &keywordsMissingValues);
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
   auto argIt = unrecognizedArguments.begin();
   if (argIt != unrecognizedArguments.end()) {
     status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3119,26 +3089,9 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  const std::vector<std::string> LIST_ARGS = {
-    "DIRECTORIES",
-    "EXECUTABLES",
-    "LIBRARIES",
-    "MODULES",
-    "POST_EXCLUDE_FILES",
-    "POST_EXCLUDE_FILES_STRICT",
-    "POST_EXCLUDE_REGEXES",
-    "POST_INCLUDE_FILES",
-    "POST_INCLUDE_REGEXES",
-    "PRE_EXCLUDE_REGEXES",
-    "PRE_INCLUDE_REGEXES",
-  };
-  auto kwbegin = keywordsMissingValues.cbegin();
-  auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
-  if (kwend != kwbegin) {
-    status.SetError(cmStrCat("Keywords missing values:\n  ",
-                             cmJoin(cmMakeRange(kwbegin, kwend), "\n  ")));
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
     cmSystemTools::SetFatalErrorOccurred();
-    return false;
+    return true;
   }
 
   cmRuntimeDependencyArchive archive(
@@ -3238,13 +3191,14 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
 bool HandleConfigureCommand(std::vector<std::string> const& args,
                             cmExecutionStatus& status)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::string Output;
-    std::string Content;
+    cm::optional<std::string> Output;
+    cm::optional<std::string> Content;
     bool EscapeQuotes = false;
     bool AtOnly = false;
-    std::string NewlineStyle;
+    // "NEWLINE_STYLE" requires one value, but we use a custom check below.
+    ArgumentParser::Maybe<std::string> NewlineStyle;
   };
 
   static auto const parser =
@@ -3256,11 +3210,8 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
       .Bind("NEWLINE_STYLE"_s, &Arguments::NewlineStyle);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingArguments;
-  std::vector<std::string> parsedKeywords;
   auto parsedArgs =
-    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
-                 &keywordsMissingArguments, &parsedKeywords);
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
 
   auto argIt = unrecognizedArguments.begin();
   if (argIt != unrecognizedArguments.end()) {
@@ -3270,28 +3221,20 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  std::vector<std::string> mandatoryOptions{ "OUTPUT", "CONTENT" };
-  for (auto const& e : mandatoryOptions) {
-    const bool optionHasNoValue =
-      std::find(keywordsMissingArguments.begin(),
-                keywordsMissingArguments.end(),
-                e) != keywordsMissingArguments.end();
-    if (optionHasNoValue) {
-      status.SetError(cmStrCat("CONFIGURE ", e, " option needs a value."));
-      cmSystemTools::SetFatalErrorOccurred();
-      return false;
-    }
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
+    cmSystemTools::SetFatalErrorOccurred();
+    return true;
   }
 
-  for (auto const& e : mandatoryOptions) {
-    const bool optionGiven =
-      std::find(parsedKeywords.begin(), parsedKeywords.end(), e) !=
-      parsedKeywords.end();
-    if (!optionGiven) {
-      status.SetError(cmStrCat("CONFIGURE ", e, " option is mandatory."));
-      cmSystemTools::SetFatalErrorOccurred();
-      return false;
-    }
+  if (!parsedArgs.Output) {
+    status.SetError("CONFIGURE OUTPUT option is mandatory.");
+    cmSystemTools::SetFatalErrorOccurred();
+    return false;
+  }
+  if (!parsedArgs.Content) {
+    status.SetError("CONFIGURE CONTENT option is mandatory.");
+    cmSystemTools::SetFatalErrorOccurred();
+    return false;
   }
 
   std::string errorMessage;
@@ -3303,7 +3246,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
 
   // Check for generator expressions
   std::string outputFile = cmSystemTools::CollapseFullPath(
-    parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory());
+    *parsedArgs.Output, status.GetMakefile().GetCurrentBinaryDirectory());
 
   std::string::size_type pos = outputFile.find_first_of("<>");
   if (pos != std::string::npos) {
@@ -3352,7 +3295,7 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
   fout.SetCopyIfDifferent(true);
 
   // copy input to output and expand variables from input at the same time
-  std::stringstream sin(parsedArgs.Content, std::ios::in);
+  std::stringstream sin(*parsedArgs.Content, std::ios::in);
   std::string inLine;
   std::string outLine;
   bool hasNewLine = false;
@@ -3375,15 +3318,19 @@ bool HandleConfigureCommand(std::vector<std::string> const& args,
 bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
                                 cmExecutionStatus& status)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
     std::string Output;
     std::string Format;
     std::string Compression;
     std::string CompressionLevel;
-    std::string MTime;
+    // "MTIME" should require one value, but it has long been accidentally
+    // accepted without one and treated as if an empty value were given.
+    // Fixing this would require a policy.
+    ArgumentParser::Maybe<std::string> MTime;
     bool Verbose = false;
-    std::vector<std::string> Paths;
+    // "PATHS" requires at least one value, but use a custom check below.
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Paths;
   };
 
   static auto const parser =
@@ -3397,10 +3344,8 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
       .Bind("PATHS"_s, &Arguments::Paths);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
   auto parsedArgs =
-    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
-                 &keywordsMissingValues);
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
   auto argIt = unrecognizedArguments.begin();
   if (argIt != unrecognizedArguments.end()) {
     status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3408,16 +3353,9 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  const std::vector<std::string> LIST_ARGS = {
-    "OUTPUT", "FORMAT", "COMPRESSION", "COMPRESSION_LEVEL", "MTIME", "PATHS"
-  };
-  auto kwbegin = keywordsMissingValues.cbegin();
-  auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
-  if (kwend != kwbegin) {
-    status.SetError(cmStrCat("Keywords missing values:\n  ",
-                             cmJoin(cmMakeRange(kwbegin, kwend), "\n  ")));
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
     cmSystemTools::SetFatalErrorOccurred();
-    return false;
+    return true;
   }
 
   const char* knownFormats[] = {
@@ -3506,13 +3444,13 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
 bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
                                  cmExecutionStatus& status)
 {
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
     std::string Input;
     bool Verbose = false;
     bool ListOnly = false;
     std::string Destination;
-    std::vector<std::string> Patterns;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Patterns;
     bool Touch = false;
   };
 
@@ -3525,10 +3463,8 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
                                .Bind("TOUCH"_s, &Arguments::Touch);
 
   std::vector<std::string> unrecognizedArguments;
-  std::vector<std::string> keywordsMissingValues;
   auto parsedArgs =
-    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
-                 &keywordsMissingValues);
+    parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments);
   auto argIt = unrecognizedArguments.begin();
   if (argIt != unrecognizedArguments.end()) {
     status.SetError(cmStrCat("Unrecognized argument: \"", *argIt, "\""));
@@ -3536,15 +3472,9 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
     return false;
   }
 
-  const std::vector<std::string> LIST_ARGS = { "INPUT", "DESTINATION",
-                                               "PATTERNS" };
-  auto kwbegin = keywordsMissingValues.cbegin();
-  auto kwend = cmRemoveMatching(keywordsMissingValues, LIST_ARGS);
-  if (kwend != kwbegin) {
-    status.SetError(cmStrCat("Keywords missing values:\n  ",
-                             cmJoin(cmMakeRange(kwbegin, kwend), "\n  ")));
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
     cmSystemTools::SetFatalErrorOccurred();
-    return false;
+    return true;
   }
 
   std::string inFile = parsedArgs.Input;
@@ -3599,10 +3529,15 @@ bool HandleArchiveExtractCommand(std::vector<std::string> const& args,
   return true;
 }
 
-bool ValidateAndConvertPermissions(const std::vector<std::string>& permissions,
-                                   mode_t& perms, cmExecutionStatus& status)
+bool ValidateAndConvertPermissions(
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> const&
+    permissions,
+  mode_t& perms, cmExecutionStatus& status)
 {
-  for (const auto& i : permissions) {
+  if (!permissions) {
+    return true;
+  }
+  for (const auto& i : *permissions) {
     if (!cmFSPermissions::stringToModeT(i, perms)) {
       status.SetError(i + " is an invalid permission specifier");
       cmSystemTools::SetFatalErrorOccurred();
@@ -3634,11 +3569,14 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
   globber.SetRecurse(recurse);
   globber.SetRecurseListDirs(recurse);
 
-  struct Arguments
+  struct Arguments : public ArgumentParser::ParseResult
   {
-    std::vector<std::string> Permissions;
-    std::vector<std::string> FilePermissions;
-    std::vector<std::string> DirectoryPermissions;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      Permissions;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      FilePermissions;
+    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+      DirectoryPermissions;
   };
 
   static auto const parser =
@@ -3648,21 +3586,20 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
       .Bind("DIRECTORY_PERMISSIONS"_s, &Arguments::DirectoryPermissions);
 
   std::vector<std::string> pathEntries;
-  std::vector<std::string> keywordsMissingValues;
-  Arguments parsedArgs = parser.Parse(cmMakeRange(args).advance(1),
-                                      &pathEntries, &keywordsMissingValues);
+  Arguments parsedArgs =
+    parser.Parse(cmMakeRange(args).advance(1), &pathEntries);
 
   // check validity of arguments
-  if (parsedArgs.Permissions.empty() && parsedArgs.FilePermissions.empty() &&
-      parsedArgs.DirectoryPermissions.empty()) // no permissions given
+  if (!parsedArgs.Permissions && !parsedArgs.FilePermissions &&
+      !parsedArgs.DirectoryPermissions) // no permissions given
   {
     status.SetError("No permissions given");
     cmSystemTools::SetFatalErrorOccurred();
     return false;
   }
 
-  if (!parsedArgs.Permissions.empty() && !parsedArgs.FilePermissions.empty() &&
-      !parsedArgs.DirectoryPermissions.empty()) // all keywords are used
+  if (parsedArgs.Permissions && parsedArgs.FilePermissions &&
+      parsedArgs.DirectoryPermissions) // all keywords are used
   {
     status.SetError("Remove either PERMISSIONS or FILE_PERMISSIONS or "
                     "DIRECTORY_PERMISSIONS from the invocation");
@@ -3670,12 +3607,9 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
     return false;
   }
 
-  if (!keywordsMissingValues.empty()) {
-    for (const auto& i : keywordsMissingValues) {
-      status.SetError(i + " is not given any arguments");
-      cmSystemTools::SetFatalErrorOccurred();
-    }
-    return false;
+  if (parsedArgs.MaybeReportError(status.GetMakefile())) {
+    cmSystemTools::SetFatalErrorOccurred();
+    return true;
   }
 
   // validate permissions
@@ -3719,7 +3653,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
     if (cmSystemTools::FileExists(i, true)) {
       bool success = true;
       const mode_t& filePermissions =
-        parsedArgs.FilePermissions.empty() ? perms : fperms;
+        parsedArgs.FilePermissions ? fperms : perms;
       if (filePermissions) {
         success = SetPermissions(i, filePermissions, status);
       }
@@ -3731,7 +3665,7 @@ bool HandleChmodCommandImpl(std::vector<std::string> const& args, bool recurse,
     else if (cmSystemTools::FileIsDirectory(i)) {
       bool success = true;
       const mode_t& directoryPermissions =
-        parsedArgs.DirectoryPermissions.empty() ? perms : dperms;
+        parsedArgs.DirectoryPermissions ? dperms : perms;
       if (directoryPermissions) {
         success = SetPermissions(i, directoryPermissions, status);
       }
index 1667807..ef55abf 100644 (file)
 #include "cmValue.h"
 
 #ifdef _WIN32
+#  include <winerror.h>
+
 #  include "cmsys/FStream.hxx"
+#else
+#  include <cerrno>
 #endif
 
 #include <cstring>
@@ -504,11 +508,12 @@ bool cmFileCopier::InstallSymlinkChain(std::string& fromFile,
       cmSystemTools::RemoveFile(toFile);
       cmSystemTools::MakeDirectory(toFilePath);
 
-      if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
-        std::ostringstream e;
-        e << this->Name << " cannot create symlink \"" << toFile
-          << "\": " << cmSystemTools::GetLastSystemError() << ".";
-        this->Status.SetError(e.str());
+      cmsys::Status status =
+        cmSystemTools::CreateSymlinkQuietly(symlinkTarget, toFile);
+      if (!status) {
+        std::string e = cmStrCat(this->Name, " cannot create symlink\n  ",
+                                 toFile, "\nbecause: ", status.GetString());
+        this->Status.SetError(e);
         return false;
       }
     }
@@ -557,12 +562,24 @@ bool cmFileCopier::InstallSymlink(const std::string& fromFile,
     cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
 
     // Create the symlink.
-    if (!cmSystemTools::CreateSymlink(symlinkTarget, toFile)) {
-      std::ostringstream e;
-      e << this->Name << " cannot duplicate symlink \"" << fromFile
-        << "\" at \"" << toFile
-        << "\": " << cmSystemTools::GetLastSystemError() << ".";
-      this->Status.SetError(e.str());
+    cmsys::Status status =
+      cmSystemTools::CreateSymlinkQuietly(symlinkTarget, toFile);
+    if (!status) {
+#ifdef _WIN32
+      bool const errorFileExists = status.GetWindows() == ERROR_FILE_EXISTS;
+#else
+      bool const errorFileExists = status.GetPOSIX() == EEXIST;
+#endif
+      std::string reason;
+      if (errorFileExists && cmSystemTools::FileIsDirectory(toFile)) {
+        reason = "A directory already exists at that location";
+      } else {
+        reason = status.GetString();
+      }
+      std::string e =
+        cmStrCat(this->Name, " cannot duplicate symlink\n  ", fromFile,
+                 "\nat\n  ", toFile, "\nbecause: ", reason);
+      this->Status.SetError(e);
       return false;
     }
   }
@@ -630,7 +647,10 @@ bool cmFileCopier::InstallDirectory(const std::string& source,
 {
   // Inform the user about this directory installation.
   this->ReportCopy(destination, TypeDir,
-                   !cmSystemTools::FileIsDirectory(destination));
+                   !( // Report "Up-to-date:" for existing directories,
+                      // but not symlinks to them.
+                     cmSystemTools::FileIsDirectory(destination) &&
+                     !cmSystemTools::FileIsSymlink(destination)));
 
   // check if default dir creation permissions were set
   mode_t default_dir_mode_v = 0;
index 1da995a..b8d345f 100644 (file)
@@ -2,15 +2,20 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFindBase.h"
 
+#include <algorithm>
 #include <cstddef>
 #include <deque>
+#include <functional>
 #include <map>
 #include <utility>
 
 #include <cm/optional>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmCMakePath.h"
+#include "cmExecutionStatus.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -24,8 +29,6 @@
 #include "cmWindowsRegistry.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
 cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status)
   : cmFindCommon(status)
   , FindCommandName(std::move(findCommandName))
@@ -138,6 +141,31 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
           cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
         return false;
       }
+    } else if (args[j] == "VALIDATOR") {
+      if (++j == args.size()) {
+        this->SetError("missing required argument for \"VALIDATOR\"");
+        return false;
+      }
+      auto command = this->Makefile->GetState()->GetCommand(args[j]);
+      if (command == nullptr) {
+        this->SetError(cmStrCat(
+          "command specified for \"VALIDATOR\" is undefined: ", args[j], '.'));
+        return false;
+      }
+      // ensure a macro is not specified as validator
+      const auto& validatorName = args[j];
+      auto macros = cmExpandedList(this->Makefile->GetProperty("MACROS"));
+      if (std::find_if(macros.begin(), macros.end(),
+                       [&validatorName](const std::string& item) {
+                         return cmSystemTools::Strucmp(validatorName.c_str(),
+                                                       item.c_str()) == 0;
+                       }) != macros.end()) {
+        this->SetError(cmStrCat(
+          "command specified for \"VALIDATOR\" is not a function: ", args[j],
+          '.'));
+        return false;
+      }
+      this->ValidatorName = args[j];
     } else if (this->CheckCommonArgument(args[j])) {
       doing = DoingNone;
     } else {
@@ -188,6 +216,36 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
   return true;
 }
 
+bool cmFindBase::Validate(const std::string& path) const
+{
+  if (this->ValidatorName.empty()) {
+    return true;
+  }
+
+  // The validator command will be executed in an isolated scope.
+  cmMakefile::ScopePushPop varScope(this->Makefile);
+  cmMakefile::PolicyPushPop polScope(this->Makefile);
+  static_cast<void>(varScope);
+  static_cast<void>(polScope);
+
+  auto resultName =
+    cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName),
+             "_VALIDATOR_STATUS"_s);
+
+  this->Makefile->AddDefinitionBool(resultName, true);
+
+  cmListFileFunction validator(
+    this->ValidatorName, 0, 0,
+    { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0),
+      cmListFileArgument(path, cmListFileArgument::Quoted, 0) });
+  cmExecutionStatus status(*this->Makefile);
+
+  if (this->Makefile->ExecuteCommand(validator, status)) {
+    return this->Makefile->GetDefinition(resultName).IsOn();
+  }
+  return false;
+}
+
 void cmFindBase::ExpandPaths()
 {
   if (!this->NoDefaultPath) {
index d197424..75d9a6d 100644 (file)
@@ -31,6 +31,11 @@ public:
    */
   virtual bool ParseArguments(std::vector<std::string> const& args);
 
+  /**
+   * To check validity of a found path using user's validator, if any
+   */
+  bool Validate(const std::string& path) const;
+
 protected:
   friend class cmFindBaseDebugState;
   void ExpandPaths();
@@ -63,6 +68,8 @@ protected:
 
   bool Required = false;
 
+  std::string ValidatorName;
+
 private:
   // Add pieces of the search.
   void FillPackageRootPath();
index 1c4039b..6296a60 100644 (file)
@@ -192,6 +192,7 @@ struct cmFindLibraryHelper
 
   // Context information.
   cmMakefile* Makefile;
+  cmFindBase const* FindBase;
   cmGlobalGenerator* GG;
 
   // List of valid prefixes and suffixes.
@@ -239,6 +240,11 @@ struct cmFindLibraryHelper
   bool CheckDirectory(std::string const& path);
   bool CheckDirectoryForName(std::string const& path, Name& name);
 
+  bool Validate(const std::string& path) const
+  {
+    return this->FindBase->Validate(path);
+  }
+
   cmFindBaseDebugState DebugSearches;
 
   void DebugLibraryFailed(std::string const& name, std::string const& path)
@@ -291,6 +297,7 @@ std::string const& get_suffixes(cmMakefile* mf)
 cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf,
                                          cmFindBase const* base)
   : Makefile(mf)
+  , FindBase(base)
   , DebugMode(base->DebugModeEnabled())
   , DebugSearches(std::move(debugName), base)
 {
@@ -416,10 +423,13 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
     if (!exists) {
       this->DebugLibraryFailed(name.Raw, path);
     } else {
-      this->DebugLibraryFound(name.Raw, path);
-      this->BestPath = cmSystemTools::CollapseFullPath(this->TestPath);
-      cmSystemTools::ConvertToUnixSlashes(this->BestPath);
-      return true;
+      auto testPath = cmSystemTools::CollapseFullPath(this->TestPath);
+      if (this->Validate(testPath)) {
+        this->DebugLibraryFound(name.Raw, path);
+        this->BestPath = testPath;
+        return true;
+      }
+      this->DebugLibraryFailed(name.Raw, path);
     }
   }
 
@@ -443,8 +453,11 @@ bool cmFindLibraryHelper::CheckDirectoryForName(std::string const& path,
       this->TestPath = cmStrCat(path, origName);
       // Make sure the path is readable and is not a directory.
       if (cmSystemTools::FileExists(this->TestPath, true)) {
-        this->DebugLibraryFound(name.Raw, dir);
+        if (!this->Validate(cmSystemTools::CollapseFullPath(this->TestPath))) {
+          continue;
+        }
 
+        this->DebugLibraryFound(name.Raw, dir);
         // This is a matching file.  Check if it is better than the
         // best name found so far.  Earlier prefixes are preferred,
         // followed by earlier suffixes.  For OpenBSD, shared library
@@ -541,7 +554,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryNamesPerDir()
     for (std::string const& n : this->Names) {
       fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
-        return cmSystemTools::CollapseFullPath(fwPath);
+        auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
+        if (this->Validate(finalPath)) {
+          return finalPath;
+        }
       }
     }
   }
@@ -558,7 +574,10 @@ std::string cmFindLibraryCommand::FindFrameworkLibraryDirsPerName()
     for (std::string const& d : this->SearchPaths) {
       fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
-        return cmSystemTools::CollapseFullPath(fwPath);
+        auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
+        if (this->Validate(finalPath)) {
+          return finalPath;
+        }
       }
     }
   }
index 8c6a0aa..3f8378b 100644 (file)
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstdio>
-#include <cstring>
 #include <deque>
 #include <functional>
 #include <iterator>
 #  include <StorageDefs.h>
 #endif
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#  include <windows.h>
+// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx
+#  if !defined(KEY_WOW64_32KEY)
+#    define KEY_WOW64_32KEY 0x0200
+#  endif
+#  if !defined(KEY_WOW64_64KEY)
+#    define KEY_WOW64_64KEY 0x0100
+#  endif
+#endif
+
 class cmExecutionStatus;
-class cmFileList;
+
+namespace {
+
+template <template <typename> class Op>
+struct StrverscmpOp
+{
+  bool operator()(const std::string& lhs, const std::string& rhs) const
+  {
+    return Op<int>()(cmSystemTools::strverscmp(lhs, rhs), 0);
+  }
+};
+
+std::size_t collectPathsForDebug(std::string& buffer,
+                                 cmSearchPath const& searchPath,
+                                 std::size_t const startIndex = 0)
+{
+  const auto& paths = searchPath.GetPaths();
+  if (paths.empty()) {
+    buffer += "  none\n";
+    return 0;
+  }
+  for (auto i = startIndex; i < paths.size(); i++) {
+    buffer += "  " + paths[i].Path + "\n";
+  }
+  return paths.size();
+}
+
+#if !(defined(_WIN32) && !defined(__CYGWIN__))
+class cmFindPackageCommandHoldFile
+{
+  const char* File;
+
+public:
+  cmFindPackageCommandHoldFile(const char* const f)
+    : File(f)
+  {
+  }
+  ~cmFindPackageCommandHoldFile()
+  {
+    if (this->File) {
+      cmSystemTools::RemoveFile(this->File);
+    }
+  }
+  cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete;
+  cmFindPackageCommandHoldFile& operator=(
+    const cmFindPackageCommandHoldFile&) = delete;
+  void Release() { this->File = nullptr; }
+};
+#endif
+
+bool isDirentryToIgnore(const char* const fname)
+{
+  assert(fname != nullptr);
+  assert(fname[0] != 0);
+  return fname[0] == '.' &&
+    (fname[1] == 0 || (fname[1] == '.' && fname[2] == 0));
+}
+
+class cmAppendPathSegmentGenerator
+{
+public:
+  cmAppendPathSegmentGenerator(cm::string_view dirName)
+    : DirName{ dirName }
+  {
+  }
+
+  std::string GetNextCandidate(const std::string& parent)
+  {
+    if (this->NeedReset) {
+      return {};
+    }
+    this->NeedReset = true;
+    return cmStrCat(parent, '/', this->DirName);
+  }
+
+  void Reset() { this->NeedReset = false; }
+
+private:
+  const cm::string_view DirName;
+  bool NeedReset = false;
+};
+
+class cmEnumPathSegmentsGenerator
+{
+public:
+  cmEnumPathSegmentsGenerator(const std::vector<cm::string_view>& init)
+    : Names{ init }
+    , Current{ this->Names.get().cbegin() }
+  {
+  }
+
+  std::string GetNextCandidate(const std::string& parent)
+  {
+    if (this->Current != this->Names.get().cend()) {
+      return cmStrCat(parent, '/', *this->Current++);
+    }
+    return {};
+  }
+
+  void Reset() { this->Current = this->Names.get().cbegin(); }
+
+private:
+  std::reference_wrapper<const std::vector<cm::string_view>> Names;
+  std::vector<cm::string_view>::const_iterator Current;
+};
+
+class cmCaseInsensitiveDirectoryListGenerator
+{
+public:
+  cmCaseInsensitiveDirectoryListGenerator(cm::string_view name)
+    : DirectoryLister{}
+    , DirName{ name }
+  {
+  }
+
+  std::string GetNextCandidate(const std::string& parent)
+  {
+    if (!this->Loaded) {
+      this->CurrentIdx = 0ul;
+      this->Loaded = true;
+      if (!this->DirectoryLister.Load(parent)) {
+        return {};
+      }
+    }
+
+    while (this->CurrentIdx < this->DirectoryLister.GetNumberOfFiles()) {
+      const char* const fname =
+        this->DirectoryLister.GetFile(this->CurrentIdx++);
+      if (isDirentryToIgnore(fname)) {
+        continue;
+      }
+      if (cmsysString_strcasecmp(fname, this->DirName.data()) == 0) {
+        auto candidate = cmStrCat(parent, '/', fname);
+        if (cmSystemTools::FileIsDirectory(candidate)) {
+          return candidate;
+        }
+      }
+    }
+    return {};
+  }
+
+  void Reset() { this->Loaded = false; }
+
+private:
+  cmsys::Directory DirectoryLister;
+  const cm::string_view DirName;
+  unsigned long CurrentIdx = 0ul;
+  bool Loaded = false;
+};
+
+class cmDirectoryListGenerator
+{
+public:
+  cmDirectoryListGenerator(std::vector<std::string> const& names)
+    : Names{ names }
+    , Matches{}
+    , Current{ this->Matches.cbegin() }
+  {
+  }
+  virtual ~cmDirectoryListGenerator() = default;
+
+  std::string GetNextCandidate(const std::string& parent)
+  {
+    // Construct a list of matches if not yet
+    if (this->Matches.empty()) {
+      cmsys::Directory directoryLister;
+      // ALERT `Directory::Load()` keeps only names
+      // internally and LOST entry type from `dirent`.
+      // So, `Directory::FileIsDirectory` gonna use
+      // `SystemTools::FileIsDirectory()` and waste a syscall.
+      // TODO Need to enhance the `Directory` class.
+      directoryLister.Load(parent);
+
+      // ATTENTION Is it guaranteed that first two entries are
+      // `.` and `..`?
+      // TODO If so, just start with index 2 and drop the
+      // `isDirentryToIgnore(i)` condition to check.
+      for (auto i = 0ul; i < directoryLister.GetNumberOfFiles(); ++i) {
+        const char* const fname = directoryLister.GetFile(i);
+        if (isDirentryToIgnore(fname)) {
+          continue;
+        }
+
+        for (const auto& n : this->Names.get()) {
+          // NOTE Customization point for `cmMacProjectDirectoryListGenerator`
+          const auto name = this->TransformNameBeforeCmp(n);
+          // Skip entries that don't match and non-directories.
+          // ATTENTION BTW, original code also didn't check if it's a symlink
+          // to a directory!
+          const auto equal =
+            (cmsysString_strncasecmp(fname, name.c_str(), name.length()) == 0);
+          if (equal && directoryLister.FileIsDirectory(i)) {
+            this->Matches.emplace_back(fname);
+          }
+        }
+      }
+      // NOTE Customization point for `cmProjectDirectoryListGenerator`
+      this->OnMatchesLoaded();
+
+      this->Current = this->Matches.cbegin();
+    }
+
+    if (this->Current != this->Matches.cend()) {
+      auto candidate = cmStrCat(parent, '/', *this->Current++);
+      return candidate;
+    }
+
+    return {};
+  }
+
+  void Reset()
+  {
+    this->Matches.clear();
+    this->Current = this->Matches.cbegin();
+  }
+
+protected:
+  virtual void OnMatchesLoaded() {}
+  virtual std::string TransformNameBeforeCmp(std::string same) { return same; }
+
+  std::reference_wrapper<const std::vector<std::string>> Names;
+  std::vector<std::string> Matches;
+  std::vector<std::string>::const_iterator Current;
+};
+
+class cmProjectDirectoryListGenerator : public cmDirectoryListGenerator
+{
+public:
+  cmProjectDirectoryListGenerator(std::vector<std::string> const& names,
+                                  cmFindPackageCommand::SortOrderType so,
+                                  cmFindPackageCommand::SortDirectionType sd)
+    : cmDirectoryListGenerator{ names }
+    , SortOrder{ so }
+    , SortDirection{ sd }
+  {
+  }
+
+protected:
+  void OnMatchesLoaded() override
+  {
+    // check if there is a specific sorting order to perform
+    if (this->SortOrder != cmFindPackageCommand::None) {
+      cmFindPackageCommand::Sort(this->Matches.begin(), this->Matches.end(),
+                                 this->SortOrder, this->SortDirection);
+    }
+  }
+
+private:
+  // sort parameters
+  const cmFindPackageCommand::SortOrderType SortOrder;
+  const cmFindPackageCommand::SortDirectionType SortDirection;
+};
+
+class cmMacProjectDirectoryListGenerator : public cmDirectoryListGenerator
+{
+public:
+  cmMacProjectDirectoryListGenerator(const std::vector<std::string>& names,
+                                     cm::string_view ext)
+    : cmDirectoryListGenerator{ names }
+    , Extension{ ext }
+  {
+  }
+
+protected:
+  std::string TransformNameBeforeCmp(std::string name) override
+  {
+    return cmStrCat(name, this->Extension);
+  }
+
+private:
+  const cm::string_view Extension;
+};
+
+class cmFileListGeneratorGlob
+{
+public:
+  cmFileListGeneratorGlob(cm::string_view pattern)
+    : Pattern(pattern)
+    , Files{}
+    , Current{}
+  {
+  }
+
+  std::string GetNextCandidate(const std::string& parent)
+  {
+    if (this->Files.empty()) {
+      // Glob the set of matching files.
+      std::string expr = cmStrCat(parent, this->Pattern);
+      cmsys::Glob g;
+      if (!g.FindFiles(expr)) {
+        return {};
+      }
+      this->Files = g.GetFiles();
+      this->Current = this->Files.cbegin();
+    }
+
+    // Skip non-directories
+    for (; this->Current != this->Files.cend() &&
+         !cmSystemTools::FileIsDirectory(*this->Current);
+         ++this->Current) {
+    }
+
+    return (this->Current != this->Files.cend()) ? *this->Current++
+                                                 : std::string{};
+  }
+
+  void Reset()
+  {
+    this->Files.clear();
+    this->Current = this->Files.cbegin();
+  }
+
+private:
+  cm::string_view Pattern;
+  std::vector<std::string> Files;
+  std::vector<std::string>::const_iterator Current;
+};
+
+#if defined(__LCC__)
+#  define CM_LCC_DIAG_SUPPRESS_1222
+#  pragma diag_suppress 1222 // invalid error number (3288, but works anyway)
+#  define CM_LCC_DIAG_SUPPRESS_3288
+#  pragma diag_suppress 3288 // parameter was declared but never referenced
+#endif
+
+void ResetGenerator()
+{
+}
+
+template <typename Generator>
+void ResetGenerator(Generator&& generator)
+{
+  std::forward<Generator&&>(generator).Reset();
+}
+
+template <typename Generator, typename... Generators>
+void ResetGenerator(Generator&& generator, Generators&&... generators)
+{
+  ResetGenerator(std::forward<Generator&&>(generator));
+  ResetGenerator(std::forward<Generators&&>(generators)...);
+}
+
+template <typename CallbackFn>
+bool TryGeneratedPaths(CallbackFn&& filesCollector,
+                       const std::string& fullPath)
+{
+  assert(!fullPath.empty() && fullPath.back() != '/');
+  return std::forward<CallbackFn&&>(filesCollector)(fullPath + '/');
+}
+
+template <typename CallbackFn, typename Generator, typename... Rest>
+bool TryGeneratedPaths(CallbackFn&& filesCollector,
+                       const std::string& startPath, Generator&& gen,
+                       Rest&&... tail)
+{
+  ResetGenerator(std::forward<Generator&&>(gen));
+  for (auto path = gen.GetNextCandidate(startPath); !path.empty();
+       path = gen.GetNextCandidate(startPath)) {
+    ResetGenerator(std::forward<Rest&&>(tail)...);
+    if (TryGeneratedPaths(std::forward<CallbackFn&&>(filesCollector), path,
+                          std::forward<Rest&&>(tail)...)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+#ifdef CM_LCC_DIAG_SUPPRESS_3288
+#  undef CM_LCC_DIAG_SUPPRESS_3288
+#  pragma diag_default 3288
+#endif
+
+#ifdef CM_LCC_DIAG_SUPPRESS_1222
+#  undef CM_LCC_DIAG_SUPPRESS_1222
+#  pragma diag_default 1222
+#endif
+
+// Parse the version number and store the results that were
+// successfully parsed.
+int parseVersion(const std::string& version, unsigned int& major,
+                 unsigned int& minor, unsigned int& patch, unsigned int& tweak)
+{
+  return std::sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch,
+                     &tweak);
+}
+
+} // anonymous namespace
 
 cmFindPackageCommand::PathLabel
   cmFindPackageCommand::PathLabel::PackageRedirect("PACKAGE_REDIRECT");
@@ -60,25 +456,10 @@ const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_INCLUDED(
 const cm::string_view cmFindPackageCommand::VERSION_ENDPOINT_EXCLUDED(
   "EXCLUDE");
 
-struct StrverscmpGreater
-{
-  bool operator()(const std::string& lhs, const std::string& rhs) const
-  {
-    return cmSystemTools::strverscmp(lhs, rhs) > 0;
-  }
-};
-
-struct StrverscmpLesser
-{
-  bool operator()(const std::string& lhs, const std::string& rhs) const
-  {
-    return cmSystemTools::strverscmp(lhs, rhs) < 0;
-  }
-};
-
 void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin,
                                 std::vector<std::string>::iterator end,
-                                SortOrderType order, SortDirectionType dir)
+                                SortOrderType const order,
+                                SortDirectionType const dir)
 {
   if (order == Name_order) {
     if (dir == Dec) {
@@ -86,14 +467,13 @@ void cmFindPackageCommand::Sort(std::vector<std::string>::iterator begin,
     } else {
       std::sort(begin, end);
     }
-  } else if (order == Natural)
-  // natural order uses letters and numbers (contiguous numbers digit are
-  // compared such that e.g. 000  00 < 01 < 010 < 09 < 0 < 1 < 9 < 10
-  {
+  } else if (order == Natural) {
+    // natural order uses letters and numbers (contiguous numbers digit are
+    // compared such that e.g. 000  00 < 01 < 010 < 09 < 0 < 1 < 9 < 10
     if (dir == Dec) {
-      std::sort(begin, end, StrverscmpGreater());
+      std::sort(begin, end, StrverscmpOp<std::greater>());
     } else {
-      std::sort(begin, end, StrverscmpLesser());
+      std::sort(begin, end, StrverscmpOp<std::less>());
     }
   }
   // else do not sort
@@ -113,11 +493,10 @@ cmFindPackageCommand::cmFindPackageCommand(cmExecutionStatus& status)
 
 void cmFindPackageCommand::AppendSearchPathGroups()
 {
-  std::vector<cmFindCommon::PathLabel>* labels;
-
   // Update the All group with new paths. Note that package redirection must
   // take precedence over everything else, so it has to be first in the array.
-  labels = &this->PathGroupLabelMap[PathGroup::All];
+  std::vector<cmFindCommon::PathLabel>* const labels =
+    &this->PathGroupLabelMap[PathGroup::All];
   labels->insert(labels->begin(), PathLabel::PackageRedirect);
   labels->insert(
     std::find(labels->begin(), labels->end(), PathLabel::CMakeSystem),
@@ -147,15 +526,15 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   }
 
   // Lookup required version of CMake.
-  if (cmValue rv =
+  if (cmValue const rv =
         this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
     unsigned int v[3] = { 0, 0, 0 };
-    sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
+    std::sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
     this->RequiredCMakeVersion = CMake_VERSION_ENCODE(v[0], v[1], v[2]);
   }
 
   // Lookup target architecture, if any.
-  if (cmValue arch =
+  if (cmValue const arch =
         this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
     this->LibraryArchitecture = *arch;
   }
@@ -184,7 +563,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   // Check if User Package Registry should be disabled
   // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has
   // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
-  if (cmValue def =
+  if (cmValue const def =
         this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) {
     this->NoUserRegistry = !cmIsOn(*def);
   } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
@@ -194,7 +573,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   // Check if System Package Registry should be disabled
   // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has
   // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
-  if (cmValue def = this->Makefile->GetDefinition(
+  if (cmValue const def = this->Makefile->GetDefinition(
         "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) {
     this->NoSystemRegistry = !cmIsOn(*def);
   } else if (this->Makefile->IsOn(
@@ -208,7 +587,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   }
 
   // Check if Sorting should be enabled
-  if (cmValue so =
+  if (cmValue const so =
         this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {
 
     if (*so == "NAME") {
@@ -219,7 +598,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       this->SortOrder = None;
     }
   }
-  if (cmValue sd =
+  if (cmValue const sd =
         this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) {
     this->SortDirection = (*sd == "ASC") ? Asc : Dec;
   }
@@ -265,9 +644,9 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   cmsys::RegularExpression versionRegex(
     R"V(^([0-9]+(\.[0-9]+)*)(\.\.\.(<?)([0-9]+(\.[0-9]+)*))?$)V");
   bool haveVersion = false;
-  std::set<unsigned int> configArgs;
-  std::set<unsigned int> moduleArgs;
-  for (unsigned int i = 1; i < args.size(); ++i) {
+  std::vector<std::size_t> configArgs;
+  std::vector<std::size_t> moduleArgs;
+  for (std::size_t i = 1u; i < args.size(); ++i) {
     if (args[i] == "QUIET") {
       this->Quiet = true;
       doing = DoingNone;
@@ -281,17 +660,17 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
       this->GlobalScope = true;
       doing = DoingNone;
     } else if (args[i] == "MODULE") {
-      moduleArgs.insert(i);
+      moduleArgs.push_back(i);
       doing = DoingNone;
       // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
       // NOLINTNEXTLINE(bugprone-branch-clone)
     } else if (args[i] == "CONFIG") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
       // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
       // NOLINTNEXTLINE(bugprone-branch-clone)
     } else if (args[i] == "NO_MODULE") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
     } else if (args[i] == "REQUIRED") {
       this->Required = true;
@@ -301,36 +680,36 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
     } else if (args[i] == "OPTIONAL_COMPONENTS") {
       doing = DoingOptionalComponents;
     } else if (args[i] == "NAMES") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNames;
     } else if (args[i] == "PATHS") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingPaths;
     } else if (args[i] == "HINTS") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingHints;
     } else if (args[i] == "PATH_SUFFIXES") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingPathSuffixes;
     } else if (args[i] == "CONFIGS") {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingConfigs;
     } else if (args[i] == "NO_POLICY_SCOPE") {
       this->PolicyScope = false;
       doing = DoingNone;
     } else if (args[i] == "NO_CMAKE_PACKAGE_REGISTRY") {
       this->NoUserRegistry = true;
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
     } else if (args[i] == "NO_CMAKE_SYSTEM_PACKAGE_REGISTRY") {
       this->NoSystemRegistry = true;
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
       // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
       // NOLINTNEXTLINE(bugprone-branch-clone)
     } else if (args[i] == "NO_CMAKE_BUILDS_PATH") {
       // Ignore legacy option.
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
     } else if (args[i] == "REGISTRY_VIEW") {
       if (++i == args.size()) {
@@ -347,7 +726,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
         return false;
       }
     } else if (this->CheckCommonArgument(args[i])) {
-      configArgs.insert(i);
+      configArgs.push_back(i);
       doing = DoingNone;
     } else if ((doing == DoingComponents) ||
                (doing == DoingOptionalComponents)) {
@@ -361,8 +740,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
         requiredComponents.insert(args[i]);
       }
 
-      std::string req_var = this->Name + "_FIND_REQUIRED_" + args[i];
-      componentVarDefs.emplace_back(req_var, isRequired);
+      componentVarDefs.emplace_back(this->Name + "_FIND_REQUIRED_" + args[i],
+                                    isRequired);
 
       // Append to the list of required components.
       components += components_sep;
@@ -420,11 +799,11 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   if (!this->UseFindModules && !this->UseConfigFiles) {
     std::ostringstream e;
     e << "given options exclusive to Module mode:\n";
-    for (unsigned int si : moduleArgs) {
+    for (auto si : moduleArgs) {
       e << "  " << args[si] << "\n";
     }
     e << "and options exclusive to Config mode:\n";
-    for (unsigned int si : configArgs) {
+    for (auto si : configArgs) {
       e << "  " << args[si] << "\n";
     }
     e << "The options are incompatible.";
@@ -443,20 +822,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   if (this->VersionComplete.empty() || components.empty()) {
     // Check whether we are recursing inside "Find<name>.cmake" within
     // another find_package(<name>) call.
-    std::string mod = cmStrCat(this->Name, "_FIND_MODULE");
+    std::string const mod = cmStrCat(this->Name, "_FIND_MODULE");
     if (this->Makefile->IsOn(mod)) {
       if (this->VersionComplete.empty()) {
         // Get version information from the outer call if necessary.
         // Requested version string.
-        std::string ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE");
+        std::string const ver = cmStrCat(this->Name, "_FIND_VERSION_COMPLETE");
         this->VersionComplete = this->Makefile->GetSafeDefinition(ver);
 
         // Whether an exact version is required.
-        std::string exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
+        std::string const exact = cmStrCat(this->Name, "_FIND_VERSION_EXACT");
         this->VersionExact = this->Makefile->IsOn(exact);
       }
       if (components.empty()) {
-        std::string components_var = this->Name + "_FIND_COMPONENTS";
+        std::string const components_var = this->Name + "_FIND_COMPONENTS";
         components = this->Makefile->GetSafeDefinition(components_var);
       }
     }
@@ -497,15 +876,6 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
     return false;
   }
 
-  // Parse the version number and store the results that were
-  // successfully parsed.
-  auto parseVersion = [](const std::string& version, unsigned int& major,
-                         unsigned int& minor, unsigned int& patch,
-                         unsigned int& tweak) -> unsigned int {
-    return sscanf(version.c_str(), "%u.%u.%u.%u", &major, &minor, &patch,
-                  &tweak);
-  };
-
   if (!this->Version.empty()) {
     this->VersionCount =
       parseVersion(this->Version, this->VersionMajor, this->VersionMinor,
@@ -533,7 +903,7 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
     }
   }
 
-  std::string disableFindPackageVar =
+  std::string const disableFindPackageVar =
     cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name);
   if (this->Makefile->IsOn(disableFindPackageVar)) {
     if (this->Required) {
@@ -557,8 +927,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
   // A dependency provider (if set) gets first look before other methods.
   // We do this before modifying the package root path stack because a
   // provider might use methods that ignore that.
-  cmState* state = this->Makefile->GetState();
-  cmState::Command providerCommand = state->GetDependencyProviderCommand(
+  cmState* const state = this->Makefile->GetState();
+  cmState::Command const providerCommand = state->GetDependencyProviderCommand(
     cmDependencyProvider::Method::FindPackage);
   if (bypassProvider) {
     if (this->DebugMode && providerCommand) {
@@ -725,11 +1095,8 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
                 "package configuration file provided by "
              << this->Name << " (" << this->Name << "Config.cmake or "
              << cmSystemTools::LowerCase(this->Name)
-             << "-config.cmake).  "
-                "Otherwise make Find"
-             << this->Name
-             << ".cmake available in "
-                "CMAKE_MODULE_PATH.";
+             << "-config.cmake).  Otherwise make Find" << this->Name
+             << ".cmake available in CMAKE_MODULE_PATH.";
         }
         aw << "\n"
               "(Variable CMAKE_FIND_PACKAGE_WARN_NO_MODULE enabled this "
@@ -813,22 +1180,22 @@ bool cmFindPackageCommand::FindPackageUsingConfigMode()
 void cmFindPackageCommand::SetVersionVariables(
   const std::function<void(const std::string&, cm::string_view)>&
     addDefinition,
-  const std::string& prefix, const std::string& version, unsigned int count,
-  unsigned int major, unsigned int minor, unsigned int patch,
-  unsigned int tweak)
+  const std::string& prefix, const std::string& version,
+  const unsigned int count, const unsigned int major, const unsigned int minor,
+  const unsigned int patch, const unsigned int tweak)
 {
   addDefinition(prefix, version);
 
   char buf[64];
   snprintf(buf, sizeof(buf), "%u", major);
   addDefinition(prefix + "_MAJOR", buf);
-  sprintf(buf, "%u", minor);
+  snprintf(buf, sizeof(buf), "%u", minor);
   addDefinition(prefix + "_MINOR", buf);
-  sprintf(buf, "%u", patch);
+  snprintf(buf, sizeof(buf), "%u", patch);
   addDefinition(prefix + "_PATCH", buf);
-  sprintf(buf, "%u", tweak);
+  snprintf(buf, sizeof(buf), "%u", tweak);
   addDefinition(prefix + "_TWEAK", buf);
-  sprintf(buf, "%u", count);
+  snprintf(buf, sizeof(buf), "%u", count);
   addDefinition(prefix + "_COUNT", buf);
 }
 
@@ -910,7 +1277,7 @@ void cmFindPackageCommand::SetModuleVariables(
 }
 
 void cmFindPackageCommand::AddFindDefinition(const std::string& var,
-                                             cm::string_view value)
+                                             const cm::string_view value)
 {
   if (cmValue old = this->Makefile->GetDefinition(var)) {
     this->OriginalDefs[var].exists = true;
@@ -954,7 +1321,7 @@ bool cmFindPackageCommand::FindModule(bool& found)
 
   if (!mfile.empty()) {
     if (system) {
-      auto it = this->DeprecatedFindModules.find(this->Name);
+      auto const it = this->DeprecatedFindModules.find(this->Name);
       if (it != this->DeprecatedFindModules.end()) {
         cmPolicies::PolicyStatus status =
           this->Makefile->GetPolicyStatus(it->second);
@@ -978,13 +1345,13 @@ bool cmFindPackageCommand::FindModule(bool& found)
     // Load the module we found, and set "<name>_FIND_MODULE" to true
     // while inside it.
     found = true;
-    std::string var = cmStrCat(this->Name, "_FIND_MODULE");
+    std::string const var = cmStrCat(this->Name, "_FIND_MODULE");
     this->Makefile->AddDefinition(var, "1");
     bool result = this->ReadListFile(mfile, DoPolicyScope);
     this->Makefile->RemoveDefinition(var);
 
     if (this->DebugMode) {
-      std::string foundVar = cmStrCat(this->Name, "_FOUND");
+      std::string const foundVar = cmStrCat(this->Name, "_FOUND");
       if (this->Makefile->IsDefinitionSet(foundVar) &&
           !this->Makefile->IsOn(foundVar)) {
 
@@ -999,7 +1366,7 @@ bool cmFindPackageCommand::FindModule(bool& found)
 }
 
 bool cmFindPackageCommand::HandlePackageMode(
-  HandlePackageModeType handlePackageModeType)
+  const HandlePackageModeType handlePackageModeType)
 {
   this->ConsideredConfigs.clear();
 
@@ -1042,8 +1409,9 @@ bool cmFindPackageCommand::HandlePackageMode(
     }
   }
 
-  std::string foundVar = cmStrCat(this->Name, "_FOUND");
-  std::string notFoundMessageVar = cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
+  std::string const foundVar = cmStrCat(this->Name, "_FOUND");
+  std::string const notFoundMessageVar =
+    cmStrCat(this->Name, "_NOT_FOUND_MESSAGE");
   std::string notFoundMessage;
 
   // If the directory for the config file was found, try to read the file.
@@ -1123,8 +1491,9 @@ bool cmFindPackageCommand::HandlePackageMode(
           << (this->VersionExact ? "exactly matches" : "is compatible with")
           << " requested version "
           << (this->VersionRange.empty() ? "" : "range ") << "\""
-          << this->VersionComplete << "\".\n"
-          << "The following configuration files were considered but not "
+          << this->VersionComplete
+          << "\".\n"
+             "The following configuration files were considered but not "
              "accepted:\n";
 
         for (ConfigFileInfo const& info :
@@ -1172,8 +1541,9 @@ bool cmFindPackageCommand::HandlePackageMode(
                "package or SDK, be sure it has been installed.";
         } else // if(!this->UseFindModules && !this->UseConfigFiles)
         {
-          e << "No \"Find" << this->Name << ".cmake\" found in "
-            << "CMAKE_MODULE_PATH.";
+          e << "No \"Find" << this->Name
+            << ".cmake\" found in "
+               "CMAKE_MODULE_PATH.";
 
           aw
             << "Find" << this->Name
@@ -1217,16 +1587,16 @@ bool cmFindPackageCommand::HandlePackageMode(
   this->Makefile->AddDefinition(foundVar, found ? "1" : "0");
 
   // Set a variable naming the configuration file that was found.
-  std::string fileVar = cmStrCat(this->Name, "_CONFIG");
+  std::string const fileVar = cmStrCat(this->Name, "_CONFIG");
   if (found) {
     this->Makefile->AddDefinition(fileVar, this->FileFound);
   } else {
     this->Makefile->RemoveDefinition(fileVar);
   }
 
-  std::string consideredConfigsVar =
+  std::string const consideredConfigsVar =
     cmStrCat(this->Name, "_CONSIDERED_CONFIGS");
-  std::string consideredVersionsVar =
+  std::string const consideredVersionsVar =
     cmStrCat(this->Name, "_CONSIDERED_VERSIONS");
 
   std::string consideredConfigFiles;
@@ -1312,7 +1682,7 @@ bool cmFindPackageCommand::FindConfig()
 
 void cmFindPackageCommand::SetConfigDirCacheVariable(const std::string& value)
 {
-  std::string help =
+  std::string const help =
     cmStrCat("The directory containing a CMake configuration file for ",
              this->Name, '.');
   this->Makefile->AddCacheDefinition(this->Variable, value, help.c_str(),
@@ -1351,7 +1721,7 @@ bool cmFindPackageCommand::FindAppBundleConfig()
 }
 
 bool cmFindPackageCommand::ReadListFile(const std::string& f,
-                                        PolicyScopeRule psr)
+                                        const PolicyScopeRule psr)
 {
   const bool noPolicyScope = !this->PolicyScope || psr == NoPolicyScope;
 
@@ -1362,12 +1732,12 @@ bool cmFindPackageCommand::ReadListFile(const std::string& f,
   if (this->Makefile->ReadDependentFile(f, noPolicyScope)) {
     return true;
   }
-  std::string e = cmStrCat("Error reading CMake code from \"", f, "\".");
+  std::string const e = cmStrCat("Error reading CMake code from \"", f, "\".");
   this->SetError(e);
   return false;
 }
 
-void cmFindPackageCommand::AppendToFoundProperty(bool found)
+void cmFindPackageCommand::AppendToFoundProperty(const bool found)
 {
   std::vector<std::string> foundContents;
   cmValue foundProp =
@@ -1410,27 +1780,28 @@ void cmFindPackageCommand::AppendToFoundProperty(bool found)
 void cmFindPackageCommand::AppendSuccessInformation()
 {
   {
-    std::string transitivePropName =
+    std::string const transitivePropName =
       cmStrCat("_CMAKE_", this->Name, "_TRANSITIVE_DEPENDENCY");
     this->Makefile->GetState()->SetGlobalProperty(transitivePropName, "False");
   }
-  std::string found = cmStrCat(this->Name, "_FOUND");
-  std::string upperFound = cmSystemTools::UpperCase(found);
+  std::string const found = cmStrCat(this->Name, "_FOUND");
+  std::string const upperFound = cmSystemTools::UpperCase(found);
 
-  bool upperResult = this->Makefile->IsOn(upperFound);
-  bool result = this->Makefile->IsOn(found);
-  bool packageFound = (result || upperResult);
+  bool const upperResult = this->Makefile->IsOn(upperFound);
+  bool const result = this->Makefile->IsOn(found);
+  bool const packageFound = (result || upperResult);
 
   this->AppendToFoundProperty(packageFound);
 
   // Record whether the find was quiet or not, so this can be used
   // e.g. in FeatureSummary.cmake
-  std::string quietInfoPropName = cmStrCat("_CMAKE_", this->Name, "_QUIET");
+  std::string const quietInfoPropName =
+    cmStrCat("_CMAKE_", this->Name, "_QUIET");
   this->Makefile->GetState()->SetGlobalProperty(
     quietInfoPropName, this->Quiet ? "TRUE" : "FALSE");
 
   // set a global property to record the required version of this package
-  std::string versionInfoPropName =
+  std::string const versionInfoPropName =
     cmStrCat("_CMAKE_", this->Name, "_REQUIRED_VERSION");
   std::string versionInfo;
   if (!this->VersionRange.empty()) {
@@ -1442,28 +1813,13 @@ void cmFindPackageCommand::AppendSuccessInformation()
   this->Makefile->GetState()->SetGlobalProperty(versionInfoPropName,
                                                 versionInfo.c_str());
   if (this->Required) {
-    std::string requiredInfoPropName =
+    std::string const requiredInfoPropName =
       cmStrCat("_CMAKE_", this->Name, "_TYPE");
     this->Makefile->GetState()->SetGlobalProperty(requiredInfoPropName,
                                                   "REQUIRED");
   }
 }
 
-inline std::size_t collectPathsForDebug(std::string& buffer,
-                                        cmSearchPath const& searchPath,
-                                        std::size_t startIndex = 0)
-{
-  const auto& paths = searchPath.GetPaths();
-  if (paths.empty()) {
-    buffer += "  none\n";
-    return 0;
-  }
-  for (std::size_t i = startIndex; i < paths.size(); i++) {
-    buffer += "  " + paths[i].Path + "\n";
-  }
-  return paths.size();
-}
-
 void cmFindPackageCommand::ComputePrefixes()
 {
   this->FillPrefixesPackageRedirect();
@@ -1674,14 +2030,6 @@ void cmFindPackageCommand::FillPrefixesSystemRegistry()
 }
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-#  include <windows.h>
-// http://msdn.microsoft.com/en-us/library/aa384253%28v=vs.85%29.aspx
-#  if !defined(KEY_WOW64_32KEY)
-#    define KEY_WOW64_32KEY 0x0200
-#  endif
-#  if !defined(KEY_WOW64_64KEY)
-#    define KEY_WOW64_64KEY 0x0100
-#  endif
 void cmFindPackageCommand::LoadPackageRegistryWinUser()
 {
   // HKEY_CURRENT_USER\\Software shares 32-bit and 64-bit views.
@@ -1704,7 +2052,8 @@ void cmFindPackageCommand::LoadPackageRegistryWinSystem()
   }
 }
 
-void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view,
+void cmFindPackageCommand::LoadPackageRegistryWin(const bool user,
+                                                  const unsigned int view,
                                                   cmSearchPath& outPaths)
 {
   std::wstring key = L"Software\\Kitware\\CMake\\Packages\\";
@@ -1756,28 +2105,8 @@ void cmFindPackageCommand::LoadPackageRegistryWin(bool user, unsigned int view,
     RegCloseKey(hKey);
   }
 }
-#else
-class cmFindPackageCommandHoldFile
-{
-  const char* File;
-
-public:
-  cmFindPackageCommandHoldFile(const char* f)
-    : File(f)
-  {
-  }
-  ~cmFindPackageCommandHoldFile()
-  {
-    if (this->File) {
-      cmSystemTools::RemoveFile(this->File);
-    }
-  }
-  cmFindPackageCommandHoldFile(const cmFindPackageCommandHoldFile&) = delete;
-  cmFindPackageCommandHoldFile& operator=(
-    const cmFindPackageCommandHoldFile&) = delete;
-  void Release() { this->File = nullptr; }
-};
 
+#else
 void cmFindPackageCommand::LoadPackageRegistryDir(std::string const& dir,
                                                   cmSearchPath& outPaths)
 {
@@ -1877,7 +2206,7 @@ void cmFindPackageCommand::FillPrefixesCMakeSystemVariable()
     std::vector<std::string> expanded = cmExpandedList(*prefix_paths);
     long count = 0;
     for (const auto& path : expanded) {
-      bool to_add =
+      bool const to_add =
         !(path == install_path_to_remove && ++count == install_prefix_count);
       if (to_add) {
         paths.AddPath(path);
@@ -1941,7 +2270,7 @@ bool cmFindPackageCommand::SearchDirectory(std::string const& dir)
     std::string d = dir;
     if (!s.empty()) {
       d += s;
-      d += "/";
+      d += '/';
     }
     if (this->CheckDirectory(d)) {
       return true;
@@ -1955,7 +2284,7 @@ bool cmFindPackageCommand::CheckDirectory(std::string const& dir)
   assert(!dir.empty() && dir.back() == '/');
 
   // Look for the file in this directory.
-  std::string d = dir.substr(0, dir.size() - 1);
+  std::string const d = dir.substr(0, dir.size() - 1);
   if (this->FindConfigFile(d, this->FileFound)) {
     // Remove duplicate slashes.
     cmSystemTools::ConvertToUnixSlashes(this->FileFound);
@@ -2028,8 +2357,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
                                             std::string& result_version)
 {
   // The version file will be loaded in an isolated scope.
-  cmMakefile::ScopePushPop varScope(this->Makefile);
-  cmMakefile::PolicyPushPop polScope(this->Makefile);
+  cmMakefile::ScopePushPop const varScope(this->Makefile);
+  cmMakefile::PolicyPushPop const polScope(this->Makefile);
   static_cast<void>(varScope);
   static_cast<void>(polScope);
 
@@ -2076,7 +2405,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
   if (this->ReadListFile(version_file, NoPolicyScope)) {
     // Check the output variables.
     bool okay = this->Makefile->IsOn("PACKAGE_VERSION_EXACT");
-    bool unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE");
+    bool const unsuitable = this->Makefile->IsOn("PACKAGE_VERSION_UNSUITABLE");
     if (!okay && !this->VersionExact) {
       okay = this->Makefile->IsOn("PACKAGE_VERSION_COMPATIBLE");
     }
@@ -2096,8 +2425,8 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
       unsigned int parsed_patch;
       unsigned int parsed_tweak;
       this->VersionFoundCount =
-        sscanf(this->VersionFound.c_str(), "%u.%u.%u.%u", &parsed_major,
-               &parsed_minor, &parsed_patch, &parsed_tweak);
+        parseVersion(this->VersionFound, parsed_major, parsed_minor,
+                     parsed_patch, parsed_tweak);
       switch (this->VersionFoundCount) {
         case 4:
           this->VersionFoundTweak = parsed_tweak;
@@ -2129,7 +2458,7 @@ bool cmFindPackageCommand::CheckVersionFile(std::string const& version_file,
 void cmFindPackageCommand::StoreVersionFound()
 {
   // Store the whole version string.
-  std::string ver = cmStrCat(this->Name, "_VERSION");
+  std::string const ver = cmStrCat(this->Name, "_VERSION");
   auto addDefinition = [this](const std::string& variable,
                               cm::string_view value) {
     this->Makefile->AddDefinition(variable, value);
@@ -2145,357 +2474,6 @@ void cmFindPackageCommand::StoreVersionFound()
   }
 }
 
-class cmFileListGeneratorBase
-{
-public:
-  virtual ~cmFileListGeneratorBase() = default;
-
-protected:
-  bool Consider(std::string const& fullPath, cmFileList& listing);
-
-private:
-  bool Search(cmFileList&);
-  virtual bool Search(std::string const& parent, cmFileList&) = 0;
-  virtual std::unique_ptr<cmFileListGeneratorBase> Clone() const = 0;
-  friend class cmFileList;
-  cmFileListGeneratorBase* SetNext(cmFileListGeneratorBase const& next);
-  std::unique_ptr<cmFileListGeneratorBase> Next;
-};
-
-class cmFileList
-{
-public:
-  virtual ~cmFileList() = default;
-  cmFileList& operator/(cmFileListGeneratorBase const& rhs)
-  {
-    if (this->Last) {
-      this->Last = this->Last->SetNext(rhs);
-    } else {
-      this->First = rhs.Clone();
-      this->Last = this->First.get();
-    }
-    return *this;
-  }
-  bool Search()
-  {
-    if (this->First) {
-      return this->First->Search(*this);
-    }
-    return false;
-  }
-
-private:
-  virtual bool Visit(std::string const& fullPath) = 0;
-  friend class cmFileListGeneratorBase;
-  std::unique_ptr<cmFileListGeneratorBase> First;
-  cmFileListGeneratorBase* Last = nullptr;
-};
-
-class cmFindPackageFileList : public cmFileList
-{
-public:
-  cmFindPackageFileList(cmFindPackageCommand* fpc, bool use_suffixes = true)
-    : FPC(fpc)
-    , UseSuffixes(use_suffixes)
-  {
-  }
-
-private:
-  bool Visit(std::string const& fullPath) override
-  {
-    if (this->UseSuffixes) {
-      return this->FPC->SearchDirectory(fullPath);
-    }
-    return this->FPC->CheckDirectory(fullPath);
-  }
-  cmFindPackageCommand* FPC;
-  bool UseSuffixes;
-};
-
-bool cmFileListGeneratorBase::Search(cmFileList& listing)
-{
-  return this->Search("", listing);
-}
-
-cmFileListGeneratorBase* cmFileListGeneratorBase::SetNext(
-  cmFileListGeneratorBase const& next)
-{
-  this->Next = next.Clone();
-  return this->Next.get();
-}
-
-bool cmFileListGeneratorBase::Consider(std::string const& fullPath,
-                                       cmFileList& listing)
-{
-  if (!fullPath.empty() && !cmSystemTools::FileIsDirectory(fullPath)) {
-    return false;
-  }
-  if (this->Next) {
-    return this->Next->Search(fullPath + "/", listing);
-  }
-  return listing.Visit(fullPath + "/");
-}
-
-class cmFileListGeneratorFixed : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorFixed(std::string str)
-    : String(std::move(str))
-  {
-  }
-  cmFileListGeneratorFixed(cmFileListGeneratorFixed const& r)
-    : String(r.String)
-  {
-  }
-
-private:
-  std::string String;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    std::string fullPath = parent + this->String;
-    return this->Consider(fullPath, lister);
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    std::unique_ptr<cmFileListGeneratorBase> g(
-      new cmFileListGeneratorFixed(*this));
-    return g;
-  }
-};
-
-class cmFileListGeneratorEnumerate : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorEnumerate(std::vector<std::string> const& v)
-    : Vector(v)
-  {
-  }
-  cmFileListGeneratorEnumerate(cmFileListGeneratorEnumerate const& r)
-    : Vector(r.Vector)
-  {
-  }
-
-private:
-  std::vector<std::string> const& Vector;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    for (std::string const& i : this->Vector) {
-      if (this->Consider(parent + i, lister)) {
-        return true;
-      }
-    }
-    return false;
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    std::unique_ptr<cmFileListGeneratorBase> g(
-      new cmFileListGeneratorEnumerate(*this));
-    return g;
-  }
-};
-
-class cmFileListGeneratorProject : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorProject(std::vector<std::string> const& names,
-                             cmFindPackageCommand::SortOrderType so,
-                             cmFindPackageCommand::SortDirectionType sd)
-    : Names(names)
-  {
-    this->SetSort(so, sd);
-  }
-  cmFileListGeneratorProject(cmFileListGeneratorProject const& r)
-    : Names(r.Names)
-  {
-    this->SetSort(r.SortOrder, r.SortDirection);
-  }
-
-  void SetSort(cmFindPackageCommand::SortOrderType o,
-               cmFindPackageCommand::SortDirectionType d)
-  {
-    this->SortOrder = o;
-    this->SortDirection = d;
-  }
-
-protected:
-  // sort parameters
-  cmFindPackageCommand::SortOrderType SortOrder;
-  cmFindPackageCommand::SortDirectionType SortDirection;
-
-private:
-  std::vector<std::string> const& Names;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    // Construct a list of matches.
-    std::vector<std::string> matches;
-    cmsys::Directory d;
-    d.Load(parent);
-    for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
-      const char* fname = d.GetFile(i);
-      if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
-        continue;
-      }
-      for (std::string const& n : this->Names) {
-        if (cmsysString_strncasecmp(fname, n.c_str(), n.length()) == 0) {
-          matches.emplace_back(fname);
-        }
-      }
-    }
-
-    // before testing the matches check if there is a specific sorting order to
-    // perform
-    if (this->SortOrder != cmFindPackageCommand::None) {
-      cmFindPackageCommand::Sort(matches.begin(), matches.end(),
-                                 this->SortOrder, this->SortDirection);
-    }
-
-    for (std::string const& i : matches) {
-      if (this->Consider(parent + i, lister)) {
-        return true;
-      }
-    }
-    return false;
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    std::unique_ptr<cmFileListGeneratorBase> g(
-      new cmFileListGeneratorProject(*this));
-    return g;
-  }
-};
-
-class cmFileListGeneratorMacProject : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorMacProject(std::vector<std::string> const& names,
-                                const char* ext)
-    : Names(names)
-    , Extension(ext)
-  {
-  }
-  cmFileListGeneratorMacProject(cmFileListGeneratorMacProject const& r)
-    : Names(r.Names)
-    , Extension(r.Extension)
-  {
-  }
-
-private:
-  std::vector<std::string> const& Names;
-  std::string Extension;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    // Construct a list of matches.
-    std::vector<std::string> matches;
-    cmsys::Directory d;
-    d.Load(parent);
-    for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
-      const char* fname = d.GetFile(i);
-      if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
-        continue;
-      }
-      for (std::string name : this->Names) {
-        name += this->Extension;
-        if (cmsysString_strcasecmp(fname, name.c_str()) == 0) {
-          matches.emplace_back(fname);
-        }
-      }
-    }
-
-    for (std::string const& i : matches) {
-      if (this->Consider(parent + i, lister)) {
-        return true;
-      }
-    }
-    return false;
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    std::unique_ptr<cmFileListGeneratorBase> g(
-      new cmFileListGeneratorMacProject(*this));
-    return g;
-  }
-};
-
-class cmFileListGeneratorCaseInsensitive : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorCaseInsensitive(std::string str)
-    : String(std::move(str))
-  {
-  }
-  cmFileListGeneratorCaseInsensitive(
-    cmFileListGeneratorCaseInsensitive const& r)
-    : String(r.String)
-  {
-  }
-
-private:
-  std::string String;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    // Look for matching files.
-    std::vector<std::string> matches;
-    cmsys::Directory d;
-    d.Load(parent);
-    for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
-      const char* fname = d.GetFile(i);
-      if (strcmp(fname, ".") == 0 || strcmp(fname, "..") == 0) {
-        continue;
-      }
-      if (cmsysString_strcasecmp(fname, this->String.c_str()) == 0) {
-        if (this->Consider(parent + fname, lister)) {
-          return true;
-        }
-      }
-    }
-    return false;
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    std::unique_ptr<cmFileListGeneratorBase> g(
-      new cmFileListGeneratorCaseInsensitive(*this));
-    return g;
-  }
-};
-
-class cmFileListGeneratorGlob : public cmFileListGeneratorBase
-{
-public:
-  cmFileListGeneratorGlob(std::string str)
-    : Pattern(std::move(str))
-  {
-  }
-  cmFileListGeneratorGlob(cmFileListGeneratorGlob const& r)
-    : Pattern(r.Pattern)
-  {
-  }
-
-private:
-  std::string Pattern;
-  bool Search(std::string const& parent, cmFileList& lister) override
-  {
-    // Glob the set of matching files.
-    std::string expr = cmStrCat(parent, this->Pattern);
-    cmsys::Glob g;
-    if (!g.FindFiles(expr)) {
-      return false;
-    }
-    std::vector<std::string> const& files = g.GetFiles();
-
-    // Look for directories among the matches.
-    for (std::string const& f : files) {
-      if (this->Consider(f, lister)) {
-        return true;
-      }
-    }
-    return false;
-  }
-  std::unique_ptr<cmFileListGeneratorBase> Clone() const override
-  {
-    return cm::make_unique<cmFileListGeneratorGlob>(*this);
-  }
-};
-
 bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
 {
   assert(!prefix_in.empty() && prefix_in.back() == '/');
@@ -2515,148 +2493,101 @@ bool cmFindPackageCommand::SearchPrefix(std::string const& prefix_in)
     return false;
   }
 
-  //  PREFIX/ (useful on windows or in build trees)
+  // PREFIX/ (useful on windows or in build trees)
   if (this->SearchDirectory(prefix_in)) {
     return true;
   }
 
   // Strip the trailing slash because the path generator is about to
   // add one.
-  std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+  std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
 
-  //  PREFIX/(cmake|CMake)/ (useful on windows or in build trees)
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
+  auto searchFn = [this](const std::string& fullPath) -> bool {
+    return this->SearchDirectory(fullPath);
+  };
+
+  auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
+  auto firstPkgDirGen =
+    cmProjectDirectoryListGenerator{ this->Names, this->SortOrder,
+                                     this->SortDirection };
+
+  // PREFIX/(cmake|CMake)/ (useful on windows or in build trees)
+  if (TryGeneratedPaths(searchFn, prefix, iCMakeGen)) {
+    return true;
   }
 
-  //  PREFIX/(Foo|foo|FOO).*/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection);
-    if (lister.Search()) {
-      return true;
-    }
+  // PREFIX/(Foo|foo|FOO).*/
+  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen)) {
+    return true;
   }
 
-  //  PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
+  // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/
+  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen)) {
+    return true;
+  }
+
+  auto secondPkgDirGen =
+    cmProjectDirectoryListGenerator{ this->Names, this->SortOrder,
+                                     this->SortDirection };
+
+  // PREFIX/(Foo|foo|FOO).*/(cmake|CMake)/(Foo|foo|FOO).*/
+  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, iCMakeGen,
+                        secondPkgDirGen)) {
+    return true;
   }
 
   // Construct list of common install locations (lib and share).
-  std::vector<std::string> common;
+  std::vector<cm::string_view> common;
+  std::string libArch;
   if (!this->LibraryArchitecture.empty()) {
-    common.push_back("lib/" + this->LibraryArchitecture);
+    libArch = "lib/" + this->LibraryArchitecture;
+    common.emplace_back(libArch);
   }
   if (this->UseLib32Paths) {
-    common.emplace_back("lib32");
+    common.emplace_back("lib32"_s);
   }
   if (this->UseLib64Paths) {
-    common.emplace_back("lib64");
+    common.emplace_back("lib64"_s);
   }
   if (this->UseLibx32Paths) {
-    common.emplace_back("libx32");
+    common.emplace_back("libx32"_s);
   }
-  common.emplace_back("lib");
-  common.emplace_back("share");
+  common.emplace_back("lib"_s);
+  common.emplace_back("share"_s);
 
-  //  PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorFixed("cmake") /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection);
-    if (lister.Search()) {
-      return true;
-    }
+  auto cmnGen = cmEnumPathSegmentsGenerator{ common };
+  auto cmakeGen = cmAppendPathSegmentGenerator{ "cmake"_s };
+
+  // PREFIX/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
+  if (TryGeneratedPaths(searchFn, prefix, cmnGen, cmakeGen, firstPkgDirGen)) {
+    return true;
   }
 
-  //  PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection);
-    if (lister.Search()) {
-      return true;
-    }
+  // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
+  if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen)) {
+    return true;
   }
 
-  //  PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
+  // PREFIX/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
+  if (TryGeneratedPaths(searchFn, prefix, cmnGen, firstPkgDirGen, iCMakeGen)) {
+    return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/cmake/(Foo|foo|FOO).*/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorFixed("cmake") /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection);
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen, cmakeGen,
+                        secondPkgDirGen)) {
+    return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection);
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
+                        secondPkgDirGen)) {
+    return true;
   }
 
   // PREFIX/(Foo|foo|FOO).*/(lib/ARCH|lib*|share)/(Foo|foo|FOO).*/(cmake|CMake)/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorEnumerate(common) /
-      cmFileListGeneratorProject(this->Names, this->SortOrder,
-                                 this->SortDirection) /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
-  }
-
-  return false;
+  return TryGeneratedPaths(searchFn, prefix, firstPkgDirGen, cmnGen,
+                           secondPkgDirGen, iCMakeGen);
 }
 
 bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
@@ -2665,56 +2596,36 @@ bool cmFindPackageCommand::SearchFrameworkPrefix(std::string const& prefix_in)
 
   // Strip the trailing slash because the path generator is about to
   // add one.
-  std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+  std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
+
+  auto searchFn = [this](const std::string& fullPath) -> bool {
+    return this->SearchDirectory(fullPath);
+  };
+
+  auto iCMakeGen = cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s };
+  auto fwGen =
+    cmMacProjectDirectoryListGenerator{ this->Names, ".framework"_s };
+  auto rGen = cmAppendPathSegmentGenerator{ "Resources"_s };
+  auto vGen = cmAppendPathSegmentGenerator{ "Versions"_s };
+  auto grGen = cmFileListGeneratorGlob{ "/*/Resources"_s };
 
   // <prefix>/Foo.framework/Resources/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".framework") /
-      cmFileListGeneratorFixed("Resources");
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen)) {
+    return true;
   }
+
   // <prefix>/Foo.framework/Resources/CMake/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".framework") /
-      cmFileListGeneratorFixed("Resources") /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, fwGen, rGen, iCMakeGen)) {
+    return true;
   }
 
   // <prefix>/Foo.framework/Versions/*/Resources/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".framework") /
-      cmFileListGeneratorFixed("Versions") /
-      cmFileListGeneratorGlob("*/Resources");
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen)) {
+    return true;
   }
 
   // <prefix>/Foo.framework/Versions/*/Resources/CMake/
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".framework") /
-      cmFileListGeneratorFixed("Versions") /
-      cmFileListGeneratorGlob("*/Resources") /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
-  }
-
-  return false;
+  return TryGeneratedPaths(searchFn, prefix, fwGen, vGen, grGen, iCMakeGen);
 }
 
 bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
@@ -2723,32 +2634,24 @@ bool cmFindPackageCommand::SearchAppBundlePrefix(std::string const& prefix_in)
 
   // Strip the trailing slash because the path generator is about to
   // add one.
-  std::string prefix = prefix_in.substr(0, prefix_in.size() - 1);
+  std::string const prefix = prefix_in.substr(0, prefix_in.size() - 1);
+
+  auto searchFn = [this](const std::string& fullPath) -> bool {
+    return this->SearchDirectory(fullPath);
+  };
+
+  auto appGen = cmMacProjectDirectoryListGenerator{ this->Names, ".app"_s };
+  auto crGen = cmAppendPathSegmentGenerator{ "Contents/Resources"_s };
 
   // <prefix>/Foo.app/Contents/Resources
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".app") /
-      cmFileListGeneratorFixed("Contents/Resources");
-    if (lister.Search()) {
-      return true;
-    }
+  if (TryGeneratedPaths(searchFn, prefix, appGen, crGen)) {
+    return true;
   }
 
   // <prefix>/Foo.app/Contents/Resources/CMake
-  {
-    cmFindPackageFileList lister(this);
-    lister / cmFileListGeneratorFixed(prefix) /
-      cmFileListGeneratorMacProject(this->Names, ".app") /
-      cmFileListGeneratorFixed("Contents/Resources") /
-      cmFileListGeneratorCaseInsensitive("cmake");
-    if (lister.Search()) {
-      return true;
-    }
-  }
-
-  return false;
+  return TryGeneratedPaths(
+    searchFn, prefix, appGen, crGen,
+    cmCaseInsensitiveDirectoryListGenerator{ "cmake"_s });
 }
 
 // TODO: Debug cmsys::Glob double slash problem.
index 80fd8f8..28e00a1 100644 (file)
@@ -153,8 +153,6 @@ private:
   bool SearchFrameworkPrefix(std::string const& prefix_in);
   bool SearchAppBundlePrefix(std::string const& prefix_in);
 
-  friend class cmFindPackageFileList;
-
   struct OriginalDef
   {
     bool exists;
index 27074ff..74a69d8 100644 (file)
@@ -88,7 +88,8 @@ std::string cmFindPathCommand::FindHeaderInFramework(
     if (!frameWorkName.empty()) {
       std::string fpath = cmStrCat(dir, frameWorkName, ".framework");
       std::string intPath = cmStrCat(fpath, "/Headers/", fileName);
-      if (cmSystemTools::FileExists(intPath)) {
+      if (cmSystemTools::FileExists(intPath) &&
+          this->Validate(this->IncludeFileInPath ? intPath : fpath)) {
         debug.FoundAt(intPath);
         if (this->IncludeFileInPath) {
           return intPath;
@@ -124,7 +125,8 @@ std::string cmFindPathCommand::FindNormalHeader(cmFindBaseDebugState& debug)
   for (std::string const& n : this->Names) {
     for (std::string const& sp : this->SearchPaths) {
       tryPath = cmStrCat(sp, n);
-      if (cmSystemTools::FileExists(tryPath)) {
+      if (cmSystemTools::FileExists(tryPath) &&
+          this->Validate(this->IncludeFileInPath ? tryPath : sp)) {
         debug.FoundAt(tryPath);
         if (this->IncludeFileInPath) {
           return tryPath;
index a64e0e4..8a2a69e 100644 (file)
@@ -27,6 +27,7 @@ struct cmFindProgramHelper
                       cmFindBase const* base)
     : DebugSearches(std::move(debugName), base)
     , Makefile(makefile)
+    , FindBase(base)
     , PolicyCMP0109(makefile->GetPolicyStatus(cmPolicies::CMP0109))
   {
 #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
@@ -56,6 +57,7 @@ struct cmFindProgramHelper
   // Debug state
   cmFindBaseDebugState DebugSearches;
   cmMakefile* Makefile;
+  cmFindBase const* FindBase;
 
   cmPolicies::PolicyStatus PolicyCMP0109;
 
@@ -94,7 +96,7 @@ struct cmFindProgramHelper
                          this->TestNameExt = cmStrCat(name, ext);
                          this->TestPath = cmSystemTools::CollapseFullPath(
                            this->TestNameExt, path);
-                         bool exists = this->FileIsExecutable(this->TestPath);
+                         bool exists = this->FileIsValid(this->TestPath);
                          exists ? this->DebugSearches.FoundAt(this->TestPath)
                                 : this->DebugSearches.FailedAt(this->TestPath);
                          if (exists) {
@@ -104,12 +106,12 @@ struct cmFindProgramHelper
                          return false;
                        });
   }
-  bool FileIsExecutable(std::string const& file) const
+  bool FileIsValid(std::string const& file) const
   {
-#ifdef _WIN32
     if (!this->FileIsExecutableCMP0109(file)) {
       return false;
     }
+#ifdef _WIN32
     // Pretend the Windows "python" app installer alias does not exist.
     if (cmSystemTools::LowerCase(file).find("/windowsapps/python") !=
         std::string::npos) {
@@ -119,10 +121,8 @@ struct cmFindProgramHelper
         return false;
       }
     }
-    return true;
-#else
-    return this->FileIsExecutableCMP0109(file);
 #endif
+    return this->FindBase->Validate(file);
   }
   bool FileIsExecutableCMP0109(std::string const& file) const
   {
index b9400c9..3465c23 100644 (file)
@@ -260,7 +260,7 @@ auto cmForEachFunctionBlocker::invoke(
     cmExecutionStatus status(mf);
     mf.ExecuteCommand(func, status);
     if (status.GetReturnInvoked()) {
-      inStatus.SetReturnInvoked();
+      inStatus.SetReturnInvoked(status.GetReturnVariables());
       result.Break = true;
       break;
     }
index 40e692d..523482a 100644 (file)
@@ -24,10 +24,11 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
       auto self = mf.RemoveFunctionBlocker();
       assert(self.get() == this);
 
-      if (!this->ArgumentsMatch(lff, mf)) {
-        cmListFileContext const& lfc = this->GetStartingContext();
-        cmListFileContext closingContext =
-          cmListFileContext::FromListFileFunction(lff, lfc.FilePath);
+      cmListFileContext const& lfc = this->GetStartingContext();
+      cmListFileContext closingContext =
+        cmListFileContext::FromListFileFunction(lff, lfc.FilePath);
+      if (this->EndCommandSupportsArguments() &&
+          !this->ArgumentsMatch(lff, mf)) {
         std::ostringstream e;
         /* clang-format off */
         e << "A logical block opening on the line\n"
@@ -37,6 +38,15 @@ bool cmFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
           << "with mis-matching arguments.";
         /* clang-format on */
         mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+      } else if (!this->EndCommandSupportsArguments() &&
+                 !lff.Arguments().empty()) {
+        std::ostringstream e;
+        /* clang-format off */
+        e << "A logical block closing on the line\n"
+          "  " << closingContext << "\n"
+          "has unexpected arguments.";
+        /* clang-format on */
+        mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
       }
 
       return this->Replay(std::move(this->Functions), status);
index 38abeba..3e096f2 100644 (file)
@@ -38,6 +38,8 @@ private:
   virtual cm::string_view StartCommandName() const = 0;
   virtual cm::string_view EndCommandName() const = 0;
 
+  virtual bool EndCommandSupportsArguments() const { return true; }
+
   virtual bool ArgumentsMatch(cmListFileFunction const& lff,
                               cmMakefile& mf) const = 0;
 
index 1359009..f4768b6 100644 (file)
@@ -120,6 +120,7 @@ bool cmFunctionHelperCommand::operator()(
       return false;
     }
     if (status.GetReturnInvoked()) {
+      makefile.RaiseScope(status.GetReturnVariables());
       break;
     }
   }
index b529b8f..c72d6a7 100644 (file)
 #endif
 
 cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
-  : OriginalLocale(this->getloc())
 {
 #ifndef CMAKE_BOOTSTRAP
   if (encoding != codecvt::None) {
-    this->imbue(std::locale(this->OriginalLocale, new codecvt(encoding)));
+    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
   }
 #else
   static_cast<void>(encoding);
@@ -124,10 +123,10 @@ cmGeneratedFileStreamBase::~cmGeneratedFileStreamBase()
 void cmGeneratedFileStreamBase::Open(std::string const& name)
 {
   // Save the original name of the file.
-  this->Name = name;
+  this->Name = cmSystemTools::CollapseFullPath(name);
 
   // Create the name of the temporary file.
-  this->TempName = name;
+  this->TempName = this->Name;
 #if defined(__VMS)
   this->TempName += "_";
 #else
@@ -231,7 +230,7 @@ int cmGeneratedFileStreamBase::RenameFile(std::string const& oldname,
 
 void cmGeneratedFileStream::SetName(const std::string& fname)
 {
-  this->Name = fname;
+  this->Name = cmSystemTools::CollapseFullPath(fname);
 }
 
 void cmGeneratedFileStream::SetTempExt(std::string const& ext)
@@ -239,13 +238,16 @@ void cmGeneratedFileStream::SetTempExt(std::string const& ext)
   this->TempExt = ext;
 }
 
-void cmGeneratedFileStream::WriteRaw(std::string const& data)
+void cmGeneratedFileStream::WriteAltEncoding(std::string const& data,
+                                             Encoding encoding)
 {
 #ifndef CMAKE_BOOTSTRAP
-  std::locale activeLocale = this->imbue(this->OriginalLocale);
+  std::locale prevLocale =
+    this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
   this->write(data.data(), data.size());
-  this->imbue(activeLocale);
+  this->imbue(prevLocale);
 #else
+  static_cast<void>(encoding);
   this->write(data.data(), data.size());
 #endif
 }
index bb7e3bf..bfc121f 100644 (file)
@@ -148,12 +148,8 @@ public:
   void SetTempExt(std::string const& ext);
 
   /**
-   * Writes the given string directly to the file without changing the
-   * encoding.
+   * Write a specific string using an alternate encoding.
+   * Afterward, the original encoding is restored.
    */
-  void WriteRaw(std::string const& data);
-
-private:
-  // The original locale of the stream (performs no encoding conversion).
-  std::locale OriginalLocale;
+  void WriteAltEncoding(std::string const& data, Encoding encoding);
 };
index 6235a2a..7d43eb1 100644 (file)
@@ -808,11 +808,16 @@ void handleSystemIncludesDep(cmLocalGenerator* lg,
                                                  dagChecker, depTgt, language),
                  result);
   }
-  if (!depTgt->IsImported() || excludeImported) {
+  if (!depTgt->GetPropertyAsBool("SYSTEM")) {
     return;
   }
-  if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
-    return;
+  if (depTgt->IsImported()) {
+    if (excludeImported) {
+      return;
+    }
+    if (depTgt->GetPropertyAsBool("IMPORTED_NO_SYSTEM")) {
+      return;
+    }
   }
 
   if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
@@ -912,11 +917,19 @@ bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
     return false;
   }
 
-  if (lang != "C" && lang != "CXX" && lang != "Fortran") {
+  if (lang != "C" && lang != "CXX" && lang != "CUDA" && lang != "Fortran") {
     // We do not define IPO behavior for other languages.
     return false;
   }
 
+  if (lang == "CUDA") {
+    // CUDA IPO requires both CUDA_ARCHITECTURES and CUDA_SEPARABLE_COMPILATION
+    if (cmIsOff(this->GetSafeProperty("CUDA_ARCHITECTURES")) ||
+        cmIsOff(this->GetSafeProperty("CUDA_SEPARABLE_COMPILATION"))) {
+      return false;
+    }
+  }
+
   cmPolicies::PolicyStatus cmp0069 = this->GetPolicyStatusCMP0069();
 
   if (cmp0069 == cmPolicies::OLD || cmp0069 == cmPolicies::WARN) {
@@ -1708,7 +1721,8 @@ void addFileSetEntry(cmGeneratorTarget const* headTarget,
         }
       }
       if (!found) {
-        if (fileSet->GetType() == "HEADERS"_s) {
+        if (fileSet->GetType() == "HEADERS"_s ||
+            fileSet->GetType() == "CXX_MODULE_HEADER_UNITS"_s) {
           headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
             ->AddGroupFile(path);
         }
@@ -1729,6 +1743,20 @@ void AddFileSetEntries(cmGeneratorTarget const* headTarget,
       addFileSetEntry(headTarget, config, dagChecker, headerSet, entries);
     }
   }
+  for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
+    for (auto const& name : cmExpandedList(entry.Value)) {
+      auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
+      addFileSetEntry(headTarget, config, dagChecker, cxxModuleSet, entries);
+    }
+  }
+  for (auto const& entry :
+       headTarget->Target->GetCxxModuleHeaderSetsEntries()) {
+    for (auto const& name : cmExpandedList(entry.Value)) {
+      auto const* cxxModuleHeaderSet = headTarget->Target->GetFileSet(name);
+      addFileSetEntry(headTarget, config, dagChecker, cxxModuleHeaderSet,
+                      entries);
+    }
+  }
 }
 
 bool processSources(cmGeneratorTarget const* tgt,
@@ -3409,7 +3437,9 @@ void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
                                              "EXPLICIT_LANGUAGE");
 }
 
-void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
+void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
+                                                 const std::string& config,
+                                                 std::string& flags) const
 {
   std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES");
 
@@ -3441,6 +3471,7 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
 
   std::string const& compiler =
     this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
+  const bool ipoEnabled = this->IsIPOEnabled("CUDA", config);
 
   // Check for special modes: `all`, `all-major`.
   if (property == "all" || property == "all-major") {
@@ -3520,6 +3551,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
   }
 
   if (compiler == "NVIDIA") {
+    if (ipoEnabled && compileOrLink == cmBuildStep::Link) {
+      if (cmValue cudaIPOFlags =
+            this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) {
+        flags += cudaIPOFlags;
+      }
+    }
+
     for (CudaArchitecture& architecture : architectures) {
       flags +=
         " --generate-code=arch=compute_" + architecture.name + ",code=[";
@@ -3532,7 +3570,13 @@ void cmGeneratorTarget::AddCUDAArchitectureFlags(std::string& flags) const
         }
       }
 
-      if (architecture.real) {
+      if (ipoEnabled) {
+        if (compileOrLink == cmBuildStep::Compile) {
+          flags += "lto_" + architecture.name;
+        } else if (compileOrLink == cmBuildStep::Link) {
+          flags += "sm_" + architecture.name;
+        }
+      } else if (architecture.real) {
         flags += "sm_" + architecture.name;
       }
 
@@ -5410,9 +5454,6 @@ std::string cmGeneratorTarget::GetObjectDirectory(
   std::string obj_dir =
     this->GlobalGenerator->ExpandCFGIntDir(this->ObjectDirectory, config);
 #if defined(__APPLE__)
-  // find and replace $(PROJECT_NAME) xcode placeholder
-  const std::string projectName = this->LocalGenerator->GetProjectName();
-  cmSystemTools::ReplaceString(obj_dir, "$(PROJECT_NAME)", projectName);
   // Replace Xcode's placeholder for the object file directory since
   // installation and export scripts need to know the real directory.
   // Xcode has build-time settings (e.g. for sanitizers) that affect this,
@@ -8719,8 +8760,92 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
 
   cmGeneratedFileStream fout(filename);
   fout.SetCopyIfDifferent(true);
-  fout << "#include <" << headerFilename << ">\n";
+  // IWYU pragma: associated allows 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 :)
+  fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n";
   fout.close();
 
   return filename;
 }
+
+bool cmGeneratorTarget::HaveCxx20ModuleSources() const
+{
+  auto const& fs_names = this->Target->GetAllFileSetNames();
+  return std::any_of(fs_names.begin(), fs_names.end(),
+                     [this](std::string const& name) -> bool {
+                       auto const* file_set = this->Target->GetFileSet(name);
+                       if (!file_set) {
+                         this->Makefile->IssueMessage(
+                           MessageType::INTERNAL_ERROR,
+                           cmStrCat("Target \"", this->Target->GetName(),
+                                    "\" is tracked to have file set \"", name,
+                                    "\", but it was not found."));
+                         return false;
+                       }
+
+                       auto const& fs_type = file_set->GetType();
+                       return fs_type == "CXX_MODULES"_s ||
+                         fs_type == "CXX_MODULE_HEADER_UNITS"_s;
+                     });
+}
+
+cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
+  std::string const& config) const
+{
+  auto const* state = this->Makefile->GetState();
+  if (!state->GetLanguageEnabled("CXX")) {
+    return Cxx20SupportLevel::MissingCxx;
+  }
+  cmValue standardDefault =
+    this->Target->GetMakefile()->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
+  if (standardDefault && !standardDefault->empty()) {
+    cmStandardLevelResolver standardResolver(this->Makefile);
+    if (!standardResolver.HaveStandardAvailable(this, "CXX", config,
+                                                "cxx_std_20")) {
+      return Cxx20SupportLevel::NoCxx20;
+    }
+  }
+  // Else, an empty CMAKE_CXX_STANDARD_DEFAULT means CMake does not detect and
+  // set a default standard level for this compiler, so assume all standards
+  // are available.
+  if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
+    return Cxx20SupportLevel::MissingExperimentalFlag;
+  }
+  return Cxx20SupportLevel::Supported;
+}
+
+void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
+{
+  // Check for `CXX_MODULE*` file sets and a lack of support.
+  if (this->HaveCxx20ModuleSources()) {
+    switch (this->HaveCxxModuleSupport(config)) {
+      case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("The \"", this->GetName(),
+                   "\" target has C++ module sources but the \"CXX\" language "
+                   "has not been enabled"));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("The \"", this->GetName(),
+                   "\" target has C++ module sources but its experimental "
+                   "support has not been requested"));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20:
+        this->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            "The \"", this->GetName(),
+            "\" target has C++ module sources but is not using at least "
+            "\"cxx_std_20\""));
+        break;
+      case cmGeneratorTarget::Cxx20SupportLevel::Supported:
+        // All is well.
+        break;
+    }
+  }
+}
index 6bce7d2..25e6a81 100644 (file)
@@ -23,6 +23,7 @@
 #include "cmStateTypes.h"
 #include "cmValue.h"
 
+enum class cmBuildStep;
 class cmComputeLinkInformation;
 class cmCustomCommand;
 class cmGlobalGenerator;
@@ -471,7 +472,9 @@ public:
   void AddExplicitLanguageFlags(std::string& flags,
                                 cmSourceFile const& sf) const;
 
-  void AddCUDAArchitectureFlags(std::string& flags) const;
+  void AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
+                                const std::string& config,
+                                std::string& flags) const;
   void AddCUDAToolkitFlags(std::string& flags) const;
 
   void AddHIPArchitectureFlags(std::string& flags) const;
@@ -1196,4 +1199,34 @@ public:
     bool operator()(cmGeneratorTarget const* t1,
                     cmGeneratorTarget const* t2) const;
   };
+
+  // C++20 module support queries.
+
+  /**
+   * Query whether the target expects C++20 module support.
+   *
+   * This will inspect the target itself to see if C++20 module
+   * support is expected to work based on its sources.
+   */
+  bool HaveCxx20ModuleSources() const;
+
+  enum class Cxx20SupportLevel
+  {
+    // C++ is not available.
+    MissingCxx,
+    // The experimental feature is not available.
+    MissingExperimentalFlag,
+    // The target does not require at least C++20.
+    NoCxx20,
+    // C++20 modules are available and working.
+    Supported,
+  };
+  /**
+   * Query whether the target has C++20 module support available (regardless of
+   * whether it is required or not).
+   */
+  Cxx20SupportLevel HaveCxxModuleSupport(std::string const& config) const;
+
+  // Check C++ module status for the target.
+  void CheckCxxModuleStatus(std::string const& config) const;
 };
index bf019c3..138d3f1 100644 (file)
@@ -183,8 +183,8 @@ void cmGhsMultiTargetGenerator::SetCompilerFlags(std::string const& config,
   auto i = this->FlagsByLanguage.find(language);
   if (i == this->FlagsByLanguage.end()) {
     std::string flags;
-    this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
-                                           language, config);
+    this->LocalGenerator->AddLanguageFlags(
+      flags, this->GeneratorTarget, cmBuildStep::Compile, language, config);
     this->LocalGenerator->AddCMP0018Flags(flags, this->GeneratorTarget,
                                           language, config);
     this->LocalGenerator->AddVisibilityPresetFlags(
index 0fe55f0..c2bf888 100644 (file)
@@ -2554,9 +2554,9 @@ bool cmGlobalGenerator::NameResolvesToFramework(
 // This is where we change the path to point to the framework directory.
 // .tbd files also can be located in SDK frameworks (they are
 // placeholders for actual libraries shipped with the OS)
-cm::optional<std::pair<std::string, std::string>>
+cm::optional<cmGlobalGenerator::FrameworkDescriptor>
 cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
-                                      bool extendedFormat) const
+                                      FrameworkFormat format) const
 {
   // Check for framework structure:
   //    (/path/to/)?FwName.framework
@@ -2571,20 +2571,29 @@ cmGlobalGenerator::SplitFrameworkPath(const std::string& path,
     auto name = frameworkPath.match(3);
     auto libname =
       cmSystemTools::GetFilenameWithoutExtension(frameworkPath.match(6));
+    if (format == FrameworkFormat::Strict && libname.empty()) {
+      return cm::nullopt;
+    }
     if (!libname.empty() && !cmHasPrefix(libname, name)) {
       return cm::nullopt;
     }
-    return std::pair<std::string, std::string>{ frameworkPath.match(2), name };
+
+    if (libname.empty() || name.size() == libname.size()) {
+      return FrameworkDescriptor{ frameworkPath.match(2), name };
+    }
+
+    return FrameworkDescriptor{ frameworkPath.match(2), name,
+                                libname.substr(name.size()) };
   }
 
-  if (extendedFormat) {
+  if (format == FrameworkFormat::Extended) {
     // path format can be more flexible: (/path/to/)?fwName(.framework)?
     auto fwDir = cmSystemTools::GetParentDirectory(path);
     auto name = cmSystemTools::GetFilenameLastExtension(path) == ".framework"
       ? cmSystemTools::GetFilenameWithoutExtension(path)
       : cmSystemTools::GetFilenameName(path);
 
-    return std::pair<std::string, std::string>{ fwDir, name };
+    return FrameworkDescriptor{ fwDir, name };
   }
 
   return cm::nullopt;
index 6e3072b..89b2ea8 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <cm/optional>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cm_codecvt.hxx"
 
@@ -367,13 +368,61 @@ public:
   /** Determine if a name resolves to a framework on disk or a built target
       that is a framework. */
   bool NameResolvesToFramework(const std::string& libname) const;
-  /** Split a framework path to the directory and name of the framework
-   * returns std::nullopt if the path does not match with framework format
+  /** Split a framework path to the directory and name of the framework as well
+   * as optiona; suffix.
+   * Returns std::nullopt if the path does not match with framework format
    * when extendedFormat is true, required format is relaxed (i.e. extension
    * `.framework' is optional). Used when FRAMEWORK link feature is
    * specified */
-  cm::optional<std::pair<std::string, std::string>> SplitFrameworkPath(
-    const std::string& path, bool extendedFormat = false) const;
+  struct FrameworkDescriptor
+  {
+    FrameworkDescriptor(std::string directory, std::string name)
+      : Directory(std::move(directory))
+      , Name(std::move(name))
+    {
+    }
+    FrameworkDescriptor(std::string directory, std::string name,
+                        std::string suffix)
+      : Directory(std::move(directory))
+      , Name(std::move(name))
+      , Suffix(std::move(suffix))
+    {
+    }
+    std::string GetLinkName() const
+    {
+      return this->Suffix.empty() ? this->Name
+                                  : cmStrCat(this->Name, ',', this->Suffix);
+    }
+    std::string GetFullName() const
+    {
+      return cmStrCat(this->Name, ".framework/"_s, this->Name, this->Suffix);
+    }
+    std::string GetFrameworkPath() const
+    {
+      return this->Directory.empty()
+        ? cmStrCat(this->Name, ".framework"_s)
+        : cmStrCat(this->Directory, '/', this->Name, ".framework"_s);
+    }
+    std::string GetFullPath() const
+    {
+      return this->Directory.empty()
+        ? this->GetFullName()
+        : cmStrCat(this->Directory, '/', this->GetFullName());
+    }
+
+    const std::string Directory;
+    const std::string Name;
+    const std::string Suffix;
+  };
+  enum class FrameworkFormat
+  {
+    Strict,
+    Relaxed,
+    Extended
+  };
+  cm::optional<FrameworkDescriptor> SplitFrameworkPath(
+    const std::string& path,
+    FrameworkFormat format = FrameworkFormat::Relaxed) const;
 
   cmMakefile* FindMakefile(const std::string& start_dir) const;
   cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;
index 6248f09..077de42 100644 (file)
@@ -3,8 +3,10 @@
 #include "cmGlobalNinjaGenerator.h"
 
 #include <algorithm>
+#include <cassert>
 #include <cctype>
 #include <cstdio>
+#include <functional>
 #include <sstream>
 #include <utility>
 
@@ -14,6 +16,7 @@
 #include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/memory>
+#include <cmext/string_view>
 
 #include <cm3p/json/reader.h>
 #include <cm3p/json/value.h>
@@ -21,7 +24,9 @@
 
 #include "cmsys/FStream.hxx"
 
+#include "cmCxxModuleMapper.h"
 #include "cmDocumentationEntry.h"
+#include "cmFileSet.h"
 #include "cmFortranParser.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpressionEvaluationFile.h"
@@ -2467,13 +2472,53 @@ cm::optional<cmSourceInfo> cmcmd_cmake_ninja_depends_fortran(
 }
 }
 
+struct CxxModuleFileSet
+{
+  std::string Name;
+  std::string RelativeDirectory;
+  std::string SourcePath;
+  std::string Type;
+  cmFileSetVisibility Visibility;
+  cm::optional<std::string> Destination;
+};
+
+struct CxxModuleBmiInstall
+{
+  std::string Component;
+  std::string Destination;
+  bool ExcludeFromAll;
+  bool Optional;
+  std::string Permissions;
+  std::string MessageLevel;
+  std::string ScriptLocation;
+};
+
+struct CxxModuleExport
+{
+  std::string Name;
+  std::string Destination;
+  std::string Prefix;
+  std::string CxxModuleInfoDir;
+  std::string Namespace;
+  bool Install;
+};
+
+struct cmGlobalNinjaGenerator::CxxModuleExportInfo
+{
+  std::map<std::string, CxxModuleFileSet> ObjectToFileSet;
+  cm::optional<CxxModuleBmiInstall> BmiInstallation;
+  std::vector<CxxModuleExport> Exports;
+  std::string Config;
+};
+
 bool cmGlobalNinjaGenerator::WriteDyndepFile(
   std::string const& dir_top_src, std::string const& dir_top_bld,
   std::string const& dir_cur_src, std::string const& dir_cur_bld,
   std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
   std::string const& module_dir,
   std::vector<std::string> const& linked_target_dirs,
-  std::string const& arg_lang, std::string const& arg_modmapfmt)
+  std::string const& arg_lang, std::string const& arg_modmapfmt,
+  CxxModuleExportInfo const& export_info)
 {
   // Setup path conversions.
   {
@@ -2498,13 +2543,15 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
     objects.push_back(std::move(info));
   }
 
+  CxxModuleUsage usages;
+
   // Map from module name to module file path, if known.
   std::map<std::string, std::string> mod_files;
 
   // Populate the module map with those provided by linked targets first.
   for (std::string const& linked_target_dir : linked_target_dirs) {
     std::string const ltmn =
-      cmStrCat(linked_target_dir, "/", arg_lang, "Modules.json");
+      cmStrCat(linked_target_dir, '/', arg_lang, "Modules.json");
     Json::Value ltm;
     cmsys::ifstream ltmf(ltmn.c_str(), std::ios::in | std::ios::binary);
     Json::Reader reader;
@@ -2515,21 +2562,71 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
       return false;
     }
     if (ltm.isObject()) {
-      for (Json::Value::iterator i = ltm.begin(); i != ltm.end(); ++i) {
-        mod_files[i.key().asString()] = i->asString();
+      Json::Value const& target_modules = ltm["modules"];
+      if (target_modules.isObject()) {
+        for (auto i = target_modules.begin(); i != target_modules.end(); ++i) {
+          mod_files[i.key().asString()] = i->asString();
+        }
+      }
+      Json::Value const& target_modules_references = ltm["references"];
+      if (target_modules_references.isObject()) {
+        for (auto i = target_modules_references.begin();
+             i != target_modules_references.end(); ++i) {
+          if (i->isObject()) {
+            Json::Value const& reference_path = (*i)["path"];
+            CxxModuleReference module_reference;
+            if (reference_path.isString()) {
+              module_reference.Path = reference_path.asString();
+            }
+            Json::Value const& reference_method = (*i)["lookup-method"];
+            if (reference_method.isString()) {
+              std::string reference = reference_method.asString();
+              if (reference == "by-name") {
+                module_reference.Method = LookupMethod::ByName;
+              } else if (reference == "include-angle") {
+                module_reference.Method = LookupMethod::IncludeAngle;
+              } else if (reference == "include-quote") {
+                module_reference.Method = LookupMethod::IncludeQuote;
+              }
+            }
+            usages.Reference[i.key().asString()] = module_reference;
+          }
+        }
+      }
+      Json::Value const& target_modules_usage = ltm["usages"];
+      if (target_modules_usage.isObject()) {
+        for (auto i = target_modules_usage.begin();
+             i != target_modules_usage.end(); ++i) {
+          if (i->isArray()) {
+            for (auto j = i->begin(); j != i->end(); ++j) {
+              usages.Usage[i.key().asString()].insert(j->asString());
+            }
+          }
+        }
       }
     }
   }
 
-  const char* module_ext = "";
-  if (arg_modmapfmt == "gcc") {
-    module_ext = ".gcm";
+  cm::optional<CxxModuleMapFormat> modmap_fmt;
+  if (arg_modmapfmt.empty()) {
+    // nothing to do.
+  } else if (arg_modmapfmt == "gcc") {
+    modmap_fmt = CxxModuleMapFormat::Gcc;
+  } else if (arg_modmapfmt == "msvc") {
+    modmap_fmt = CxxModuleMapFormat::Msvc;
+  } else {
+    cmSystemTools::Error(
+      cmStrCat("-E cmake_ninja_dyndep does not understand the ", arg_modmapfmt,
+               " module map format"));
+    return false;
   }
 
+  auto module_ext = CxxModuleMapExtension(modmap_fmt);
+
   // Extend the module map with those provided by this target.
   // We do this after loading the modules provided by linked targets
   // in case we have one of the same name that must be preferred.
-  Json::Value tm = Json::objectValue;
+  Json::Value target_modules = Json::objectValue;
   for (cmScanDepInfo const& object : objects) {
     for (auto const& p : object.Provides) {
       std::string mod;
@@ -2542,12 +2639,13 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
         }
       } else {
         // Assume the module file path matches the logical module name.
-        std::string safe_logical_name = p.LogicalName;
+        std::string safe_logical_name =
+          p.LogicalName; // TODO: needs fixing for header units
         cmSystemTools::ReplaceString(safe_logical_name, ":", "-");
         mod = cmStrCat(module_dir, safe_logical_name, module_ext);
       }
       mod_files[p.LogicalName] = mod;
-      tm[p.LogicalName] = mod;
+      target_modules[p.LogicalName] = mod;
     }
   }
 
@@ -2555,6 +2653,32 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
   ddf << "ninja_dyndep_version = 1.0\n";
 
   {
+    CxxModuleLocations locs;
+    locs.RootDirectory = ".";
+    locs.PathForGenerator = [this](std::string const& path) -> std::string {
+      return this->ConvertToNinjaPath(path);
+    };
+    locs.BmiLocationForModule =
+      [&mod_files](std::string const& logical) -> cm::optional<std::string> {
+      auto m = mod_files.find(logical);
+      if (m != mod_files.end()) {
+        return m->second;
+      }
+      return {};
+    };
+
+    // Insert information about the current target's modules.
+    if (modmap_fmt) {
+      auto cycle_modules = CxxModuleUsageSeed(locs, objects, usages);
+      if (!cycle_modules.empty()) {
+        cmSystemTools::Error(
+          cmStrCat("Circular dependency detected in the C++ module import "
+                   "graph. See modules named: \"",
+                   cmJoin(cycle_modules, R"(", ")"_s), '"'));
+        return false;
+      }
+    }
+
     cmNinjaBuild build("dyndep");
     build.Outputs.emplace_back("");
     for (cmScanDepInfo const& object : objects) {
@@ -2576,60 +2700,332 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
         build.Variables.emplace("restat", "1");
       }
 
-      if (arg_modmapfmt.empty()) {
-        // nothing to do.
-      } else {
-        std::stringstream mm;
-        if (arg_modmapfmt == "gcc") {
-          // Documented in GCC's documentation. The format is a series of lines
-          // with a module name and the associated filename separated by
-          // spaces. The first line may use `$root` as the module name to
-          // specify a "repository root". That is used to anchor any relative
-          // paths present in the file (CMake should never generate any).
-
-          // Write the root directory to use for module paths.
-          mm << "$root .\n";
-
-          for (auto const& l : object.Provides) {
-            auto m = mod_files.find(l.LogicalName);
-            if (m != mod_files.end()) {
-              mm << l.LogicalName << " " << this->ConvertToNinjaPath(m->second)
-                 << "\n";
-            }
-          }
-          for (auto const& r : object.Requires) {
-            auto m = mod_files.find(r.LogicalName);
-            if (m != mod_files.end()) {
-              mm << r.LogicalName << " " << this->ConvertToNinjaPath(m->second)
-                 << "\n";
-            }
-          }
-        } else {
-          cmSystemTools::Error(
-            cmStrCat("-E cmake_ninja_dyndep does not understand the ",
-                     arg_modmapfmt, " module map format"));
-          return false;
-        }
+      if (modmap_fmt) {
+        auto mm = CxxModuleMapContent(*modmap_fmt, locs, object, usages);
 
         // XXX(modmap): If changing this path construction, change
         // `cmNinjaTargetGenerator::WriteObjectBuildStatements` to generate the
         // corresponding file path.
         cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
-        mmf << mm.str();
+        mmf << mm;
       }
 
       this->WriteBuild(ddf, build);
     }
   }
 
+  Json::Value target_module_info = Json::objectValue;
+  target_module_info["modules"] = target_modules;
+
+  auto& target_usages = target_module_info["usages"] = Json::objectValue;
+  for (auto const& u : usages.Usage) {
+    auto& mod_usage = target_usages[u.first] = Json::arrayValue;
+    for (auto const& v : u.second) {
+      mod_usage.append(v);
+    }
+  }
+
+  auto name_for_method = [](LookupMethod method) -> cm::static_string_view {
+    switch (method) {
+      case LookupMethod::ByName:
+        return "by-name"_s;
+      case LookupMethod::IncludeAngle:
+        return "include-angle"_s;
+      case LookupMethod::IncludeQuote:
+        return "include-quote"_s;
+    }
+    assert(false && "unsupported lookup method");
+    return ""_s;
+  };
+
+  auto& target_references = target_module_info["references"] =
+    Json::objectValue;
+  for (auto const& r : usages.Reference) {
+    auto& mod_ref = target_references[r.first] = Json::objectValue;
+    mod_ref["path"] = r.second.Path;
+    mod_ref["lookup-method"] = std::string(name_for_method(r.second.Method));
+  }
+
   // Store the map of modules provided by this target in a file for
   // use by dependents that reference this target in linked-target-dirs.
   std::string const target_mods_file = cmStrCat(
     cmSystemTools::GetFilenamePath(arg_dd), '/', arg_lang, "Modules.json");
   cmGeneratedFileStream tmf(target_mods_file);
-  tmf << tm;
+  tmf << target_module_info;
+
+  bool result = true;
+
+  // Fortran doesn't support any of the file-set or BMI installation considered
+  // below.
+  if (arg_lang != "Fortran"_s) {
+    // Prepare the export information blocks.
+    std::string const config_upper =
+      cmSystemTools::UpperCase(export_info.Config);
+    std::vector<std::pair<std::unique_ptr<cmGeneratedFileStream>,
+                          CxxModuleExport const*>>
+      exports;
+    for (auto const& exp : export_info.Exports) {
+      std::unique_ptr<cmGeneratedFileStream> properties;
+
+      std::string const export_dir =
+        cmStrCat(exp.Prefix, '/', exp.CxxModuleInfoDir, '/');
+      std::string const property_file_path = cmStrCat(
+        export_dir, "target-", exp.Name, '-', export_info.Config, ".cmake");
+      properties = cm::make_unique<cmGeneratedFileStream>(property_file_path);
+
+      // Set up the preamble.
+      *properties << "set_property(TARGET \"" << exp.Namespace << exp.Name
+                  << "\"\n"
+                  << "  PROPERTY IMPORTED_CXX_MODULES_" << config_upper
+                  << '\n';
+
+      exports.emplace_back(std::move(properties), &exp);
+    }
+
+    std::unique_ptr<cmGeneratedFileStream> bmi_install_script;
+    if (export_info.BmiInstallation) {
+      bmi_install_script = cm::make_unique<cmGeneratedFileStream>(
+        export_info.BmiInstallation->ScriptLocation);
+    }
+
+    auto cmEscape = [](cm::string_view str) {
+      return cmOutputConverter::EscapeForCMake(
+        str, cmOutputConverter::WrapQuotes::NoWrap);
+    };
+    auto install_destination =
+      [&cmEscape](std::string const& dest) -> std::pair<bool, std::string> {
+      if (cmSystemTools::FileIsFullPath(dest)) {
+        return std::make_pair(true, cmEscape(dest));
+      }
+      return std::make_pair(false,
+                            cmStrCat("${_IMPORT_PREFIX}/", cmEscape(dest)));
+    };
 
-  return true;
+    // public/private requirement tracking.
+    std::set<std::string> private_modules;
+    std::map<std::string, std::set<std::string>> public_source_requires;
+
+    for (cmScanDepInfo const& object : objects) {
+      // Convert to forward slashes.
+      auto output_path = object.PrimaryOutput;
+#  ifdef _WIN32
+      cmSystemTools::ConvertToUnixSlashes(output_path);
+#  endif
+      // Find the fileset for this object.
+      auto fileset_info_itr = export_info.ObjectToFileSet.find(output_path);
+      bool const has_provides = !object.Provides.empty();
+      if (fileset_info_itr == export_info.ObjectToFileSet.end()) {
+        // If it provides anything, it should have a `CXX_MODULES` or
+        // `CXX_MODULE_INTERNAL_PARTITIONS` type and be present.
+        if (has_provides) {
+          // Take the first module provided to provide context.
+          auto const& provides = object.Provides[0];
+          char const* ok_types = "`CXX_MODULES`";
+          if (provides.LogicalName.find(':') != std::string::npos) {
+            ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
+                       "it is not `export`ed)";
+          }
+          cmSystemTools::Error(
+            cmStrCat("Output ", object.PrimaryOutput, " provides the `",
+                     provides.LogicalName,
+                     "` module but it is not found in a `FILE_SET` of type ",
+                     ok_types));
+          result = false;
+        }
+
+        // This object file does not provide anything, so nothing more needs to
+        // be done.
+        continue;
+      }
+
+      auto const& file_set = fileset_info_itr->second;
+
+      // Verify the fileset type for the object.
+      if (file_set.Type == "CXX_MODULES"_s) {
+        if (!has_provides) {
+          cmSystemTools::Error(cmStrCat(
+            "Output ", object.PrimaryOutput,
+            " is of type `CXX_MODULES` but does not provide a module"));
+          result = false;
+          continue;
+        }
+      } else if (file_set.Type == "CXX_MODULE_INTERNAL_PARTITIONS"_s) {
+        if (!has_provides) {
+          cmSystemTools::Error(cmStrCat(
+            "Source ", file_set.SourcePath,
+            " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
+            "provide a module"));
+          result = false;
+          continue;
+        }
+        auto const& provides = object.Provides[0];
+        if (provides.LogicalName.find(':') == std::string::npos) {
+          cmSystemTools::Error(cmStrCat(
+            "Source ", file_set.SourcePath,
+            " is of type `CXX_MODULE_INTERNAL_PARTITIONS` but does not "
+            "provide a module partition"));
+          result = false;
+          continue;
+        }
+      } else if (file_set.Type == "CXX_MODULE_HEADERS"_s) {
+        // TODO.
+      } else {
+        if (has_provides) {
+          auto const& provides = object.Provides[0];
+          char const* ok_types = "`CXX_MODULES`";
+          if (provides.LogicalName.find(':') != std::string::npos) {
+            ok_types = "`CXX_MODULES` (or `CXX_MODULE_INTERNAL_PARTITIONS` if "
+                       "it is not `export`ed)";
+          }
+          cmSystemTools::Error(cmStrCat(
+            "Source ", file_set.SourcePath, " provides the `",
+            provides.LogicalName, "` C++ module but is of type `",
+            file_set.Type, "` module but must be of type ", ok_types));
+          result = false;
+        }
+
+        // Not a C++ module; ignore.
+        continue;
+      }
+
+      if (!cmFileSetVisibilityIsForInterface(file_set.Visibility)) {
+        // Nothing needs to be conveyed about non-`PUBLIC` modules.
+        for (auto const& p : object.Provides) {
+          private_modules.insert(p.LogicalName);
+        }
+        continue;
+      }
+
+      // The module is public. Record what it directly requires.
+      {
+        auto& reqs = public_source_requires[file_set.SourcePath];
+        for (auto const& r : object.Requires) {
+          reqs.insert(r.LogicalName);
+        }
+      }
+
+      // Write out properties and install rules for any exports.
+      for (auto const& p : object.Provides) {
+        bool bmi_dest_is_abs = false;
+        std::string bmi_destination;
+        if (export_info.BmiInstallation) {
+          auto dest =
+            install_destination(export_info.BmiInstallation->Destination);
+          bmi_dest_is_abs = dest.first;
+          bmi_destination = cmStrCat(dest.second, '/');
+        }
+
+        std::string install_bmi_path;
+        std::string build_bmi_path;
+        auto m = mod_files.find(p.LogicalName);
+        if (m != mod_files.end()) {
+          install_bmi_path =
+            cmStrCat(bmi_destination,
+                     cmEscape(cmSystemTools::GetFilenameName(m->second)));
+          build_bmi_path = cmEscape(m->second);
+        }
+
+        for (auto const& exp : exports) {
+          std::string iface_source;
+          if (exp.second->Install && file_set.Destination) {
+            auto dest = install_destination(*file_set.Destination);
+            iface_source = cmStrCat(
+              dest.second, '/', cmEscape(file_set.RelativeDirectory),
+              cmEscape(cmSystemTools::GetFilenameName(file_set.SourcePath)));
+          } else {
+            iface_source = cmEscape(file_set.SourcePath);
+          }
+
+          std::string bmi_path;
+          if (exp.second->Install && export_info.BmiInstallation) {
+            bmi_path = install_bmi_path;
+          } else if (!exp.second->Install) {
+            bmi_path = build_bmi_path;
+          }
+
+          if (iface_source.empty()) {
+            // No destination for the C++ module source; ignore this property
+            // value.
+            continue;
+          }
+
+          *exp.first << "    \"" << cmEscape(p.LogicalName) << '='
+                     << iface_source;
+          if (!bmi_path.empty()) {
+            *exp.first << ',' << bmi_path;
+          }
+          *exp.first << "\"\n";
+        }
+
+        if (bmi_install_script) {
+          auto const& bmi_install = *export_info.BmiInstallation;
+
+          *bmi_install_script << "if (CMAKE_INSTALL_COMPONENT STREQUAL \""
+                              << cmEscape(bmi_install.Component) << '\"';
+          if (!bmi_install.ExcludeFromAll) {
+            *bmi_install_script << " OR NOT CMAKE_INSTALL_COMPONENT";
+          }
+          *bmi_install_script << ")\n";
+          *bmi_install_script << "  file(INSTALL\n"
+                                 "    DESTINATION \"";
+          if (!bmi_dest_is_abs) {
+            *bmi_install_script << "${CMAKE_INSTALL_PREFIX}/";
+          }
+          *bmi_install_script << cmEscape(bmi_install.Destination)
+                              << "\"\n"
+                                 "    TYPE FILE\n";
+          if (bmi_install.Optional) {
+            *bmi_install_script << "    OPTIONAL\n";
+          }
+          if (!bmi_install.MessageLevel.empty()) {
+            *bmi_install_script << "    " << bmi_install.MessageLevel << "\n";
+          }
+          if (!bmi_install.Permissions.empty()) {
+            *bmi_install_script << "    PERMISSIONS" << bmi_install.Permissions
+                                << "\n";
+          }
+          *bmi_install_script << "    FILES \"" << m->second << "\")\n";
+          if (bmi_dest_is_abs) {
+            *bmi_install_script
+              << "  list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n"
+                 "    \""
+              << cmEscape(cmSystemTools::GetFilenameName(m->second))
+              << "\")\n"
+                 "  if (CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
+                 "    message(WARNING\n"
+                 "      \"ABSOLUTE path INSTALL DESTINATION : "
+                 "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
+                 "  endif ()\n"
+                 "  if (CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n"
+                 "    message(FATAL_ERROR\n"
+                 "      \"ABSOLUTE path INSTALL DESTINATION forbidden (by "
+                 "caller): ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n"
+                 "  endif ()\n";
+          }
+          *bmi_install_script << "endif ()\n";
+        }
+      }
+    }
+
+    // Add trailing parenthesis for the `set_property` call.
+    for (auto const& exp : exports) {
+      *exp.first << ")\n";
+    }
+
+    // Check that public sources only require public modules.
+    for (auto const& pub_reqs : public_source_requires) {
+      for (auto const& req : pub_reqs.second) {
+        if (private_modules.count(req)) {
+          cmSystemTools::Error(cmStrCat(
+            "Public C++ module source `", pub_reqs.first, "` requires the `",
+            req, "` C++ module which is provided by a private source"));
+          result = false;
+        }
+      }
+    }
+  }
+
+  return result;
 }
 
 int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
@@ -2703,6 +3099,59 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
     }
   }
 
+  cmGlobalNinjaGenerator::CxxModuleExportInfo export_info;
+  export_info.Config = tdi["config"].asString();
+  if (export_info.Config.empty()) {
+    export_info.Config = "noconfig";
+  }
+  Json::Value const& tdi_exports = tdi["exports"];
+  if (tdi_exports.isArray()) {
+    for (auto const& tdi_export : tdi_exports) {
+      CxxModuleExport exp;
+      exp.Install = tdi_export["install"].asBool();
+      exp.Name = tdi_export["export-name"].asString();
+      exp.Destination = tdi_export["destination"].asString();
+      exp.Prefix = tdi_export["export-prefix"].asString();
+      exp.CxxModuleInfoDir = tdi_export["cxx-module-info-dir"].asString();
+      exp.Namespace = tdi_export["namespace"].asString();
+
+      export_info.Exports.push_back(exp);
+    }
+  }
+  auto const& bmi_installation = tdi["bmi-installation"];
+  if (bmi_installation.isObject()) {
+    CxxModuleBmiInstall bmi_install;
+
+    bmi_install.Component = bmi_installation["component"].asString();
+    bmi_install.Destination = bmi_installation["destination"].asString();
+    bmi_install.ExcludeFromAll = bmi_installation["exclude-from-all"].asBool();
+    bmi_install.Optional = bmi_installation["optional"].asBool();
+    bmi_install.Permissions = bmi_installation["permissions"].asString();
+    bmi_install.MessageLevel = bmi_installation["message-level"].asString();
+    bmi_install.ScriptLocation =
+      bmi_installation["script-location"].asString();
+
+    export_info.BmiInstallation = bmi_install;
+  }
+  Json::Value const& tdi_cxx_modules = tdi["cxx-modules"];
+  if (tdi_cxx_modules.isObject()) {
+    for (auto i = tdi_cxx_modules.begin(); i != tdi_cxx_modules.end(); ++i) {
+      CxxModuleFileSet& fsi = export_info.ObjectToFileSet[i.key().asString()];
+      auto const& tdi_cxx_module_info = *i;
+      fsi.Name = tdi_cxx_module_info["name"].asString();
+      fsi.RelativeDirectory =
+        tdi_cxx_module_info["relative-directory"].asString();
+      fsi.SourcePath = tdi_cxx_module_info["source"].asString();
+      fsi.Type = tdi_cxx_module_info["type"].asString();
+      fsi.Visibility = cmFileSetVisibilityFromName(
+        tdi_cxx_module_info["visibility"].asString(), nullptr);
+      auto const& tdi_fs_dest = tdi_cxx_module_info["destination"];
+      if (tdi_fs_dest.isString()) {
+        fsi.Destination = tdi_fs_dest.asString();
+      }
+    }
+  }
+
   cmake cm(cmake::RoleInternal, cmState::Unknown);
   cm.SetHomeDirectory(dir_top_src);
   cm.SetHomeOutputDirectory(dir_top_bld);
@@ -2710,7 +3159,8 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
   if (!ggd ||
       !cm::static_reference_cast<cmGlobalNinjaGenerator>(ggd).WriteDyndepFile(
         dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld, arg_dd, arg_ddis,
-        module_dir, linked_target_dirs, arg_lang, arg_modmapfmt)) {
+        module_dir, linked_target_dirs, arg_lang, arg_modmapfmt,
+        export_info)) {
     return 1;
   }
   return 0;
index 03387a8..defa264 100644 (file)
@@ -417,13 +417,15 @@ public:
   bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
   void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
 
+  struct CxxModuleExportInfo;
   bool WriteDyndepFile(
     std::string const& dir_top_src, std::string const& dir_top_bld,
     std::string const& dir_cur_src, std::string const& dir_cur_bld,
     std::string const& arg_dd, std::vector<std::string> const& arg_ddis,
     std::string const& module_dir,
     std::vector<std::string> const& linked_target_dirs,
-    std::string const& arg_lang, std::string const& arg_modmapfmt);
+    std::string const& arg_lang, std::string const& arg_modmapfmt,
+    CxxModuleExportInfo const& export_info);
 
   virtual std::string BuildAlias(const std::string& alias,
                                  const std::string& /*config*/) const
index 29eeb5a..bea2ae7 100644 (file)
@@ -20,7 +20,6 @@
 #include "cmDocumentationEntry.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
-#include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudio71Generator.h"
 #include "cmGlobalVisualStudio7Generator.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -38,7 +37,6 @@
 #include "cmXMLWriter.h"
 #include "cmake.h"
 
-static const char vs10generatorName[] = "Visual Studio 10 2010";
 static std::map<std::string, std::vector<cmIDEFlagTable>> loadedFlagJsonFiles;
 
 static void ConvertToWindowsSlashes(std::string& s)
@@ -51,137 +49,14 @@ static void ConvertToWindowsSlashes(std::string& s)
   }
 }
 
-// Map generator name without year to name with year.
-static const char* cmVS10GenName(const std::string& name, std::string& genName)
-{
-  if (strncmp(name.c_str(), vs10generatorName,
-              sizeof(vs10generatorName) - 6) != 0) {
-    return 0;
-  }
-  const char* p = name.c_str() + sizeof(vs10generatorName) - 6;
-  if (cmHasLiteralPrefix(p, " 2010")) {
-    p += 5;
-  }
-  genName = std::string(vs10generatorName) + p;
-  return p;
-}
-
-class cmGlobalVisualStudio10Generator::Factory
-  : public cmGlobalGeneratorFactory
-{
-public:
-  std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
-    const std::string& name, bool allowArch, cmake* cm) const override
-  {
-    std::string genName;
-    const char* p = cmVS10GenName(name, genName);
-    if (!p) {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-    if (!*p) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio10Generator(cm, genName, ""));
-    }
-    if (!allowArch || *p++ != ' ') {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-    if (strcmp(p, "Win64") == 0) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio10Generator(cm, genName, "x64"));
-    }
-    if (strcmp(p, "IA64") == 0) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio10Generator(cm, genName, "Itanium"));
-    }
-    return std::unique_ptr<cmGlobalGenerator>();
-  }
-
-  void GetDocumentation(cmDocumentationEntry& entry) const override
-  {
-    entry.Name = std::string(vs10generatorName) + " [arch]";
-    entry.Brief = "Deprecated.  Generates Visual Studio 2010 project files.  "
-                  "Optional [arch] can be \"Win64\" or \"IA64\".";
-  }
-
-  std::vector<std::string> GetGeneratorNames() const override
-  {
-    std::vector<std::string> names;
-    names.push_back(vs10generatorName);
-    return names;
-  }
-
-  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
-  {
-    std::vector<std::string> names;
-    names.push_back(vs10generatorName + std::string(" IA64"));
-    names.push_back(vs10generatorName + std::string(" Win64"));
-    return names;
-  }
-
-  bool SupportsToolset() const override { return true; }
-  bool SupportsPlatform() const override { return true; }
-
-  std::vector<std::string> GetKnownPlatforms() const override
-  {
-    std::vector<std::string> platforms;
-    platforms.emplace_back("x64");
-    platforms.emplace_back("Win32");
-    platforms.emplace_back("Itanium");
-    return platforms;
-  }
-
-  std::string GetDefaultPlatformName() const override { return "Win32"; }
-};
-
-std::unique_ptr<cmGlobalGeneratorFactory>
-cmGlobalVisualStudio10Generator::NewFactory()
-{
-  return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory);
-}
-
 cmGlobalVisualStudio10Generator::cmGlobalVisualStudio10Generator(
   cmake* cm, const std::string& name,
   std::string const& platformInGeneratorName)
   : cmGlobalVisualStudio8Generator(cm, name, platformInGeneratorName)
 {
-  std::string vc10Express;
-  this->ExpressEdition = cmSystemTools::ReadRegistryValue(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\10.0\\Setup\\VC;"
-    "ProductDir",
-    vc10Express, cmSystemTools::KeyWOW64_32);
-  this->CudaEnabled = false;
-  {
-    std::string envPlatformToolset;
-    if (cmSystemTools::GetEnv("PlatformToolset", envPlatformToolset) &&
-        envPlatformToolset == "Windows7.1SDK") {
-      // We are running from a Windows7.1SDK command prompt.
-      this->DefaultPlatformToolset = "Windows7.1SDK";
-    } else {
-      this->DefaultPlatformToolset = "v100";
-    }
-  }
-  this->DefaultCLFlagTableName = "v10";
-  this->DefaultCSharpFlagTableName = "v10";
-  this->DefaultLibFlagTableName = "v10";
-  this->DefaultLinkFlagTableName = "v10";
   this->DefaultCudaFlagTableName = "v10";
   this->DefaultCudaHostFlagTableName = "v10";
-  this->DefaultMasmFlagTableName = "v10";
   this->DefaultNasmFlagTableName = "v10";
-  this->DefaultRCFlagTableName = "v10";
-
-  this->Version = VSVersion::VS10;
-  this->PlatformToolsetNeedsDebugEnum = false;
-}
-
-bool cmGlobalVisualStudio10Generator::MatchesGeneratorName(
-  const std::string& name) const
-{
-  std::string genName;
-  if (cmVS10GenName(name, genName)) {
-    return genName == this->GetName();
-  }
-  return false;
 }
 
 bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s,
@@ -195,21 +70,6 @@ bool cmGlobalVisualStudio10Generator::SetSystemName(std::string const& s,
   return this->cmGlobalVisualStudio8Generator::SetSystemName(s, mf);
 }
 
-bool cmGlobalVisualStudio10Generator::SetGeneratorPlatform(
-  std::string const& p, cmMakefile* mf)
-{
-  if (!this->cmGlobalVisualStudio8Generator::SetGeneratorPlatform(p, mf)) {
-    return false;
-  }
-  if (this->GetPlatformName() == "Itanium" ||
-      this->GetPlatformName() == "x64") {
-    if (this->IsExpressEdition() && !this->Find64BitTools(mf)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 static void cmCudaToolVersion(std::string& s)
 {
   // "CUDA x.y.props" => "x.y"
@@ -389,6 +249,27 @@ bool cmGlobalVisualStudio10Generator::SetGeneratorToolset(
         this->GeneratorToolsetVersion.clear();
         this->GeneratorToolsetVersionProps = {};
       } break;
+      case AuxToolset::PropsIndeterminate: {
+        std::ostringstream e;
+        /* clang-format off */
+        e <<
+          "Generator\n"
+          "  " << this->GetName() << "\n"
+          "given toolset and version specification\n"
+          "  " << this->GetPlatformToolsetString() << ",version=" <<
+          this->GeneratorToolsetVersion << "\n"
+          "has multiple matches installed at\n" <<
+          "  " << auxProps << "\n" <<
+          "The toolset and version specification must resolve \n" <<
+           "to a single installed toolset";
+        ;
+        /* clang-format on */
+        mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+
+        // Clear the configured tool-set
+        this->GeneratorToolsetVersion.clear();
+        this->GeneratorToolsetVersionProps = {};
+      } break;
     }
   }
 
@@ -1286,44 +1167,6 @@ cmGlobalVisualStudio10Generator::GenerateBuildCommand(
   return makeCommands;
 }
 
-bool cmGlobalVisualStudio10Generator::Find64BitTools(cmMakefile* mf)
-{
-  if (this->DefaultPlatformToolset == "v100") {
-    // The v100 64-bit toolset does not exist in the express edition.
-    this->DefaultPlatformToolset.clear();
-  }
-  if (this->GetPlatformToolset()) {
-    return true;
-  }
-  // This edition does not come with 64-bit tools.  Look for them.
-  //
-  // TODO: Detect available tools?  x64\v100 exists but does not work?
-  // HKLM\\SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\4.0;VCTargetsPath
-  // c:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/Platforms/
-  //   {Itanium,Win32,x64}/PlatformToolsets/{v100,v90,Windows7.1SDK}
-  std::string winSDK_7_1;
-  if (cmSystemTools::ReadRegistryValue(
-        "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\"
-        "Windows\\v7.1;InstallationFolder",
-        winSDK_7_1)) {
-    std::ostringstream m;
-    m << "Found Windows SDK v7.1: " << winSDK_7_1;
-    mf->DisplayStatus(m.str(), -1);
-    this->DefaultPlatformToolset = "Windows7.1SDK";
-    return true;
-  } else {
-    std::ostringstream e;
-    /* clang-format off */
-    e << "Cannot enable 64-bit tools with Visual Studio 2010 Express.\n"
-      << "Install the Microsoft Windows SDK v7.1 to get 64-bit tools:\n"
-      << "  http://msdn.microsoft.com/en-us/windows/bb980924.aspx";
-    /* clang-format on */
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str().c_str());
-    cmSystemTools::SetFatalErrorOccurred();
-    return false;
-  }
-}
-
 std::string cmGlobalVisualStudio10Generator::GenerateRuleFile(
   std::string const& output) const
 {
@@ -1361,7 +1204,6 @@ const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const
 {
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return "4.0";
 
index 4977a84..b32c0a7 100644 (file)
@@ -14,7 +14,6 @@
 #include "cmGlobalVisualStudio8Generator.h"
 
 class cmGeneratorTarget;
-class cmGlobalGeneratorFactory;
 class cmLocalGenerator;
 class cmMakefile;
 class cmSourceFile;
@@ -29,14 +28,9 @@ struct cmIDEFlagTable;
 class cmGlobalVisualStudio10Generator : public cmGlobalVisualStudio8Generator
 {
 public:
-  static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory();
-
   bool IsVisualStudioAtLeast10() const override { return true; }
 
-  bool MatchesGeneratorName(const std::string& name) const override;
-
   bool SetSystemName(std::string const& s, cmMakefile* mf) override;
-  bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override;
   bool SetGeneratorToolset(std::string const& ts, bool build,
                            cmMakefile* mf) override;
 
@@ -131,7 +125,6 @@ public:
   bool TargetsAndroid() const { return this->SystemIsAndroid; }
 
   const char* GetCMakeCFGIntDir() const override { return "$(Configuration)"; }
-  bool Find64BitTools(cmMakefile* mf);
 
   /** Generate an <output>.rule file path for a given command output.  */
   std::string GenerateRuleFile(std::string const& output) const override;
@@ -200,7 +193,8 @@ protected:
     None,
     Default,
     PropsExist,
-    PropsMissing
+    PropsMissing,
+    PropsIndeterminate
   };
   virtual AuxToolset FindAuxToolset(std::string& version,
                                     std::string& props) const;
@@ -243,9 +237,6 @@ protected:
   bool MSBuildCommandInitialized = false;
 
 private:
-  class Factory;
-  friend class Factory;
-
   struct LongestSourcePath
   {
     LongestSourcePath()
@@ -269,7 +260,7 @@ private:
 
   std::string GeneratorToolsetVersion;
 
-  bool PlatformToolsetNeedsDebugEnum;
+  bool PlatformToolsetNeedsDebugEnum = false;
 
   bool ParseGeneratorToolset(std::string const& ts, cmMakefile* mf);
 
@@ -290,7 +281,7 @@ private:
   std::string VCTargetsPath;
   bool FindVCTargetsPath(cmMakefile* mf);
 
-  bool CudaEnabled;
+  bool CudaEnabled = false;
 
   // We do not use the reload macros for VS >= 10.
   std::string GetUserMacrosDirectory() override { return ""; }
index 10dc258..086d3af 100644 (file)
@@ -77,7 +77,7 @@ public:
   void GetDocumentation(cmDocumentationEntry& entry) const override
   {
     entry.Name = std::string(vs11generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2012 project files.  "
+    entry.Brief = "Deprecated.  Generates Visual Studio 2012 project files.  "
                   "Optional [arch] can be \"Win64\" or \"ARM\".";
   }
 
index f7f7317..ff76762 100644 (file)
@@ -304,23 +304,23 @@ void cmGlobalVisualStudio7Generator::Generate()
                                 GetSLNFile(this->LocalGenerators[0].get()));
   }
 
-  if (this->Version == VSVersion::VS10 &&
+  if (this->Version == VSVersion::VS11 &&
       !this->CMakeInstance->GetIsInTryCompile()) {
-    std::string cmakeWarnVS10;
+    std::string cmakeWarnVS11;
     if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
-          "CMAKE_WARN_VS10")) {
-      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS10");
-      cmakeWarnVS10 = *cached;
+          "CMAKE_WARN_VS11")) {
+      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS11");
+      cmakeWarnVS11 = *cached;
     } else {
-      cmSystemTools::GetEnv("CMAKE_WARN_VS10", cmakeWarnVS10);
+      cmSystemTools::GetEnv("CMAKE_WARN_VS11", cmakeWarnVS11);
     }
-    if (cmakeWarnVS10.empty() || !cmIsOff(cmakeWarnVS10)) {
+    if (cmakeWarnVS11.empty() || !cmIsOff(cmakeWarnVS11)) {
       this->CMakeInstance->IssueMessage(
         MessageType::WARNING,
-        "The \"Visual Studio 10 2010\" generator is deprecated "
+        "The \"Visual Studio 11 2012\" generator is deprecated "
         "and will be removed in a future version of CMake."
         "\n"
-        "Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.");
+        "Add CMAKE_WARN_VS11=OFF to the cache to disable this warning.");
     }
   }
 }
@@ -395,12 +395,27 @@ void cmGlobalVisualStudio7Generator::WriteTargetsToSolution(
 {
   VisualStudioFolders.clear();
 
+  std::vector<std::string> configs =
+    root->GetMakefile()->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+
   for (cmGeneratorTarget const* target : projectTargets) {
     if (!this->IsInSolution(target)) {
       continue;
     }
     bool written = false;
 
+    for (auto const& c : configs) {
+      target->CheckCxxModuleStatus(c);
+    }
+
+    if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) {
+      root->GetMakefile()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("The \"", target->GetName(),
+                 "\" target contains C++ module sources which are not "
+                 "supported by the generator"));
+    }
+
     // handle external vc project files
     cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
index a55cf45..288069c 100644 (file)
@@ -157,6 +157,8 @@ protected:
     cmValue typeGuid,
     const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0;
 
+  virtual bool SupportsCxxModuleDyndep() const { return false; }
+
   std::string ConvertToSolutionPath(const std::string& path);
 
   std::set<std::string> IsPartOfDefaultBuild(
index c6af20a..9d168d0 100644 (file)
@@ -99,8 +99,6 @@ const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "9.0";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
-      return "10.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return "11.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
@@ -128,14 +126,6 @@ void cmGlobalVisualStudioGenerator::WriteSLNHeader(std::ostream& fout)
       fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
       fout << "# Visual Studio 2008\n";
       break;
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
-      fout << "Microsoft Visual Studio Solution File, Format Version 11.00\n";
-      if (this->ExpressEdition) {
-        fout << "# Visual C++ Express 2010\n";
-      } else {
-        fout << "# Visual Studio 2010\n";
-      }
-      break;
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
       if (this->ExpressEdition) {
@@ -630,7 +620,8 @@ bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
 
         RegCloseKey(hsubkey);
       } else {
-        std::cout << "error opening subkey: " << subkeyname << std::endl;
+        std::cout << "error opening subkey: "
+                  << cmsys::Encoding::ToNarrow(subkeyname) << std::endl;
         std::cout << std::endl;
       }
 
index f45b4d4..576e4f2 100644 (file)
@@ -35,7 +35,6 @@ public:
   enum class VSVersion : uint16_t
   {
     VS9 = 90,
-    VS10 = 100,
     VS11 = 110,
     VS12 = 120,
     /* VS13 = 130 was skipped */
index 7e36881..be318c1 100644 (file)
@@ -127,8 +127,6 @@ static unsigned int VSVersionToMajor(
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return 9;
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
-      return 10;
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return 11;
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
@@ -151,8 +149,6 @@ static const char* VSVersionToToolset(
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "v90";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
-      return "v100";
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return "v110";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
@@ -175,8 +171,6 @@ static std::string VSVersionToMajorString(
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "9";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
-      return "10";
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return "11";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
@@ -198,7 +192,6 @@ static const char* VSVersionToAndroidToolset(
 {
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
@@ -500,7 +493,6 @@ bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
   std::string genName;
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -743,7 +735,6 @@ cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
 {
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
@@ -770,12 +761,15 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
   cmSystemTools::ConvertToUnixSlashes(instancePath);
 
   // Translate three-component format accepted by "vcvarsall -vcvars_ver=".
-  cmsys::RegularExpression threeComponent(
+  cmsys::RegularExpression threeComponentRegex(
     "^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
-  if (threeComponent.find(version)) {
+  // The two-component format represents the two major components of the
+  // three-component format
+  cmsys::RegularExpression twoComponentRegex("^([0-9]+\\.[0-9]+)$");
+  if (threeComponentRegex.find(version)) {
     // Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
     // with two matching components to check their three-component version.
-    std::string const& twoComponent = threeComponent.match(1);
+    std::string const& twoComponent = threeComponentRegex.match(1);
     std::string pattern =
       cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
                "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
@@ -801,6 +795,36 @@ cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
         }
       }
     }
+  } else if (twoComponentRegex.find(version)) {
+    std::string const& twoComponent = twoComponentRegex.match(1);
+    std::string pattern =
+      cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
+               "*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
+    cmsys::Glob glob;
+    glob.SetRecurseThroughSymlinks(false);
+    if (glob.FindFiles(pattern) && !glob.GetFiles().empty()) {
+      // Since we are only using the first two components of the
+      // toolset version, we require a single match.
+      if (glob.GetFiles().size() == 1) {
+        std::string const& txt = glob.GetFiles()[0];
+        std::string ver;
+        cmsys::ifstream fin(txt.c_str());
+        if (fin && std::getline(fin, ver)) {
+          // Strip trailing whitespace.
+          ver = ver.substr(0, ver.find_first_not_of("0123456789."));
+          // We assume the version is correct, since it is the only one that
+          // matched.
+          cmsys::RegularExpression extractVersion(
+            "VCToolsVersion\\.([0-9.]+)\\.txt$");
+          if (extractVersion.find(txt)) {
+            version = extractVersion.match(1);
+          }
+        }
+      } else {
+        props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s);
+        return AuxToolset::PropsIndeterminate;
+      }
+    }
   }
 
   if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
index 23c365a..0658021 100644 (file)
@@ -468,6 +468,10 @@ bool cmGlobalXCodeGenerator::Open(const std::string& bindir,
     }
     CFRelease(cfStr);
   }
+#else
+  (void)bindir;
+  (void)projectName;
+  (void)dryRun;
 #endif
 
   return ret;
@@ -603,7 +607,6 @@ std::string cmGlobalXCodeGenerator::PostBuildMakeTarget(
 }
 
 #define CMAKE_CHECK_BUILD_SYSTEM_TARGET "ZERO_CHECK"
-#define OBJECT_LIBRARY_ARTIFACT_DIR std::string()
 
 void cmGlobalXCodeGenerator::AddExtraTargets(
   cmLocalGenerator* root, std::vector<cmLocalGenerator*>& gens)
@@ -1188,13 +1191,9 @@ std::string GetTargetObjectDirArch(T const& target,
 std::string cmGlobalXCodeGenerator::GetLibraryOrFrameworkPath(
   const std::string& path) const
 {
-  auto fwItems = this->SplitFrameworkPath(path);
-  if (fwItems) {
-    if (fwItems->first.empty()) {
-      return cmStrCat(fwItems->second, ".framework");
-    } else {
-      return cmStrCat(fwItems->first, '/', fwItems->second, ".framework");
-    }
+  auto fwDescriptor = this->SplitFrameworkPath(path);
+  if (fwDescriptor) {
+    return fwDescriptor->GetFrameworkPath();
   }
 
   return path;
@@ -1372,6 +1371,18 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
     return true;
   }
 
+  for (std::string const& configName : this->CurrentConfigurationTypes) {
+    gtgt->CheckCxxModuleStatus(configName);
+  }
+
+  if (gtgt->HaveCxx20ModuleSources()) {
+    gtgt->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", gtgt->GetName(),
+               "\" target contains C++ module sources which are not "
+               "supported by the generator"));
+  }
+
   auto& gtgt_visited = this->CommandsVisited[gtgt];
   auto& deps = this->GetTargetDirectDepends(gtgt);
   for (auto& d : deps) {
@@ -2352,8 +2363,8 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     std::string& flags = cflags[lang];
 
     // Add language-specific flags.
-    this->CurrentLocalGenerator->AddLanguageFlags(flags, gtgt, lang,
-                                                  configName);
+    this->CurrentLocalGenerator->AddLanguageFlags(
+      flags, gtgt, cmBuildStep::Compile, lang, configName);
 
     if (gtgt->IsIPOEnabled(lang, configName)) {
       this->CurrentLocalGenerator->AppendFeatureOptions(flags, lang, "IPO");
@@ -2376,7 +2387,20 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
       gtgt->GetName());
     return;
   }
-  std::string const& langForPreprocessor = llang;
+
+  // Choose a language to use for target-wide preprocessor definitions.
+  static const char* ppLangs[] = { "CXX", "C", "OBJCXX", "OBJC" };
+  std::string langForPreprocessorDefinitions;
+  if (cm::contains(ppLangs, llang)) {
+    langForPreprocessorDefinitions = llang;
+  } else {
+    for (const char* l : ppLangs) {
+      if (languages.count(l)) {
+        langForPreprocessorDefinitions = l;
+        break;
+      }
+    }
+  }
 
   if (gtgt->IsIPOEnabled(llang, configName)) {
     const char* ltoValue =
@@ -2393,25 +2417,45 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
 
   // Add preprocessor definitions for this target and configuration.
   BuildObjectListOrString ppDefs(this, true);
-  if (languages.count("Swift")) {
-    // FIXME: Xcode warns that Swift does not support definition values.
-    // C/CXX sources mixed in Swift targets will not see CMAKE_INTDIR.
-  } else {
-    this->AppendDefines(
-      ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"");
-  }
+  this->AppendDefines(
+    ppDefs, "CMAKE_INTDIR=\"$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)\"");
   if (const std::string* exportMacro = gtgt->GetExportMacro()) {
     // Add the export symbol definition for shared library objects.
     this->AppendDefines(ppDefs, exportMacro->c_str());
   }
   std::vector<std::string> targetDefines;
-  if (!langForPreprocessor.empty()) {
+  if (!langForPreprocessorDefinitions.empty()) {
     gtgt->GetCompileDefinitions(targetDefines, configName,
-                                langForPreprocessor);
+                                langForPreprocessorDefinitions);
   }
   this->AppendDefines(ppDefs, targetDefines);
   buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS",
                               ppDefs.CreateList());
+  if (languages.count("Swift")) {
+    // Swift uses a separate attribute for definitions.
+    std::vector<std::string> targetSwiftDefines;
+    gtgt->GetCompileDefinitions(targetSwiftDefines, configName, "Swift");
+    // Remove the '=value' parts, as Swift does not support them.
+    std::for_each(targetSwiftDefines.begin(), targetSwiftDefines.end(),
+                  [](std::string& def) {
+                    std::string::size_type pos = def.find('=');
+                    if (pos != std::string::npos) {
+                      def.erase(pos);
+                    }
+                  });
+    if (this->XcodeVersion < 80) {
+      std::string defineString;
+      std::set<std::string> defines(targetSwiftDefines.begin(),
+                                    targetSwiftDefines.end());
+      this->CurrentLocalGenerator->JoinDefines(defines, defineString, "Swift");
+      cflags["Swift"] += " " + defineString;
+    } else {
+      BuildObjectListOrString swiftDefs(this, true);
+      this->AppendDefines(swiftDefs, targetSwiftDefines);
+      buildSettings->AddAttribute("SWIFT_ACTIVE_COMPILATION_CONDITIONS",
+                                  swiftDefs.CreateList());
+    }
+  }
 
   std::string extraLinkOptionsVar;
   std::string extraLinkOptions;
@@ -2499,18 +2543,28 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
     soName += *soversion;
   }
 
+  if (gtgt->CanCompileSources()) {
+    std::string const tmpDir =
+      this->GetTargetTempDir(gtgt, this->GetCMakeCFGIntDir());
+    buildSettings->AddAttribute("TARGET_TEMP_DIR", this->CreateString(tmpDir));
+
+    std::string outDir;
+    if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+      // We cannot suppress the archive, so hide it with intermediate files.
+      outDir = tmpDir;
+    } else {
+      outDir = gtgt->GetDirectory(configName);
+    }
+    buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
+                                this->CreateString(outDir));
+  }
+
   // Set attributes to specify the proper name for the target.
   std::string pndir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
   if (gtgt->GetType() == cmStateEnums::STATIC_LIBRARY ||
       gtgt->GetType() == cmStateEnums::SHARED_LIBRARY ||
       gtgt->GetType() == cmStateEnums::MODULE_LIBRARY ||
       gtgt->GetType() == cmStateEnums::EXECUTABLE) {
-    if (!gtgt->UsesDefaultOutputDir(configName,
-                                    cmStateEnums::RuntimeBinaryArtifact)) {
-      std::string pncdir = gtgt->GetDirectory(configName);
-      buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
-                                  this->CreateString(pncdir));
-    }
 
     if (gtgt->IsFrameworkOnApple() || gtgt->IsCFBundleOnApple()) {
       pnprefix = "";
@@ -2520,20 +2574,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
                                 this->CreateString(pnprefix));
     buildSettings->AddAttribute("EXECUTABLE_SUFFIX",
                                 this->CreateString(pnsuffix));
-  } else if (gtgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-    pnprefix = "lib";
-    pnbase = gtgt->GetName();
-    pnsuffix = ".a";
-
-    std::string pncdir = this->GetObjectsDirectory(
-      this->CurrentProject, configName, gtgt, OBJECT_LIBRARY_ARTIFACT_DIR);
-    buildSettings->AddAttribute("CONFIGURATION_BUILD_DIR",
-                                this->CreateString(pncdir));
   }
 
   // Store the product name for all target types.
   buildSettings->AddAttribute("PRODUCT_NAME", this->CreateString(realName));
-  buildSettings->AddAttribute("SYMROOT", this->CreateString(pndir));
 
   // Handle settings for each target type.
   switch (gtgt->GetType()) {
@@ -2681,10 +2725,12 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
   BuildObjectListOrString sysfdirs(this, true);
   const bool emitSystemIncludes = this->XcodeVersion >= 83;
 
+  // Choose a language to use for target-wide include directories.
+  std::string const& langForIncludes = llang;
   std::vector<std::string> includes;
-  if (!langForPreprocessor.empty()) {
+  if (!langForIncludes.empty()) {
     this->CurrentLocalGenerator->GetIncludeDirectories(
-      includes, gtgt, langForPreprocessor, configName);
+      includes, gtgt, langForIncludes, configName);
   }
   std::set<std::string> emitted;
   emitted.insert("/System/Library/Frameworks");
@@ -2697,7 +2743,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
         std::string incpath = this->XCodeEscapePath(frameworkDir);
         if (emitSystemIncludes &&
             gtgt->IsSystemIncludeDirectory(include, configName,
-                                           langForPreprocessor)) {
+                                           langForIncludes)) {
           sysfdirs.Add(incpath);
         } else {
           fdirs.Add(incpath);
@@ -2707,7 +2753,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
       std::string incpath = this->XCodeEscapePath(include);
       if (emitSystemIncludes &&
           gtgt->IsSystemIncludeDirectory(include, configName,
-                                         langForPreprocessor)) {
+                                         langForIncludes)) {
         sysdirs.Add(incpath);
       } else {
         dirs.Add(incpath);
@@ -2721,7 +2767,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
         std::string incpath = this->XCodeEscapePath(fwDir);
         if (emitSystemIncludes &&
             gtgt->IsSystemIncludeDirectory(fwDir, configName,
-                                           langForPreprocessor)) {
+                                           langForIncludes)) {
           sysfdirs.Add(incpath);
         } else {
           fdirs.Add(incpath);
@@ -2734,6 +2780,9 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt,
   }
   if (!dirs.IsEmpty()) {
     buildSettings->AddAttribute("HEADER_SEARCH_PATHS", dirs.CreateList());
+    if (languages.count("Swift")) {
+      buildSettings->AddAttribute("SWIFT_INCLUDE_PATHS", dirs.CreateList());
+    }
   }
   if (!sysfdirs.IsEmpty()) {
     buildSettings->AddAttribute("SYSTEM_FRAMEWORK_SEARCH_PATHS",
@@ -3618,9 +3667,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
       linkDir = libItem->Value.Value;
     }
     if (cmHasSuffix(libItem->GetFeatureName(), "FRAMEWORK"_s)) {
-      auto fwItems = this->SplitFrameworkPath(linkDir, true);
-      if (fwItems && !fwItems->first.empty()) {
-        linkDir = std::move(fwItems->first);
+      auto fwDescriptor = this->SplitFrameworkPath(
+        linkDir, cmGlobalGenerator::FrameworkFormat::Extended);
+      if (fwDescriptor && !fwDescriptor->Directory.empty()) {
+        linkDir = fwDescriptor->Directory;
         if (std::find(frameworkSearchPaths.begin(), frameworkSearchPaths.end(),
                       linkDir) == frameworkSearchPaths.end()) {
           frameworkSearchPaths.push_back(linkDir);
@@ -3753,14 +3803,20 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
     // add the library search paths
     {
       BuildObjectListOrString libSearchPaths(this, true);
+
       std::string linkDirs;
       for (auto const& libDir : cli->GetDirectories()) {
         if (!libDir.empty() && libDir != "/usr/lib") {
-          libSearchPaths.Add(this->XCodeEscapePath(
-            libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"));
+          cmPolicies::PolicyStatus cmp0142 =
+            target->GetTarget()->GetPolicyStatusCMP0142();
+          if (cmp0142 == cmPolicies::OLD || cmp0142 == cmPolicies::WARN) {
+            libSearchPaths.Add(this->XCodeEscapePath(
+              libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"));
+          }
           libSearchPaths.Add(this->XCodeEscapePath(libDir));
         }
       }
+
       // Add previously collected paths where to look for libraries
       // that were added to "Link Binary With Libraries"
       for (auto& libDir : linkSearchPaths) {
@@ -3800,6 +3856,7 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
       const auto& fwPaths = cli->GetFrameworkPaths();
       emitted.insert(fwPaths.begin(), fwPaths.end());
       BuildObjectListOrString libPaths(this, true);
+      BuildObjectListOrString fwSearchPaths(this, true);
       for (auto const& libItem : configItemMap[configName]) {
         auto const& libName = *libItem;
         if (libName.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
@@ -3810,13 +3867,14 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
           bool isFramework =
             cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
           if (isFramework) {
-            const auto fwItems =
-              this->SplitFrameworkPath(cleanPath, isFramework);
-            if (!fwItems->first.empty() &&
-                emitted.insert(fwItems->first).second) {
+            const auto fwDescriptor = this->SplitFrameworkPath(
+              cleanPath, cmGlobalGenerator::FrameworkFormat::Extended);
+            if (!fwDescriptor->Directory.empty() &&
+                emitted.insert(fwDescriptor->Directory).second) {
               // This is a search path we had not added before and it isn't
               // an implicit search path, so we need it
-              libPaths.Add("-F " + this->XCodeEscapePath(fwItems->first));
+              fwSearchPaths.Add(
+                this->XCodeEscapePath(fwDescriptor->Directory));
             }
             if (libName.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s) {
               // use the full path
@@ -3824,10 +3882,10 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
                 libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
                   .Value);
             } else {
-              libPaths.Add(
-                libName
-                  .GetFormattedItem(this->XCodeEscapePath(fwItems->second))
-                  .Value);
+              libPaths.Add(libName
+                             .GetFormattedItem(this->XCodeEscapePath(
+                               fwDescriptor->GetLinkName()))
+                             .Value);
             }
           } else {
             libPaths.Add(
@@ -3855,9 +3913,16 @@ void cmGlobalXCodeGenerator::AddDependAndLinkInformation(cmXCodeObject* target)
           target->AddDependTarget(configName, libName.Target->GetName());
         }
       }
-      this->AppendBuildSettingAttribute(target,
-                                        this->GetTargetLinkFlagsVar(gt),
-                                        libPaths.CreateList(), configName);
+      if (!libPaths.IsEmpty()) {
+        this->AppendBuildSettingAttribute(target,
+                                          this->GetTargetLinkFlagsVar(gt),
+                                          libPaths.CreateList(), configName);
+      }
+      if (!fwSearchPaths.IsEmpty()) {
+        this->AppendBuildSettingAttribute(target, "FRAMEWORK_SEARCH_PATHS",
+                                          fwSearchPaths.CreateList(),
+                                          configName);
+      }
     }
   }
 }
@@ -4339,7 +4404,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
                                 this->CreateString(swiftVersion));
   }
 
-  std::string symroot = cmStrCat(root->GetCurrentBinaryDirectory(), "/build");
+  std::string const symroot = this->GetSymrootDir();
   buildSettings->AddAttribute("SYMROOT", this->CreateString(symroot));
 
   // Inside a try_compile project, do not require signing on any platform.
@@ -4438,14 +4503,17 @@ bool cmGlobalXCodeGenerator::CreateXCodeObjects(
   return true;
 }
 
-std::string cmGlobalXCodeGenerator::GetObjectsDirectory(
-  const std::string& projName, const std::string& configName,
-  const cmGeneratorTarget* t, const std::string& variant) const
+std::string cmGlobalXCodeGenerator::GetSymrootDir() const
 {
-  std::string dir = cmStrCat(
-    t->GetLocalGenerator()->GetCurrentBinaryDirectory(), '/', projName,
-    ".build/", configName, '/', t->GetName(), ".build/", variant);
-  return dir;
+  return cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(), "/build");
+}
+
+std::string cmGlobalXCodeGenerator::GetTargetTempDir(
+  cmGeneratorTarget const* gt, std::string const& configName) const
+{
+  // Use a path inside the SYMROOT.
+  return cmStrCat(this->GetSymrootDir(), '/', gt->GetName(), ".build/",
+                  configName);
 }
 
 void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf)
@@ -4571,10 +4639,8 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile(
         for (auto objLib : objlibs) {
 
           const std::string objLibName = objLib->GetName();
-          std::string d = cmStrCat(
-            this->GetObjectsDirectory(this->CurrentProject, configName, objLib,
-                                      OBJECT_LIBRARY_ARTIFACT_DIR),
-            "lib", objLibName, ".a");
+          std::string d = cmStrCat(this->GetTargetTempDir(gt, configName),
+                                   "/lib", objLibName, ".a");
 
           std::string dependency = this->ConvertToRelativeForMake(d);
           makefileStream << "\\\n\t" << dependency;
@@ -4588,8 +4654,8 @@ void cmGlobalXCodeGenerator::CreateXCodeDependHackMakefile(
         // if building for more than one architecture
         // then remove those executables as well
         if (this->Architectures.size() > 1) {
-          std::string universal = this->GetObjectsDirectory(
-            this->CurrentProject, configName, gt, "$(OBJDIR)/");
+          std::string universal =
+            cmStrCat(this->GetTargetTempDir(gt, configName), "/$(OBJDIR)/");
           for (const auto& architecture : this->Architectures) {
             std::string universalFile = cmStrCat(universal, architecture, '/',
                                                  gt->GetFullName(configName));
@@ -4986,14 +5052,10 @@ bool cmGlobalXCodeGenerator::ShouldStripResourcePath(cmMakefile*) const
 void cmGlobalXCodeGenerator::ComputeTargetObjectDirectory(
   cmGeneratorTarget* gt) const
 {
-  std::string configName = this->GetCMakeCFGIntDir();
   auto objectDirArch = GetTargetObjectDirArch(*gt, this->ObjectDirArch);
-
-  std::string dir =
-    cmStrCat(this->GetObjectsDirectory("$(PROJECT_NAME)", configName, gt,
-                                       "$(OBJECT_FILE_DIR_normal:base)/"),
-             objectDirArch, '/');
-  gt->ObjectDirectory = dir;
+  gt->ObjectDirectory =
+    cmStrCat(this->GetTargetTempDir(gt, this->GetCMakeCFGIntDir()),
+             "/$(OBJECT_FILE_DIR_normal:base)/", objectDirArch, '/');
 }
 
 std::string cmGlobalXCodeGenerator::GetDeploymentPlatform(const cmMakefile* mf)
index 92e4528..9ae75fb 100644 (file)
@@ -339,10 +339,9 @@ private:
 
   std::string GetLibraryOrFrameworkPath(const std::string& path) const;
 
-  std::string GetObjectsDirectory(const std::string& projName,
-                                  const std::string& configName,
-                                  const cmGeneratorTarget* t,
-                                  const std::string& variant) const;
+  std::string GetSymrootDir() const;
+  std::string GetTargetTempDir(cmGeneratorTarget const* gt,
+                               std::string const& configName) const;
 
   static std::string GetDeploymentPlatform(const cmMakefile* mf);
 
index 0da72b1..c2a09c1 100644 (file)
@@ -150,7 +150,7 @@ bool cmIfFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
       cmExecutionStatus status(mf);
       mf.ExecuteCommand(func, status);
       if (status.GetReturnInvoked()) {
-        inStatus.SetReturnInvoked();
+        inStatus.SetReturnInvoked(status.GetReturnVariables());
         return true;
       }
       if (status.GetBreakInvoked()) {
index 7ca5b23..82adca8 100644 (file)
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmsys/Glob.hxx"
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
+#include "cmExperimental.h"
 #include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstallCommandArguments.h"
+#include "cmInstallCxxModuleBmiGenerator.h"
 #include "cmInstallDirectoryGenerator.h"
 #include "cmInstallExportGenerator.h"
 #include "cmInstallFileSetGenerator.h"
@@ -54,13 +58,13 @@ namespace {
 
 struct RuntimeDependenciesArgs
 {
-  std::vector<std::string> Directories;
-  std::vector<std::string> PreIncludeRegexes;
-  std::vector<std::string> PreExcludeRegexes;
-  std::vector<std::string> PostIncludeRegexes;
-  std::vector<std::string> PostExcludeRegexes;
-  std::vector<std::string> PostIncludeFiles;
-  std::vector<std::string> PostExcludeFiles;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PreIncludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PreExcludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeRegexes;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostIncludeFiles;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> PostExcludeFiles;
 };
 
 auto const RuntimeDependenciesArgHelper =
@@ -109,6 +113,8 @@ public:
     const cmInstallCommandArguments* args) const;
   std::string GetLibraryDestination(
     const cmInstallCommandArguments* args) const;
+  std::string GetCxxModulesBmiDestination(
+    const cmInstallCommandArguments* args) const;
   std::string GetIncludeDestination(
     const cmInstallCommandArguments* args) const;
   std::string GetSysconfDestination(
@@ -401,16 +407,17 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Archive;
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Object;
-    std::vector<std::string> Framework;
-    std::vector<std::string> Bundle;
-    std::vector<std::string> Includes;
-    std::vector<std::string> PrivateHeader;
-    std::vector<std::string> PublicHeader;
-    std::vector<std::string> Resource;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Archive;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Object;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Includes;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PrivateHeader;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> PublicHeader;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Resource;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> CxxModulesBmi;
     std::vector<std::vector<std::string>> FileSets;
   };
 
@@ -426,7 +433,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       .Bind("PRIVATE_HEADER"_s, &ArgVectors::PrivateHeader)
       .Bind("PUBLIC_HEADER"_s, &ArgVectors::PublicHeader)
       .Bind("RESOURCE"_s, &ArgVectors::Resource)
-      .Bind("FILE_SET"_s, &ArgVectors::FileSets);
+      .Bind("FILE_SET"_s, &ArgVectors::FileSets)
+      .Bind("CXX_MODULES_BMI"_s, &ArgVectors::CxxModulesBmi);
 
   std::vector<std::string> genericArgVector;
   ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector);
@@ -434,26 +442,25 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   // now parse the generic args (i.e. the ones not specialized on LIBRARY/
   // ARCHIVE, RUNTIME etc. (see above)
   // These generic args also contain the targets and the export stuff
-  std::vector<std::string> targetList;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
   std::string exports;
-  std::vector<std::string> runtimeDependenciesArgVector;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
+    runtimeDependenciesArgVector;
   std::string runtimeDependencySetArg;
   std::vector<std::string> unknownArgs;
-  std::vector<std::string> parsedArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
   genericArgs.Bind("TARGETS"_s, targetList);
   genericArgs.Bind("EXPORT"_s, exports);
   genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector);
   genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg);
-  genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs);
+  genericArgs.Parse(genericArgVector, &unknownArgs);
   bool success = genericArgs.Finalize();
 
-  bool withRuntimeDependencies =
-    std::find(parsedArgs.begin(), parsedArgs.end(), "RUNTIME_DEPENDENCIES") !=
-    parsedArgs.end();
   RuntimeDependenciesArgs runtimeDependenciesArgs =
-    RuntimeDependenciesArgHelper.Parse(runtimeDependenciesArgVector,
-                                       &unknownArgs);
+    runtimeDependenciesArgVector
+    ? RuntimeDependenciesArgHelper.Parse(*runtimeDependenciesArgVector,
+                                         &unknownArgs)
+    : RuntimeDependenciesArgs();
 
   cmInstallCommandArguments archiveArgs(helper.DefaultComponentName);
   cmInstallCommandArguments libraryArgs(helper.DefaultComponentName);
@@ -467,6 +474,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   cmInstallCommandIncludesArgument includesArgs;
   std::vector<cmInstallCommandFileSetArguments> fileSetArgs(
     argVectors.FileSets.size(), { helper.DefaultComponentName });
+  cmInstallCommandArguments cxxModuleBmiArgs(helper.DefaultComponentName);
 
   // now parse the args for specific parts of the target (e.g. LIBRARY,
   // RUNTIME, ARCHIVE etc.
@@ -490,6 +498,15 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     fileSetArgs[i] = std::move(fileSetArg);
   }
 
+  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
+    *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
+  if (!supportCxx20FileSetTypes) {
+    std::copy(argVectors.CxxModulesBmi.begin(), argVectors.CxxModulesBmi.end(),
+              std::back_inserter(unknownArgs));
+  } else {
+    cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs);
+  }
+
   if (!unknownArgs.empty()) {
     // Unknown argument.
     status.SetError(
@@ -510,6 +527,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   for (auto& fileSetArg : fileSetArgs) {
     fileSetArg.SetGenericArguments(&genericArgs);
   }
+  cxxModuleBmiArgs.SetGenericArguments(&genericArgs);
 
   success = success && archiveArgs.Finalize();
   success = success && libraryArgs.Finalize();
@@ -523,6 +541,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   for (auto& fileSetArg : fileSetArgs) {
     success = success && fileSetArg.Finalize();
   }
+  if (supportCxx20FileSetTypes) {
+    success = success && cxxModuleBmiArgs.Finalize();
+  }
 
   if (!success) {
     return false;
@@ -536,7 +557,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       publicHeaderArgs.GetNamelinkOnly() || resourceArgs.GetNamelinkOnly() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
-                    -> bool { return fileSetArg.GetNamelinkOnly(); })) {
+                    -> 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.");
@@ -548,7 +570,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       publicHeaderArgs.GetNamelinkSkip() || resourceArgs.GetNamelinkSkip() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
-                    -> bool { return fileSetArg.GetNamelinkSkip(); })) {
+                    -> 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.");
@@ -564,7 +587,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       resourceArgs.HasNamelinkComponent() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
-                    -> bool { return fileSetArg.HasNamelinkComponent(); })) {
+                    -> 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 "
@@ -583,7 +607,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       !publicHeaderArgs.GetType().empty() || !resourceArgs.GetType().empty() ||
       std::any_of(fileSetArgs.begin(), fileSetArgs.end(),
                   [](const cmInstallCommandFileSetArguments& fileSetArg)
-                    -> bool { return !fileSetArg.GetType().empty(); })) {
+                    -> bool { return !fileSetArg.GetType().empty(); }) ||
+      !cxxModuleBmiArgs.GetType().empty()) {
     status.SetError(
       "TARGETS given TYPE option. The TYPE option may only be specified in "
       " install(FILES) and install(DIRECTORIES).");
@@ -597,7 +622,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   }
 
   cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr;
-  if (withRuntimeDependencies) {
+  if (runtimeDependenciesArgVector) {
     if (!runtimeDependencySetArg.empty()) {
       status.SetError("TARGETS cannot have both RUNTIME_DEPENDENCIES and "
                       "RUNTIME_DEPENDENCY_SET.");
@@ -706,6 +731,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
   bool installsPublicHeader = false;
   bool installsResource = false;
   std::vector<bool> installsFileSet(fileSetArgs.size(), false);
+  bool installsCxxModuleBmi = false;
 
   // Generate install script code to install the given targets.
   for (cmTarget* ti : targets) {
@@ -722,6 +748,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     std::unique_ptr<cmInstallFilesGenerator> publicHeaderGenerator;
     std::unique_ptr<cmInstallFilesGenerator> resourceGenerator;
     std::vector<std::unique_ptr<cmInstallFileSetGenerator>> fileSetGenerators;
+    std::unique_ptr<cmInstallCxxModuleBmiGenerator> cxxModuleBmiGenerator;
 
     // Avoid selecting default destinations for PUBLIC_HEADER and
     // PRIVATE_HEADER if any artifacts are specified.
@@ -760,6 +787,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
         for (auto const& gen : fileSetGenerators) {
           te->FileSetGenerators[gen->GetFileSet()] = gen.get();
         }
+        te->CxxModuleBmiGenerator = cxxModuleBmiGenerator.get();
         target.AddInstallIncludeDirectories(
           *te, cmMakeRange(includesArgs.GetIncludeDirs()));
         te->NamelinkOnly = namelinkOnly;
@@ -1105,6 +1133,19 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
       }
     }
 
+    if (supportCxx20FileSetTypes &&
+        !cxxModuleBmiArgs.GetDestination().empty()) {
+      cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>(
+        target.GetName(),
+        helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs),
+        cxxModuleBmiArgs.GetPermissions(),
+        cxxModuleBmiArgs.GetConfigurations(), cxxModuleBmiArgs.GetComponent(),
+        cmInstallGenerator::SelectMessageLevel(target.GetMakefile()),
+        cxxModuleBmiArgs.GetExcludeFromAll(), cxxModuleBmiArgs.GetOptional(),
+        helper.Makefile->GetBacktrace());
+      target.SetHaveInstallRule(true);
+    }
+
     // Add this install rule to an export if one was specified.
     if (!addTargetExport()) {
       return false;
@@ -1121,6 +1162,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     installsPrivateHeader = installsPrivateHeader || privateHeaderGenerator;
     installsPublicHeader = installsPublicHeader || publicHeaderGenerator;
     installsResource = installsResource || resourceGenerator;
+    installsCxxModuleBmi = installsCxxModuleBmi || cxxModuleBmiGenerator;
 
     helper.Makefile->AddInstallGenerator(std::move(archiveGenerator));
     helper.Makefile->AddInstallGenerator(std::move(libraryGenerator));
@@ -1135,9 +1177,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
     for (auto& gen : fileSetGenerators) {
       helper.Makefile->AddInstallGenerator(std::move(gen));
     }
+    helper.Makefile->AddInstallGenerator(std::move(cxxModuleBmiGenerator));
   }
 
-  if (withRuntimeDependencies && !runtimeDependencySet->Empty()) {
+  if (runtimeDependenciesArgVector && !runtimeDependencySet->Empty()) {
     AddInstallRuntimeDependenciesGenerator(
       helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs,
       std::move(runtimeDependenciesArgs), installsRuntime, installsLibrary,
@@ -1192,6 +1235,10 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
         fileSetArgs[i].GetComponent());
     }
   }
+  if (installsCxxModuleBmi) {
+    helper.Makefile->GetGlobalGenerator()->AddInstallComponent(
+      cxxModuleBmiArgs.GetComponent());
+  }
 
   return true;
 }
@@ -1206,10 +1253,10 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Framework;
-    std::vector<std::string> Bundle;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Bundle;
   };
 
   static auto const argHelper = cmArgumentParser<ArgVectors>{}
@@ -1223,7 +1270,7 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args,
 
   // now parse the generic args (i.e. the ones not specialized on LIBRARY,
   // RUNTIME etc. (see above)
-  std::vector<std::string> targetList;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> targetList;
   std::string runtimeDependencySetArg;
   std::vector<std::string> unknownArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
@@ -1464,7 +1511,7 @@ bool HandleFilesMode(std::vector<std::string> const& args,
   // This is the FILES mode.
   bool programs = (args[0] == "PROGRAMS");
   cmInstallCommandArguments ica(helper.DefaultComponentName);
-  std::vector<std::string> files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> files;
   ica.Bind(programs ? "PROGRAMS"_s : "FILES"_s, files);
   std::vector<std::string> unknownArgs;
   ica.Parse(args, &unknownArgs);
@@ -1950,7 +1997,7 @@ bool HandleExportAndroidMKMode(std::vector<std::string> const& args,
     cm::make_unique<cmInstallExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), fname, name_space, exportOld, true,
+      ica.GetExcludeFromAll(), fname, name_space, "", exportOld, true,
       helper.Makefile->GetBacktrace()));
 
   return true;
@@ -1973,12 +2020,19 @@ bool HandleExportMode(std::vector<std::string> const& args,
   std::string name_space;
   bool exportOld = false;
   std::string filename;
+  std::string cxx_modules_directory;
 
   ica.Bind("EXPORT"_s, exp);
   ica.Bind("NAMESPACE"_s, name_space);
   ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
   ica.Bind("FILE"_s, filename);
 
+  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
+    *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
+  if (supportCxx20FileSetTypes) {
+    ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
+  }
+
   std::vector<std::string> unknownArgs;
   ica.Parse(args, &unknownArgs);
 
@@ -2064,8 +2118,8 @@ bool HandleExportMode(std::vector<std::string> const& args,
     cm::make_unique<cmInstallExportGenerator>(
       &exportSet, ica.GetDestination(), ica.GetPermissions(),
       ica.GetConfigurations(), ica.GetComponent(), message,
-      ica.GetExcludeFromAll(), fname, name_space, exportOld, false,
-      helper.Makefile->GetBacktrace()));
+      ica.GetExcludeFromAll(), fname, name_space, cxx_modules_directory,
+      exportOld, false, helper.Makefile->GetBacktrace()));
 
   return true;
 }
@@ -2088,9 +2142,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
 
   struct ArgVectors
   {
-    std::vector<std::string> Library;
-    std::vector<std::string> Runtime;
-    std::vector<std::string> Framework;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Library;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Runtime;
+    ArgumentParser::MaybeEmpty<std::vector<std::string>> Framework;
   };
 
   static auto const argHelper = cmArgumentParser<ArgVectors>{}
@@ -2106,11 +2160,9 @@ bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args,
   // These generic args also contain the runtime dependency set
   std::string runtimeDependencySetArg;
   std::vector<std::string> runtimeDependencyArgVector;
-  std::vector<std::string> parsedArgs;
   cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
   genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg);
-  genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr,
-                    &parsedArgs);
+  genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector);
   bool success = genericArgs.Finalize();
 
   cmInstallCommandArguments libraryArgs(helper.DefaultComponentName);
@@ -2280,6 +2332,15 @@ std::string Helper::GetLibraryDestination(
   return this->GetDestination(args, "CMAKE_INSTALL_LIBDIR", "lib");
 }
 
+std::string Helper::GetCxxModulesBmiDestination(
+  const cmInstallCommandArguments* args) const
+{
+  if (args) {
+    return args->GetDestination();
+  }
+  return {};
+}
+
 std::string Helper::GetIncludeDestination(
   const cmInstallCommandArguments* args) const
 {
index 79bd945..6e46aac 100644 (file)
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 
 class cmInstallCommandArguments : public cmArgumentParser<void>
 {
@@ -44,8 +45,8 @@ private:
   std::string NamelinkComponent;
   bool ExcludeFromAll = false;
   std::string Rename;
-  std::vector<std::string> Permissions;
-  std::vector<std::string> Configurations;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Permissions;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Configurations;
   bool Optional = false;
   bool NamelinkOnly = false;
   bool NamelinkSkip = false;
diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx
new file mode 100644 (file)
index 0000000..1ef1eaa
--- /dev/null
@@ -0,0 +1,75 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmInstallCxxModuleBmiGenerator.h"
+
+#include <ostream>
+#include <utility>
+
+#include "cmGeneratorExpression.h"
+#include "cmGeneratorTarget.h"
+#include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
+#include "cmLocalGenerator.h"
+#include "cmOutputConverter.h"
+#include "cmStringAlgorithms.h"
+
+cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator(
+  std::string target, std::string const& dest, std::string file_permissions,
+  std::vector<std::string> const& configurations, std::string const& component,
+  MessageLevel message, bool exclude_from_all, bool optional,
+  cmListFileBacktrace backtrace)
+  : cmInstallGenerator(dest, configurations, component, message,
+                       exclude_from_all, false, std::move(backtrace))
+  , TargetName(std::move(target))
+  , FilePermissions(std::move(file_permissions))
+  , Optional(optional)
+{
+  this->ActionsPerConfig = true;
+}
+
+cmInstallCxxModuleBmiGenerator::~cmInstallCxxModuleBmiGenerator() = default;
+
+bool cmInstallCxxModuleBmiGenerator::Compute(cmLocalGenerator* lg)
+{
+  this->LocalGenerator = lg;
+
+  this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
+  if (!this->Target) {
+    // If no local target has been found, find it in the global scope.
+    this->Target =
+      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
+  }
+
+  return true;
+}
+
+std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation(
+  std::string const& config) const
+{
+  char const* config_name = config.c_str();
+  if (config.empty()) {
+    config_name = "noconfig";
+  }
+  return cmStrCat(this->Target->GetSupportDirectory(),
+                  "/install-cxx-module-bmi-", config_name, ".cmake");
+}
+
+std::string cmInstallCxxModuleBmiGenerator::GetDestination(
+  std::string const& config) const
+{
+  return cmGeneratorExpression::Evaluate(this->Destination,
+                                         this->LocalGenerator, config);
+}
+
+void cmInstallCxxModuleBmiGenerator::GenerateScriptForConfig(
+  std::ostream& os, const std::string& config, Indent indent)
+{
+  auto const& loc = this->GetScriptLocation(config);
+  if (loc.empty()) {
+    return;
+  }
+  os << indent << "include(\""
+     << cmOutputConverter::EscapeForCMake(
+          loc, cmOutputConverter::WrapQuotes::NoWrap)
+     << "\" OPTIONAL)\n";
+}
diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h
new file mode 100644 (file)
index 0000000..21edb2e
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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 <iosfwd>
+#include <string>
+#include <vector>
+
+#include "cmInstallGenerator.h"
+#include "cmScriptGenerator.h"
+
+class cmGeneratorTarget;
+class cmListFileBacktrace;
+class cmLocalGenerator;
+
+/** \class cmInstallCxxModuleBmiGenerator
+ * \brief Generate C++ module BMI installation rules.
+ */
+class cmInstallCxxModuleBmiGenerator : public cmInstallGenerator
+{
+public:
+  cmInstallCxxModuleBmiGenerator(
+    std::string target, std::string const& dest, std::string file_permissions,
+    std::vector<std::string> const& configurations,
+    std::string const& component, MessageLevel message, bool exclude_from_all,
+    bool optional, cmListFileBacktrace backtrace);
+  ~cmInstallCxxModuleBmiGenerator() override;
+
+  bool Compute(cmLocalGenerator* lg) override;
+
+  std::string const& GetFilePermissions() const
+  {
+    return this->FilePermissions;
+  }
+  std::string GetDestination(std::string const& config) const;
+  std::string GetScriptLocation(std::string const& config) const;
+  cmGeneratorTarget const* GetTarget() const { return this->Target; }
+  bool GetOptional() const { return this->Optional; }
+  MessageLevel GetMessageLevel() const { return this->Message; }
+
+protected:
+  void GenerateScriptForConfig(std::ostream& os, const std::string& config,
+                               Indent indent) override;
+
+  std::string const TargetName;
+  cmGeneratorTarget const* Target = nullptr;
+  cmLocalGenerator* LocalGenerator = nullptr;
+  std::string const FilePermissions;
+  bool const Optional;
+};
index b80437d..1d81b0b 100644 (file)
@@ -23,7 +23,8 @@ cmInstallExportGenerator::cmInstallExportGenerator(
   cmExportSet* exportSet, std::string const& destination,
   std::string file_permissions, std::vector<std::string> const& configurations,
   std::string const& component, MessageLevel message, bool exclude_from_all,
-  std::string filename, std::string name_space, bool exportOld, bool android,
+  std::string filename, std::string name_space,
+  std::string cxx_modules_directory, bool exportOld, bool android,
   cmListFileBacktrace backtrace)
   : cmInstallGenerator(destination, configurations, component, message,
                        exclude_from_all, false, std::move(backtrace))
@@ -31,6 +32,7 @@ cmInstallExportGenerator::cmInstallExportGenerator(
   , FilePermissions(std::move(file_permissions))
   , FileName(std::move(filename))
   , Namespace(std::move(name_space))
+  , CxxModulesDirectory(std::move(cxx_modules_directory))
   , ExportOld(exportOld)
 {
   if (android) {
@@ -141,6 +143,75 @@ void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
     os << indent << "endif()\n";
     files.clear();
   }
+
+  // Now create a configuration-specific install rule for the C++ module import
+  // property file of each configuration.
+  auto cxx_module_dest =
+    cmStrCat(this->Destination, '/', this->CxxModulesDirectory);
+  std::string config_file_example;
+  for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
+    config_file_example = i.second;
+    break;
+  }
+  if (!config_file_example.empty()) {
+    // Remove old per-configuration export files if the main changes.
+    std::string installedDir = cmStrCat(
+      "$ENV{DESTDIR}", ConvertToAbsoluteDestination(cxx_module_dest), '/');
+    std::string installedFile = cmStrCat(installedDir, "/cxx-modules.cmake");
+    std::string toInstallFile =
+      cmStrCat(cmSystemTools::GetFilenamePath(config_file_example),
+               "/cxx-modules.cmake");
+    os << indent << "if(EXISTS \"" << installedFile << "\")\n";
+    Indent indentN = indent.Next();
+    Indent indentNN = indentN.Next();
+    Indent indentNNN = indentNN.Next();
+    /* clang-format off */
+    os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
+       << indentN << "     \"" << installedFile << "\"\n"
+       << indentN << "     \"" << toInstallFile << "\")\n";
+    os << indentN << "if(_cmake_export_file_changed)\n";
+    os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
+       << this->EFGen->GetConfigImportFileGlob() << "\")\n";
+    os << indentNN << "if(_cmake_old_config_files)\n";
+    os << indentNNN << "string(REPLACE \";\" \", \" _cmake_old_config_files_text \"${_cmake_old_config_files}\")\n";
+    os << indentNNN << R"(message(STATUS "Old C++ module export file \")" << installedFile
+       << "\\\" will be replaced.  Removing files [${_cmake_old_config_files_text}].\")\n";
+    os << indentNNN << "unset(_cmake_old_config_files_text)\n";
+    os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
+    os << indentNN << "endif()\n";
+    os << indentNN << "unset(_cmake_old_config_files)\n";
+    os << indentN << "endif()\n";
+    os << indentN << "unset(_cmake_export_file_changed)\n";
+    os << indent << "endif()\n";
+    /* clang-format on */
+
+    // All of these files are siblings; get its location to know where the
+    // "anchor" file is.
+    files.push_back(toInstallFile);
+    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files,
+                         false, this->FilePermissions.c_str(), nullptr,
+                         nullptr, nullptr, indent);
+    files.clear();
+  }
+  for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
+    files.push_back(i.second);
+    std::string config_test = this->CreateConfigTest(i.first);
+    os << indent << "if(" << config_test << ")\n";
+    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, files,
+                         false, this->FilePermissions.c_str(), nullptr,
+                         nullptr, nullptr, indent.Next());
+    os << indent << "endif()\n";
+    files.clear();
+  }
+  for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) {
+    std::string config_test = this->CreateConfigTest(i.first);
+    os << indent << "if(" << config_test << ")\n";
+    this->AddInstallRule(os, cxx_module_dest, cmInstallType_FILES, i.second,
+                         false, this->FilePermissions.c_str(), nullptr,
+                         nullptr, nullptr, indent.Next());
+    os << indent << "endif()\n";
+    files.clear();
+  }
 }
 
 void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os,
index 02fe1fa..346ca67 100644 (file)
@@ -28,7 +28,8 @@ public:
                            const std::vector<std::string>& configurations,
                            std::string const& component, MessageLevel message,
                            bool exclude_from_all, std::string filename,
-                           std::string name_space, bool exportOld,
+                           std::string name_space,
+                           std::string cxx_modules_directory, bool exportOld,
                            bool android, cmListFileBacktrace backtrace);
   cmInstallExportGenerator(const cmInstallExportGenerator&) = delete;
   ~cmInstallExportGenerator() override;
@@ -50,6 +51,10 @@ public:
   std::string GetDestinationFile() const;
   std::string GetFileName() const { return this->FileName; }
   std::string GetTempDir() const;
+  std::string GetCxxModuleDirectory() const
+  {
+    return this->CxxModulesDirectory;
+  }
 
 protected:
   void GenerateScript(std::ostream& os) override;
@@ -64,6 +69,7 @@ protected:
   std::string const FilePermissions;
   std::string const FileName;
   std::string const Namespace;
+  std::string const CxxModulesDirectory;
   bool const ExportOld;
   cmLocalGenerator* LocalGenerator = nullptr;
 
index 43f1b8e..b06dc3d 100644 (file)
@@ -57,7 +57,6 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
   // For this we only consider targets
   using ItemVector = cmComputeLinkInformation::ItemVector;
   ItemVector const& items = cli.GetItems();
-  std::string config = cli.GetConfig();
   return std::any_of(
     items.begin(), items.end(),
     [](cmComputeLinkInformation::Item const& item) -> bool {
@@ -69,6 +68,26 @@ bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
     });
 }
 
+bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag(
+  cmComputeLinkInformation& cli)
+{
+  // Determine if this item might requires device linking.
+  // For this we only consider targets
+  using ItemVector = cmComputeLinkInformation::ItemVector;
+  ItemVector const& items = cli.GetItems();
+  std::string config = cli.GetConfig();
+  return std::any_of(
+    items.begin(), items.end(),
+    [config](cmComputeLinkInformation::Item const& item) -> bool {
+      return item.Target &&
+        item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
+        // this dependency requires us to device link it
+        !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
+        item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") &&
+        item.Target->IsIPOEnabled("CUDA", config);
+    });
+}
+
 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
   cmComputeLinkInformation& cli, std::string const& stdLibString,
   std::vector<BT<std::string>>& linkLibraries)
index dee625b..0916307 100644 (file)
@@ -30,6 +30,7 @@ public:
     delete;
 
   bool ComputeRequiresDeviceLinking(cmComputeLinkInformation& cli);
+  bool ComputeRequiresDeviceLinkingIPOFlag(cmComputeLinkInformation& cli);
 
   void ComputeLinkLibraries(
     cmComputeLinkInformation& cli, std::string const& stdLibString,
index 91157cb..6270c82 100644 (file)
@@ -347,6 +347,7 @@ enum class NestingStateEnum
   Foreach,
   Function,
   Macro,
+  Block
 };
 
 struct NestingState
@@ -434,6 +435,16 @@ cm::optional<cmListFileContext> cmListFileParser::CheckNesting() const
         return cmListFileContext::FromListFileFunction(func, this->FileName);
       }
       stack.pop_back();
+    } else if (name == "block") {
+      stack.push_back({
+        NestingStateEnum::Block,
+        cmListFileContext::FromListFileFunction(func, this->FileName),
+      });
+    } else if (name == "endblock") {
+      if (!TopIs(stack, NestingStateEnum::Block)) {
+        return cmListFileContext::FromListFileFunction(func, this->FileName);
+      }
+      stack.pop_back();
     }
   }
 
index 99bd05f..b2b724a 100644 (file)
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
@@ -36,6 +37,7 @@
 #include "cmInstallScriptGenerator.h"
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
+#include "cmLinkLineDeviceComputer.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
@@ -1385,7 +1387,7 @@ std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags(
 }
 
 void cmLocalGenerator::GetDeviceLinkFlags(
-  cmLinkLineComputer& linkLineComputer, const std::string& config,
+  cmLinkLineDeviceComputer& linkLineComputer, const std::string& config,
   std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath,
   std::string& linkPath, cmGeneratorTarget* target)
 {
@@ -1393,6 +1395,18 @@ void cmLocalGenerator::GetDeviceLinkFlags(
 
   cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
 
+  auto linklang = linkLineComputer.GetLinkerLanguage(target, config);
+  auto ipoEnabled = target->IsIPOEnabled(linklang, config);
+  if (!ipoEnabled) {
+    ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli);
+  }
+  if (ipoEnabled) {
+    if (cmValue cudaIPOFlags = this->Makefile->GetDefinition(
+          "CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO")) {
+      linkFlags += cudaIPOFlags;
+    }
+  }
+
   if (pcli) {
     // Compute the required device link libraries when
     // resolving gpu lang device symbols
@@ -1400,6 +1414,8 @@ void cmLocalGenerator::GetDeviceLinkFlags(
                               linkPath);
   }
 
+  // iterate link deps and see if any of them need IPO
+
   std::vector<std::string> linkOpts;
   target->GetLinkOptions(linkOpts, config, "CUDA");
   // LINK_OPTIONS are escaped.
@@ -1594,7 +1610,8 @@ std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
   cmMakefile* mf = this->GetMakefile();
 
   // Add language-specific flags.
-  this->AddLanguageFlags(compileFlags, target, lang, config);
+  this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang,
+                         config);
 
   if (target->IsIPOEnabled(lang, config)) {
     this->AppendFeatureOptions(compileFlags, lang, "IPO");
@@ -1907,6 +1924,7 @@ void cmLocalGenerator::AddArchitectureFlags(std::string& flags,
 
 void cmLocalGenerator::AddLanguageFlags(std::string& flags,
                                         cmGeneratorTarget const* target,
+                                        cmBuildStep compileOrLink,
                                         const std::string& lang,
                                         const std::string& config)
 {
@@ -1930,7 +1948,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
       }
     }
   } else if (lang == "CUDA") {
-    target->AddCUDAArchitectureFlags(flags);
+    target->AddCUDAArchitectureFlags(compileOrLink, config, flags);
     target->AddCUDAToolkitFlags(flags);
   } else if (lang == "ISPC") {
     target->AddISPCTargetFlags(flags);
@@ -2027,6 +2045,31 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
       }
     }
   }
+
+  // Add MSVC debug information format flags if CMP0141 is NEW.
+  if (cm::optional<std::string> msvcDebugInformationFormat =
+        this->GetMSVCDebugFormatName(config, target)) {
+    if (!msvcDebugInformationFormat->empty()) {
+      if (cmValue msvcDebugInformationFormatOptions =
+            this->Makefile->GetDefinition(
+              cmStrCat("CMAKE_", lang,
+                       "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_",
+                       *msvcDebugInformationFormat))) {
+        this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions);
+      } else if ((this->Makefile->GetSafeDefinition(
+                    cmStrCat("CMAKE_", lang, "_COMPILER_ID")) == "MSVC"_s ||
+                  this->Makefile->GetSafeDefinition(
+                    cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) == "MSVC"_s) &&
+                 !cmSystemTools::GetErrorOccurredFlag()) {
+        // The compiler uses the MSVC ABI so it needs a known runtime library.
+        this->IssueMessage(MessageType::FATAL_ERROR,
+                           cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '",
+                                    *msvcDebugInformationFormat,
+                                    "' not known for this ", lang,
+                                    " compiler."));
+      }
+    }
+  }
 }
 
 void cmLocalGenerator::AddLanguageFlagsForLinking(
@@ -2042,7 +2085,7 @@ void cmLocalGenerator::AddLanguageFlagsForLinking(
     this->AddCompilerRequirementFlag(flags, target, lang, config);
   }
 
-  this->AddLanguageFlags(flags, target, lang, config);
+  this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config);
 
   if (target->IsIPOEnabled(lang, config)) {
     this->AppendFeatureOptions(flags, lang, "IPO");
@@ -2578,7 +2621,9 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
         if (pchSource.empty() || pchHeader.empty()) {
           if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) {
             for (auto* sf : sources) {
-              if (pchLangSet.find(sf->GetLanguage()) == pchLangSet.end()) {
+              const auto sourceLanguage = sf->GetLanguage();
+              if (!sourceLanguage.empty() &&
+                  pchLangSet.find(sourceLanguage) == pchLangSet.end()) {
                 sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON");
               }
             }
@@ -2630,13 +2675,24 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
                   this->Makefile->GetSafeDefinition(
                     cmStrCat("CMAKE_", lang, "_FLAGS_", configUpper));
 
-                bool editAndContinueDebugInfo =
-                  langFlags.find("/ZI") != std::string::npos ||
-                  langFlags.find("-ZI") != std::string::npos;
-
-                bool enableDebuggingInformation =
-                  langFlags.find("/Zi") != std::string::npos ||
-                  langFlags.find("-Zi") != std::string::npos;
+                bool editAndContinueDebugInfo = false;
+                bool programDatabaseDebugInfo = false;
+                cm::optional<std::string> msvcDebugInformationFormat =
+                  this->GetMSVCDebugFormatName(config, target);
+                if (msvcDebugInformationFormat &&
+                    !msvcDebugInformationFormat->empty()) {
+                  editAndContinueDebugInfo =
+                    *msvcDebugInformationFormat == "EditAndContinue";
+                  programDatabaseDebugInfo =
+                    *msvcDebugInformationFormat == "ProgramDatabase";
+                } else {
+                  editAndContinueDebugInfo =
+                    langFlags.find("/ZI") != std::string::npos ||
+                    langFlags.find("-ZI") != std::string::npos;
+                  programDatabaseDebugInfo =
+                    langFlags.find("/Zi") != std::string::npos ||
+                    langFlags.find("-Zi") != std::string::npos;
+                }
 
                 // MSVC 2008 is producing both .pdb and .idb files with /Zi.
                 bool msvc2008OrLess =
@@ -2652,7 +2708,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
                 if (editAndContinueDebugInfo || msvc2008OrLess) {
                   this->CopyPchCompilePdb(config, target, *ReuseFrom,
                                           reuseTarget, { ".pdb", ".idb" });
-                } else if (enableDebuggingInformation) {
+                } else if (programDatabaseDebugInfo) {
                   this->CopyPchCompilePdb(config, target, *ReuseFrom,
                                           reuseTarget, { ".pdb" });
                 }
@@ -2671,7 +2727,7 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
                   cmStrCat(linkerProperty, configUpper),
                   cmStrCat(" ",
                            this->ConvertToOutputFormat(pchSourceObj, SHELL)),
-                  true);
+                  cm::nullopt, true);
               } else if (reuseTarget->GetType() ==
                          cmStateEnums::OBJECT_LIBRARY) {
                 // FIXME: This can propagate more than one level, unlike
@@ -2816,6 +2872,26 @@ void cmLocalGenerator::CopyPchCompilePdb(
                               target_compile_pdb_dir);
 }
 
+cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName(
+  std::string const& config, cmGeneratorTarget const* target)
+{
+  // MSVC debug information format selection is activated by the presence
+  // of a default whether or not it is overridden by a property.
+  cm::optional<std::string> msvcDebugInformationFormat;
+  cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition(
+    "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT");
+  if (cmNonempty(msvcDebugInformationFormatDefault)) {
+    cmValue msvcDebugInformationFormatValue =
+      target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT");
+    if (!msvcDebugInformationFormatValue) {
+      msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault;
+    }
+    msvcDebugInformationFormat = cmGeneratorExpression::Evaluate(
+      *msvcDebugInformationFormatValue, this, config, target);
+  }
+  return msvcDebugInformationFormat;
+}
+
 namespace {
 
 inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf,
index 7cae1fc..765441c 100644 (file)
@@ -35,6 +35,7 @@ class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmImplicitDependsList;
 class cmLinkLineComputer;
+class cmLinkLineDeviceComputer;
 class cmMakefile;
 class cmRulePlaceholderExpander;
 class cmSourceFile;
@@ -59,6 +60,13 @@ enum class cmDependencyScannerKind
   Compiler
 };
 
+/** What to compute language flags for */
+enum class cmBuildStep
+{
+  Compile,
+  Link
+};
+
 /** Target and source file which have a specific output.  */
 struct cmSourcesWithOutput
 {
@@ -143,7 +151,8 @@ public:
                             const std::string& filterArch = std::string());
 
   void AddLanguageFlags(std::string& flags, cmGeneratorTarget const* target,
-                        const std::string& lang, const std::string& config);
+                        cmBuildStep compileOrLink, const std::string& lang,
+                        const std::string& config);
   void AddLanguageFlagsForLinking(std::string& flags,
                                   cmGeneratorTarget const* target,
                                   const std::string& lang,
@@ -476,7 +485,7 @@ public:
 
   /** Fill out these strings for the given target.  Libraries to link,
    *  flags, and linkflags. */
-  void GetDeviceLinkFlags(cmLinkLineComputer& linkLineComputer,
+  void GetDeviceLinkFlags(cmLinkLineDeviceComputer& linkLineComputer,
                           const std::string& config, std::string& linkLibs,
                           std::string& linkFlags, std::string& frameworkPath,
                           std::string& linkPath, cmGeneratorTarget* target);
@@ -637,6 +646,10 @@ private:
                          cmGeneratorTarget* reuseTarget,
                          std::vector<std::string> const& extensions);
 
+  // Returns MSVC_DEBUG_INFORMATION_FORMAT value if CMP0141 is NEW.
+  cm::optional<std::string> GetMSVCDebugFormatName(
+    std::string const& config, cmGeneratorTarget const* target);
+
   struct UnityBatchedSource
   {
     cmSourceFile* Source = nullptr;
index 106f76b..c11f5b4 100644 (file)
@@ -88,27 +88,11 @@ void cmLocalNinjaGenerator::Generate()
       cmGlobalNinjaGenerator::WriteComment(this->GetRulesFileStream(),
                                            "localized /showIncludes string");
       this->GetRulesFileStream() << "msvc_deps_prefix = ";
-#ifdef _WIN32
-      // Ninja uses the ANSI Windows APIs, so strings in the rules file
-      // typically need to be ANSI encoded. However, in this case the compiler
-      // is being invoked using the UTF-8 codepage so the /showIncludes prefix
-      // will be UTF-8 encoded on stdout. Ninja can't successfully compare this
-      // UTF-8 encoded prefix to the ANSI encoded msvc_deps_prefix if it
-      // contains any non-ASCII characters and dependency checking will fail.
-      // As a workaround, leave the msvc_deps_prefix UTF-8 encoded even though
-      // the rest of the file is ANSI encoded.
-      if (GetConsoleOutputCP() == CP_UTF8 && GetACP() != CP_UTF8 &&
-          this->GetGlobalGenerator()->GetMakefileEncoding() != codecvt::None) {
-        this->GetRulesFileStream().WriteRaw(showIncludesPrefix);
-      } else {
-        // Ninja 1.11 and above uses the UTF-8 code page if it's supported, so
-        // in that case we can write it normally without using raw bytes.
-        this->GetRulesFileStream() << showIncludesPrefix;
-      }
-#else
-      // It's safe to use the standard encoding on other platforms.
-      this->GetRulesFileStream() << showIncludesPrefix;
-#endif
+      // 'cl /showIncludes' encodes output in the console output code page.
+      // It may differ from the encoding used for file paths in 'build.ninja'.
+      // Ninja matches the showIncludes prefix using its raw byte sequence.
+      this->GetRulesFileStream().WriteAltEncoding(
+        showIncludesPrefix, cmGeneratedFileStream::Encoding::ConsoleOutput);
       this->GetRulesFileStream() << "\n\n";
     }
   }
index e125470..de1d3cd 100644 (file)
@@ -1412,13 +1412,16 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
 
     if (needRescanDependInfo || needRescanDirInfo || needRescanDependencies) {
       // The dependencies must be regenerated.
-      std::string targetName = cmSystemTools::GetFilenameName(targetDir);
-      targetName = targetName.substr(0, targetName.length() - 4);
-      std::string message =
-        cmStrCat("Scanning dependencies of target ", targetName);
-      cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta |
-                                         cmsysTerminal_Color_ForegroundBold,
-                                       message.c_str(), true, color);
+      if (verbose) {
+        std::string targetName = cmSystemTools::GetFilenameName(targetDir);
+        targetName = targetName.substr(0, targetName.length() - 4);
+        std::string message =
+          cmStrCat("Scanning dependencies of target ", targetName);
+        cmSystemTools::MakefileColorEcho(
+          cmsysTerminal_Color_ForegroundMagenta |
+            cmsysTerminal_Color_ForegroundBold,
+          message.c_str(), true, color);
+      }
 
       status = this->ScanDependencies(targetDir, dependFile,
                                       internalDependFile, validDependencies);
@@ -1447,13 +1450,19 @@ bool cmLocalUnixMakefileGenerator3::UpdateDependencies(
                                         this->GetBinaryDirectory())
                       : std::function<bool(const std::string&)>())) {
       // regenerate dependencies files
-      std::string targetName =
-        cmCMakePath(targetDir).GetFileName().RemoveExtension().GenericString();
-      auto message = cmStrCat(
-        "Consolidate compiler generated dependencies of target ", targetName);
-      cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundMagenta |
-                                         cmsysTerminal_Color_ForegroundBold,
-                                       message.c_str(), true, color);
+      if (verbose) {
+        std::string targetName = cmCMakePath(targetDir)
+                                   .GetFileName()
+                                   .RemoveExtension()
+                                   .GenericString();
+        auto message =
+          cmStrCat("Consolidate compiler generated dependencies of target ",
+                   targetName);
+        cmSystemTools::MakefileColorEcho(
+          cmsysTerminal_Color_ForegroundMagenta |
+            cmsysTerminal_Color_ForegroundBold,
+          message.c_str(), true, color);
+      }
 
       // Open the make depends file.  This should be copy-if-different
       // because the make tool may try to reload it needlessly otherwise.
index 75fe262..7bfe3b7 100644 (file)
@@ -15,8 +15,8 @@ class cmMakefile;
 /** \class cmLocalVisualStudio10Generator
  * \brief Write Visual Studio 10 project files.
  *
- * cmLocalVisualStudio10Generator produces a Visual Studio 10 project
- * file for each target in its directory.
+ * cmLocalVisualStudio10Generator produces a MSBuild project file for each
+ * target in its directory.
  */
 class cmLocalVisualStudio10Generator : public cmLocalVisualStudio7Generator
 {
index f65add1..af2d31d 100644 (file)
@@ -195,10 +195,10 @@ void cmLocalVisualStudio7Generator::GenerateTarget(cmGeneratorTarget* target)
   this->FortranProject = gg->TargetIsFortranOnly(target);
   this->WindowsCEProject = gg->TargetsWindowsCE();
 
-  // Intel Fortran for VS10 uses VS9 format ".vfproj" files.
+  // Intel Fortran always uses VS9 format ".vfproj" files.
   cmGlobalVisualStudioGenerator::VSVersion realVersion = gg->GetVersion();
   if (this->FortranProject &&
-      gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+      gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS11) {
     gg->SetVersion(cmGlobalVisualStudioGenerator::VSVersion::VS9);
   }
 
@@ -680,7 +680,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration(
     langForClCompile = linkLanguage;
     if (langForClCompile == "C" || langForClCompile == "CXX" ||
         langForClCompile == "Fortran") {
-      this->AddLanguageFlags(flags, target, langForClCompile, configName);
+      this->AddLanguageFlags(flags, target, cmBuildStep::Compile,
+                             langForClCompile, configName);
     }
     // set the correct language
     if (linkLanguage == "C") {
index ef12487..47ad749 100644 (file)
@@ -126,7 +126,7 @@ bool cmMacroHelperCommand::operator()(
       return false;
     }
     if (status.GetReturnInvoked()) {
-      inStatus.SetReturnInvoked();
+      inStatus.SetReturnInvoked(status.GetReturnVariables());
       return true;
     }
     if (status.GetBreakInvoked()) {
index 469eac3..6e0d704 100644 (file)
@@ -149,6 +149,29 @@ void cmMakefile::IssueMessage(MessageType t, std::string const& text) const
   this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace);
 }
 
+Message::LogLevel cmMakefile::GetCurrentLogLevel() const
+{
+  const cmake* cmakeInstance = this->GetCMakeInstance();
+
+  const Message::LogLevel logLevelCliOrDefault = cmakeInstance->GetLogLevel();
+  assert("Expected a valid log level here" &&
+         logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED);
+
+  Message::LogLevel result = logLevelCliOrDefault;
+
+  // If the log-level was set via the command line option, it takes precedence
+  // over the CMAKE_MESSAGE_LOG_LEVEL variable.
+  if (!cmakeInstance->WasLogLevelSetViaCLI()) {
+    const Message::LogLevel logLevelFromVar = cmake::StringToLogLevel(
+      this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL"));
+    if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) {
+      result = logLevelFromVar;
+    }
+  }
+
+  return result;
+}
+
 bool cmMakefile::CheckCMP0037(std::string const& targetName,
                               cmStateEnums::TargetType targetType) const
 {
@@ -766,6 +789,7 @@ void cmMakefile::RunListFile(cmListFile const& listFile,
       break;
     }
     if (status.GetReturnInvoked()) {
+      this->RaiseScope(status.GetReturnVariables());
       // Exit early due to return command.
       break;
     }
@@ -1759,7 +1783,8 @@ void cmMakefile::ConfigureSubDirectory(cmMakefile* mf)
 
 void cmMakefile::AddSubDirectory(const std::string& srcPath,
                                  const std::string& binPath,
-                                 bool excludeFromAll, bool immediate)
+                                 bool excludeFromAll, bool immediate,
+                                 bool system)
 {
   if (this->DeferRunning) {
     this->IssueMessage(
@@ -1789,6 +1814,9 @@ void cmMakefile::AddSubDirectory(const std::string& srcPath,
   if (excludeFromAll) {
     subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE");
   }
+  if (system) {
+    subMf->SetProperty("SYSTEM", "TRUE");
+  }
 
   if (immediate) {
     this->ConfigureSubDirectory(subMf);
@@ -3456,7 +3484,7 @@ void cmMakefile::AddTargetObject(std::string const& tgtName,
 #endif
 }
 
-void cmMakefile::EnableLanguage(std::vector<std::string> const& lang,
+void cmMakefile::EnableLanguage(std::vector<std::string> const& languages,
                                 bool optional)
 {
   if (this->DeferRunning) {
@@ -3468,24 +3496,48 @@ void cmMakefile::EnableLanguage(std::vector<std::string> const& lang,
   if (const char* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) {
     this->AddDefinition("CMAKE_CFG_INTDIR", def);
   }
+
+  std::vector<std::string> unique_languages;
+  {
+    std::vector<std::string> duplicate_languages;
+    for (std::string const& language : languages) {
+      if (!cm::contains(unique_languages, language)) {
+        unique_languages.push_back(language);
+      } else if (!cm::contains(duplicate_languages, language)) {
+        duplicate_languages.push_back(language);
+      }
+    }
+    if (!duplicate_languages.empty()) {
+      auto quantity = duplicate_languages.size() == 1 ? std::string(" has")
+                                                      : std::string("s have");
+      this->IssueMessage(MessageType::AUTHOR_WARNING,
+                         "Languages to be enabled may not be specified more "
+                         "than once at the same time. The following language" +
+                           quantity + " been specified multiple times: " +
+                           cmJoin(duplicate_languages, ", "));
+    }
+  }
+
   // If RC is explicitly listed we need to do it after other languages.
   // On some platforms we enable RC implicitly while enabling others.
   // Do not let that look like recursive enable_language(RC).
-  std::vector<std::string> langs;
-  std::vector<std::string> langsRC;
-  langs.reserve(lang.size());
-  for (std::string const& i : lang) {
-    if (i == "RC") {
-      langsRC.push_back(i);
+  std::vector<std::string> languages_without_RC;
+  std::vector<std::string> languages_for_RC;
+  languages_without_RC.reserve(unique_languages.size());
+  for (std::string const& language : unique_languages) {
+    if (language == "RC") {
+      languages_for_RC.push_back(language);
     } else {
-      langs.push_back(i);
+      languages_without_RC.push_back(language);
     }
   }
-  if (!langs.empty()) {
-    this->GetGlobalGenerator()->EnableLanguage(langs, this, optional);
+  if (!languages_without_RC.empty()) {
+    this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this,
+                                               optional);
   }
-  if (!langsRC.empty()) {
-    this->GetGlobalGenerator()->EnableLanguage(langsRC, this, optional);
+  if (!languages_for_RC.empty()) {
+    this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this,
+                                               optional);
   }
 }
 
@@ -4116,6 +4168,18 @@ void cmMakefile::RaiseScope(const std::string& var, const char* varDef)
 #endif
 }
 
+void cmMakefile::RaiseScope(const std::vector<std::string>& variables)
+{
+  for (auto const& varName : variables) {
+    if (this->IsNormalDefinitionSet(varName)) {
+      this->RaiseScope(varName, this->GetDefinition(varName));
+    } else {
+      // unset variable in parent scope
+      this->RaiseScope(varName, nullptr);
+    }
+  }
+}
+
 cmTarget* cmMakefile::AddImportedTarget(const std::string& name,
                                         cmStateEnums::TargetType type,
                                         bool global)
@@ -4406,7 +4470,7 @@ bool cmMakefile::SetPolicy(cmPolicies::PolicyID id,
   }
 
   // Deprecate old policies.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0097 &&
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0102 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
@@ -4471,6 +4535,19 @@ bool cmMakefile::SetPolicyVersion(std::string const& version_min,
                                         cmPolicies::WarnCompat::On);
 }
 
+cmMakefile::VariablePushPop::VariablePushPop(cmMakefile* m)
+  : Makefile(m)
+{
+  this->Makefile->StateSnapshot =
+    this->Makefile->GetState()->CreateVariableScopeSnapshot(
+      this->Makefile->StateSnapshot);
+}
+
+cmMakefile::VariablePushPop::~VariablePushPop()
+{
+  this->Makefile->PopSnapshot();
+}
+
 bool cmMakefile::HasCMP0054AlreadyBeenReported(
   cmListFileContext const& context) const
 {
index 27838b2..3866aca 100644 (file)
@@ -270,7 +270,7 @@ public:
    */
   void AddSubDirectory(const std::string& fullSrcDir,
                        const std::string& fullBinDir, bool excludeFromAll,
-                       bool immediate);
+                       bool immediate, bool system);
 
   void Configure();
 
@@ -376,6 +376,20 @@ public:
   };
   friend class PolicyPushPop;
 
+  /** Helper class to push and pop variables scopes automatically. */
+  class VariablePushPop
+  {
+  public:
+    VariablePushPop(cmMakefile* m);
+    ~VariablePushPop();
+
+    VariablePushPop(VariablePushPop const&) = delete;
+    VariablePushPop& operator=(VariablePushPop const&) = delete;
+
+  private:
+    cmMakefile* Makefile;
+  };
+
   /**
    * Determine if the given context, name pair has already been reported
    * in context of CMP0054.
@@ -861,6 +875,11 @@ public:
   void PushScope();
   void PopScope();
   void RaiseScope(const std::string& var, const char* value);
+  void RaiseScope(const std::string& var, cmValue value)
+  {
+    this->RaiseScope(var, value.GetCStr());
+  }
+  void RaiseScope(const std::vector<std::string>& variables);
 
   // push and pop loop scopes
   void PushLoopBlockBarrier();
@@ -924,6 +943,7 @@ public:
   };
 
   void IssueMessage(MessageType t, std::string const& text) const;
+  Message::LogLevel GetCurrentLogLevel() const;
 
   /** Set whether or not to report a CMP0000 violation.  */
   void SetCheckCMP0000(bool b) { this->CheckCMP0000 = b; }
index 3849c6f..54f03b9 100644 (file)
@@ -136,17 +136,11 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule(
   std::vector<std::string> depends;
   this->AppendLinkDepends(depends, linkLanguage);
 
-  // Build a list of compiler flags and linker flags.
-  std::string langFlags;
-  std::string linkFlags;
-
   // Add language feature flags.
+  std::string langFlags;
   this->LocalGenerator->AddLanguageFlagsForLinking(
     langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
 
-  // Add device-specific linker flags.
-  this->GetDeviceLinkFlags(linkFlags, linkLanguage);
-
   // Construct a list of files associated with this executable that
   // may need to be cleaned.
   std::vector<std::string> exeCleanFiles;
@@ -173,23 +167,32 @@ void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule(
     // Set path conversion for link script shells.
     this->LocalGenerator->SetLinkScriptShell(useLinkScript);
 
-    std::unique_ptr<cmLinkLineComputer> linkLineComputer(
+    std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer(
       new cmLinkLineDeviceComputer(
         this->LocalGenerator,
         this->LocalGenerator->GetStateSnapshot().GetDirectory()));
     linkLineComputer->SetForResponse(useResponseFileForLibs);
     linkLineComputer->SetRelink(relink);
 
+    // Create set of linking flags.
+    std::string linkFlags;
+    std::string ignored_;
+    this->LocalGenerator->GetDeviceLinkFlags(
+      *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_,
+      ignored_, this->GeneratorTarget);
+
     // Collect up flags to link in needed libraries.
     std::string linkLibs;
-    this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
-                         useResponseFileForLibs, depends);
+    this->CreateLinkLibs(
+      linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends,
+      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);
+    this->CreateObjectLists(
+      useLinkScript, false, useResponseFileForObjects, buildObjs, depends,
+      false, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
 
     cmRulePlaceholderExpander::RuleVariables vars;
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
index f30ec27..45ef8c8 100644 (file)
@@ -287,10 +287,6 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules(
   this->LocalGenerator->AddLanguageFlagsForLinking(
     langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
 
-  // Create set of linking flags.
-  std::string linkFlags;
-  this->GetDeviceLinkFlags(linkFlags, linkLanguage);
-
   // Clean files associated with this library.
   std::set<std::string> libCleanFiles;
   libCleanFiles.insert(
@@ -315,22 +311,31 @@ void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules(
 
     // Collect up flags to link in needed libraries.
     std::string linkLibs;
-    std::unique_ptr<cmLinkLineComputer> linkLineComputer(
+    std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer(
       new cmLinkLineDeviceComputer(
         this->LocalGenerator,
         this->LocalGenerator->GetStateSnapshot().GetDirectory()));
     linkLineComputer->SetForResponse(useResponseFileForLibs);
     linkLineComputer->SetRelink(relink);
 
-    this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
-                         useResponseFileForLibs, depends);
+    // Create set of linking flags.
+    std::string linkFlags;
+    std::string ignored_;
+    this->LocalGenerator->GetDeviceLinkFlags(
+      *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_,
+      ignored_, this->GeneratorTarget);
+
+    this->CreateLinkLibs(
+      linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends,
+      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);
+    this->CreateObjectLists(
+      useLinkScript, false, // useArchiveRules
+      useResponseFileForObjects, buildObjs, depends, false,
+      cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
 
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
     objectDir = this->LocalGenerator->ConvertToOutputFormat(
index aec6577..d19bbb9 100644 (file)
@@ -21,6 +21,7 @@
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -46,6 +47,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmTarget.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -107,7 +109,7 @@ std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New(
   return result;
 }
 
-std::string cmMakefileTargetGenerator::GetConfigName()
+std::string cmMakefileTargetGenerator::GetConfigName() const
 {
   auto const& configNames = this->LocalGenerator->GetConfigNames();
   assert(configNames.size() == 1);
@@ -190,6 +192,16 @@ void cmMakefileTargetGenerator::CreateRuleFile()
 
 void cmMakefileTargetGenerator::WriteTargetBuildRules()
 {
+  this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName());
+
+  if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", this->GeneratorTarget->GetName(),
+               "\" target contains C++ module sources which are not supported "
+               "by the generator"));
+  }
+
   // -- Write the custom commands for this target
 
   // Evaluates generator expressions and expands prop_value
@@ -302,6 +314,40 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
     }
   }
 
+  std::map<std::string, std::string> file_set_map;
+
+  auto const* tgt = this->GeneratorTarget->Target;
+  for (auto const& name : tgt->GetAllFileSetNames()) {
+    auto const* file_set = tgt->GetFileSet(name);
+    if (!file_set) {
+      this->Makefile->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", name,
+                 "\", but it was not found."));
+      continue;
+    }
+
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->LocalGenerator, this->GetConfigName(),
+      this->GeneratorTarget);
+
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files, entry,
+                                  this->LocalGenerator, this->GetConfigName(),
+                                  this->GeneratorTarget);
+    }
+
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        file_set_map[filename] = file_set->GetType();
+      }
+    }
+  }
+
   std::vector<cmSourceFile const*> objectSources;
   this->GeneratorTarget->GetObjectSources(objectSources,
                                           this->GetConfigName());
@@ -314,6 +360,25 @@ void cmMakefileTargetGenerator::WriteTargetBuildRules()
       this->WriteObjectRuleFiles(*sf);
     }
   }
+
+  for (cmSourceFile const* sf : objectSources) {
+    auto const& path = sf->GetFullPath();
+    auto const it = file_set_map.find(path);
+    if (it != file_set_map.end()) {
+      auto const& file_set_type = it->second;
+      if (file_set_type == "CXX_MODULES"_s ||
+          file_set_type == "CXX_MODULE_HEADER_UNITS"_s) {
+        if (sf->GetLanguage() != "CXX"_s) {
+          this->Makefile->IssueMessage(
+            MessageType::FATAL_ERROR,
+            cmStrCat(
+              "Target \"", tgt->GetName(), "\" contains the source\n  ", path,
+              "\nin a file set of type \"", file_set_type,
+              R"(" but the source is not classified as a "CXX" source.)"));
+        }
+      }
+    }
+  }
 }
 
 void cmMakefileTargetGenerator::WriteCommonCodeRules()
@@ -977,8 +1042,10 @@ void cmMakefileTargetGenerator::WriteObjectRuleFiles(
          lang == "OBJCXX")) {
       std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
       cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
-      if (cmNonempty(clauncher)) {
-        compilerLauncher = *clauncher;
+      std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
+        *clauncher, this->LocalGenerator, config);
+      if (!evaluatedClauncher.empty()) {
+        compilerLauncher = evaluatedClauncher;
       }
     }
 
@@ -2080,7 +2147,7 @@ bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries(
 }
 
 std::string cmMakefileTargetGenerator::CreateResponseFile(
-  const char* name, std::string const& options,
+  const std::string& name, std::string const& options,
   std::vector<std::string>& makefile_depends)
 {
   // FIXME: Find a better way to determine the response file encoding,
@@ -2126,7 +2193,8 @@ cmMakefileTargetGenerator::CreateLinkLineComputer(
 
 void cmMakefileTargetGenerator::CreateLinkLibs(
   cmLinkLineComputer* linkLineComputer, std::string& linkLibs,
-  bool useResponseFile, std::vector<std::string>& makefile_depends)
+  bool useResponseFile, std::vector<std::string>& makefile_depends,
+  ResponseFlagFor responseMode)
 {
   std::string frameworkPath;
   std::string linkPath;
@@ -2139,20 +2207,13 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
   if (useResponseFile &&
       linkLibs.find_first_not_of(' ') != std::string::npos) {
     // Lookup the response file reference flag.
-    std::string responseFlagVar =
-      cmStrCat("CMAKE_",
-               this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
-               "_RESPONSE_FILE_LINK_FLAG");
-    std::string responseFlag;
-    if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
-      responseFlag = *p;
-    } else {
-      responseFlag = "@";
-    }
+    std::string responseFlag = this->GetResponseFlag(responseMode);
 
     // Create this response file.
+    std::string responseFileName =
+      (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp";
     std::string link_rsp =
-      this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends);
+      this->CreateResponseFile(responseFileName, linkLibs, makefile_depends);
 
     // Reference the response file.
     linkLibs = cmStrCat(responseFlag,
@@ -2164,7 +2225,7 @@ void cmMakefileTargetGenerator::CreateLinkLibs(
 void cmMakefileTargetGenerator::CreateObjectLists(
   bool useLinkScript, bool useArchiveRules, bool useResponseFile,
   std::string& buildObjs, std::vector<std::string>& makefile_depends,
-  bool useWatcomQuote)
+  bool useWatcomQuote, ResponseFlagFor responseMode)
 {
   std::string variableName;
   std::string variableNameExternal;
@@ -2179,27 +2240,19 @@ void cmMakefileTargetGenerator::CreateObjectLists(
     this->WriteObjectsStrings(object_strings, responseFileLimit);
 
     // Lookup the response file reference flag.
-    std::string responseFlagVar =
-      cmStrCat("CMAKE_",
-               this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
-               "_RESPONSE_FILE_LINK_FLAG");
-    std::string responseFlag;
-    if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
-      responseFlag = *p;
-    } else {
-      responseFlag = "@";
-    }
+    std::string responseFlag = this->GetResponseFlag(responseMode);
 
     // Write a response file for each string.
     const char* sep = "";
     for (unsigned int i = 0; i < object_strings.size(); ++i) {
       // Number the response files.
-      char rsp[32];
-      snprintf(rsp, sizeof(rsp), "objects%u.rsp", i + 1);
+      std::string responseFileName =
+        (responseMode == Link) ? "objects" : "deviceObjects";
+      responseFileName += std::to_string(i + 1);
 
       // Create this response file.
-      std::string objects_rsp =
-        this->CreateResponseFile(rsp, object_strings[i], makefile_depends);
+      std::string objects_rsp = this->CreateResponseFile(
+        responseFileName, object_strings[i], makefile_depends);
 
       // Separate from previous response file references.
       buildObjs += sep;
@@ -2251,7 +2304,7 @@ void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
     }
     std::string name = cmStrCat("includes_", lang, ".rsp");
     std::string arg = std::move(responseFlag) +
-      this->CreateResponseFile(name.c_str(), includeFlags,
+      this->CreateResponseFile(name, includeFlags,
                                this->FlagFileDepends[lang]);
     this->LocalGenerator->AppendFlags(flags, arg);
   } else {
@@ -2304,3 +2357,22 @@ void cmMakefileTargetGenerator::GenDefFile(
     fout << src->GetFullPath() << "\n";
   }
 }
+
+std::string cmMakefileTargetGenerator::GetResponseFlag(
+  ResponseFlagFor mode) const
+{
+  std::string responseFlag = "@";
+  std::string responseFlagVar;
+
+  auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
+  if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) {
+    responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG");
+  } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) {
+    responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG";
+  }
+
+  if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
+    responseFlag = *p;
+  }
+  return responseFlag;
+}
index cb804e0..dafa650 100644 (file)
@@ -56,7 +56,7 @@ public:
 
   cmGeneratorTarget* GetGeneratorTarget() { return this->GeneratorTarget; }
 
-  std::string GetConfigName();
+  std::string GetConfigName() const;
 
 protected:
   void GetDeviceLinkFlags(std::string& linkFlags,
@@ -157,22 +157,31 @@ protected:
   /** Create a response file with the given set of options.  Returns
       the relative path from the target build working directory to the
       response file name.  */
-  std::string CreateResponseFile(const char* name, std::string const& options,
+  std::string CreateResponseFile(const std::string& name,
+                                 std::string const& options,
                                  std::vector<std::string>& makefile_depends);
 
   bool CheckUseResponseFileForObjects(std::string const& l) const;
   bool CheckUseResponseFileForLibraries(std::string const& l) const;
 
+  enum ResponseFlagFor
+  {
+    Link,
+    DeviceLink
+  };
+
   /** Create list of flags for link libraries. */
   void CreateLinkLibs(cmLinkLineComputer* linkLineComputer,
                       std::string& linkLibs, bool useResponseFile,
-                      std::vector<std::string>& makefile_depends);
+                      std::vector<std::string>& makefile_depends,
+                      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,
+                         ResponseFlagFor responseMode = ResponseFlagFor::Link);
 
   /** Add commands for generate def files */
   void GenDefFile(std::vector<std::string>& real_link_commands);
@@ -180,6 +189,9 @@ protected:
   void AddIncludeFlags(std::string& flags, const std::string& lang,
                        const std::string& config) override;
 
+  /** Return the response flag for the given configuration */
+  std::string GetResponseFlag(ResponseFlagFor mode) const;
+
   virtual void CloseFileStreams();
   cmLocalUnixMakefileGenerator3* LocalGenerator;
   cmGlobalUnixMakefileGenerator3* GlobalGenerator;
index cd57600..fa29ec9 100644 (file)
@@ -81,94 +81,83 @@ bool cmMessageCommand(std::vector<std::string> const& args,
 
   auto type = MessageType::MESSAGE;
   auto fatal = false;
-  auto level = cmake::LogLevel::LOG_UNDEFINED;
+  auto level = Message::LogLevel::LOG_UNDEFINED;
   auto checkingType = CheckingType::UNDEFINED;
   if (*i == "SEND_ERROR") {
     type = MessageType::FATAL_ERROR;
-    level = cmake::LogLevel::LOG_ERROR;
+    level = Message::LogLevel::LOG_ERROR;
     ++i;
   } else if (*i == "FATAL_ERROR") {
     fatal = true;
     type = MessageType::FATAL_ERROR;
-    level = cmake::LogLevel::LOG_ERROR;
+    level = Message::LogLevel::LOG_ERROR;
     ++i;
   } else if (*i == "WARNING") {
     type = MessageType::WARNING;
-    level = cmake::LogLevel::LOG_WARNING;
+    level = Message::LogLevel::LOG_WARNING;
     ++i;
   } else if (*i == "AUTHOR_WARNING") {
     if (mf.IsSet("CMAKE_SUPPRESS_DEVELOPER_ERRORS") &&
         !mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_ERRORS")) {
       fatal = true;
       type = MessageType::AUTHOR_ERROR;
-      level = cmake::LogLevel::LOG_ERROR;
+      level = Message::LogLevel::LOG_ERROR;
     } else if (!mf.IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) {
       type = MessageType::AUTHOR_WARNING;
-      level = cmake::LogLevel::LOG_WARNING;
+      level = Message::LogLevel::LOG_WARNING;
     } else {
       return true;
     }
     ++i;
   } else if (*i == "CHECK_START") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_START;
     ++i;
   } else if (*i == "CHECK_PASS") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_PASS;
     ++i;
   } else if (*i == "CHECK_FAIL") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     checkingType = CheckingType::CHECK_FAIL;
     ++i;
   } else if (*i == "STATUS") {
-    level = cmake::LogLevel::LOG_STATUS;
+    level = Message::LogLevel::LOG_STATUS;
     ++i;
   } else if (*i == "VERBOSE") {
-    level = cmake::LogLevel::LOG_VERBOSE;
+    level = Message::LogLevel::LOG_VERBOSE;
     ++i;
   } else if (*i == "DEBUG") {
-    level = cmake::LogLevel::LOG_DEBUG;
+    level = Message::LogLevel::LOG_DEBUG;
     ++i;
   } else if (*i == "TRACE") {
-    level = cmake::LogLevel::LOG_TRACE;
+    level = Message::LogLevel::LOG_TRACE;
     ++i;
   } else if (*i == "DEPRECATION") {
     if (mf.IsOn("CMAKE_ERROR_DEPRECATED")) {
       fatal = true;
       type = MessageType::DEPRECATION_ERROR;
-      level = cmake::LogLevel::LOG_ERROR;
+      level = Message::LogLevel::LOG_ERROR;
     } else if (!mf.IsSet("CMAKE_WARN_DEPRECATED") ||
                mf.IsOn("CMAKE_WARN_DEPRECATED")) {
       type = MessageType::DEPRECATION_WARNING;
-      level = cmake::LogLevel::LOG_WARNING;
+      level = Message::LogLevel::LOG_WARNING;
     } else {
       return true;
     }
     ++i;
   } else if (*i == "NOTICE") {
     // `NOTICE` message type is going to be output to stderr
-    level = cmake::LogLevel::LOG_NOTICE;
+    level = Message::LogLevel::LOG_NOTICE;
     ++i;
   } else {
     // Messages w/o any type are `NOTICE`s
-    level = cmake::LogLevel::LOG_NOTICE;
+    level = Message::LogLevel::LOG_NOTICE;
   }
   assert("Message log level expected to be set" &&
-         level != cmake::LogLevel::LOG_UNDEFINED);
-
-  auto desiredLevel = mf.GetCMakeInstance()->GetLogLevel();
-  assert("Expected a valid log level here" &&
-         desiredLevel != cmake::LogLevel::LOG_UNDEFINED);
-
-  // Command line option takes precedence over the cache variable
-  if (!mf.GetCMakeInstance()->WasLogLevelSetViaCLI()) {
-    const auto desiredLevelFromCache =
-      cmake::StringToLogLevel(mf.GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL"));
-    if (desiredLevelFromCache != cmake::LogLevel::LOG_UNDEFINED) {
-      desiredLevel = desiredLevelFromCache;
-    }
-  }
+         level != Message::LogLevel::LOG_UNDEFINED);
+
+  Message::LogLevel desiredLevel = mf.GetCurrentLogLevel();
 
   if (desiredLevel < level) {
     // Suppress the message
@@ -178,17 +167,17 @@ bool cmMessageCommand(std::vector<std::string> const& args,
   auto message = cmJoin(cmMakeRange(i, args.cend()), "");
 
   switch (level) {
-    case cmake::LogLevel::LOG_ERROR:
-    case cmake::LogLevel::LOG_WARNING:
+    case Message::LogLevel::LOG_ERROR:
+    case Message::LogLevel::LOG_WARNING:
       // we've overridden the message type, above, so display it directly
       mf.GetMessenger()->DisplayMessage(type, message, mf.GetBacktrace());
       break;
 
-    case cmake::LogLevel::LOG_NOTICE:
+    case Message::LogLevel::LOG_NOTICE:
       cmSystemTools::Message(IndentText(message, mf));
       break;
 
-    case cmake::LogLevel::LOG_STATUS:
+    case Message::LogLevel::LOG_STATUS:
       switch (checkingType) {
         case CheckingType::CHECK_START:
           mf.DisplayStatus(IndentText(message, mf), -1);
@@ -209,9 +198,9 @@ bool cmMessageCommand(std::vector<std::string> const& args,
       }
       break;
 
-    case cmake::LogLevel::LOG_VERBOSE:
-    case cmake::LogLevel::LOG_DEBUG:
-    case cmake::LogLevel::LOG_TRACE:
+    case Message::LogLevel::LOG_VERBOSE:
+    case Message::LogLevel::LOG_DEBUG:
+    case Message::LogLevel::LOG_TRACE:
       mf.DisplayStatus(IndentText(message, mf), -1);
       break;
 
index 44de429..decb4b3 100644 (file)
@@ -16,3 +16,19 @@ enum class MessageType
   DEPRECATION_ERROR,
   DEPRECATION_WARNING
 };
+
+namespace Message {
+
+/** \brief Define log level constants. */
+enum class LogLevel
+{
+  LOG_UNDEFINED,
+  LOG_ERROR,
+  LOG_WARNING,
+  LOG_NOTICE,
+  LOG_STATUS,
+  LOG_VERBOSE,
+  LOG_DEBUG,
+  LOG_TRACE
+};
+}
index d4f1608..bda8a5f 100644 (file)
@@ -537,7 +537,6 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
   // this target requires separable cuda compilation
   // now build the correct command depending on if the target is
   // an executable or a dynamic library.
-  std::string linkCmd;
   switch (this->GetGeneratorTarget()->GetType()) {
     case cmStateEnums::STATIC_LIBRARY:
     case cmStateEnums::SHARED_LIBRARY:
@@ -1086,10 +1085,12 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
       this->GetGeneratorTarget()->GetObjectSources(sources, config);
       cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
       for (const auto& source : sources) {
+        const std::string sourcePath = source->GetLanguage() == "Swift"
+          ? this->GetCompiledSourceNinjaPath(source)
+          : this->GetObjectFilePath(source, config);
         oss << " "
-            << LocalGen->ConvertToOutputFormat(
-                 this->GetCompiledSourceNinjaPath(source),
-                 cmOutputConverter::SHELL);
+            << LocalGen->ConvertToOutputFormat(sourcePath,
+                                               cmOutputConverter::SHELL);
       }
       return oss.str();
     }();
@@ -1107,10 +1108,15 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
     std::vector<cmSourceFile const*> sources;
     gt->GetObjectSources(sources, config);
     for (const auto& source : sources) {
-      linkBuild.Outputs.push_back(
-        this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
-      linkBuild.ExplicitDeps.emplace_back(
-        this->GetCompiledSourceNinjaPath(source));
+      if (source->GetLanguage() == "Swift") {
+        linkBuild.Outputs.push_back(
+          this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
+        linkBuild.ExplicitDeps.emplace_back(
+          this->GetCompiledSourceNinjaPath(source));
+      } else {
+        linkBuild.ExplicitDeps.emplace_back(
+          this->GetObjectFilePath(source, config));
+      }
     }
     linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
   } else {
index 3fac7f5..e4427f5 100644 (file)
 
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmExportBuildFileGenerator.h"
+#include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalNinjaGenerator.h"
+#include "cmInstallCxxModuleBmiGenerator.h"
+#include "cmInstallExportGenerator.h"
+#include "cmInstallFileSetGenerator.h"
+#include "cmInstallGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmLocalNinjaGenerator.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
-#include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmTargetExport.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -153,17 +159,12 @@ std::string cmNinjaTargetGenerator::LanguageDyndepRule(
 bool cmNinjaTargetGenerator::NeedCxxModuleSupport(
   std::string const& lang, std::string const& config) const
 {
-  if (lang != "CXX") {
+  if (lang != "CXX"_s) {
     return false;
   }
-  if (!this->Makefile->IsOn("CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP")) {
-    return false;
-  }
-  cmGeneratorTarget const* tgt = this->GetGeneratorTarget();
-  cmStandardLevelResolver standardResolver(this->Makefile);
-  bool const uses_cxx20 =
-    standardResolver.HaveStandardAvailable(tgt, "CXX", config, "cxx_std_20");
-  return uses_cxx20 && this->GetGlobalGenerator()->CheckCxxModuleSupport();
+  return this->GetGeneratorTarget()->HaveCxxModuleSupport(config) ==
+    cmGeneratorTarget::Cxx20SupportLevel::Supported &&
+    this->GetGlobalGenerator()->CheckCxxModuleSupport();
 }
 
 bool cmNinjaTargetGenerator::NeedDyndep(std::string const& lang,
@@ -255,51 +256,53 @@ std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
       flags, genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS));
   }
 
-  if (this->NeedCxxModuleSupport(language, config)) {
-    auto const& path = source->GetFullPath();
-    auto const* tgt = this->GeneratorTarget->Target;
+  auto const& path = source->GetFullPath();
+  auto const* tgt = this->GeneratorTarget->Target;
 
-    std::string file_set_type;
+  std::string file_set_type;
 
-    for (auto const& name : tgt->GetAllFileSetNames()) {
-      auto const* file_set = tgt->GetFileSet(name);
-      if (!file_set) {
-        this->GetMakefile()->IssueMessage(
-          MessageType::INTERNAL_ERROR,
-          cmStrCat("Target `", tgt->GetName(),
-                   "` is tracked to have file set `", name,
-                   "`, but it was not found."));
-        continue;
-      }
+  for (auto const& name : tgt->GetAllFileSetNames()) {
+    auto const* file_set = tgt->GetFileSet(name);
+    if (!file_set) {
+      this->GetMakefile()->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", name,
+                 "\", but it was not found."));
+      continue;
+    }
 
-      auto fileEntries = file_set->CompileFileEntries();
-      auto directoryEntries = file_set->CompileDirectoryEntries();
-      auto directories = file_set->EvaluateDirectoryEntries(
-        directoryEntries, this->LocalGenerator, config, this->GeneratorTarget);
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->LocalGenerator, config, this->GeneratorTarget);
 
-      std::map<std::string, std::vector<std::string>> files;
-      for (auto const& entry : fileEntries) {
-        file_set->EvaluateFileEntry(directories, files, entry,
-                                    this->LocalGenerator, config,
-                                    this->GeneratorTarget);
-      }
+    std::map<std::string, std::vector<std::string>> files;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files, entry,
+                                  this->LocalGenerator, config,
+                                  this->GeneratorTarget);
+    }
 
-      for (auto const& it : files) {
-        for (auto const& filename : it.second) {
-          if (filename == path) {
-            file_set_type = file_set->GetType();
-            break;
-          }
+    for (auto const& it : files) {
+      for (auto const& filename : it.second) {
+        if (filename == path) {
+          file_set_type = file_set->GetType();
+          break;
         }
       }
+    }
 
-      if (!file_set_type.empty()) {
-        std::string source_type_var = cmStrCat(
-          "CMAKE_EXPERIMENTAL_CXX_MODULE_SOURCE_TYPE_FLAG_", file_set_type);
-        cmMakefile* mf = this->GetMakefile();
-        if (cmValue source_type_flag = mf->GetDefinition(source_type_var)) {
-          this->LocalGenerator->AppendFlags(flags, *source_type_flag);
-        }
+    if (file_set_type == "CXX_MODULES"_s ||
+        file_set_type == "CXX_MODULE_HEADER_UNITS"_s) {
+      if (source->GetLanguage() != "CXX"_s) {
+        this->GetMakefile()->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat(
+            "Target \"", tgt->GetName(), "\" contains the source\n  ", path,
+            "\nin a file set of type \"", file_set_type,
+            R"(" but the source is not classified as a "CXX" source.)"));
+        continue;
       }
     }
   }
@@ -909,8 +912,10 @@ void cmNinjaTargetGenerator::WriteCompileRule(const std::string& lang,
        lang == "OBJCXX")) {
     std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
     cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
-    if (cmNonempty(clauncher)) {
-      compilerLauncher = *clauncher;
+    std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
+      *clauncher, this->LocalGenerator, config);
+    if (!evaluatedClauncher.empty()) {
+      compilerLauncher = evaluatedClauncher;
     }
   }
 
@@ -1038,6 +1043,8 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
   const std::string& config, const std::string& fileConfig,
   bool firstForConfig)
 {
+  this->GeneratorTarget->CheckCxxModuleStatus(config);
+
   // Write comments.
   cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
   this->GetImplFileStream(fileConfig)
@@ -1338,9 +1345,11 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatement(
     }
   }
 
-  this->ExportObjectCompileCommand(
-    language, sourceFilePath, objectDir, objectFileName, objectFileDir,
-    vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
+  if (firstForConfig) {
+    this->ExportObjectCompileCommand(
+      language, sourceFilePath, objectDir, objectFileName, objectFileDir,
+      vars["FLAGS"], vars["DEFINES"], vars["INCLUDES"], config);
+  }
 
   objBuild.Outputs.push_back(objectFileName);
   if (firstForConfig) {
@@ -1616,8 +1625,9 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
     mod_dir = this->GeneratorTarget->GetFortranModuleDirectory(
       this->Makefile->GetHomeOutputDirectory());
   } else if (lang == "CXX") {
-    mod_dir =
-      cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory);
+    mod_dir = this->GetGlobalGenerator()->ExpandCFGIntDir(
+      cmSystemTools::CollapseFullPath(this->GeneratorTarget->ObjectDirectory),
+      config);
   }
   if (mod_dir.empty()) {
     mod_dir = this->Makefile->GetCurrentBinaryDirectory();
@@ -1654,6 +1664,215 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
     tdi_linked_target_dirs.append(l);
   }
 
+  cmTarget* tgt = this->GeneratorTarget->Target;
+  auto all_file_sets = tgt->GetAllFileSetNames();
+  Json::Value& tdi_cxx_module_info = tdi["cxx-modules"] = Json::objectValue;
+  for (auto const& file_set_name : all_file_sets) {
+    auto* file_set = tgt->GetFileSet(file_set_name);
+    if (!file_set) {
+      this->GetMakefile()->IssueMessage(
+        MessageType::INTERNAL_ERROR,
+        cmStrCat("Target \"", tgt->GetName(),
+                 "\" is tracked to have file set \"", file_set_name,
+                 "\", but it was not found."));
+      continue;
+    }
+    auto fs_type = file_set->GetType();
+    // We only care about C++ module sources here.
+    if (fs_type != "CXX_MODULES"_s) {
+      continue;
+    }
+
+    auto fileEntries = file_set->CompileFileEntries();
+    auto directoryEntries = file_set->CompileDirectoryEntries();
+
+    auto directories = file_set->EvaluateDirectoryEntries(
+      directoryEntries, this->GeneratorTarget->LocalGenerator, config,
+      this->GeneratorTarget);
+    std::map<std::string, std::vector<std::string>> files_per_dirs;
+    for (auto const& entry : fileEntries) {
+      file_set->EvaluateFileEntry(directories, files_per_dirs, entry,
+                                  this->GeneratorTarget->LocalGenerator,
+                                  config, this->GeneratorTarget);
+    }
+
+    std::map<std::string, cmSourceFile const*> sf_map;
+    {
+      std::vector<cmSourceFile const*> objectSources;
+      this->GeneratorTarget->GetObjectSources(objectSources, config);
+      for (auto const* sf : objectSources) {
+        auto full_path = sf->GetFullPath();
+        if (full_path.empty()) {
+          this->GetMakefile()->IssueMessage(
+            MessageType::INTERNAL_ERROR,
+            cmStrCat("Target \"", tgt->GetName(),
+                     "\" has a full path-less source file."));
+          continue;
+        }
+        sf_map[full_path] = sf;
+      }
+    }
+
+    Json::Value fs_dest = Json::nullValue;
+    for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) {
+      if (auto const* fsg =
+            dynamic_cast<cmInstallFileSetGenerator const*>(ig.get())) {
+        if (fsg->GetTarget() == this->GeneratorTarget &&
+            fsg->GetFileSet() == file_set) {
+          fs_dest = fsg->GetDestination(config);
+          continue;
+        }
+      }
+    }
+
+    for (auto const& files_per_dir : files_per_dirs) {
+      for (auto const& file : files_per_dir.second) {
+        auto lookup = sf_map.find(file);
+        if (lookup == sf_map.end()) {
+          this->GetMakefile()->IssueMessage(
+            MessageType::INTERNAL_ERROR,
+            cmStrCat("Target \"", tgt->GetName(), "\" has source file \"",
+                     file,
+                     R"(" which is not in any of its "FILE_SET BASE_DIRS".)"));
+          continue;
+        }
+
+        auto const* sf = lookup->second;
+
+        if (!sf) {
+          this->GetMakefile()->IssueMessage(
+            MessageType::INTERNAL_ERROR,
+            cmStrCat("Target \"", tgt->GetName(), "\" has source file \"",
+                     file, "\" which has not been tracked properly."));
+          continue;
+        }
+
+        auto obj_path = this->GetObjectFilePath(sf, config);
+        Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] =
+          Json::objectValue;
+
+        tdi_module_info["source"] = file;
+        tdi_module_info["relative-directory"] = files_per_dir.first;
+        tdi_module_info["name"] = file_set->GetName();
+        tdi_module_info["type"] = file_set->GetType();
+        tdi_module_info["visibility"] =
+          std::string(cmFileSetVisibilityToName(file_set->GetVisibility()));
+        tdi_module_info["destination"] = fs_dest;
+      }
+    }
+  }
+
+  tdi["config"] = config;
+
+  // Add information about the export sets that this target is a member of.
+  Json::Value& tdi_exports = tdi["exports"] = Json::arrayValue;
+  std::string export_name = this->GeneratorTarget->GetExportName();
+
+  cmInstallCxxModuleBmiGenerator const* bmi_gen = nullptr;
+  for (auto const& ig : this->GetMakefile()->GetInstallGenerators()) {
+    if (auto const* bmig =
+          dynamic_cast<cmInstallCxxModuleBmiGenerator const*>(ig.get())) {
+      if (bmig->GetTarget() == this->GeneratorTarget) {
+        bmi_gen = bmig;
+        continue;
+      }
+    }
+  }
+  if (bmi_gen) {
+    Json::Value tdi_bmi_info = Json::objectValue;
+
+    tdi_bmi_info["permissions"] = bmi_gen->GetFilePermissions();
+    tdi_bmi_info["destination"] = bmi_gen->GetDestination(config);
+    const char* msg_level = "";
+    switch (bmi_gen->GetMessageLevel()) {
+      case cmInstallGenerator::MessageDefault:
+        break;
+      case cmInstallGenerator::MessageAlways:
+        msg_level = "MESSAGE_ALWAYS";
+        break;
+      case cmInstallGenerator::MessageLazy:
+        msg_level = "MESSAGE_LAZY";
+        break;
+      case cmInstallGenerator::MessageNever:
+        msg_level = "MESSAGE_NEVER";
+        break;
+    }
+    tdi_bmi_info["message-level"] = msg_level;
+    tdi_bmi_info["script-location"] = bmi_gen->GetScriptLocation(config);
+
+    tdi["bmi-installation"] = tdi_bmi_info;
+  } else {
+    tdi["bmi-installation"] = Json::nullValue;
+  }
+
+  auto const& all_install_exports =
+    this->GetGlobalGenerator()->GetExportSets();
+  for (auto const& exp : all_install_exports) {
+    // Ignore exports sets which are not for this target.
+    auto const& targets = exp.second.GetTargetExports();
+    auto tgt_export =
+      std::find_if(targets.begin(), targets.end(),
+                   [this](std::unique_ptr<cmTargetExport> const& te) {
+                     return te->Target == this->GeneratorTarget;
+                   });
+    if (tgt_export == targets.end()) {
+      continue;
+    }
+
+    auto const* installs = exp.second.GetInstallations();
+    for (auto const* install : *installs) {
+      Json::Value tdi_export_info = Json::objectValue;
+
+      auto const& ns = install->GetNamespace();
+      auto const& dest = install->GetDestination();
+      auto const& cxxm_dir = install->GetCxxModuleDirectory();
+      auto const& export_prefix = install->GetTempDir();
+
+      tdi_export_info["namespace"] = ns;
+      tdi_export_info["export-name"] = export_name;
+      tdi_export_info["destination"] = dest;
+      tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
+      tdi_export_info["export-prefix"] = export_prefix;
+      tdi_export_info["install"] = true;
+
+      tdi_exports.append(tdi_export_info);
+    }
+  }
+
+  auto const& all_build_exports =
+    this->GetMakefile()->GetExportBuildFileGenerators();
+  for (auto const& exp : all_build_exports) {
+    std::vector<std::string> targets;
+    exp->GetTargets(targets);
+
+    // Ignore exports sets which are not for this target.
+    auto const& name = this->GeneratorTarget->GetName();
+    bool has_current_target =
+      std::any_of(targets.begin(), targets.end(),
+                  [name](std::string const& tname) { return tname == name; });
+    if (!has_current_target) {
+      continue;
+    }
+
+    Json::Value tdi_export_info = Json::objectValue;
+
+    auto const& ns = exp->GetNamespace();
+    auto const& main_fn = exp->GetMainExportFileName();
+    auto const& cxxm_dir = exp->GetCxxModuleDirectory();
+    auto dest = cmsys::SystemTools::GetParentDirectory(main_fn);
+    auto const& export_prefix =
+      cmSystemTools::GetFilenamePath(exp->GetMainExportFileName());
+
+    tdi_export_info["namespace"] = ns;
+    tdi_export_info["export-name"] = export_name;
+    tdi_export_info["destination"] = dest;
+    tdi_export_info["cxx-module-info-dir"] = cxxm_dir;
+    tdi_export_info["export-prefix"] = export_prefix;
+    tdi_export_info["install"] = false;
+
+    tdi_exports.append(tdi_export_info);
+  }
+
   std::string const tdin = this->GetTargetDependInfoPath(lang, config);
   cmGeneratedFileStream tdif(tdin);
   tdif << tdi;
index 6883535..299ab3a 100644 (file)
@@ -527,6 +527,13 @@ bool cmOutputConverter::Shell_ArgumentNeedsQuotes(cm::string_view in,
     }
   }
 
+  /* UNC paths in MinGW Makefiles need quotes.  */
+  if ((flags & Shell_Flag_MinGWMake) && (flags & Shell_Flag_Make)) {
+    if (in.size() > 1 && in[0] == '\\' && in[1] == '\\') {
+      return true;
+    }
+  }
+
   return false;
 }
 
index 95f3e7e..7e19566 100644 (file)
@@ -10,6 +10,7 @@
 #include <cm/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -41,11 +42,18 @@ namespace {
 
 using options_map = std::map<std::string, bool>;
 using single_map = std::map<std::string, std::string>;
-using multi_map = std::map<std::string, std::vector<std::string>>;
-using options_set = std::set<std::string>;
+using multi_map =
+  std::map<std::string, ArgumentParser::NonEmpty<std::vector<std::string>>>;
+using options_set = std::set<cm::string_view>;
 
 struct UserArgumentParser : public cmArgumentParser<void>
 {
+  void BindKeywordsMissingValue(std::vector<cm::string_view>& ref)
+  {
+    this->cmArgumentParser<void>::BindKeywordMissingValue(
+      [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
+  }
+
   template <typename T, typename H>
   void Bind(std::vector<std::string> const& names,
             std::map<std::string, T>& ref, H duplicateKey)
@@ -208,9 +216,10 @@ bool cmParseArgumentsCommand(std::vector<std::string> const& args,
     }
   }
 
-  std::vector<std::string> keywordsMissingValues;
+  std::vector<cm::string_view> keywordsMissingValues;
+  parser.BindKeywordsMissingValue(keywordsMissingValues);
 
-  parser.Parse(list, &unparsed, &keywordsMissingValues);
+  parser.Parse(list, &unparsed);
 
   PassParsedArguments(
     prefix, status.GetMakefile(), options, singleValArgs, multiValArgs,
index cb7402c..4643868 100644 (file)
@@ -421,7 +421,17 @@ class cmMakefile;
   SELECT(                                                                     \
     POLICY, CMP0139,                                                          \
     "The if() command supports path comparisons using PATH_EQUAL operator.",  \
-    3, 24, 0, cmPolicies::WARN)
+    3, 24, 0, cmPolicies::WARN)                                               \
+  SELECT(POLICY, CMP0140, "The return() command checks its arguments.", 3,    \
+         25, 0, cmPolicies::WARN)                                             \
+  SELECT(                                                                     \
+    POLICY, CMP0141,                                                          \
+    "MSVC debug information format flags are selected by an abstraction.", 3, \
+    25, 0, cmPolicies::WARN)                                                  \
+  SELECT(POLICY, CMP0142,                                                     \
+         "The Xcode generator does not append per-config suffixes to "        \
+         "library search paths.",                                             \
+         3, 25, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -458,7 +468,8 @@ class cmMakefile;
   F(CMP0112)                                                                  \
   F(CMP0113)                                                                  \
   F(CMP0119)                                                                  \
-  F(CMP0131)
+  F(CMP0131)                                                                  \
+  F(CMP0142)
 
 /** \class cmPolicies
  * \brief Handles changes in CMake behavior and policies
index 40f3ab5..96649ab 100644 (file)
@@ -1792,13 +1792,16 @@ void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename,
   // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition
   // when the Xcode generator supports per-config sources.
   if (!this->MultiConfig || this->GlobalGen->IsXcode()) {
-    this->AddGeneratedSource(filename.Default, genVars, prepend);
+    cmSourceFile* sf =
+      this->AddGeneratedSource(filename.Default, genVars, prepend);
+    handleSkipPch(sf);
     return;
   }
   for (auto const& cfg : this->ConfigsList) {
     std::string const& filenameCfg = filename.Config.at(cfg);
     // Register source at makefile
-    this->RegisterGeneratedSource(filenameCfg);
+    cmSourceFile* sf = this->RegisterGeneratedSource(filenameCfg);
+    handleSkipPch(sf);
     // Add source file to target for this configuration.
     this->GenTarget->AddSource(
       cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend);
@@ -1847,8 +1850,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
 
 void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
 {
-  this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName,
-                                          false);
+  this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName);
 }
 
 void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
@@ -2159,3 +2161,18 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
 
   return true;
 }
+
+void cmQtAutoGenInitializer::handleSkipPch(cmSourceFile* sf)
+{
+  bool skipPch = true;
+  for (auto const& pair : this->AutogenTarget.Sources) {
+    if (!pair.first->GetIsGenerated() &&
+        !pair.first->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
+      skipPch = false;
+    }
+  }
+
+  if (skipPch) {
+    sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON");
+  }
+}
index 33749ba..6d5261a 100644 (file)
@@ -154,6 +154,8 @@ private:
   bool GetQtExecutable(GenVarsT& genVars, const std::string& executable,
                        bool ignoreMissingTarget) const;
 
+  void handleSkipPch(cmSourceFile* sf);
+
   cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr;
   cmGeneratorTarget* GenTarget = nullptr;
   cmGlobalGenerator* GlobalGen = nullptr;
index 5905669..765b772 100644 (file)
@@ -2,12 +2,52 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmReturnCommand.h"
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmExecutionStatus.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPolicies.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
 // cmReturnCommand
-bool cmReturnCommand(std::vector<std::string> const&,
+bool cmReturnCommand(std::vector<std::string> const& args,
                      cmExecutionStatus& status)
 {
-  status.SetReturnInvoked();
+  if (!args.empty()) {
+    switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0140)) {
+      case cmPolicies::WARN:
+        status.GetMakefile().IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat(
+            cmPolicies::GetPolicyWarning(cmPolicies::CMP0140), '\n',
+            "return() checks its arguments when the policy is set to NEW. "
+            "Since the policy is not set the OLD behavior will be used so "
+            "the arguments will be ignored."));
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
+        return true;
+      case cmPolicies::REQUIRED_IF_USED:
+      case cmPolicies::REQUIRED_ALWAYS:
+        status.GetMakefile().IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat('\n', cmPolicies::GetPolicyWarning(cmPolicies::CMP0140)));
+        cmSystemTools::SetFatalErrorOccurred();
+        return false;
+      default:
+        break;
+    }
+    if (args[0] != "PROPAGATE"_s) {
+      status.SetError(
+        cmStrCat("called with unsupported argument \"", args[0], '"'));
+      cmSystemTools::SetFatalErrorOccurred();
+      return false;
+    }
+    status.SetReturnInvoked({ args.begin() + 1, args.end() });
+  } else {
+    status.SetReturnInvoked();
+  }
   return true;
 }
index 82a374a..81ef3da 100644 (file)
@@ -188,6 +188,19 @@ bool cmScanDepFormat_P1689_Parse(std::string const& arg_pp,
             return false;
           }
 
+          if (provide.isMember("is-interface")) {
+            Json::Value const& is_interface = provide["is-interface"];
+            if (!is_interface.isBool()) {
+              cmSystemTools::Error(
+                cmStrCat("-E cmake_ninja_dyndep failed to parse ", arg_pp,
+                         ": is-interface is not a boolean"));
+              return false;
+            }
+            provide_info.IsInterface = is_interface.asBool();
+          } else {
+            provide_info.IsInterface = true;
+          }
+
           info->Provides.push_back(provide_info);
         }
       }
@@ -308,6 +321,8 @@ bool cmScanDepFormat_P1689_Write(std::string const& path,
       provide_obj["source-path"] = EncodeFilename(provide.SourcePath);
     }
 
+    provide_obj["is-interface"] = provide.IsInterface;
+
     provides.append(provide_obj);
   }
 
index dae28d9..dc55bf1 100644 (file)
@@ -18,6 +18,11 @@ struct cmSourceReqInfo
   std::string SourcePath;
   std::string CompiledModulePath;
   bool UseSourcePath = false;
+
+  // Provides-only fields.
+  bool IsInterface = true;
+
+  // Requires-only fields.
   LookupMethod Method = LookupMethod::ByName;
 };
 
index 166ee56..32f9bec 100644 (file)
@@ -133,7 +133,7 @@ void cmScriptGenerator::GenerateScriptActionsOnce(std::ostream& os,
     std::string config_test = this->CreateConfigTest(this->Configurations);
     os << indent << "if(" << config_test << ")\n";
     this->GenerateScriptActions(os, indent.Next());
-    os << indent << "endif(" << config_test << ")\n";
+    os << indent << "endif()\n";
   }
 }
 
index db10cd4..521cf63 100644 (file)
@@ -9,6 +9,7 @@
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -561,7 +562,8 @@ bool HandleTarget(cmTarget* target, cmMakefile& makefile,
 {
   // Set or append the property.
   if (appendMode) {
-    target->AppendProperty(propertyName, propertyValue, appendAsString);
+    target->AppendProperty(propertyName, propertyValue,
+                           makefile.GetBacktrace(), appendAsString);
   } else {
     if (remove) {
       target->SetProperty(propertyName, nullptr);
index 785f356..d2eac0c 100644 (file)
@@ -18,6 +18,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -378,25 +379,29 @@ std::unordered_map<std::string, StandardLevelComputer>
         "C", std::vector<int>{ 90, 99, 11, 17, 23 },
         std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
     { "CXX",
-      StandardLevelComputer{
-        "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
-        std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
+      StandardLevelComputer{ "CXX",
+                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+                             std::vector<std::string>{ "98", "11", "14", "17",
+                                                       "20", "23", "26" } } },
     { "CUDA",
-      StandardLevelComputer{
-        "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23 },
-        std::vector<std::string>{ "03", "11", "14", "17", "20", "23" } } },
+      StandardLevelComputer{ "CUDA",
+                             std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 },
+                             std::vector<std::string>{ "03", "11", "14", "17",
+                                                       "20", "23", "26" } } },
     { "OBJC",
       StandardLevelComputer{
         "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
         std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
     { "OBJCXX",
-      StandardLevelComputer{
-        "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
-        std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
+      StandardLevelComputer{ "OBJCXX",
+                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+                             std::vector<std::string>{ "98", "11", "14", "17",
+                                                       "20", "23", "26" } } },
     { "HIP",
-      StandardLevelComputer{
-        "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
-        std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }
+      StandardLevelComputer{ "HIP",
+                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+                             std::vector<std::string>{ "98", "11", "14", "17",
+                                                       "20", "23", "26" } } }
   };
 }
 
@@ -416,7 +421,8 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature(
   cmTarget* target, const std::string& feature, std::string* error) const
 {
   if (cmGeneratorExpression::Find(feature) != std::string::npos) {
-    target->AppendProperty("COMPILE_FEATURES", feature);
+    target->AppendProperty("COMPILE_FEATURES", feature,
+                           this->Makefile->GetBacktrace());
     return true;
   }
 
@@ -426,7 +432,8 @@ bool cmStandardLevelResolver::AddRequiredTargetFeature(
     return false;
   }
 
-  target->AppendProperty("COMPILE_FEATURES", feature);
+  target->AppendProperty("COMPILE_FEATURES", feature,
+                         this->Makefile->GetBacktrace());
 
   // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
   // property due to COMPILE_FEATURES.  The language standard selection
index 3d38e73..e54ccfc 100644 (file)
@@ -281,7 +281,7 @@ cmStateSnapshot cmState::Reset()
     it->CompileOptions.clear();
     it->LinkOptions.clear();
     it->LinkDirectories.clear();
-    it->DirectoryEnd = pos;
+    it->CurrentScope = pos;
     it->NormalTargetNames.clear();
     it->ImportedTargetNames.clear();
     it->Properties.Clear();
@@ -819,7 +819,7 @@ cmStateSnapshot cmState::CreateBaseSnapshot()
   pos->CompileOptionsPosition = 0;
   pos->LinkOptionsPosition = 0;
   pos->LinkDirectoriesPosition = 0;
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->Policies = this->PolicyStack.Root();
   pos->PolicyRoot = this->PolicyStack.Root();
   pos->PolicyScope = this->PolicyStack.Root();
@@ -846,7 +846,7 @@ cmStateSnapshot cmState::CreateBuildsystemDirectorySnapshot(
     originSnapshot.Position->BuildSystemDirectory);
   pos->ExecutionListFile =
     this->ExecutionListFiles.Push(originSnapshot.Position->ExecutionListFile);
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->Policies = originSnapshot.Position->Policies;
   pos->PolicyRoot = originSnapshot.Position->Policies;
   pos->PolicyScope = originSnapshot.Position->Policies;
@@ -876,7 +876,7 @@ cmStateSnapshot cmState::CreateDeferCallSnapshot(
   pos->ExecutionListFile = this->ExecutionListFiles.Push(
     originSnapshot.Position->ExecutionListFile, fileName);
   assert(originSnapshot.Position->Vars.IsValid());
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   return { this, pos };
 }
@@ -891,7 +891,7 @@ cmStateSnapshot cmState::CreateFunctionCallSnapshot(
   pos->Keep = false;
   pos->ExecutionListFile = this->ExecutionListFiles.Push(
     originSnapshot.Position->ExecutionListFile, fileName);
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   assert(originSnapshot.Position->Vars.IsValid());
   cmLinkedTree<cmDefinitions>::iterator origin = originSnapshot.Position->Vars;
@@ -910,7 +910,7 @@ cmStateSnapshot cmState::CreateMacroCallSnapshot(
   pos->ExecutionListFile = this->ExecutionListFiles.Push(
     originSnapshot.Position->ExecutionListFile, fileName);
   assert(originSnapshot.Position->Vars.IsValid());
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   return { this, pos };
 }
@@ -925,7 +925,7 @@ cmStateSnapshot cmState::CreateIncludeFileSnapshot(
   pos->ExecutionListFile = this->ExecutionListFiles.Push(
     originSnapshot.Position->ExecutionListFile, fileName);
   assert(originSnapshot.Position->Vars.IsValid());
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   return { this, pos };
 }
@@ -938,6 +938,7 @@ cmStateSnapshot cmState::CreateVariableScopeSnapshot(
   pos->ScopeParent = originSnapshot.Position;
   pos->SnapshotType = cmStateEnums::VariableScopeType;
   pos->Keep = false;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   assert(originSnapshot.Position->Vars.IsValid());
 
@@ -957,7 +958,7 @@ cmStateSnapshot cmState::CreateInlineListFileSnapshot(
   pos->Keep = true;
   pos->ExecutionListFile = this->ExecutionListFiles.Push(
     originSnapshot.Position->ExecutionListFile, fileName);
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   return { this, pos };
 }
@@ -969,7 +970,7 @@ cmStateSnapshot cmState::CreatePolicyScopeSnapshot(
     this->SnapshotData.Push(originSnapshot.Position, *originSnapshot.Position);
   pos->SnapshotType = cmStateEnums::PolicyScopeType;
   pos->Keep = false;
-  pos->BuildSystemDirectory->DirectoryEnd = pos;
+  pos->BuildSystemDirectory->CurrentScope = pos;
   pos->PolicyScope = originSnapshot.Position->Policies;
   return { this, pos };
 }
@@ -989,7 +990,7 @@ cmStateSnapshot cmState::Pop(cmStateSnapshot const& originSnapshot)
     prevPos->BuildSystemDirectory->LinkOptions.size();
   prevPos->LinkDirectoriesPosition =
     prevPos->BuildSystemDirectory->LinkDirectories.size();
-  prevPos->BuildSystemDirectory->DirectoryEnd = prevPos;
+  prevPos->BuildSystemDirectory->CurrentScope = prevPos;
 
   if (!pos->Keep && this->SnapshotData.IsLast(pos)) {
     if (pos->Vars != prevPos->Vars) {
index fd46eed..ec14834 100644 (file)
@@ -62,7 +62,7 @@ struct cmStateDetail::PolicyStackEntry : public cmPolicies::PolicyMap
 
 struct cmStateDetail::BuildsystemDirectoryStateType
 {
-  cmStateDetail::PositionType DirectoryEnd;
+  cmStateDetail::PositionType CurrentScope;
 
   std::string Location;
   std::string OutputLocation;
index f73df8f..cb5f11f 100644 (file)
@@ -64,7 +64,7 @@ bool cmStateSnapshot::IsValid() const
 
 cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const
 {
-  return { this->State, this->Position->BuildSystemDirectory->DirectoryEnd };
+  return { this->State, this->Position->BuildSystemDirectory->CurrentScope };
 }
 
 cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const
@@ -76,7 +76,7 @@ cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const
   cmStateDetail::PositionType parentPos = this->Position->DirectoryParent;
   if (parentPos != this->State->SnapshotData.Root()) {
     snapshot = cmStateSnapshot(this->State,
-                               parentPos->BuildSystemDirectory->DirectoryEnd);
+                               parentPos->BuildSystemDirectory->CurrentScope);
   }
 
   return snapshot;
@@ -177,9 +177,9 @@ cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy(cmPolicies::PolicyID id,
   while (true) {
     assert(dir.IsValid());
     cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator leaf =
-      dir->DirectoryEnd->Policies;
+      dir->CurrentScope->Policies;
     cmLinkedTree<cmStateDetail::PolicyStackEntry>::iterator root =
-      dir->DirectoryEnd->PolicyRoot;
+      dir->CurrentScope->PolicyRoot;
     for (; leaf != root; ++leaf) {
       if (parent_scope) {
         parent_scope = false;
@@ -190,7 +190,7 @@ cmPolicies::PolicyStatus cmStateSnapshot::GetPolicy(cmPolicies::PolicyID id,
         return status;
       }
     }
-    cmStateDetail::PositionType e = dir->DirectoryEnd;
+    cmStateDetail::PositionType e = dir->CurrentScope;
     cmStateDetail::PositionType p = e->DirectoryParent;
     if (p == this->State->SnapshotData.Root()) {
       break;
@@ -317,6 +317,25 @@ void cmStateSnapshot::SetDefaultDefinitions()
   this->SetDefinition("CMAKE_HOST_SOLARIS", "1");
 #endif
 
+#if defined(__OpenBSD__)
+  this->SetDefinition("BSD", "OpenBSD");
+  this->SetDefinition("CMAKE_HOST_BSD", "OpenBSD");
+#elif defined(__FreeBSD__)
+  this->SetDefinition("BSD", "FreeBSD");
+  this->SetDefinition("CMAKE_HOST_BSD", "FreeBSD");
+#elif defined(__NetBSD__)
+  this->SetDefinition("BSD", "NetBSD");
+  this->SetDefinition("CMAKE_HOST_BSD", "NetBSD");
+#elif defined(__DragonFly__)
+  this->SetDefinition("BSD", "DragonFlyBSD");
+  this->SetDefinition("CMAKE_HOST_BSD", "DragonFlyBSD");
+#endif
+
+#if defined(__linux__)
+  this->SetDefinition("LINUX", "1");
+  this->SetDefinition("CMAKE_HOST_LINUX", "1");
+#endif
+
   this->SetDefinition("CMAKE_MAJOR_VERSION",
                       std::to_string(cmVersion::GetMajorVersion()));
   this->SetDefinition("CMAKE_MINOR_VERSION",
index c3ee695..c12d1fe 100644 (file)
@@ -143,7 +143,8 @@ bool HandleHexCommand(std::vector<std::string> const& args,
 
   std::string::size_type hexIndex = 0;
   for (auto const& c : instr) {
-    sprintf(&output[hexIndex], "%.2x", static_cast<unsigned char>(c) & 0xFF);
+    snprintf(&output[hexIndex], 3, "%.2x",
+             static_cast<unsigned char>(c) & 0xFF);
     hexIndex += 2;
   }
 
@@ -1013,7 +1014,7 @@ int ParseIndex(
   Json::ArrayIndex index = static_cast<Json::ArrayIndex>(lindex);
   if (index >= max) {
     cmAlphaNum sizeStr{ max };
-    throw json_error({ "expected an index less then "_s, sizeStr.View(),
+    throw json_error({ "expected an index less than "_s, sizeStr.View(),
                        " got '"_s, str, "'"_s },
                      progress);
   }
index 2477d7a..47082f1 100644 (file)
@@ -32,7 +32,7 @@ bool cmSubdirCommand(std::vector<std::string> const& args,
     std::string srcPath = mf.GetCurrentSourceDirectory() + "/" + i;
     if (cmSystemTools::FileIsDirectory(srcPath)) {
       std::string binPath = mf.GetCurrentBinaryDirectory() + "/" + i;
-      mf.AddSubDirectory(srcPath, binPath, excludeFromAll, false);
+      mf.AddSubDirectory(srcPath, binPath, excludeFromAll, false, false);
     }
     // otherwise it is a full path
     else if (cmSystemTools::FileIsDirectory(i)) {
@@ -40,7 +40,7 @@ bool cmSubdirCommand(std::vector<std::string> const& args,
       // element from the source path and use that
       std::string binPath = mf.GetCurrentBinaryDirectory() + "/" +
         cmSystemTools::GetFilenameName(i);
-      mf.AddSubDirectory(i, binPath, excludeFromAll, false);
+      mf.AddSubDirectory(i, binPath, excludeFromAll, false, false);
     } else {
       status.SetError(cmStrCat("Incorrect SUBDIRS command. Directory: ", i,
                                " does not exist."));
index f077801..ee74908 100644 (file)
 // NOLINTNEXTLINE(bugprone-reserved-identifier)
 #  define _XOPEN_SOURCE 700
 #endif
+#if defined(__APPLE__)
+// Restore Darwin APIs removed by _POSIX_C_SOURCE.
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+#  define _DARWIN_C_SOURCE
+#endif
 
 #include "cmSystemTools.h"
 
 #include <cm/optional>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include <cm3p/uv.h>
 
@@ -87,7 +93,6 @@
 #  include <unistd.h>
 
 #  include <sys/time.h>
-#  include <sys/types.h>
 #endif
 
 #if defined(_WIN32) &&                                                        \
@@ -1000,6 +1005,93 @@ void cmSystemTools::InitializeLibUV()
 #endif
 }
 
+#if defined(_WIN32)
+#  include <random>
+
+#  include <wctype.h>
+#  ifdef _MSC_VER
+using mode_t = cmSystemTools::SystemTools::mode_t;
+#  endif
+#else
+#  include <sys/stat.h>
+#endif
+
+inline int Mkdir(const char* dir, const mode_t* mode)
+{
+#if defined(_WIN32)
+  int ret = _wmkdir(cmSystemTools::ConvertToWindowsExtendedPath(dir).c_str());
+  if (ret == 0 && mode)
+    cmSystemTools::SystemTools::SetPermissions(dir, *mode);
+  return ret;
+#else
+  return mkdir(dir, mode ? *mode : 0777);
+#endif
+}
+
+cmsys::Status cmSystemTools::MakeTempDirectory(std::string& path,
+                                               const mode_t* mode)
+{
+  if (path.empty()) {
+    return cmsys::Status::POSIX(EINVAL);
+  }
+  return cmSystemTools::MakeTempDirectory(&path.front(), mode);
+}
+
+cmsys::Status cmSystemTools::MakeTempDirectory(char* path, const mode_t* mode)
+{
+  if (!path) {
+    return cmsys::Status::POSIX(EINVAL);
+  }
+
+  // verify that path ends with "XXXXXX"
+  const auto l = std::strlen(path);
+  if (!cmHasLiteralSuffix(cm::string_view{ path, l }, "XXXXXX")) {
+    return cmsys::Status::POSIX(EINVAL);
+  }
+
+  // create parent directories
+  auto* sep = path;
+  while ((sep = strchr(sep, '/'))) {
+    // all underlying functions use C strings,
+    // so temporarily end the string here
+    *sep = '\0';
+    Mkdir(path, mode);
+
+    *sep = '/';
+    ++sep;
+  }
+
+#ifdef _WIN32
+  const int nchars = 36;
+  const char chars[nchars + 1] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+  std::random_device rd;
+  std::mt19937 rg{ rd() };
+  std::uniform_int_distribution<int> dist{ 0, nchars - 1 };
+
+  for (auto tries = 100; tries; --tries) {
+    for (auto n = l - 6; n < l; ++n) {
+      path[n] = chars[dist(rg)];
+    }
+    if (Mkdir(path, mode) == 0) {
+      return cmsys::Status::Success();
+    } else if (errno != EEXIST) {
+      return cmsys::Status::POSIX_errno();
+    }
+  }
+  return cmsys::Status::POSIX(EAGAIN);
+#else
+  if (mkdtemp(path)) {
+    if (mode) {
+      chmod(path, *mode);
+    }
+  } else {
+    return cmsys::Status::POSIX_errno();
+  }
+  return cmsys::Status::Success();
+#endif
+}
+
 #ifdef _WIN32
 namespace {
 bool cmMoveFile(std::wstring const& oldname, std::wstring const& newname,
@@ -1256,7 +1348,7 @@ std::string cmSystemTools::ComputeCertificateThumbprint(
                       certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) {
                   for (DWORD i = 0; i < hashLength; i++) {
                     // Convert each byte to hexadecimal
-                    sprintf(pHashPrint, "%02X", hashData[i]);
+                    snprintf(pHashPrint, 3, "%02X", hashData[i]);
                     pHashPrint += 2;
                   }
                   *pHashPrint = '\0';
@@ -1539,8 +1631,7 @@ std::string cmSystemTools::RelativeIfUnder(std::string const& top,
 bool cmSystemTools::UnsetEnv(const char* value)
 {
 #  if !defined(HAVE_UNSETENV)
-  std::string var = cmStrCat(value, '=');
-  return cmSystemTools::PutEnv(var);
+  return cmSystemTools::UnPutEnv(value);
 #  else
   unsetenv(value);
   return true;
@@ -1551,9 +1642,18 @@ std::vector<std::string> cmSystemTools::GetEnvironmentVariables()
 {
   std::vector<std::string> env;
   int cc;
+#  ifdef _WIN32
+  // if program starts with main, _wenviron is initially NULL, call to
+  // _wgetenv and create wide-character string environment
+  _wgetenv(L"");
+  for (cc = 0; _wenviron[cc]; ++cc) {
+    env.emplace_back(cmsys::Encoding::ToNarrow(_wenviron[cc]));
+  }
+#  else
   for (cc = 0; environ[cc]; ++cc) {
     env.emplace_back(environ[cc]);
   }
+#  endif
   return env;
 }
 
@@ -1564,6 +1664,144 @@ void cmSystemTools::AppendEnv(std::vector<std::string> const& env)
   }
 }
 
+void cmSystemTools::EnvDiff::AppendEnv(std::vector<std::string> const& env)
+{
+  for (std::string const& eit : env) {
+    this->PutEnv(eit);
+  }
+}
+
+void cmSystemTools::EnvDiff::PutEnv(const std::string& env)
+{
+  auto const eq_loc = env.find('=');
+  if (eq_loc != std::string::npos) {
+    std::string name = env.substr(0, eq_loc);
+    diff[name] = env.substr(eq_loc + 1);
+  } else {
+    this->UnPutEnv(env);
+  }
+}
+
+void cmSystemTools::EnvDiff::UnPutEnv(const std::string& env)
+{
+  diff[env] = {};
+}
+
+bool cmSystemTools::EnvDiff::ParseOperation(const std::string& envmod)
+{
+  char path_sep = GetSystemPathlistSeparator();
+
+  auto apply_diff = [this](const std::string& name,
+                           std::function<void(std::string&)> const& apply) {
+    cm::optional<std::string> old_value = diff[name];
+    std::string output;
+    if (old_value) {
+      output = *old_value;
+    } else {
+      const char* curval = cmSystemTools::GetEnv(name);
+      if (curval) {
+        output = curval;
+      }
+    }
+    apply(output);
+    diff[name] = output;
+  };
+
+  // Split on `=`
+  auto const eq_loc = envmod.find_first_of('=');
+  if (eq_loc == std::string::npos) {
+    cmSystemTools::Error(cmStrCat(
+      "Error: Missing `=` after the variable name in: ", envmod, '\n'));
+    return false;
+  }
+
+  auto const name = envmod.substr(0, eq_loc);
+
+  // Split value on `:`
+  auto const op_value_start = eq_loc + 1;
+  auto const colon_loc = envmod.find_first_of(':', op_value_start);
+  if (colon_loc == std::string::npos) {
+    cmSystemTools::Error(
+      cmStrCat("Error: Missing `:` after the operation in: ", envmod, '\n'));
+    return false;
+  }
+  auto const op = envmod.substr(op_value_start, colon_loc - op_value_start);
+
+  auto const value_start = colon_loc + 1;
+  auto const value = envmod.substr(value_start);
+
+  // Determine what to do with the operation.
+  if (op == "reset"_s) {
+    auto entry = diff.find(name);
+    if (entry != diff.end()) {
+      diff.erase(entry);
+    }
+  } else if (op == "set"_s) {
+    diff[name] = value;
+  } else if (op == "unset"_s) {
+    diff[name] = {};
+  } else if (op == "string_append"_s) {
+    apply_diff(name, [&value](std::string& output) { output += value; });
+  } else if (op == "string_prepend"_s) {
+    apply_diff(name,
+               [&value](std::string& output) { output.insert(0, value); });
+  } else if (op == "path_list_append"_s) {
+    apply_diff(name, [&value, path_sep](std::string& output) {
+      if (!output.empty()) {
+        output += path_sep;
+      }
+      output += value;
+    });
+  } else if (op == "path_list_prepend"_s) {
+    apply_diff(name, [&value, path_sep](std::string& output) {
+      if (!output.empty()) {
+        output.insert(output.begin(), path_sep);
+      }
+      output.insert(0, value);
+    });
+  } else if (op == "cmake_list_append"_s) {
+    apply_diff(name, [&value](std::string& output) {
+      if (!output.empty()) {
+        output += ';';
+      }
+      output += value;
+    });
+  } else if (op == "cmake_list_prepend"_s) {
+    apply_diff(name, [&value](std::string& output) {
+      if (!output.empty()) {
+        output.insert(output.begin(), ';');
+      }
+      output.insert(0, value);
+    });
+  } else {
+    cmSystemTools::Error(cmStrCat(
+      "Error: Unrecognized environment manipulation argument: ", op, '\n'));
+    return false;
+  }
+
+  return true;
+}
+
+void cmSystemTools::EnvDiff::ApplyToCurrentEnv(std::ostringstream* measurement)
+{
+  for (auto const& env_apply : diff) {
+    if (env_apply.second) {
+      auto const env_update =
+        cmStrCat(env_apply.first, '=', *env_apply.second);
+      cmSystemTools::PutEnv(env_update);
+      if (measurement) {
+        *measurement << env_update << std::endl;
+      }
+    } else {
+      cmSystemTools::UnsetEnv(env_apply.first.c_str());
+      if (measurement) {
+        // Signify that this variable is being actively unset
+        *measurement << '#' << env_apply.first << "=\n";
+      }
+    }
+  }
+}
+
 cmSystemTools::SaveRestoreEnvironment::SaveRestoreEnvironment()
 {
   this->Env = cmSystemTools::GetEnvironmentVariables();
@@ -3352,8 +3590,19 @@ std::string cmSystemTools::EncodeURL(std::string const& in, bool escapeSlashes)
 }
 
 cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
-                                           std::string const& newName,
-                                           std::string* errorMessage)
+                                           std::string const& newName)
+{
+  cmsys::Status status =
+    cmSystemTools::CreateSymlinkQuietly(origName, newName);
+  if (!status) {
+    cmSystemTools::Error(cmStrCat("failed to create symbolic link '", newName,
+                                  "': ", status.GetString()));
+  }
+  return status;
+}
+
+cmsys::Status cmSystemTools::CreateSymlinkQuietly(std::string const& origName,
+                                                  std::string const& newName)
 {
   uv_fs_t req;
   int flags = 0;
@@ -3373,20 +3622,23 @@ cmsys::Status cmSystemTools::CreateSymlink(std::string const& origName,
 #else
     status = cmsys::Status::POSIX(-err);
 #endif
-    std::string e = cmStrCat("failed to create symbolic link '", newName,
-                             "': ", status.GetString());
-    if (errorMessage) {
-      *errorMessage = std::move(e);
-    } else {
-      cmSystemTools::Error(e);
-    }
   }
   return status;
 }
 
 cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
-                                        std::string const& newName,
-                                        std::string* errorMessage)
+                                        std::string const& newName)
+{
+  cmsys::Status status = cmSystemTools::CreateLinkQuietly(origName, newName);
+  if (!status) {
+    cmSystemTools::Error(
+      cmStrCat("failed to create link '", newName, "': ", status.GetString()));
+  }
+  return status;
+}
+
+cmsys::Status cmSystemTools::CreateLinkQuietly(std::string const& origName,
+                                               std::string const& newName)
 {
   uv_fs_t req;
   int err =
@@ -3400,13 +3652,6 @@ cmsys::Status cmSystemTools::CreateLink(std::string const& origName,
 #else
     status = cmsys::Status::POSIX(-err);
 #endif
-    std::string e =
-      cmStrCat("failed to create link '", newName, "': ", status.GetString());
-    if (errorMessage) {
-      *errorMessage = std::move(e);
-    } else {
-      cmSystemTools::Error(e);
-    }
   }
   return status;
 }
index ec650f7..87b354c 100644 (file)
@@ -4,11 +4,18 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#if !defined(_WIN32)
+#  include <sys/types.h>
+#endif
+
 #include <cstddef>
 #include <functional>
+#include <map>
+#include <sstream>
 #include <string>
 #include <vector>
 
+#include <cm/optional>
 #include <cm/string_view>
 
 #include "cmsys/Process.h"
@@ -148,6 +155,27 @@ public:
     Failure,
   };
 
+#if defined(_MSC_VER)
+  /** Visual C++ does not define mode_t. */
+  using mode_t = unsigned short;
+#endif
+
+  /**
+   * Make a new temporary directory.  The path must end in "XXXXXX", and will
+   * be modified to reflect the name of the directory created.  This function
+   * is similar to POSIX mkdtemp (and is implemented using the same where that
+   * function is available).
+   *
+   * This function can make a full path even if none of the directories existed
+   * prior to calling this function.
+   *
+   * Note that this function may modify \p path even if it does not succeed.
+   */
+  static cmsys::Status MakeTempDirectory(char* path,
+                                         const mode_t* mode = nullptr);
+  static cmsys::Status MakeTempDirectory(std::string& path,
+                                         const mode_t* mode = nullptr);
+
   /** Copy a file. */
   static bool CopySingleFile(const std::string& oldname,
                              const std::string& newname);
@@ -377,6 +405,42 @@ public:
   /** Append multiple variables to the current environment. */
   static void AppendEnv(std::vector<std::string> const& env);
 
+  /**
+   * Helper class to represent an environment diff directly. This is to avoid
+   * repeated in-place environment modification (i.e. via setenv/putenv), which
+   * could be slow.
+   */
+  class EnvDiff
+  {
+  public:
+    /** Append multiple variables to the current environment diff */
+    void AppendEnv(std::vector<std::string> const& env);
+
+    /**
+     * Add a single variable (or remove if no = sign) to the current
+     * environment diff.
+     */
+    void PutEnv(const std::string& env);
+
+    /** Remove a single variable from the current environment diff. */
+    void UnPutEnv(const std::string& env);
+
+    /**
+     * Apply an ENVIRONMENT_MODIFICATION operation to this diff. Returns
+     * false and issues an error on parse failure.
+     */
+    bool ParseOperation(const std::string& envmod);
+
+    /**
+     * Apply this diff to the actual environment, optionally writing out the
+     * modifications to a CTest-compatible measurement stream.
+     */
+    void ApplyToCurrentEnv(std::ostringstream* measurement = nullptr);
+
+  private:
+    std::map<std::string, cm::optional<std::string>> diff;
+  };
+
   /** Helper class to save and restore the environment.
       Instantiate this class as an automatic variable on
       the stack. Its constructor saves a copy of the current
@@ -531,14 +595,16 @@ public:
   /** Create a symbolic link if the platform supports it.  Returns whether
       creation succeeded. */
   static cmsys::Status CreateSymlink(std::string const& origName,
-                                     std::string const& newName,
-                                     std::string* errorMessage = nullptr);
+                                     std::string const& newName);
+  static cmsys::Status CreateSymlinkQuietly(std::string const& origName,
+                                            std::string const& newName);
 
   /** Create a hard link if the platform supports it.  Returns whether
       creation succeeded. */
   static cmsys::Status CreateLink(std::string const& origName,
-                                  std::string const& newName,
-                                  std::string* errorMessage = nullptr);
+                                  std::string const& newName);
+  static cmsys::Status CreateLinkQuietly(std::string const& origName,
+                                         std::string const& newName);
 
   /** Get the system name. */
   static cm::string_view GetSystemName();
index cbe5d7d..874195b 100644 (file)
@@ -272,6 +272,8 @@ public:
   cmListFileBacktrace Backtrace;
 
   FileSetType HeadersFileSets;
+  FileSetType CxxModulesFileSets;
+  FileSetType CxxModuleHeadersFileSets;
 
   cmTargetInternals();
 
@@ -293,6 +295,12 @@ public:
                                 cm::string_view fileSetType) const;
   cmValue GetFileSetPaths(cmTarget const* self, std::string const& fileSetName,
                           cm::string_view fileSetType) const;
+
+  cmListFileBacktrace GetBacktrace(
+    cm::optional<cmListFileBacktrace> const& bt) const
+  {
+    return bt ? *bt : this->Makefile->GetBacktrace();
+  }
 };
 
 cmTargetInternals::cmTargetInternals()
@@ -301,6 +309,19 @@ cmTargetInternals::cmTargetInternals()
                     "The default header set"_s, "Header set"_s,
                     FileSetEntries("HEADER_SETS"_s),
                     FileSetEntries("INTERFACE_HEADER_SETS"_s))
+  , CxxModulesFileSets("CXX_MODULES"_s, "CXX_MODULE_DIRS"_s,
+                       "CXX_MODULE_SET"_s, "CXX_MODULE_DIRS_"_s,
+                       "CXX_MODULE_SET_"_s, "C++ module"_s,
+                       "The default C++ module set"_s, "C++ module set"_s,
+                       FileSetEntries("CXX_MODULE_SETS"_s),
+                       FileSetEntries("INTERFACE_CXX_MODULE_SETS"_s))
+  , CxxModuleHeadersFileSets(
+      "CXX_MODULE_HEADER_UNITS"_s, "CXX_MODULE_HEADER_UNIT_DIRS"_s,
+      "CXX_MODULE_HEADER_UNIT_SET"_s, "CXX_MODULE_HEADER_UNIT_DIRS_"_s,
+      "CXX_MODULE_HEADER_UNIT_SET_"_s, "C++ module header"_s,
+      "The default C++ module header set"_s, "C++ module header set"_s,
+      FileSetEntries("CXX_MODULE_HEADER_UNIT_SETS"_s),
+      FileSetEntries("INTERFACE_CXX_MODULE_HEADER_UNIT_SETS"_s))
 {
 }
 
@@ -542,6 +563,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("AUTORCC_OPTIONS");
     initProp("LINK_DEPENDS_NO_SHARED");
     initProp("LINK_INTERFACE_LIBRARIES");
+    initProp("MSVC_DEBUG_INFORMATION_FORMAT");
     initProp("MSVC_RUNTIME_LIBRARY");
     initProp("WATCOM_RUNTIME_LIBRARY");
     initProp("WIN32_EXECUTABLE");
@@ -605,12 +627,16 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
       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");
@@ -760,6 +786,10 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     }
   }
 
+  if (this->IsImported() || mf->GetPropertyAsBool("SYSTEM")) {
+    this->SetProperty("SYSTEM", "ON");
+  }
+
   for (auto const& prop : mf->GetState()->GetPropertyDefinitions().GetMap()) {
     if (prop.first.second == cmProperty::TARGET &&
         !prop.second.GetInitializeFromVariable().empty()) {
@@ -1223,7 +1253,8 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
       ? targetNameGenex(lib)
       : lib;
     this->AppendProperty("LINK_LIBRARIES",
-                         this->GetDebugGeneratorExpressions(libName, llt));
+                         this->GetDebugGeneratorExpressions(libName, llt),
+                         mf.GetBacktrace());
   }
 
   if (cmGeneratorExpression::Find(lib) != std::string::npos ||
@@ -1367,11 +1398,32 @@ cmBTStringRange cmTarget::GetHeaderSetsEntries() const
   return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries);
 }
 
+cmBTStringRange cmTarget::GetCxxModuleSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModulesFileSets.SelfEntries.Entries);
+}
+
+cmBTStringRange cmTarget::GetCxxModuleHeaderSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModuleHeadersFileSets.SelfEntries.Entries);
+}
+
 cmBTStringRange cmTarget::GetInterfaceHeaderSetsEntries() const
 {
   return cmMakeRange(this->impl->HeadersFileSets.InterfaceEntries.Entries);
 }
 
+cmBTStringRange cmTarget::GetInterfaceCxxModuleSetsEntries() const
+{
+  return cmMakeRange(this->impl->CxxModulesFileSets.InterfaceEntries.Entries);
+}
+
+cmBTStringRange cmTarget::GetInterfaceCxxModuleHeaderSetsEntries() const
+{
+  return cmMakeRange(
+    this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries);
+}
+
 namespace {
 #define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
 MAKE_PROP(C_STANDARD);
@@ -1631,13 +1683,21 @@ void cmTarget::StoreProperty(const std::string& prop, ValueType value)
   } else if (this->impl->HeadersFileSets.WriteProperties(
                this, this->impl.get(), prop, value, true)) {
     /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModulesFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, true)) {
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, true)) {
+    /* Handled in the `if` condition. */
   } else {
     this->impl->Properties.SetProperty(prop, value);
   }
 }
 
 void cmTarget::AppendProperty(const std::string& prop,
-                              const std::string& value, bool asString)
+                              const std::string& value,
+                              cm::optional<cmListFileBacktrace> const& bt,
+                              bool asString)
 {
   if (prop == "NAME") {
     this->impl->Makefile->IssueMessage(MessageType::FATAL_ERROR,
@@ -1668,32 +1728,32 @@ void cmTarget::AppendProperty(const std::string& prop,
   }
   if (prop == "INCLUDE_DIRECTORIES") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->IncludeDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_OPTIONS") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->CompileOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_FEATURES") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->CompileFeaturesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_DEFINITIONS") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->CompileDefinitionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_OPTIONS") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_DIRECTORIES") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "PRECOMPILE_HEADERS") {
@@ -1706,32 +1766,32 @@ void cmTarget::AppendProperty(const std::string& prop,
       return;
     }
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->PrecompileHeadersEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_LIBRARIES") {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkImplementationPropertyEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propINTERFACE_LINK_LIBRARIES) {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkInterfacePropertyEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT) {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkInterfaceDirectPropertyEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE) {
     if (!value.empty()) {
-      cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+      cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
       this->impl->LinkInterfaceDirectExcludePropertyEntries.emplace_back(value,
                                                                          lfbt);
     }
   } else if (prop == "SOURCES") {
-    cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
+    cmListFileBacktrace lfbt = this->impl->GetBacktrace(bt);
     this->impl->SourceEntries.emplace_back(value, lfbt);
   } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) {
     this->impl->Makefile->IssueMessage(
@@ -1742,6 +1802,13 @@ void cmTarget::AppendProperty(const std::string& prop,
     this->impl->Makefile->IssueMessage(
       MessageType::FATAL_ERROR, prop + " property may not be appended.");
   } else if (this->impl->HeadersFileSets.WriteProperties(
+               this, this->impl.get(), prop, value,
+               false)) { // NOLINT(bugprone-branch-clone)
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModulesFileSets.WriteProperties(
+               this, this->impl.get(), prop, value, false)) {
+    /* Handled in the `if` condition. */
+  } else if (this->impl->CxxModuleHeadersFileSets.WriteProperties(
                this, this->impl.get(), prop, value, false)) {
     /* Handled in the `if` condition. */
   } else {
@@ -2307,6 +2374,17 @@ cmValue cmTarget::GetProperty(const std::string& prop) const
     if (headers.first) {
       return headers.second;
     }
+    auto cxx_modules = this->impl->CxxModulesFileSets.ReadProperties(
+      this, this->impl.get(), prop);
+    if (cxx_modules.first) {
+      return cxx_modules.second;
+    }
+    auto cxx_module_headers =
+      this->impl->CxxModuleHeadersFileSets.ReadProperties(
+        this, this->impl.get(), prop);
+    if (cxx_module_headers.first) {
+      return cxx_module_headers.second;
+    }
   }
 
   cmValue retVal = this->impl->Properties.GetPropertyValue(prop);
@@ -2584,6 +2662,11 @@ std::pair<cmFileSet*, bool> cmTarget::GetOrCreateFileSet(
     auto bt = this->impl->Makefile->GetBacktrace();
     if (type == this->impl->HeadersFileSets.TypeName) {
       this->impl->HeadersFileSets.AddFileSet(name, vis, std::move(bt));
+    } else if (type == this->impl->CxxModulesFileSets.TypeName) {
+      this->impl->CxxModulesFileSets.AddFileSet(name, vis, std::move(bt));
+    } else if (type == this->impl->CxxModuleHeadersFileSets.TypeName) {
+      this->impl->CxxModuleHeadersFileSets.AddFileSet(name, vis,
+                                                      std::move(bt));
     }
   }
   return std::make_pair(&result.first->second, result.second);
@@ -2594,6 +2677,12 @@ std::string cmTarget::GetFileSetsPropertyName(const std::string& type)
   if (type == "HEADERS") {
     return "HEADER_SETS";
   }
+  if (type == "CXX_MODULES") {
+    return "CXX_MODULE_SETS";
+  }
+  if (type == "CXX_MODULE_HEADER_UNITS") {
+    return "CXX_MODULE_HEADER_UNIT_SETS";
+  }
   return "";
 }
 
@@ -2602,6 +2691,12 @@ std::string cmTarget::GetInterfaceFileSetsPropertyName(const std::string& type)
   if (type == "HEADERS") {
     return "INTERFACE_HEADER_SETS";
   }
+  if (type == "CXX_MODULES") {
+    return "INTERFACE_CXX_MODULE_SETS";
+  }
+  if (type == "CXX_MODULE_HEADER_UNITS") {
+    return "INTERFACE_CXX_MODULE_HEADER_UNIT_SETS";
+  }
   return "";
 }
 
@@ -2629,6 +2724,8 @@ std::vector<std::string> cmTarget::GetAllInterfaceFileSets() const
   };
 
   appendEntries(this->impl->HeadersFileSets.InterfaceEntries.Entries);
+  appendEntries(this->impl->CxxModulesFileSets.InterfaceEntries.Entries);
+  appendEntries(this->impl->CxxModuleHeadersFileSets.InterfaceEntries.Entries);
 
   return result;
 }
index 467c4da..38bd036 100644 (file)
@@ -185,8 +185,10 @@ public:
   {
     this->SetProperty(prop, cmValue(value));
   }
-  void AppendProperty(const std::string& prop, const std::string& value,
-                      bool asString = false);
+  void AppendProperty(
+    const std::string& prop, const std::string& value,
+    cm::optional<cmListFileBacktrace> const& bt = cm::nullopt,
+    bool asString = false);
   //! Might return a nullptr if the property is not set or invalid
   cmValue GetProperty(const std::string& prop) const;
   //! Always returns a valid pointer
@@ -281,8 +283,12 @@ public:
   cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
 
   cmBTStringRange GetHeaderSetsEntries() const;
+  cmBTStringRange GetCxxModuleSetsEntries() const;
+  cmBTStringRange GetCxxModuleHeaderSetsEntries() const;
 
   cmBTStringRange GetInterfaceHeaderSetsEntries() const;
+  cmBTStringRange GetInterfaceCxxModuleSetsEntries() const;
+  cmBTStringRange GetInterfaceCxxModuleHeaderSetsEntries() const;
 
   std::string ImportedGetFullPath(const std::string& config,
                                   cmStateEnums::ArtifactType artifact) const;
index b56b245..268bfac 100644 (file)
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileDefinitionsCommand.h"
 
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
@@ -28,7 +29,8 @@ private:
                            const std::vector<std::string>& content,
                            bool /*prepend*/, bool /*system*/) override
   {
-    tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content));
+    tgt->AppendProperty("COMPILE_DEFINITIONS", this->Join(content),
+                        this->Makefile->GetBacktrace());
     return true; // Successfully handled.
   }
 
index 885ac74..1cef888 100644 (file)
@@ -8,6 +8,7 @@
 
 class cmFileSet;
 class cmGeneratorTarget;
+class cmInstallCxxModuleBmiGenerator;
 class cmInstallFileSetGenerator;
 class cmInstallFilesGenerator;
 class cmInstallTargetGenerator;
@@ -32,6 +33,7 @@ public:
   cmInstallTargetGenerator* BundleGenerator;
   cmInstallFilesGenerator* HeaderGenerator;
   std::map<cmFileSet*, cmInstallFileSetGenerator*> FileSetGenerators;
+  cmInstallCxxModuleBmiGenerator* CxxModuleBmiGenerator;
   ///@}
 
   bool NamelinkOnly = false;
index b4b4319..cb83873 100644 (file)
@@ -88,7 +88,8 @@ void TargetIncludeDirectoriesImpl::HandleInterfaceContent(
                                                         system);
   if (system) {
     std::string joined = this->Join(content);
-    tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined);
+    tgt->AppendProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", joined,
+                        this->Makefile->GetBacktrace());
   }
 }
 
index af870da..0b123b2 100644 (file)
@@ -622,7 +622,7 @@ bool TLL::HandleLibrary(ProcessingState currentProcessingState,
 void TLL::AppendProperty(std::string const& prop, std::string const& value)
 {
   this->AffectsProperty(prop);
-  this->Target->AppendProperty(prop, value);
+  this->Target->AppendProperty(prop, value, this->Makefile.GetBacktrace());
 }
 
 void TLL::AffectsProperty(std::string const& prop)
@@ -633,14 +633,16 @@ void TLL::AffectsProperty(std::string const& prop)
   // Add a wrapper to the expression to tell LookupLinkItem to look up
   // names in the caller's directory.
   if (this->Props.insert(prop).second) {
-    this->Target->AppendProperty(prop, this->DirectoryId);
+    this->Target->AppendProperty(prop, this->DirectoryId,
+                                 this->Makefile.GetBacktrace());
   }
 }
 
 TLL::~TLL()
 {
   for (std::string const& prop : this->Props) {
-    this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP);
+    this->Target->AppendProperty(prop, CMAKE_DIRECTORY_ID_SEP,
+                                 this->Makefile.GetBacktrace());
   }
 }
 
index a5066cc..4dd158d 100644 (file)
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include "cmGeneratorExpression.h"
+#include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
@@ -48,7 +49,8 @@ private:
   {
     std::string const& base = this->Makefile->GetCurrentSourceDirectory();
     tgt->AppendProperty("PRECOMPILE_HEADERS",
-                        this->Join(ConvertToAbsoluteContent(content, base)));
+                        this->Join(ConvertToAbsoluteContent(content, base)),
+                        this->Makefile->GetBacktrace());
     return true;
   }
 
index b1367e1..53e25b5 100644 (file)
@@ -9,6 +9,8 @@
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
+#include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmListFileCache.h"
@@ -27,8 +29,8 @@ struct FileSetArgs
 {
   std::string Type;
   std::string FileSet;
-  std::vector<std::string> BaseDirs;
-  std::vector<std::string> Files;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> BaseDirs;
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> Files;
 };
 
 auto const FileSetArgsParser = cmArgumentParser<FileSetArgs>()
@@ -77,7 +79,8 @@ private:
   {
     tgt->AppendProperty("SOURCES",
                         this->Join(this->ConvertToAbsoluteContent(
-                          tgt, content, IsInterface::No, CheckCMP0076::Yes)));
+                          tgt, content, IsInterface::No, CheckCMP0076::Yes)),
+                        this->Makefile->GetBacktrace());
     return true; // Successfully handled.
   }
 
@@ -196,7 +199,7 @@ std::vector<std::string> TargetSourcesImpl::ConvertToAbsoluteContent(
 bool TargetSourcesImpl::HandleFileSetMode(
   const std::string& scope, const std::vector<std::string>& content)
 {
-  auto args = FileSetsArgsParser.Parse(content);
+  auto args = FileSetsArgsParser.Parse(content, /*unparsedArguments=*/nullptr);
 
   for (auto& argList : args.FileSets) {
     argList.emplace(argList.begin(), "FILE_SET"_s);
@@ -256,9 +259,31 @@ bool TargetSourcesImpl::HandleOneFileSet(
       this->SetError("Must specify a TYPE when creating file set");
       return false;
     }
-    if (type != "HEADERS"_s) {
-      this->SetError("File set TYPE may only be \"HEADERS\"");
-      return false;
+    bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
+      *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
+
+    if (supportCxx20FileSetTypes) {
+      if (type != "HEADERS"_s && type != "CXX_MODULES"_s &&
+          type != "CXX_MODULE_HEADER_UNITS"_s) {
+        this->SetError(
+          R"(File set TYPE may only be "HEADERS", "CXX_MODULES", or "CXX_MODULE_HEADER_UNITS")");
+        return false;
+      }
+
+      if (cmFileSetVisibilityIsForInterface(visibility) &&
+          !cmFileSetVisibilityIsForSelf(visibility) &&
+          !this->Target->IsImported()) {
+        if (type == "CXX_MODULES"_s || type == "CXX_MODULE_HEADER_UNITS"_s) {
+          this->SetError(
+            R"(File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS" may not have "INTERFACE" visibility)");
+          return false;
+        }
+      }
+    } else {
+      if (type != "HEADERS"_s) {
+        this->SetError("File set TYPE may only be \"HEADERS\"");
+        return false;
+      }
     }
 
     if (args.BaseDirs.empty()) {
@@ -294,17 +319,19 @@ bool TargetSourcesImpl::HandleOneFileSet(
   if (!baseDirectories.empty()) {
     fileSet.first->AddDirectoryEntry(
       BT<std::string>(baseDirectories, this->Makefile->GetBacktrace()));
-    if (type == "HEADERS"_s) {
+    if (type == "HEADERS"_s || type == "CXX_MODULE_HEADER_UNITS"_s) {
       for (auto const& dir : cmExpandedList(baseDirectories)) {
         auto interfaceDirectoriesGenex =
           cmStrCat("$<BUILD_INTERFACE:", dir, ">");
         if (cmFileSetVisibilityIsForSelf(visibility)) {
           this->Target->AppendProperty("INCLUDE_DIRECTORIES",
-                                       interfaceDirectoriesGenex);
+                                       interfaceDirectoriesGenex,
+                                       this->Makefile->GetBacktrace());
         }
         if (cmFileSetVisibilityIsForInterface(visibility)) {
           this->Target->AppendProperty("INTERFACE_INCLUDE_DIRECTORIES",
-                                       interfaceDirectoriesGenex);
+                                       interfaceDirectoriesGenex,
+                                       this->Makefile->GetBacktrace());
         }
       }
     }
index 130c228..a2c4ce1 100644 (file)
@@ -2,34 +2,69 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTryCompileCommand.h"
 
+#include "cmCoreTryCompile.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmRange.h"
+#include "cmState.h"
+#include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
+#include "cmValue.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
-
-// cmTryCompileCommand
-bool cmTryCompileCommand::InitialPass(std::vector<std::string> const& argv,
-                                      cmExecutionStatus&)
+bool cmTryCompileCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status)
 {
-  if (argv.size() < 3) {
+  cmMakefile& mf = status.GetMakefile();
+
+  if (args.size() < 3) {
+    mf.IssueMessage(
+      MessageType::FATAL_ERROR,
+      "The try_compile() command requires at least 3 arguments.");
     return false;
   }
 
-  if (this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
-      cmake::FIND_PACKAGE_MODE) {
-    this->Makefile->IssueMessage(
+  if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) {
+    mf.IssueMessage(
       MessageType::FATAL_ERROR,
       "The try_compile() command is not supported in --find-package mode.");
     return false;
   }
 
-  this->TryCompileCode(argv, false);
+  cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE;
+  cmValue tt = mf.GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE");
+  if (cmNonempty(tt)) {
+    if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) {
+      targetType = cmStateEnums::EXECUTABLE;
+    } else if (*tt ==
+               cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY)) {
+      targetType = cmStateEnums::STATIC_LIBRARY;
+    } else {
+      mf.IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Invalid value '", *tt,
+                 "' for CMAKE_TRY_COMPILE_TARGET_TYPE.  Only '",
+                 cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE),
+                 "' and '",
+                 cmState::GetTargetTypeName(cmStateEnums::STATIC_LIBRARY),
+                 "' are allowed."));
+      return false;
+    }
+  }
+
+  cmCoreTryCompile tc(&mf);
+  cmCoreTryCompile::Arguments arguments =
+    tc.ParseArgs(cmMakeRange(args), false);
+  if (!arguments) {
+    return true;
+  }
+  tc.TryCompileCode(arguments, targetType);
 
   // if They specified clean then we clean up what we can
-  if (this->SrcFileSignature) {
-    if (!this->Makefile->GetCMakeInstance()->GetDebugTryCompile()) {
-      this->CleanupFiles(this->BinaryDirectory);
+  if (tc.SrcFileSignature) {
+    if (!mf.GetCMakeInstance()->GetDebugTryCompile()) {
+      tc.CleanupFiles(tc.BinaryDirectory);
     }
   }
   return true;
index d8cc16e..6a3430b 100644 (file)
@@ -7,33 +7,7 @@
 #include <string>
 #include <vector>
 
-#include <cm/memory>
-
-#include "cmCommand.h"
-#include "cmCoreTryCompile.h"
-
 class cmExecutionStatus;
 
-/** \class cmTryCompileCommand
- * \brief Specifies where to install some files
- *
- * cmTryCompileCommand is used to test if source code can be compiled
- */
-class cmTryCompileCommand : public cmCoreTryCompile
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  std::unique_ptr<cmCommand> Clone() override
-  {
-    return cm::make_unique<cmTryCompileCommand>();
-  }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-};
+bool cmTryCompileCommand(std::vector<std::string> const& args,
+                         cmExecutionStatus& status);
index c82ac64..1e81195 100644 (file)
@@ -4,9 +4,14 @@
 
 #include <cstdio>
 
+#include <cm/optional>
+
 #include "cmsys/FStream.hxx"
 
+#include "cmArgumentParserTypes.h"
+#include "cmCoreTryCompile.h"
 #include "cmDuration.h"
+#include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmValue.h"
 #include "cmake.h"
 
-class cmExecutionStatus;
+namespace {
 
-// cmTryRunCommand
-bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
-                                  cmExecutionStatus&)
+class TryRunCommandImpl : public cmCoreTryCompile
 {
-  if (argv.size() < 4) {
-    return false;
-  }
-
-  if (this->Makefile->GetCMakeInstance()->GetWorkingMode() ==
-      cmake::FIND_PACKAGE_MODE) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      "The try_run() command is not supported in --find-package mode.");
-    return false;
+public:
+  TryRunCommandImpl(cmMakefile* mf)
+    : cmCoreTryCompile(mf)
+  {
   }
 
-  // build an arg list for TryCompile and extract the runArgs,
-  std::vector<std::string> tryCompile;
-
-  this->CompileResultVariable.clear();
-  this->RunResultVariable.clear();
-  this->OutputVariable.clear();
-  this->RunOutputVariable.clear();
-  this->CompileOutputVariable.clear();
-
-  std::string runArgs;
-  unsigned int i;
-  for (i = 1; i < argv.size(); ++i) {
-    if (argv[i] == "ARGS") {
-      ++i;
-      while (i < argv.size() && argv[i] != "COMPILE_DEFINITIONS" &&
-             argv[i] != "CMAKE_FLAGS" && argv[i] != "LINK_OPTIONS" &&
-             argv[i] != "LINK_LIBRARIES") {
-        runArgs += " ";
-        runArgs += argv[i];
-        ++i;
-      }
-      if (i < argv.size()) {
-        tryCompile.push_back(argv[i]);
-      }
-    } else {
-      if (argv[i] == "OUTPUT_VARIABLE") {
-        if (argv.size() <= (i + 1)) {
-          cmSystemTools::Error(
-            "OUTPUT_VARIABLE specified but there is no variable");
-          return false;
-        }
-        i++;
-        this->OutputVariable = argv[i];
-      } else if (argv[i] == "RUN_OUTPUT_VARIABLE") {
-        if (argv.size() <= (i + 1)) {
-          cmSystemTools::Error(
-            "RUN_OUTPUT_VARIABLE specified but there is no variable");
-          return false;
-        }
-        i++;
-        this->RunOutputVariable = argv[i];
-      } else if (argv[i] == "COMPILE_OUTPUT_VARIABLE") {
-        if (argv.size() <= (i + 1)) {
-          cmSystemTools::Error(
-            "COMPILE_OUTPUT_VARIABLE specified but there is no variable");
-          return false;
-        }
-        i++;
-        this->CompileOutputVariable = argv[i];
-      } else if (argv[i] == "WORKING_DIRECTORY") {
-        if (argv.size() <= (i + 1)) {
-          cmSystemTools::Error(
-            "WORKING_DIRECTORY specified but there is no variable");
-          return false;
-        }
-        i++;
-        this->WorkingDirectory = argv[i];
-      } else {
-        tryCompile.push_back(argv[i]);
-      }
-    }
+  bool TryRunCode(std::vector<std::string> const& args);
+
+  void RunExecutable(const std::string& runArgs,
+                     cm::optional<std::string> const& workDir,
+                     std::string* runOutputContents,
+                     std::string* runOutputStdOutContents,
+                     std::string* runOutputStdErrContents);
+  void DoNotRunExecutable(const std::string& runArgs,
+                          const std::string& srcFile,
+                          std::string const& compileResultVariable,
+                          std::string* runOutputContents,
+                          std::string* runOutputStdOutContents,
+                          std::string* runOutputStdErrContents);
+
+  bool NoCache;
+  std::string RunResultVariable;
+};
+
+bool TryRunCommandImpl::TryRunCode(std::vector<std::string> const& argv)
+{
+  this->RunResultVariable = argv[0];
+  cmCoreTryCompile::Arguments arguments =
+    this->ParseArgs(cmMakeRange(argv).advance(1), true);
+  if (!arguments) {
+    return true;
   }
+  this->NoCache = arguments.NoCache;
 
   // although they could be used together, don't allow it, because
   // using OUTPUT_VARIABLE makes crosscompiling harder
-  if (!this->OutputVariable.empty() &&
-      (!this->RunOutputVariable.empty() ||
-       !this->CompileOutputVariable.empty())) {
+  if (arguments.OutputVariable &&
+      (arguments.CompileOutputVariable || arguments.RunOutputVariable ||
+       arguments.RunOutputStdOutVariable ||
+       arguments.RunOutputStdErrVariable)) {
     cmSystemTools::Error(
       "You cannot use OUTPUT_VARIABLE together with COMPILE_OUTPUT_VARIABLE "
-      "or RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE and/or "
-      "RUN_OUTPUT_VARIABLE.");
+      ", RUN_OUTPUT_VARIABLE, RUN_OUTPUT_STDOUT_VARIABLE or "
+      "RUN_OUTPUT_STDERR_VARIABLE. "
+      "Please use only COMPILE_OUTPUT_VARIABLE, RUN_OUTPUT_VARIABLE, "
+      "RUN_OUTPUT_STDOUT_VARIABLE "
+      "and/or RUN_OUTPUT_STDERR_VARIABLE.");
+    return false;
+  }
+
+  if ((arguments.RunOutputStdOutVariable ||
+       arguments.RunOutputStdErrVariable) &&
+      arguments.RunOutputVariable) {
+    cmSystemTools::Error(
+      "You cannot use RUN_OUTPUT_STDOUT_VARIABLE or "
+      "RUN_OUTPUT_STDERR_VARIABLE together "
+      "with RUN_OUTPUT_VARIABLE. Please use only COMPILE_OUTPUT_VARIABLE or "
+      "RUN_OUTPUT_STDOUT_VARIABLE and/or RUN_OUTPUT_STDERR_VARIABLE.");
     return false;
   }
 
-  if (!this->WorkingDirectory.empty()) {
-    if (!cmSystemTools::MakeDirectory(this->WorkingDirectory)) {
+  if (arguments.RunWorkingDirectory) {
+    if (!cmSystemTools::MakeDirectory(*arguments.RunWorkingDirectory)) {
       cmSystemTools::Error(cmStrCat("Error creating working directory \"",
-                                    this->WorkingDirectory, "\"."));
+                                    *arguments.RunWorkingDirectory, "\"."));
       return false;
     }
   }
 
   bool captureRunOutput = false;
-  if (!this->OutputVariable.empty()) {
+  bool captureRunOutputStdOutErr = false;
+  if (arguments.OutputVariable) {
     captureRunOutput = true;
-    tryCompile.emplace_back("OUTPUT_VARIABLE");
-    tryCompile.push_back(this->OutputVariable);
+  } else if (arguments.CompileOutputVariable) {
+    arguments.OutputVariable = arguments.CompileOutputVariable;
   }
-  if (!this->CompileOutputVariable.empty()) {
-    tryCompile.emplace_back("OUTPUT_VARIABLE");
-    tryCompile.push_back(this->CompileOutputVariable);
-  }
-  if (!this->RunOutputVariable.empty()) {
+  if (arguments.RunOutputStdOutVariable || arguments.RunOutputStdErrVariable) {
+    captureRunOutputStdOutErr = true;
+  } else if (arguments.RunOutputVariable) {
     captureRunOutput = true;
   }
 
-  this->RunResultVariable = argv[0];
-  this->CompileResultVariable = argv[1];
-
   // do the try compile
-  int res = this->TryCompileCode(tryCompile, true);
+  bool compiled = this->TryCompileCode(arguments, cmStateEnums::EXECUTABLE);
 
   // now try running the command if it compiled
-  if (!res) {
+  if (compiled) {
     if (this->OutputFile.empty()) {
       cmSystemTools::Error(this->FindErrorMessage);
     } else {
+      std::string runArgs;
+      if (arguments.RunArgs) {
+        runArgs = cmStrCat(" ", cmJoin(*arguments.RunArgs, " "));
+      }
+
       // "run" it and capture the output
       std::string runOutputContents;
+      std::string runOutputStdOutContents;
+      std::string runOutputStdErrContents;
       if (this->Makefile->IsOn("CMAKE_CROSSCOMPILING") &&
           !this->Makefile->IsDefinitionSet("CMAKE_CROSSCOMPILING_EMULATOR")) {
         this->DoNotRunExecutable(
-          runArgs, argv[3], captureRunOutput ? &runOutputContents : nullptr);
+          runArgs, *arguments.SourceDirectoryOrFile,
+          *arguments.CompileResultVariable,
+          captureRunOutput ? &runOutputContents : nullptr,
+          captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable
+            ? &runOutputStdOutContents
+            : nullptr,
+          captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable
+            ? &runOutputStdErrContents
+            : nullptr);
       } else {
-        this->RunExecutable(runArgs, &runOutputContents);
+        this->RunExecutable(
+          runArgs, arguments.RunWorkingDirectory,
+          captureRunOutput ? &runOutputContents : nullptr,
+          captureRunOutputStdOutErr && arguments.RunOutputStdOutVariable
+            ? &runOutputStdOutContents
+            : nullptr,
+          captureRunOutputStdOutErr && arguments.RunOutputStdErrVariable
+            ? &runOutputStdErrContents
+            : nullptr);
       }
 
       // now put the output into the variables
-      if (!this->RunOutputVariable.empty()) {
-        this->Makefile->AddDefinition(this->RunOutputVariable,
+      if (arguments.RunOutputVariable) {
+        this->Makefile->AddDefinition(*arguments.RunOutputVariable,
                                       runOutputContents);
       }
+      if (arguments.RunOutputStdOutVariable) {
+        this->Makefile->AddDefinition(*arguments.RunOutputStdOutVariable,
+                                      runOutputStdOutContents);
+      }
+      if (arguments.RunOutputStdErrVariable) {
+        this->Makefile->AddDefinition(*arguments.RunOutputStdErrVariable,
+                                      runOutputStdErrContents);
+      }
 
-      if (!this->OutputVariable.empty()) {
+      if (arguments.OutputVariable && !arguments.CompileOutputVariable) {
         // if the TryCompileCore saved output in this outputVariable then
         // prepend that output to this output
         cmValue compileOutput =
-          this->Makefile->GetDefinition(this->OutputVariable);
+          this->Makefile->GetDefinition(*arguments.OutputVariable);
         if (compileOutput) {
           runOutputContents = *compileOutput + runOutputContents;
         }
-        this->Makefile->AddDefinition(this->OutputVariable, runOutputContents);
+        this->Makefile->AddDefinition(*arguments.OutputVariable,
+                                      runOutputContents);
       }
     }
   }
@@ -179,8 +184,10 @@ bool cmTryRunCommand::InitialPass(std::vector<std::string> const& argv,
   return true;
 }
 
-void cmTryRunCommand::RunExecutable(const std::string& runArgs,
-                                    std::string* out)
+void TryRunCommandImpl::RunExecutable(const std::string& runArgs,
+                                      cm::optional<std::string> const& workDir,
+                                      std::string* out, std::string* stdOut,
+                                      std::string* stdErr)
 {
   int retVal = -1;
 
@@ -204,9 +211,10 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
     finalCommand += runArgs;
   }
   bool worked = cmSystemTools::RunSingleCommand(
-    finalCommand, out, out, &retVal,
-    this->WorkingDirectory.empty() ? nullptr : this->WorkingDirectory.c_str(),
-    cmSystemTools::OUTPUT_NONE, cmDuration::zero());
+    finalCommand, stdOut || stdErr ? stdOut : out,
+    stdOut || stdErr ? stdErr : out, &retVal,
+    workDir ? workDir->c_str() : nullptr, cmSystemTools::OUTPUT_NONE,
+    cmDuration::zero());
   // set the run var
   char retChar[16];
   const char* retStr;
@@ -216,18 +224,23 @@ void cmTryRunCommand::RunExecutable(const std::string& runArgs,
   } else {
     retStr = "FAILED_TO_RUN";
   }
-  this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr,
-                                     "Result of try_run()",
-                                     cmStateEnums::INTERNAL);
+  if (this->NoCache) {
+    this->Makefile->AddDefinition(this->RunResultVariable, retStr);
+  } else {
+    this->Makefile->AddCacheDefinition(this->RunResultVariable, retStr,
+                                       "Result of try_run()",
+                                       cmStateEnums::INTERNAL);
+  }
 }
 
 /* This is only used when cross compiling. Instead of running the
  executable, two cache variables are created which will hold the results
  the executable would have produced.
 */
-void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
-                                         const std::string& srcFile,
-                                         std::string* out)
+void TryRunCommandImpl::DoNotRunExecutable(
+  const std::string& runArgs, const std::string& srcFile,
+  std::string const& compileResultVariable, std::string* out,
+  std::string* stdOut, std::string* stdErr)
 {
   // copy the executable out of the CMakeFiles/ directory, so it is not
   // removed at the end of try_run() and the user can run it manually
@@ -246,6 +259,10 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
 
   std::string internalRunOutputName =
     this->RunResultVariable + "__TRYRUN_OUTPUT";
+  std::string internalRunOutputStdOutName =
+    this->RunResultVariable + "__TRYRUN_OUTPUT_STDOUT";
+  std::string internalRunOutputStdErrName =
+    this->RunResultVariable + "__TRYRUN_OUTPUT_STDERR";
   bool error = false;
 
   if (!this->Makefile->GetDefinition(this->RunResultVariable)) {
@@ -269,7 +286,51 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
   }
 
   // is the output from the executable used ?
-  if (out) {
+  if (stdOut || stdErr) {
+    if (!this->Makefile->GetDefinition(internalRunOutputStdOutName)) {
+      // if the variables doesn't exist, create it with a helpful error text
+      // and mark it as advanced
+      std::string comment = cmStrCat(
+        "Output of try_run(), contains the text, which the executable "
+        "would have printed on stdout on its target platform.\n",
+        detailsString);
+
+      this->Makefile->AddCacheDefinition(
+        internalRunOutputStdOutName, "PLEASE_FILL_OUT-NOTFOUND",
+        comment.c_str(), cmStateEnums::STRING);
+      cmState* state = this->Makefile->GetState();
+      cmValue existing =
+        state->GetCacheEntryValue(internalRunOutputStdOutName);
+      if (existing) {
+        state->SetCacheEntryProperty(internalRunOutputStdOutName, "ADVANCED",
+                                     "1");
+      }
+
+      error = true;
+    }
+
+    if (!this->Makefile->GetDefinition(internalRunOutputStdErrName)) {
+      // if the variables doesn't exist, create it with a helpful error text
+      // and mark it as advanced
+      std::string comment = cmStrCat(
+        "Output of try_run(), contains the text, which the executable "
+        "would have printed on stderr on its target platform.\n",
+        detailsString);
+
+      this->Makefile->AddCacheDefinition(
+        internalRunOutputStdErrName, "PLEASE_FILL_OUT-NOTFOUND",
+        comment.c_str(), cmStateEnums::STRING);
+      cmState* state = this->Makefile->GetState();
+      cmValue existing =
+        state->GetCacheEntryValue(internalRunOutputStdErrName);
+      if (existing) {
+        state->SetCacheEntryProperty(internalRunOutputStdErrName, "ADVANCED",
+                                     "1");
+      }
+
+      error = true;
+    }
+  } else if (out) {
     if (!this->Makefile->GetDefinition(internalRunOutputName)) {
       // if the variables doesn't exist, create it with a helpful error text
       // and mark it as advanced
@@ -317,7 +378,34 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
                  " to\n"
                  "   the exit code (in many cases 0 for success), otherwise "
                  "enter \"FAILED_TO_RUN\".\n");
-      if (out) {
+      if (stdOut || stdErr) {
+        if (stdOut) {
+          comment += internalRunOutputStdOutName;
+          comment +=
+            "\n   contains the text the executable "
+            "would have printed on stdout.\n"
+            "   If the executable would not have been able to run, set ";
+          comment += internalRunOutputStdOutName;
+          comment += " empty.\n"
+                     "   Otherwise check if the output is evaluated by the "
+                     "calling CMake code. If so,\n"
+                     "   check what the source file would have printed when "
+                     "called with the given arguments.\n";
+        }
+        if (stdErr) {
+          comment += internalRunOutputStdErrName;
+          comment +=
+            "\n   contains the text the executable "
+            "would have printed on stderr.\n"
+            "   If the executable would not have been able to run, set ";
+          comment += internalRunOutputStdErrName;
+          comment += " empty.\n"
+                     "   Otherwise check if the output is evaluated by the "
+                     "calling CMake code. If so,\n"
+                     "   check what the source file would have printed when "
+                     "called with the given arguments.\n";
+        }
+      } else if (out) {
         comment += internalRunOutputName;
         comment +=
           "\n   contains the text the executable "
@@ -330,8 +418,9 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
                    "   check what the source file would have printed when "
                    "called with the given arguments.\n";
       }
+
       comment += "The ";
-      comment += this->CompileResultVariable;
+      comment += compileResultVariable;
       comment += " variable holds the build result for this try_run().\n\n"
                  "Source file   : ";
       comment += srcFile + "\n";
@@ -370,7 +459,37 @@ void cmTryRunCommand::DoNotRunExecutable(const std::string& runArgs,
     return;
   }
 
-  if (out) {
+  if (stdOut || stdErr) {
+    if (stdOut) {
+      (*stdOut) = *this->Makefile->GetDefinition(internalRunOutputStdOutName);
+    }
+    if (stdErr) {
+      (*stdErr) = *this->Makefile->GetDefinition(internalRunOutputStdErrName);
+    }
+  } else if (out) {
     (*out) = *this->Makefile->GetDefinition(internalRunOutputName);
   }
 }
+}
+
+bool cmTryRunCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status)
+{
+  cmMakefile& mf = status.GetMakefile();
+
+  if (args.size() < 4) {
+    mf.IssueMessage(MessageType::FATAL_ERROR,
+                    "The try_run() command requires at least 4 arguments.");
+    return false;
+  }
+
+  if (mf.GetCMakeInstance()->GetWorkingMode() == cmake::FIND_PACKAGE_MODE) {
+    mf.IssueMessage(
+      MessageType::FATAL_ERROR,
+      "The try_run() command is not supported in --find-package mode.");
+    return false;
+  }
+
+  TryRunCommandImpl tr(&mf);
+  return tr.TryRunCode(args);
+}
index d45acd8..38e3638 100644 (file)
@@ -7,47 +7,7 @@
 #include <string>
 #include <vector>
 
-#include <cm/memory>
-
-#include "cmCommand.h"
-#include "cmCoreTryCompile.h"
-
 class cmExecutionStatus;
 
-/** \class cmTryRunCommand
- * \brief Specifies where to install some files
- *
- * cmTryRunCommand is used to test if source code can be compiled
- */
-class cmTryRunCommand : public cmCoreTryCompile
-{
-public:
-  /**
-   * This is a virtual constructor for the command.
-   */
-  std::unique_ptr<cmCommand> Clone() override
-  {
-    return cm::make_unique<cmTryRunCommand>();
-  }
-
-  /**
-   * This is called when the command is first encountered in
-   * the CMakeLists.txt file.
-   */
-  bool InitialPass(std::vector<std::string> const& args,
-                   cmExecutionStatus& status) override;
-
-private:
-  void RunExecutable(const std::string& runArgs,
-                     std::string* runOutputContents);
-  void DoNotRunExecutable(const std::string& runArgs,
-                          const std::string& srcFile,
-                          std::string* runOutputContents);
-
-  std::string CompileResultVariable;
-  std::string RunResultVariable;
-  std::string OutputVariable;
-  std::string RunOutputVariable;
-  std::string CompileOutputVariable;
-  std::string WorkingDirectory;
-};
+bool cmTryRunCommand(std::vector<std::string> const& args,
+                     cmExecutionStatus& status);
index 9f3d620..4cfb561 100644 (file)
@@ -354,6 +354,18 @@ std::ostream& cmVisualStudio10TargetGenerator::Elem::WriteString(
 
 void cmVisualStudio10TargetGenerator::Generate()
 {
+  for (std::string const& config : this->Configurations) {
+    this->GeneratorTarget->CheckCxxModuleStatus(config);
+  }
+
+  if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("The \"", this->GeneratorTarget->GetName(),
+               "\" target contains C++ module sources which are not supported "
+               "by the generator"));
+  }
+
   this->ProjectType = computeProjectType(this->GeneratorTarget);
   this->Managed = this->ProjectType == VsProjectType::csproj;
   const std::string ProjectFileExtension =
@@ -483,7 +495,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
       e1.Element("PreferredToolArchitecture", hostArch);
     }
 
-    // ALL_BUILD and ZERO_CHECK projects transitively include
+    // The ALL_BUILD, PACKAGE, and ZERO_CHECK projects transitively include
     // Microsoft.Common.CurrentVersion.targets which triggers Target
     // ResolveNugetPackageAssets when SDK-style targets are in the project.
     // However, these projects have no nuget packages to reference and the
@@ -491,7 +503,7 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
     // Setting ResolveNugetPackages to false skips this target and the build
     // succeeds.
     cm::string_view targetName{ this->GeneratorTarget->GetName() };
-    if (targetName == "ALL_BUILD" ||
+    if (targetName == "ALL_BUILD" || targetName == "PACKAGE" ||
         targetName == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
       Elem e1(e0, "PropertyGroup");
       e1.Element("ResolveNugetPackages", "false");
@@ -899,9 +911,11 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
         e1.Element("TargetFrameworks", *targetFramework);
       } else {
         e1.Element("TargetFramework", *targetFramework);
+        e1.Element("AppendTargetFrameworkToOutputPath", "false");
       }
     } else {
       e1.Element("TargetFramework", "net5.0");
+      e1.Element("AppendTargetFrameworkToOutputPath", "false");
     }
 
     std::string outputType;
@@ -957,6 +971,10 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
     std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/";
     ConvertToWindowsSlash(outDir);
     e1.Element("OutputPath", outDir);
+
+    Options& o = *(this->ClOptions[config]);
+    OptionsHelper oh(o, e1);
+    oh.OutputFlagMap();
   }
 
   this->WriteDotNetDocumentationFile(e0);
@@ -1507,6 +1525,10 @@ void cmVisualStudio10TargetGenerator::WriteMSToolConfigurationValues(
       this->ASanEnabledConfigurations.end()) {
     e1.Element("EnableAsan", "true");
   }
+  if (this->FuzzerEnabledConfigurations.find(config) !=
+      this->FuzzerEnabledConfigurations.end()) {
+    e1.Element("EnableFuzzer", "true");
+  }
   {
     auto s = this->SpectreMitigation.find(config);
     if (s != this->SpectreMitigation.end()) {
@@ -1788,11 +1810,8 @@ void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp(
   e2.WritePlatformConfigTag("Command", cond, script);
   e2.WritePlatformConfigTag("AdditionalInputs", cond, additional_inputs);
   e2.WritePlatformConfigTag("Outputs", cond, outputs);
-  if (this->LocalGenerator->GetVersion() >
-      cmGlobalVisualStudioGenerator::VSVersion::VS10) {
-    // VS >= 11 let us turn off linking of custom command outputs.
-    e2.WritePlatformConfigTag("LinkObjects", cond, "false");
-  }
+  // Turn off linking of custom command outputs.
+  e2.WritePlatformConfigTag("LinkObjects", cond, "false");
   if (symbolic &&
       this->LocalGenerator->GetVersion() >=
         cmGlobalVisualStudioGenerator::VSVersion::VS16) {
@@ -2357,28 +2376,6 @@ void cmVisualStudio10TargetGenerator::WriteSource(Elem& e2,
   // we must use relative paths.
   bool forceRelative = sf->GetLanguage() == "CUDA";
   std::string sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative);
-  if (this->LocalGenerator->GetVersion() ==
-        cmGlobalVisualStudioGenerator::VSVersion::VS10 &&
-      cmSystemTools::FileIsFullPath(sourceFile)) {
-    // Normal path conversion resulted in a full path.  VS 10 (but not 11)
-    // refuses to show the property page in the IDE for a source file with a
-    // full path (not starting in a '.' or '/' AFAICT).  CMake <= 2.8.4 used a
-    // relative path but to allow deeper build trees CMake 2.8.[5678] used a
-    // full path except for custom commands.  Custom commands do not work
-    // without a relative path, but they do not seem to be involved in tools
-    // with the above behavior.  For other sources we now use a relative path
-    // when the combined path will not be too long so property pages appear.
-    std::string sourceRel = this->ConvertPath(sf->GetFullPath(), true);
-    size_t const maxLen = 250;
-    if (sf->GetCustomCommand() ||
-        ((this->LocalGenerator->GetCurrentBinaryDirectory().length() + 1 +
-          sourceRel.length()) <= maxLen)) {
-      forceRelative = true;
-      sourceFile = sourceRel;
-    } else {
-      this->GlobalGenerator->PathTooLong(this->GeneratorTarget, sf, sourceRel);
-    }
-  }
   ConvertToWindowsSlash(sourceFile);
   e2.Attribute("Include", sourceFile);
 
@@ -2875,7 +2872,7 @@ void cmVisualStudio10TargetGenerator::WritePathAndIncrementalLinkOptions(
   Elem& e0)
 {
   cmStateEnums::TargetType ttype = this->GeneratorTarget->GetType();
-  if (ttype > cmStateEnums::GLOBAL_TARGET) {
+  if (ttype > cmStateEnums::INTERFACE_LIBRARY) {
     return;
   }
   if (this->ProjectType == VsProjectType::csproj) {
@@ -3117,6 +3114,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
   this->LangForClCompile = langForClCompile;
   if (!langForClCompile.empty()) {
     this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
+                                           cmBuildStep::Compile,
                                            langForClCompile, configName);
     this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget,
                                             langForClCompile, configName);
@@ -3128,10 +3126,17 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
   }
 
   // Check if ASan is enabled.
-  if (flags.find("/fsanitize=address") != std::string::npos) {
+  if (flags.find("/fsanitize=address") != std::string::npos ||
+      flags.find("-fsanitize=address") != std::string::npos) {
     this->ASanEnabledConfigurations.insert(configName);
   }
 
+  // Check if (lib)Fuzzer is enabled.
+  if (flags.find("/fsanitize=fuzzer") != std::string::npos ||
+      flags.find("-fsanitize=fuzzer") != std::string::npos) {
+    this->FuzzerEnabledConfigurations.insert(configName);
+  }
+
   // Precompile Headers
   std::string pchHeader =
     this->GeneratorTarget->GetPchHeader(configName, linkLanguage);
@@ -3173,7 +3178,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions(
     // anymore, because cmGeneratorTarget may not be aware that the
     // target uses C++/CLI.
     if (flags.find("/clr") != std::string::npos ||
-        defineFlags.find("/clr") != std::string::npos) {
+        flags.find("-clr") != std::string::npos ||
+        defineFlags.find("/clr") != std::string::npos ||
+        defineFlags.find("-clr") != std::string::npos) {
       if (configName == this->Configurations[0]) {
         std::string message = "For the target \"" +
           this->GeneratorTarget->GetName() +
@@ -3492,8 +3499,8 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions(
 
   // Get compile flags for CUDA in this directory.
   std::string flags;
-  this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget, "CUDA",
-                                         configName);
+  this->LocalGenerator->AddLanguageFlags(
+    flags, this->GeneratorTarget, cmBuildStep::Compile, "CUDA", configName);
   this->LocalGenerator->AddCompileOptions(flags, this->GeneratorTarget, "CUDA",
                                           configName);
 
@@ -3685,21 +3692,28 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions(
   this->GeneratorTarget->GetLinkOptions(linkOpts, configName, "CUDA");
   // LINK_OPTIONS are escaped.
   this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts);
+
+  cmComputeLinkInformation* pcli =
+    this->GeneratorTarget->GetLinkInformation(configName);
+  if (doDeviceLinking && pcli) {
+
+    cmLinkLineDeviceComputer computer(
+      this->LocalGenerator,
+      this->LocalGenerator->GetStateSnapshot().GetDirectory());
+    std::string ignored_;
+    this->LocalGenerator->GetDeviceLinkFlags(computer, configName, ignored_,
+                                             linkFlags, ignored_, ignored_,
+                                             this->GeneratorTarget);
+
+    this->LocalGenerator->AddLanguageFlagsForLinking(
+      linkFlags, this->GeneratorTarget, "CUDA", configName);
+  }
   cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags);
 
   // For static libraries that have device linking enabled compute
   // the  libraries
   if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY &&
       doDeviceLinking) {
-    cmComputeLinkInformation* pcli =
-      this->GeneratorTarget->GetLinkInformation(configName);
-    if (!pcli) {
-      cmSystemTools::Error(
-        "CMake can not compute cmComputeLinkInformation for target: " +
-        this->Name);
-      return false;
-    }
-
     cmComputeLinkInformation& cli = *pcli;
     cmLinkLineDeviceComputer computer(
       this->LocalGenerator,
@@ -3757,7 +3771,8 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions(
 
   std::string flags;
   this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
-                                         "ASM_MASM", configName);
+                                         cmBuildStep::Compile, "ASM_MASM",
+                                         configName);
 
   masmOptions.Parse(flags);
 
@@ -3809,7 +3824,8 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions(
 
   std::string flags;
   this->LocalGenerator->AddLanguageFlags(flags, this->GeneratorTarget,
-                                         "ASM_NASM", configName);
+                                         cmBuildStep::Compile, "ASM_NASM",
+                                         configName);
   flags += " -f";
   flags += this->Makefile->GetSafeDefinition("CMAKE_ASM_NASM_OBJECT_FORMAT");
   nasmOptions.Parse(flags);
index 8d777a3..17dcecd 100644 (file)
@@ -231,6 +231,7 @@ private:
   bool TargetCompileAsWinRT;
   std::set<std::string> IPOEnabledConfigurations;
   std::set<std::string> ASanEnabledConfigurations;
+  std::set<std::string> FuzzerEnabledConfigurations;
   std::map<std::string, std::string> SpectreMitigation;
   cmGlobalVisualStudio10Generator* const GlobalGenerator;
   cmLocalVisualStudio10Generator* const LocalGenerator;
index 00c65ed..e6f5ece 100644 (file)
@@ -75,7 +75,6 @@ void cmVisualStudioGeneratorOptions::FixExceptionHandlingDefault()
   // the flag to disable exception handling.  When the user does
   // remove the flag we need to override the IDE default of on.
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS10:
     case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -101,14 +100,12 @@ void cmVisualStudioGeneratorOptions::SetVerboseMakefile(bool verbose)
   // to the generated project to disable logo suppression.  Otherwise
   // the GUI default is to enable suppression.
   //
-  // On Visual Studio 10 (and later!), the value of this attribute should be
-  // an empty string, instead of "FALSE", in order to avoid a warning:
-  //   "cl ... warning D9035: option 'nologo-' has been deprecated"
-  //
+  // On Visual Studio 9, the value of this attribute should be
+  // "FALSE", instead of an empty string.
   if (verbose &&
       this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
     this->FlagMap["SuppressStartupBanner"] =
-      this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10 ? "FALSE"
+      this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9 ? "FALSE"
                                                                      : "";
   }
 }
@@ -161,71 +158,12 @@ bool cmVisualStudioGeneratorOptions::UsingSBCS() const
 
 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
 {
-  // Extract temporary values stored by our flag table.
-  FlagValue arch = this->TakeFlag("cmake-temp-arch");
-  FlagValue code = this->TakeFlag("cmake-temp-code");
-  FlagValue gencode = this->TakeFlag("cmake-temp-gencode");
-
-  // No -code allowed without -arch.
-  if (arch.empty()) {
-    code.clear();
-  }
-
-  // Create a CodeGeneration field with [arch],[code] syntax in each entry.
-  // CUDA will convert it to `-gencode=arch=[arch],code="[code],[arch]"`.
-  FlagValue& result = this->FlagMap["CodeGeneration"];
-
-  // If there are no flags, leave the CodeGeneration field empty.
-  if (arch.empty() && gencode.empty()) {
-    return;
-  }
-
-  // First entries for the -arch=<arch> [-code=<code>,...] pair.
-  if (!arch.empty()) {
-    std::string arch_name = arch[0];
-    if (arch_name == "all" || arch_name == "all-major" ||
-        arch_name == "native") {
-      AppendFlagString("AdditionalOptions", "-arch=" + arch_name);
-      return;
-    }
-    std::vector<std::string> codes;
-    if (!code.empty()) {
-      codes = cmTokenize(code[0], ",");
-    }
-    if (codes.empty()) {
-      codes.push_back(arch_name);
-      // nvcc -arch=<arch> has a special case that allows a real
-      // architecture to be specified instead of a virtual arch.
-      // It translates to -arch=<virtual> -code=<real>.
-      cmSystemTools::ReplaceString(arch_name, "sm_", "compute_");
-    }
-    for (std::string const& c : codes) {
-      std::string entry = arch_name + "," + c;
-      result.push_back(entry);
-    }
-  }
-
-  // Now add entries for the following signatures:
-  // -gencode=<arch>,<code>
-  // -gencode=<arch>,[<code1>,<code2>]
-  // -gencode=<arch>,"<code1>,<code2>"
-  for (std::string const& e : gencode) {
-    std::string entry = e;
-    cmSystemTools::ReplaceString(entry, "arch=", "");
-    cmSystemTools::ReplaceString(entry, "code=", "");
-    cmSystemTools::ReplaceString(entry, "[", "");
-    cmSystemTools::ReplaceString(entry, "]", "");
-    cmSystemTools::ReplaceString(entry, "\"", "");
-
-    std::vector<std::string> codes = cmTokenize(entry, ",");
-    if (codes.size() >= 2) {
-      auto gencode_arch = cm::cbegin(codes);
-      for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) {
-        std::string code_entry = *gencode_arch + "," + *ci;
-        result.push_back(code_entry);
-      }
-    }
-  }
+  // Create an empty CodeGeneration field, and pass the the actual
+  // compile flags via additional options so that we have consistent
+  // behavior and avoid issues with MSBuild extensions injecting
+  // virtual code when we request real only.
+  FlagValue& code_gen_flag = this->FlagMap["CodeGeneration"];
+  code_gen_flag = "";
 }
 
 void cmVisualStudioGeneratorOptions::FixManifestUACFlags()
@@ -432,7 +370,7 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
   }
 
   std::ostringstream oss;
-  if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+  if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
     oss << "%(" << tag << ")";
   }
   std::vector<std::string>::const_iterator de =
@@ -440,13 +378,13 @@ void cmVisualStudioGeneratorOptions::OutputPreprocessorDefinitions(
   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
     // Escape the definition for the compiler.
     std::string define;
-    if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+    if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9) {
       define = this->LocalGenerator->EscapeForShell(di, true);
     } else {
       define = di;
     }
     // Escape this flag for the MSBuild.
-    if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+    if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
       cmVS10EscapeForMSBuild(define);
       if (lang == "RC") {
         cmSystemTools::ReplaceString(define, "\"", "\\\"");
@@ -488,7 +426,7 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
     }
 
     // Escape this include for the MSBuild.
-    if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+    if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
       cmVS10EscapeForMSBuild(include);
     }
     oss << sep << include;
@@ -500,7 +438,7 @@ void cmVisualStudioGeneratorOptions::OutputAdditionalIncludeDirectories(
     }
   }
 
-  if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+  if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
     oss << sep << "%(" << tag << ")";
   }
 
@@ -514,7 +452,7 @@ void cmVisualStudioGeneratorOptions::OutputFlagMap(std::ostream& fout,
     std::ostringstream oss;
     const char* sep = "";
     for (std::string i : m.second) {
-      if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS10) {
+      if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
         cmVS10EscapeForMSBuild(i);
       }
       oss << sep << i;
index fb94273..e80d1fc 100644 (file)
@@ -96,7 +96,7 @@ bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
       cmExecutionStatus status(mf);
       mf.ExecuteCommand(fn, status);
       if (status.GetReturnInvoked()) {
-        inStatus.SetReturnInvoked();
+        inStatus.SetReturnInvoked(status.GetReturnVariables());
         return true;
       }
       if (status.GetBreakInvoked()) {
index 2eed297..f4a0e7b 100644 (file)
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include "cmConfigure.h" // IWYU pragma: keep
+
 #include <cstdint> // IWYU pragma: keep
 #include <string>
 #include <vector>
index adc500a..e727d22 100644 (file)
@@ -67,9 +67,14 @@ void cmXCodeScheme::WriteXCodeXCScheme(std::ostream& fout,
   xout.Attribute("LastUpgradeVersion", WriteVersionString());
   xout.Attribute("version", "1.3");
 
+  cmValue propDftCfg =
+    Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_CONFIGURATION");
+  std::string launchConfiguration =
+    !propDftCfg.IsEmpty() ? *propDftCfg : "Debug";
+
   WriteBuildAction(xout, container);
   WriteTestAction(xout, FindConfiguration("Debug"), container);
-  WriteLaunchAction(xout, FindConfiguration("Debug"), container);
+  WriteLaunchAction(xout, FindConfiguration(launchConfiguration), container);
   WriteProfileAction(xout, FindConfiguration("Release"));
   WriteAnalyzeAction(xout, FindConfiguration("Debug"));
   WriteArchiveAction(xout, FindConfiguration("Release"));
@@ -147,7 +152,15 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
                  "Xcode.DebuggerFoundation.Debugger.LLDB");
   xout.Attribute("selectedLauncherIdentifier",
                  "Xcode.DebuggerFoundation.Launcher.LLDB");
-  xout.Attribute("launchStyle", "0");
+  {
+    cmValue launchMode =
+      this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE");
+    std::string value = "0"; // == 'AUTO'
+    if (launchMode && *launchMode == "WAIT") {
+      value = "1";
+    }
+    xout.Attribute("launchStyle", value);
+  }
   WriteCustomWorkingDirectory(xout, configuration);
 
   xout.Attribute("ignoresPersistentStateOnLaunch", "NO");
@@ -190,6 +203,23 @@ void cmXCodeScheme::WriteLaunchAction(cmXMLWriter& xout,
 
   WriteLaunchActionAttribute(xout, "enableUBSanitizer",
                              "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER");
+
+  if (cmValue value = this->Target->GetTarget()->GetProperty(
+        "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION")) {
+    if (value.IsOff()) {
+      xout.Attribute("enableGPUValidationMode",
+                     "1"); // unset means YES, "1" means NO
+    }
+  }
+
+  if (cmValue value = this->Target->GetTarget()->GetProperty(
+        "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION")) {
+    if (value.IsOn()) {
+      xout.Attribute("enableGPUShaderValidationMode",
+                     "2"); // unset means NO, "2" means YES
+    }
+  }
+
   WriteLaunchActionAttribute(
     xout, "stopOnEveryUBSanitizerIssue",
     "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP");
index 8115306..2d2a377 100644 (file)
@@ -19,6 +19,12 @@ codecvt::codecvt(Encoding e)
 #endif
 {
   switch (e) {
+    case codecvt::ConsoleOutput:
+#if defined(_WIN32)
+      m_noconv = false;
+      m_codepage = GetConsoleOutputCP();
+      break;
+#endif
     case codecvt::ANSI:
 #if defined(_WIN32)
       m_noconv = false;
index 9af083f..f628de7 100644 (file)
@@ -15,7 +15,8 @@ public:
     None,
     UTF8,
     UTF8_WITH_BOM,
-    ANSI
+    ANSI,
+    ConsoleOutput,
   };
 
 #ifndef CMAKE_BOOTSTRAP
index 1c1cab3..013a87b 100644 (file)
@@ -3,6 +3,7 @@
 #include "cmake.h"
 
 #include <algorithm>
+#include <array>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
+#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32)
+#  include <unistd.h>
+#endif
+
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
@@ -55,6 +60,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
+#include "cmUVProcessChain.h"
 #include "cmUtils.hxx"
 #include "cmVersionConfig.h"
 #include "cmWorkingDirectory.h"
@@ -62,6 +68,7 @@
 #if !defined(CMAKE_BOOTSTRAP)
 #  include <unordered_map>
 
+#  include <cm3p/curl/curl.h>
 #  include <cm3p/json/writer.h>
 
 #  include "cmFileAPI.h"
@@ -81,7 +88,6 @@
 #    include "cmGlobalBorlandMakefileGenerator.h"
 #    include "cmGlobalJOMMakefileGenerator.h"
 #    include "cmGlobalNMakeMakefileGenerator.h"
-#    include "cmGlobalVisualStudio10Generator.h"
 #    include "cmGlobalVisualStudio11Generator.h"
 #    include "cmGlobalVisualStudio12Generator.h"
 #    include "cmGlobalVisualStudio14Generator.h"
@@ -251,6 +257,8 @@ Json::Value cmake::ReportCapabilitiesJson() const
   std::vector<cmake::GeneratorInfo> generatorInfoList;
   this->GetRegisteredGenerators(generatorInfoList);
 
+  auto* curlVersion = curl_version_info(CURLVERSION_FIRST);
+
   JsonValueMapType generatorMap;
   for (cmake::GeneratorInfo const& gi : generatorInfoList) {
     if (gi.isAlias) { // skip aliases, they are there for compatibility reasons
@@ -285,6 +293,7 @@ Json::Value cmake::ReportCapabilitiesJson() const
   obj["generators"] = generators;
   obj["fileApi"] = cmFileAPI::ReportCapabilities();
   obj["serverMode"] = false;
+  obj["tls"] = static_cast<bool>(curlVersion->features & CURL_VERSION_SSL);
 
   return obj;
 }
@@ -777,6 +786,8 @@ enum class ListPresets
   Configure,
   Build,
   Test,
+  Package,
+  Workflow,
   All,
 };
 }
@@ -951,7 +962,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                      CommandArgument::Values::One,
                      [](std::string const& value, cmake* state) -> bool {
                        const auto logLevel = StringToLogLevel(value);
-                       if (logLevel == LogLevel::LOG_UNDEFINED) {
+                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
                          cmSystemTools::Error(
                            "Invalid level specified for --log-level");
                          return false;
@@ -967,7 +978,7 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                      CommandArgument::Values::One,
                      [](std::string const& value, cmake* state) -> bool {
                        const auto logLevel = StringToLogLevel(value);
-                       if (logLevel == LogLevel::LOG_UNDEFINED) {
+                       if (logLevel == Message::LogLevel::LOG_UNDEFINED) {
                          cmSystemTools::Error(
                            "Invalid level specified for --loglevel");
                          return false;
@@ -1132,12 +1143,16 @@ void cmake::SetArgs(const std::vector<std::string>& args)
         listPresets = ListPresets::Build;
       } else if (value == "test") {
         listPresets = ListPresets::Test;
+      } else if (value == "package") {
+        listPresets = ListPresets::Package;
+      } else if (value == "workflow") {
+        listPresets = ListPresets::Workflow;
       } else if (value == "all") {
         listPresets = ListPresets::All;
       } else {
         cmSystemTools::Error(
           "Invalid value specified for --list-presets.\n"
-          "Valid values are configure, build, test, or all. "
+          "Valid values are configure, build, test, package, or all. "
           "When no value is passed the default is configure.");
         return false;
       }
@@ -1282,9 +1297,13 @@ void cmake::SetArgs(const std::vector<std::string>& args)
     cmCMakePresetsGraph presetsGraph;
     auto result = presetsGraph.ReadProjectPresets(this->GetHomeDirectory());
     if (result != cmCMakePresetsGraph::ReadFileResult::READ_OK) {
-      cmSystemTools::Error(
+      std::string errorMsg =
         cmStrCat("Could not read presets from ", this->GetHomeDirectory(),
-                 ": ", cmCMakePresetsGraph::ResultToString(result)));
+                 ": ", cmCMakePresetsGraph::ResultToString(result));
+      if (!presetsGraph.errors.empty()) {
+        errorMsg = cmStrCat(errorMsg, "\nErrors:\n", presetsGraph.errors);
+      }
+      cmSystemTools::Error(errorMsg);
       return;
     }
 
@@ -1295,6 +1314,10 @@ void cmake::SetArgs(const std::vector<std::string>& args)
         presetsGraph.PrintBuildPresetList();
       } else if (listPresets == ListPresets::Test) {
         presetsGraph.PrintTestPresetList();
+      } else if (listPresets == ListPresets::Package) {
+        presetsGraph.PrintPackagePresetList();
+      } else if (listPresets == ListPresets::Workflow) {
+        presetsGraph.PrintWorkflowPresetList();
       } else if (listPresets == ListPresets::All) {
         presetsGraph.PrintAllPresets();
       }
@@ -1398,23 +1421,52 @@ void cmake::SetArgs(const std::vector<std::string>& args)
 #endif
 }
 
-cmake::LogLevel cmake::StringToLogLevel(const std::string& levelStr)
-{
-  using LevelsPair = std::pair<std::string, LogLevel>;
-  static const std::vector<LevelsPair> levels = {
-    { "error", LogLevel::LOG_ERROR },     { "warning", LogLevel::LOG_WARNING },
-    { "notice", LogLevel::LOG_NOTICE },   { "status", LogLevel::LOG_STATUS },
-    { "verbose", LogLevel::LOG_VERBOSE }, { "debug", LogLevel::LOG_DEBUG },
-    { "trace", LogLevel::LOG_TRACE }
+namespace {
+using LevelsPair = std::pair<cm::string_view, Message::LogLevel>;
+using LevelsPairArray = std::array<LevelsPair, 7>;
+const LevelsPairArray& getStringToLogLevelPairs()
+{
+  static const LevelsPairArray levels = {
+    { { "error", Message::LogLevel::LOG_ERROR },
+      { "warning", Message::LogLevel::LOG_WARNING },
+      { "notice", Message::LogLevel::LOG_NOTICE },
+      { "status", Message::LogLevel::LOG_STATUS },
+      { "verbose", Message::LogLevel::LOG_VERBOSE },
+      { "debug", Message::LogLevel::LOG_DEBUG },
+      { "trace", Message::LogLevel::LOG_TRACE } }
   };
+  return levels;
+}
+} // namespace
 
-  const auto levelStrLowCase = cmSystemTools::LowerCase(levelStr);
+Message::LogLevel cmake::StringToLogLevel(cm::string_view levelStr)
+{
+  const LevelsPairArray& levels = getStringToLogLevelPairs();
+
+  const auto levelStrLowCase =
+    cmSystemTools::LowerCase(std::string{ levelStr });
 
+  // NOLINTNEXTLINE(readability-qualified-auto)
   const auto it = std::find_if(levels.cbegin(), levels.cend(),
                                [&levelStrLowCase](const LevelsPair& p) {
                                  return p.first == levelStrLowCase;
                                });
-  return (it != levels.cend()) ? it->second : LogLevel::LOG_UNDEFINED;
+  return (it != levels.cend()) ? it->second : Message::LogLevel::LOG_UNDEFINED;
+}
+
+std::string cmake::LogLevelToString(Message::LogLevel level)
+{
+  const LevelsPairArray& levels = getStringToLogLevelPairs();
+
+  // NOLINTNEXTLINE(readability-qualified-auto)
+  const auto it =
+    std::find_if(levels.cbegin(), levels.cend(),
+                 [&level](const LevelsPair& p) { return p.second == level; });
+  const cm::string_view levelStrLowerCase =
+    (it != levels.cend()) ? it->first : "undefined";
+  std::string levelStrUpperCase =
+    cmSystemTools::UpperCase(std::string{ levelStrLowerCase });
+  return levelStrUpperCase;
 }
 
 cmake::TraceFormat cmake::StringToTraceFormat(const std::string& traceStr)
@@ -2096,6 +2148,9 @@ int cmake::ActualConfigure()
   this->UpdateConversionPathTable();
   this->CleanupCommandsAndMacros();
 
+  cmSystemTools::RemoveADirectory(this->GetHomeOutputDirectory() +
+                                  "/CMakeFiles/CMakeScratch");
+
   int res = this->DoPreConfigureChecks();
   if (res < 0) {
     return -2;
@@ -2319,7 +2374,6 @@ std::unique_ptr<cmGlobalGenerator> cmake::EvaluateDefaultGlobalGenerator()
     { "14.0", "Visual Studio 14 2015" }, //
     { "12.0", "Visual Studio 12 2013" }, //
     { "11.0", "Visual Studio 11 2012" }, //
-    { "10.0", "Visual Studio 10 2010" }, //
     { "9.0", "Visual Studio 9 2008" }
   };
   static const char* const vsEntries[] = {
@@ -2648,7 +2702,6 @@ void cmake::AddDefaultGenerators()
   this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio11Generator::NewFactory());
-  this->Generators.push_back(cmGlobalVisualStudio10Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory());
   this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory());
   this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory());
@@ -3619,6 +3672,214 @@ bool cmake::Open(const std::string& dir, bool dryRun)
   return gen->Open(dir, *cachedProjectName, dryRun);
 }
 
+#if !defined(CMAKE_BOOTSTRAP)
+template <typename T>
+const T* cmake::FindPresetForWorkflow(
+  cm::static_string_view type,
+  const std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
+  const cmCMakePresetsGraph::WorkflowPreset::WorkflowStep& step)
+{
+  auto it = presets.find(step.PresetName);
+  if (it == presets.end()) {
+    cmSystemTools::Error(cmStrCat("No such ", type, " preset in ",
+                                  this->GetHomeDirectory(), ": \"",
+                                  step.PresetName, '"'));
+    return nullptr;
+  }
+
+  if (it->second.Unexpanded.Hidden) {
+    cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ",
+                                  this->GetHomeDirectory(), ": \"",
+                                  step.PresetName, '"'));
+    return nullptr;
+  }
+
+  if (!it->second.Expanded) {
+    cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"",
+                                  step.PresetName,
+                                  "\": Invalid macro expansion"));
+    return nullptr;
+  }
+
+  if (!it->second.Expanded->ConditionResult) {
+    cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ",
+                                  this->GetHomeDirectory(), ": \"",
+                                  step.PresetName, '"'));
+    return nullptr;
+  }
+
+  return &*it->second.Expanded;
+}
+
+std::function<int()> cmake::BuildWorkflowStep(
+  const std::vector<std::string>& args)
+{
+  cmUVProcessChainBuilder builder;
+  builder
+    .AddCommand(args)
+#  ifdef _WIN32
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, _fileno(stdout))
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, _fileno(stderr));
+#  else
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, STDOUT_FILENO)
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, STDERR_FILENO);
+#  endif
+  return [builder]() -> int {
+    auto chain = builder.Start();
+    chain.Wait();
+    return static_cast<int>(chain.GetStatus().front()->ExitStatus);
+  };
+}
+#endif
+
+int cmake::Workflow(const std::string& presetName,
+                    WorkflowListPresets listPresets, WorkflowFresh fresh)
+{
+#ifndef CMAKE_BOOTSTRAP
+  this->SetHomeDirectory(cmSystemTools::GetCurrentWorkingDirectory());
+  this->SetHomeOutputDirectory(cmSystemTools::GetCurrentWorkingDirectory());
+
+  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)));
+    return 1;
+  }
+
+  if (listPresets == WorkflowListPresets::Yes) {
+    settingsFile.PrintWorkflowPresetList();
+    return 0;
+  }
+
+  auto presetPair = settingsFile.WorkflowPresets.find(presetName);
+  if (presetPair == settingsFile.WorkflowPresets.end()) {
+    cmSystemTools::Error(cmStrCat("No such workflow preset in ",
+                                  this->GetHomeDirectory(), ": \"", presetName,
+                                  '"'));
+    settingsFile.PrintWorkflowPresetList();
+    return 1;
+  }
+
+  if (presetPair->second.Unexpanded.Hidden) {
+    cmSystemTools::Error(cmStrCat("Cannot use hidden workflow preset in ",
+                                  this->GetHomeDirectory(), ": \"", presetName,
+                                  '"'));
+    settingsFile.PrintWorkflowPresetList();
+    return 1;
+  }
+
+  auto const& expandedPreset = presetPair->second.Expanded;
+  if (!expandedPreset) {
+    cmSystemTools::Error(cmStrCat("Could not evaluate workflow preset \"",
+                                  presetName, "\": Invalid macro expansion"));
+    settingsFile.PrintWorkflowPresetList();
+    return 1;
+  }
+
+  if (!expandedPreset->ConditionResult) {
+    cmSystemTools::Error(cmStrCat("Cannot use disabled workflow preset in ",
+                                  this->GetHomeDirectory(), ": \"", presetName,
+                                  '"'));
+    settingsFile.PrintWorkflowPresetList();
+    return 1;
+  }
+
+  struct CalculatedStep
+  {
+    int StepNumber;
+    cm::static_string_view Type;
+    std::string Name;
+    std::function<int()> Action;
+
+    CalculatedStep(int stepNumber, cm::static_string_view type,
+                   std::string name, std::function<int()> action)
+      : StepNumber(stepNumber)
+      , Type(type)
+      , Name(std::move(name))
+      , Action(std::move(action))
+    {
+    }
+  };
+
+  std::vector<CalculatedStep> steps;
+  steps.reserve(expandedPreset->Steps.size());
+  int stepNumber = 1;
+  for (auto const& step : expandedPreset->Steps) {
+    switch (step.PresetType) {
+      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::
+        Configure: {
+        auto const* configurePreset = this->FindPresetForWorkflow(
+          "configure"_s, settingsFile.ConfigurePresets, step);
+        if (!configurePreset) {
+          return 1;
+        }
+        std::vector<std::string> args{ cmSystemTools::GetCMakeCommand(),
+                                       "--preset", step.PresetName };
+        if (fresh == WorkflowFresh::Yes) {
+          args.emplace_back("--fresh");
+        }
+        steps.emplace_back(stepNumber, "configure"_s, step.PresetName,
+                           this->BuildWorkflowStep(args));
+      } break;
+      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Build: {
+        auto const* buildPreset = this->FindPresetForWorkflow(
+          "build"_s, settingsFile.BuildPresets, step);
+        if (!buildPreset) {
+          return 1;
+        }
+        steps.emplace_back(
+          stepNumber, "build"_s, step.PresetName,
+          this->BuildWorkflowStep({ cmSystemTools::GetCMakeCommand(),
+                                    "--build", "--preset", step.PresetName }));
+      } break;
+      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Test: {
+        auto const* testPreset = this->FindPresetForWorkflow(
+          "test"_s, settingsFile.TestPresets, step);
+        if (!testPreset) {
+          return 1;
+        }
+        steps.emplace_back(
+          stepNumber, "test"_s, step.PresetName,
+          this->BuildWorkflowStep({ cmSystemTools::GetCTestCommand(),
+                                    "--preset", step.PresetName }));
+      } break;
+      case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type::Package: {
+        auto const* packagePreset = this->FindPresetForWorkflow(
+          "package"_s, settingsFile.PackagePresets, step);
+        if (!packagePreset) {
+          return 1;
+        }
+        steps.emplace_back(
+          stepNumber, "package"_s, step.PresetName,
+          this->BuildWorkflowStep({ cmSystemTools::GetCPackCommand(),
+                                    "--preset", step.PresetName }));
+      } break;
+    }
+    stepNumber++;
+  }
+
+  int stepResult;
+  bool first = true;
+  for (auto const& step : steps) {
+    if (!first) {
+      std::cout << "\n";
+    }
+    std::cout << "Executing workflow step " << step.StepNumber << " of "
+              << steps.size() << ": " << step.Type << " preset \"" << step.Name
+              << "\"\n\n"
+              << std::flush;
+    if ((stepResult = step.Action()) != 0) {
+      return stepResult;
+    }
+    first = false;
+  }
+#endif
+
+  return 0;
+}
+
 void cmake::WatchUnusedCli(const std::string& var)
 {
 #ifndef CMAKE_BOOTSTRAP
index 3c6af17..3183577 100644 (file)
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include <cm/string_view>
+#include <cmext/string_view>
 
 #include "cmGeneratedFileStream.h"
 #include "cmInstalledFile.h"
@@ -119,19 +120,6 @@ public:
     FIND_PACKAGE_MODE
   };
 
-  /** \brief Define log level constants. */
-  enum LogLevel
-  {
-    LOG_UNDEFINED,
-    LOG_ERROR,
-    LOG_WARNING,
-    LOG_NOTICE,
-    LOG_STATUS,
-    LOG_VERBOSE,
-    LOG_DEBUG,
-    LOG_TRACE
-  };
-
   /** \brief Define supported trace formats **/
   enum TraceFormat
   {
@@ -469,9 +457,10 @@ public:
   bool WasLogLevelSetViaCLI() const { return this->LogLevelWasSetViaCLI; }
 
   //! Get the selected log level for `message()` commands during the cmake run.
-  LogLevel GetLogLevel() const { return this->MessageLogLevel; }
-  void SetLogLevel(LogLevel level) { this->MessageLogLevel = level; }
-  static LogLevel StringToLogLevel(const std::string& levelStr);
+  Message::LogLevel GetLogLevel() const { return this->MessageLogLevel; }
+  void SetLogLevel(Message::LogLevel level) { this->MessageLogLevel = level; }
+  static Message::LogLevel StringToLogLevel(cm::string_view levelStr);
+  static std::string LogLevelToString(Message::LogLevel level);
   static TraceFormat StringToTraceFormat(const std::string& levelStr);
 
   bool HasCheckInProgress() const
@@ -612,6 +601,20 @@ public:
   //! run the --open option
   bool Open(const std::string& dir, bool dryRun);
 
+  //! run the --workflow option
+  enum class WorkflowListPresets
+  {
+    No,
+    Yes,
+  };
+  enum class WorkflowFresh
+  {
+    No,
+    Yes,
+  };
+  int Workflow(const std::string& presetName, WorkflowListPresets listPresets,
+               WorkflowFresh fresh);
+
   void UnwatchUnusedCli(const std::string& var);
   void WatchUnusedCli(const std::string& var);
 
@@ -732,7 +735,7 @@ private:
   std::set<std::string> DebugFindPkgs;
   std::set<std::string> DebugFindVars;
 
-  LogLevel MessageLogLevel = LogLevel::LOG_STATUS;
+  Message::LogLevel MessageLogLevel = Message::LogLevel::LOG_STATUS;
   bool LogLevelWasSetViaCLI = false;
   bool LogContext = false;
 
@@ -752,6 +755,16 @@ private:
   void AppendExtraGeneratorsDocumentation(std::vector<cmDocumentationEntry>&);
 
 #if !defined(CMAKE_BOOTSTRAP)
+  template <typename T>
+  const T* FindPresetForWorkflow(
+    cm::static_string_view type,
+    const std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
+    const cmCMakePresetsGraph::WorkflowPreset::WorkflowStep& step);
+
+  std::function<int()> BuildWorkflowStep(const std::vector<std::string>& args);
+#endif
+
+#if !defined(CMAKE_BOOTSTRAP)
   std::unique_ptr<cmMakefileProfilingData> ProfilingOutput;
 #endif
 };
@@ -873,6 +886,7 @@ private:
   F(cxx_std_17)                                                               \
   F(cxx_std_20)                                                               \
   F(cxx_std_23)                                                               \
+  F(cxx_std_26)                                                               \
   FOR_EACH_CXX98_FEATURE(F)                                                   \
   FOR_EACH_CXX11_FEATURE(F)                                                   \
   FOR_EACH_CXX14_FEATURE(F)
@@ -883,7 +897,8 @@ private:
   F(cuda_std_14)                                                              \
   F(cuda_std_17)                                                              \
   F(cuda_std_20)                                                              \
-  F(cuda_std_23)
+  F(cuda_std_23)                                                              \
+  F(cuda_std_26)
 
 #define FOR_EACH_HIP_FEATURE(F)                                               \
   F(hip_std_98)                                                               \
@@ -891,4 +906,5 @@ private:
   F(hip_std_14)                                                               \
   F(hip_std_17)                                                               \
   F(hip_std_20)                                                               \
-  F(hip_std_23)
+  F(hip_std_23)                                                               \
+  F(hip_std_26)
index e7010c9..79e3d19 100644 (file)
@@ -1,6 +1,6 @@
 <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
           manifestVersion="1.0"
-                 xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
+          xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <!-- Windows Vista -->
index f931e9d..723932e 100644 (file)
@@ -71,7 +71,7 @@ const char* cmDocumentationUsageNote[][2] = {
 const char* cmDocumentationOptions[][2] = {
   CMAKE_STANDARD_OPTIONS_TABLE,
   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
-  { "--list-presets", "List available presets." },
+  { "--list-presets[=<type>]", "List available presets." },
   { "-E", "CMake command mode." },
   { "-L[A][H]", "List non-advanced cached variables." },
   { "--fresh",
@@ -82,9 +82,9 @@ const char* cmDocumentationOptions[][2] = {
   { "-N", "View mode only." },
   { "-P <file>", "Process script mode." },
   { "--find-package", "Legacy pkg-config like mode.  Do not use." },
-  { "--graphviz=[file]",
-    "Generate graphviz of dependencies, see "
-    "CMakeGraphVizOptions.cmake for more." },
+  { "--graphviz=<file>",
+    "Generate graphviz of dependencies, see CMakeGraphVizOptions.cmake for "
+    "more." },
   { "--system-information [file]", "Dump information about this system." },
   { "--log-level=<ERROR|WARNING|NOTICE|STATUS|VERBOSE|DEBUG|TRACE>",
     "Set the verbosity of messages from CMake files. "
@@ -109,8 +109,7 @@ const char* cmDocumentationOptions[][2] = {
   { "--warn-uninitialized", "Warn about uninitialized values." },
   { "--no-warn-unused-cli", "Don't warn about command line options." },
   { "--check-system-vars",
-    "Find problems with variable usage in system "
-    "files." },
+    "Find problems with variable usage in system files." },
   { "--compile-no-warning-as-error",
     "Ignore COMPILE_WARNING_AS_ERROR property and "
     "CMAKE_COMPILE_WARNING_AS_ERROR variable." },
@@ -616,7 +615,7 @@ int do_build(int ac, char const* const* av)
       "  <dir>          = Project binary directory to be built.\n"
       "  --preset <preset>, --preset=<preset>\n"
       "                 = Specify a build preset.\n"
-      "  --list-presets\n"
+      "  --list-presets[=<type>]\n"
       "                 = List available build presets.\n"
       "  --parallel [<jobs>], -j [<jobs>]\n"
       "                 = Build in parallel using the given number of jobs. \n"
@@ -627,14 +626,14 @@ int do_build(int ac, char const* const* av)
       "                   specifies a default parallel level when this "
       "option\n"
       "                   is not given.\n"
-      "  --target <tgt>..., -t <tgt>... \n"
+      "  -t <tgt>..., --target <tgt>...\n"
       "                 = Build <tgt> instead of default targets.\n"
       "  --config <cfg> = For multi-configuration tools, choose <cfg>.\n"
       "  --clean-first  = Build target 'clean' first, then build.\n"
       "                   (To clean only, use --target 'clean'.)\n"
       "  --resolve-package-references={on|only|off}\n"
       "                 = Restore/resolve package references during build.\n"
-      "  --verbose, -v  = Enable verbose output - if supported - including\n"
+      "  -v, --verbose  = Enable verbose output - if supported - including\n"
       "                   the build commands to be executed. \n"
       "  --             = Pass remaining options to the native tool.\n"
       ;
@@ -912,6 +911,90 @@ int do_install(int ac, char const* const* av)
 #endif
 }
 
+int do_workflow(int ac, char const* const* av)
+{
+#ifdef CMAKE_BOOTSTRAP
+  std::cerr << "This cmake does not support --workflow\n";
+  return -1;
+#else
+  using WorkflowListPresets = cmake::WorkflowListPresets;
+  using WorkflowFresh = cmake::WorkflowFresh;
+  std::string presetName;
+  auto listPresets = WorkflowListPresets::No;
+  auto fresh = WorkflowFresh::No;
+
+  using CommandArgument =
+    cmCommandLineArgument<bool(std::string const& value)>;
+
+  std::vector<CommandArgument> arguments = {
+    CommandArgument{ "--preset", CommandArgument::Values::One,
+                     CommandArgument::setToValue(presetName) },
+    CommandArgument{ "--list-presets", CommandArgument::Values::Zero,
+                     [&listPresets](const std::string&) -> bool {
+                       listPresets = WorkflowListPresets::Yes;
+                       return true;
+                     } },
+    CommandArgument{ "--fresh", CommandArgument::Values::Zero,
+                     [&fresh](const std::string&) -> bool {
+                       fresh = WorkflowFresh::Yes;
+                       return true;
+                     } },
+  };
+
+  std::vector<std::string> inputArgs;
+
+  inputArgs.reserve(ac - 2);
+  cm::append(inputArgs, av + 2, av + ac);
+
+  decltype(inputArgs.size()) i = 0;
+  for (; i < inputArgs.size(); ++i) {
+    std::string const& arg = inputArgs[i];
+    bool matched = false;
+    bool parsed = false;
+    for (auto const& m : arguments) {
+      matched = m.matches(arg);
+      if (matched) {
+        parsed = m.parse(arg, i, inputArgs);
+        break;
+      }
+    }
+    if (!(matched && parsed)) {
+      if (!matched) {
+        presetName.clear();
+        listPresets = WorkflowListPresets::No;
+        std::cerr << "Unknown argument " << arg << std::endl;
+      }
+      break;
+    }
+  }
+
+  if (presetName.empty() && listPresets == WorkflowListPresets::No) {
+    /* clang-format off */
+    std::cerr <<
+      "Usage: cmake --workflow [options]\n"
+      "Options:\n"
+      "  --preset <preset> = Workflow preset to execute.\n"
+      "  --list-presets    = List available workflow presets.\n"
+      "  --fresh           = Configure a fresh build tree, removing any "
+                            "existing cache file.\n"
+      ;
+    /* clang-format on */
+    return 1;
+  }
+
+  cmake cm(cmake::RoleInternal, cmState::Project);
+  cmSystemTools::SetMessageCallback(
+    [&cm](const std::string& msg, const cmMessageMetadata& md) {
+      cmakemainMessageCallback(msg, md, &cm);
+    });
+  cm.SetProgressCallback([&cm](const std::string& msg, float prog) {
+    cmakemainProgressCallback(msg, prog, &cm);
+  });
+
+  return cm.Workflow(presetName, listPresets, fresh);
+#endif
+}
+
 int do_open(int ac, char const* const* av)
 {
 #ifdef CMAKE_BOOTSTRAP
@@ -981,6 +1064,9 @@ int main(int ac, char const* const* av)
     if (strcmp(av[1], "--open") == 0) {
       return do_open(ac, av);
     }
+    if (strcmp(av[1], "--workflow") == 0) {
+      return do_workflow(ac, av);
+    }
     if (strcmp(av[1], "-E") == 0) {
       return do_command(ac, av, std::move(consoleBuf));
     }
index 8921aa0..69eb19e 100644 (file)
@@ -273,6 +273,7 @@ int main()
     std::string clrest = rest;
     // rc: /fo x.dir\x.rc.res  ->  cl: /out:x.dir\x.rc.res.dep.obj
     clrest = replace(clrest, "/fo ", "/out:");
+    clrest = replace(clrest, "-fo ", "-out:");
     clrest = replace(clrest, objfile, objfile + ".dep.obj ");
 
     cl = "\"" + cl + "\" /P /DRC_INVOKED /TC ";
index 9ab39f1..67394f9 100644 (file)
@@ -360,17 +360,29 @@ int HandleIWYU(const std::string& runCmd, const std::string& /* sourceFile */,
 int HandleTidy(const std::string& runCmd, const std::string& sourceFile,
                const std::vector<std::string>& orig_cmd)
 {
-  // Construct the clang-tidy command line by taking what was given
-  // and adding our compiler command line.  The clang-tidy tool will
-  // automatically skip over the compiler itself and extract the
-  // options.
-  int ret;
   std::vector<std::string> tidy_cmd = cmExpandedList(runCmd, true);
   tidy_cmd.push_back(sourceFile);
-  tidy_cmd.emplace_back("--");
-  cm::append(tidy_cmd, orig_cmd);
+
+  // clang-tidy supports working out the compile commands from a
+  // compile_commands.json file in a directory given by a "-p" option, or by
+  // passing the compiler command line arguments after --. When the latter
+  // strategy is used and the build is using a compiler other than the system
+  // default, clang-tidy may erroneously use the system default compiler's
+  // headers instead of those from the custom compiler. It doesn't do that if
+  // given a compile_commands.json to work with instead, so prefer to use the
+  // compile_commands.json file when "-p" is present.
+  if (!cm::contains(tidy_cmd.cbegin(), tidy_cmd.cend() - 1, "-p")) {
+    // Construct the clang-tidy command line by taking what was given
+    // and adding our compiler command line.  The clang-tidy tool will
+    // automatically skip over the compiler itself and extract the
+    // options. If the compiler is a custom compiler, clang-tidy might
+    // not correctly handle that with this approach.
+    tidy_cmd.emplace_back("--");
+    cm::append(tidy_cmd, orig_cmd);
+  }
 
   // Run the tidy command line.  Capture its stdout and hide its stderr.
+  int ret;
   std::string stdOut;
   std::string stdErr;
   if (!cmSystemTools::RunSingleCommand(tidy_cmd, &stdOut, &stdErr, &ret,
@@ -791,6 +803,10 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
     }
 
     if (args[1] == "env") {
+#ifndef CMAKE_BOOTSTRAP
+      cmSystemTools::EnvDiff env;
+#endif
+
       auto ai = args.cbegin() + 2;
       auto ae = args.cend();
       for (; ai != ae; ++ai) {
@@ -803,16 +819,40 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
         }
         if (cmHasLiteralPrefix(a, "--unset=")) {
           // Unset environment variable.
+#ifdef CMAKE_BOOTSTRAP
           cmSystemTools::UnPutEnv(a.substr(8));
+#else
+          env.UnPutEnv(a.substr(8));
+#endif
+        } else if (a == "--modify") {
+#ifdef CMAKE_BOOTSTRAP
+          std::cerr
+            << "cmake -E env: --modify not available during bootstrapping\n";
+          return 1;
+#else
+          if (++ai == ae) {
+            std::cerr << "cmake -E env: --modify missing a parameter\n";
+            return 1;
+          }
+          std::string const& op = *ai;
+          if (!env.ParseOperation(op)) {
+            std::cerr << "cmake -E env: invalid parameter to --modify: " << op
+                      << '\n';
+            return 1;
+          }
+#endif
         } else if (!a.empty() && a[0] == '-') {
           // Environment variable and command names cannot start in '-',
           // so this must be an unknown option.
-          std::cerr << "cmake -E env: unknown option '" << a << '\''
-                    << std::endl;
+          std::cerr << "cmake -E env: unknown option '" << a << "'\n";
           return 1;
         } else if (a.find('=') != std::string::npos) {
           // Set environment variable.
+#ifdef CMAKE_BOOTSTRAP
           cmSystemTools::PutEnv(a);
+#else
+          env.PutEnv(a);
+#endif
         } else {
           // This is the beginning of the command.
           break;
@@ -820,10 +860,14 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
       }
 
       if (ai == ae) {
-        std::cerr << "cmake -E env: no command given" << std::endl;
+        std::cerr << "cmake -E env: no command given\n";
         return 1;
       }
 
+#ifndef CMAKE_BOOTSTRAP
+      env.ApplyToCurrentEnv();
+#endif
+
       // Execute command from remaining arguments.
       std::vector<std::string> cmd(ai, ae);
       int retval;
@@ -1668,16 +1712,15 @@ cmsys::Status cmcmd::SymlinkInternal(std::string const& file,
   }
   std::string linktext = cmSystemTools::GetFilenameName(file);
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  std::string errorMessage;
-  cmsys::Status status =
-    cmSystemTools::CreateSymlink(linktext, link, &errorMessage);
+  cmsys::Status status = cmSystemTools::CreateSymlinkQuietly(linktext, link);
   // Creating a symlink will fail with ERROR_PRIVILEGE_NOT_HELD if the user
   // does not have SeCreateSymbolicLinkPrivilege, or if developer mode is not
   // active. In that case, we try to copy the file.
   if (status.GetWindows() == ERROR_PRIVILEGE_NOT_HELD) {
     status = cmSystemTools::CopyFileAlways(file, link);
   } else if (!status) {
-    cmSystemTools::Error(errorMessage);
+    cmSystemTools::Error(cmStrCat("failed to create symbolic link '", link,
+                                  "': ", status.GetString()));
   }
   return status;
 #else
@@ -2242,13 +2285,18 @@ bool cmVSLink::Parse(std::vector<std::string>::const_iterator argBeg,
   // Parse the link command to extract information we need.
   for (; arg != argEnd; ++arg) {
     if (cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL:YES") == 0 ||
-        cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0) {
+        cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL:YES") == 0 ||
+        cmSystemTools::Strucmp(arg->c_str(), "/INCREMENTAL") == 0 ||
+        cmSystemTools::Strucmp(arg->c_str(), "-INCREMENTAL") == 0) {
       this->Incremental = true;
-    } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0) {
+    } else if (cmSystemTools::Strucmp(arg->c_str(), "/MANIFEST:NO") == 0 ||
+               cmSystemTools::Strucmp(arg->c_str(), "-MANIFEST:NO") == 0) {
       this->LinkGeneratesManifest = false;
-    } else if (cmHasLiteralPrefix(*arg, "/Fe")) {
+    } else if (cmHasLiteralPrefix(*arg, "/Fe") ||
+               cmHasLiteralPrefix(*arg, "-Fe")) {
       this->TargetFile = arg->substr(3);
-    } else if (cmHasLiteralPrefix(*arg, "/out:")) {
+    } else if (cmHasLiteralPrefix(*arg, "/out:") ||
+               cmHasLiteralPrefix(*arg, "-out:")) {
       this->TargetFile = arg->substr(5);
     }
   }
index d520c14..f239576 100644 (file)
@@ -43,12 +43,12 @@ public:
   {
     std::string Name;
 #if defined(_WIN32) && !defined(__CYGWIN__)
-    _wfinddata_t FindData;
+    WIN32_FIND_DATAW FindData;
 #endif
     FileData(std::string name
 #if defined(_WIN32) && !defined(__CYGWIN__)
              ,
-             _wfinddata_t data
+             WIN32_FIND_DATAW data
 #endif
              )
       : Name(std::move(name))
@@ -115,8 +115,8 @@ std::string Directory::GetFilePath(std::size_t i) const
 bool Directory::FileIsDirectory(std::size_t i) const
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  _wfinddata_t const& data = this->Internal->Files[i].FindData;
-  return (data.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
+  auto const& data = this->Internal->Files[i].FindData;
+  return (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
 #else
   std::string const& path = this->GetFilePath(i);
   return kwsys::SystemTools::FileIsDirectory(path);
@@ -127,9 +127,9 @@ bool Directory::FileIsSymlink(std::size_t i) const
 {
   std::string const& path = this->GetFilePath(i);
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  _wfinddata_t const& data = this->Internal->Files[i].FindData;
+  auto const& data = this->Internal->Files[i].FindData;
   return kwsys::SystemTools::FileIsSymlinkWithAttr(
-    Encoding::ToWindowsExtendedPath(path), data.attrib);
+    Encoding::ToWindowsExtendedPath(path), data.dwFileAttributes);
 #else
   return kwsys::SystemTools::FileIsSymlink(path);
 #endif
@@ -157,7 +157,7 @@ namespace KWSYS_NAMESPACE {
 Status Directory::Load(std::string const& name, std::string* errorMessage)
 {
   this->Clear();
-  intptr_t srchHandle;
+  HANDLE srchHandle;
   char* buf;
   size_t bufLength;
   size_t n = name.size();
@@ -176,14 +176,14 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
       snprintf(buf, bufLength, "%s/*", name.c_str());
     }
   }
-  struct _wfinddata_t data; // data of current file
+  WIN32_FIND_DATAW data; // data of current file
 
   // Now put them into the file array
   srchHandle =
-    _wfindfirst((wchar_t*)Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
+    FindFirstFileW(Encoding::ToWindowsExtendedPath(buf).c_str(), &data);
   delete[] buf;
 
-  if (srchHandle == -1) {
+  if (srchHandle == INVALID_HANDLE_VALUE) {
     Status status = Status::POSIX_errno();
     if (errorMessage) {
       *errorMessage = status.GetString();
@@ -193,10 +193,11 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
 
   // Loop through names
   do {
-    this->Internal->Files.emplace_back(Encoding::ToNarrow(data.name), data);
-  } while (_wfindnext(srchHandle, &data) != -1);
+    this->Internal->Files.emplace_back(Encoding::ToNarrow(data.cFileName),
+                                       data);
+  } while (FindNextFileW(srchHandle, &data));
   this->Internal->Path = name;
-  if (_findclose(srchHandle) == -1) {
+  if (!FindClose(srchHandle)) {
     Status status = Status::POSIX_errno();
     if (errorMessage) {
       *errorMessage = status.GetString();
@@ -209,7 +210,7 @@ Status Directory::Load(std::string const& name, std::string* errorMessage)
 unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
                                                      std::string* errorMessage)
 {
-  intptr_t srchHandle;
+  HANDLE srchHandle;
   char* buf;
   size_t bufLength;
   size_t n = name.size();
@@ -222,13 +223,13 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
     buf = new char[n + 2 + 1];
     snprintf(buf, bufLength, "%s/*", name.c_str());
   }
-  struct _wfinddata_t data; // data of current file
+  WIN32_FIND_DATAW data; // data of current file
 
   // Now put them into the file array
-  srchHandle = _wfindfirst((wchar_t*)Encoding::ToWide(buf).c_str(), &data);
+  srchHandle = FindFirstFileW(Encoding::ToWide(buf).c_str(), &data);
   delete[] buf;
 
-  if (srchHandle == -1) {
+  if (srchHandle == INVALID_HANDLE_VALUE) {
     if (errorMessage) {
       if (unsigned int errorId = GetLastError()) {
         LPSTR message = nullptr;
@@ -250,8 +251,8 @@ unsigned long Directory::GetNumberOfFilesInDirectory(const std::string& name,
   unsigned long count = 0;
   do {
     count++;
-  } while (_wfindnext(srchHandle, &data) != -1);
-  _findclose(srchHandle);
+  } while (FindNextFileW(srchHandle, &data));
+  FindClose(srchHandle);
   return count;
 }
 
index 5889a4b..a20901c 100644 (file)
@@ -536,9 +536,11 @@ public:
   StringMap TranslationMap;
 #endif
 #ifdef _WIN32
-  static std::string GetCasePathName(std::string const& pathIn);
+  static std::string GetCasePathName(std::string const& pathIn,
+                                     bool const cache);
   static std::string GetActualCaseForPathCached(std::string const& path);
   static const char* GetEnvBuffered(const char* key);
+  std::map<std::string, std::string, SystemToolsPathCaseCmp> FindFileMap;
   std::map<std::string, std::string, SystemToolsPathCaseCmp> PathCaseMap;
   std::map<std::string, std::string> EnvMap;
 #endif
@@ -571,7 +573,8 @@ public:
 static SystemToolsStatic* SystemToolsStatics;
 
 #ifdef _WIN32
-std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
+std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn,
+                                               bool const cache)
 {
   std::string casePath;
 
@@ -623,14 +626,31 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
       } else {
         std::string test_str = casePath;
         test_str += path_components[idx];
-        WIN32_FIND_DATAW findData;
-        HANDLE hFind =
-          ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
-        if (INVALID_HANDLE_VALUE != hFind) {
-          path_components[idx] = Encoding::ToNarrow(findData.cFileName);
-          ::FindClose(hFind);
-        } else {
-          converting = false;
+
+        bool found_in_cache = false;
+        if (cache) {
+          auto const it = SystemToolsStatics->FindFileMap.find(test_str);
+          if (it != SystemToolsStatics->FindFileMap.end()) {
+            path_components[idx] = it->second;
+            found_in_cache = true;
+          }
+        }
+
+        if (!found_in_cache) {
+          WIN32_FIND_DATAW findData;
+          HANDLE hFind =
+            ::FindFirstFileW(Encoding::ToWide(test_str).c_str(), &findData);
+          if (INVALID_HANDLE_VALUE != hFind) {
+            auto case_file_name = Encoding::ToNarrow(findData.cFileName);
+            if (cache) {
+              SystemToolsStatics->FindFileMap.emplace(test_str,
+                                                      case_file_name);
+            }
+            path_components[idx] = std::move(case_file_name);
+            ::FindClose(hFind);
+          } else {
+            converting = false;
+          }
         }
       }
     }
@@ -642,19 +662,16 @@ std::string SystemToolsStatic::GetCasePathName(std::string const& pathIn)
 
 std::string SystemToolsStatic::GetActualCaseForPathCached(std::string const& p)
 {
-  // Check to see if actual case has already been called
-  // for this path, and the result is stored in the PathCaseMap
-  auto& pcm = SystemToolsStatics->PathCaseMap;
-  {
-    auto itr = pcm.find(p);
-    if (itr != pcm.end()) {
-      return itr->second;
-    }
-  }
-  std::string casePath = SystemToolsStatic::GetCasePathName(p);
-  if (casePath.size() <= MAX_PATH) {
-    pcm[p] = casePath;
+  std::string casePath;
+
+  auto it = SystemToolsStatics->PathCaseMap.find(p);
+  if (it != SystemToolsStatics->PathCaseMap.end()) {
+    casePath = it->second;
+  } else {
+    casePath = SystemToolsStatic::GetCasePathName(p, true);
+    SystemToolsStatics->PathCaseMap.emplace(p, casePath);
   }
+
   return casePath;
 }
 #endif
@@ -3067,17 +3084,14 @@ std::string SystemTools::GetRealPath(const std::string& path,
   return ret;
 }
 
-bool SystemTools::FileIsDirectory(const std::string& inName)
+// Remove any trailing slash from the name except in a root component.
+static const char* RemoveTrailingSlashes(
+  const std::string& inName, char (&local_buffer)[KWSYS_SYSTEMTOOLS_MAXPATH],
+  std::string& string_buffer)
 {
-  if (inName.empty()) {
-    return false;
-  }
   size_t length = inName.size();
   const char* name = inName.c_str();
 
-  // Remove any trailing slash from the name except in a root component.
-  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
-  std::string string_buffer;
   size_t last = length - 1;
   if (last > 0 && (name[last] == '/' || name[last] == '\\') &&
       strcmp(name, "/") != 0 && name[last - 1] != ':') {
@@ -3091,6 +3105,19 @@ bool SystemTools::FileIsDirectory(const std::string& inName)
     }
   }
 
+  return name;
+}
+
+bool SystemTools::FileIsDirectory(const std::string& inName)
+{
+  if (inName.empty()) {
+    return false;
+  }
+
+  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
+  std::string string_buffer;
+  const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
+
 // Now check the file node type.
 #if defined(_WIN32)
   DWORD attr =
@@ -3107,9 +3134,21 @@ bool SystemTools::FileIsDirectory(const std::string& inName)
   }
 }
 
-bool SystemTools::FileIsExecutable(const std::string& name)
+bool SystemTools::FileIsExecutable(const std::string& inName)
 {
-  return !FileIsDirectory(name) && TestFileAccess(name, TEST_FILE_EXECUTE);
+#ifdef _WIN32
+  char local_buffer[KWSYS_SYSTEMTOOLS_MAXPATH];
+  std::string string_buffer;
+  const auto name = RemoveTrailingSlashes(inName, local_buffer, string_buffer);
+  const auto attr =
+    GetFileAttributesW(Encoding::ToWindowsExtendedPath(name).c_str());
+
+  // On Windows any file that exists and is not a directory is considered
+  // readable and therefore also executable:
+  return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
+#else
+  return !FileIsDirectory(inName) && TestFileAccess(inName, TEST_FILE_EXECUTE);
+#endif
 }
 
 #if defined(_WIN32)
@@ -3655,7 +3694,7 @@ std::string SystemTools::RelativePath(const std::string& local,
 std::string SystemTools::GetActualCaseForPath(const std::string& p)
 {
 #ifdef _WIN32
-  return SystemToolsStatic::GetCasePathName(p);
+  return SystemToolsStatic::GetCasePathName(p, false);
 #else
   return p;
 #endif
index 5341841..526bcda 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index b3230ac..b0765f5 100644 (file)
     ]
   },
   {
-    "name":  "cmake-temp-gencode",
-    "switch": "gencode=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue",
-      "SemicolonAppendable"
-    ]
-  },
-  {
-    "name":  "cmake-temp-gencode",
-    "switch": "gencode",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing",
-      "SemicolonAppendable"
-    ]
-  },
-  {
-    "name":  "cmake-temp-gencode",
-    "switch": "-generate-code=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue",
-      "SemicolonAppendable"
-    ]
-  },
-  {
-    "name":  "cmake-temp-gencode",
-    "switch": "-generate-code",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing",
-      "SemicolonAppendable"
-    ]
-  },
-  {
-    "name":  "cmake-temp-code",
-    "switch": "code=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue"
-    ]
-  },
-  {
-    "name":  "cmake-temp-code",
-    "switch": "code",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing"
-    ]
-  },
-  {
-    "name":  "cmake-temp-code",
-    "switch": "-gpu-code=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue"
-    ]
-  },
-  {
-    "name":  "cmake-temp-code",
-    "switch": "-gpu-code",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing"
-    ]
-  },
-  {
-    "name":  "cmake-temp-arch",
-    "switch": "arch=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue"
-    ]
-  },
-  {
-    "name":  "cmake-temp-arch",
-    "switch": "arch",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing"
-    ]
-  },
-  {
-    "name":  "cmake-temp-arch",
-    "switch": "-gpu-architecture=",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserValue"
-    ]
-  },
-  {
-    "name":  "cmake-temp-arch",
-    "switch": "-gpu-architecture",
-    "comment": "",
-    "value": "",
-    "flags": [
-      "UserFollowing"
-    ]
-  },
-  {
     "name":  "FastMath",
     "switch": "use_fast_math",
     "comment": "",
index 5341841..526bcda 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 5341841..526bcda 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 5341841..526bcda 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 5341841..526bcda 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 9ea8f4b..d57a97e 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 9ea8f4b..d57a97e 100644 (file)
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib",
     "comment": "",
     "value": "true",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib-",
     "comment": "",
     "value": "false",
     "flags": []
   },
   {
-    "name":  "NoStandardLib",
+    "name":  "NoStdLib",
     "switch": "nostdlib+",
     "comment": "",
     "value": "true",
index 1b7e57d..8f6b355 100644 (file)
@@ -24,6 +24,12 @@ if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode|Ninja" AND
     elseif("${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64")
       list(APPEND C_FLAGS -arch arm64)
     endif()
+    if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
+      # Just in case the user is passing -flto, we need to pass -fno-lto to
+      # clang when generating the assembly file, or else clang will generate
+      # LLVM IR instead of assembly.
+      list(APPEND C_FLAGS -fno-lto)
+    endif()
     # Clang on OS X, and perhaps other compilers, do not support -g
     # for both generating and assembling, so drop it from generating.
     list(REMOVE_ITEM C_FLAGS -g)
index 1d45162..612d4b4 100644 (file)
@@ -47,12 +47,10 @@ if(WIN32)
   list(APPEND CMakeLib_TESTS
     testVisualStudioSlnParser.cxx
     )
-  configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testVisualStudioSlnParser.h.in
-                 ${CMAKE_CURRENT_BINARY_DIR}/testVisualStudioSlnParser.h @ONLY)
+  configure_file(testVisualStudioSlnParser.h.in testVisualStudioSlnParser.h @ONLY)
 endif()
 
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testXMLParser.h.in
-               ${CMAKE_CURRENT_BINARY_DIR}/testXMLParser.h @ONLY)
+configure_file(testXMLParser.h.in testXMLParser.h @ONLY)
 
 create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
 add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
index 4bef6c5..b4bc921 100644 (file)
@@ -1,6 +1,6 @@
-foreach (_retval 0 1)
-  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/memtester.cxx.in" "${CMAKE_CURRENT_BINARY_DIR}/ret${_retval}.cxx" @ONLY)
-endforeach ()
+foreach(_retval IN ITEMS 0 1)
+  configure_file(memtester.cxx.in ret${_retval}.cxx @ONLY)
+endforeach()
 
 include_directories(${CMake_SOURCE_DIR}/Source ${CMake_BINARY_DIR}/Source)
 
index 965690c..e044794 100644 (file)
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
+#include <functional>
 #include <initializer_list>
 #include <iostream>
+#include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include <cm/optional>
 #include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
 
 namespace {
 
-struct Result
+struct Result : public ArgumentParser::ParseResult
 {
   bool Option1 = false;
   bool Option2 = false;
 
   std::string String1;
-  std::string String2;
+  cm::optional<std::string> String2;
+  cm::optional<std::string> String3;
+  ArgumentParser::Maybe<std::string> String4;
+  ArgumentParser::NonEmpty<std::string> String5;
+  ArgumentParser::NonEmpty<std::string> String6;
 
-  std::vector<std::string> List1;
-  std::vector<std::string> List2;
-  std::vector<std::string> List3;
+  ArgumentParser::NonEmpty<std::vector<std::string>> List1;
+  ArgumentParser::NonEmpty<std::vector<std::string>> List2;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List3;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List4;
+  cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> List5;
+  cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> List6;
 
   std::vector<std::vector<std::string>> Multi1;
   std::vector<std::vector<std::string>> Multi2;
-  std::vector<std::vector<std::string>> Multi3;
+  cm::optional<std::vector<std::vector<std::string>>> Multi3;
+  cm::optional<std::vector<std::vector<std::string>>> Multi4;
+
+  cm::optional<std::string> Pos0;
+  cm::optional<std::string> Pos1;
+  cm::optional<std::string> Pos2;
+
+  bool Func0_ = false;
+  ArgumentParser::Continue Func0(cm::string_view)
+  {
+    Func0_ = true;
+    return ArgumentParser::Continue::No;
+  }
+
+  std::string Func1_;
+  ArgumentParser::Continue Func1(cm::string_view arg)
+  {
+    Func1_ = std::string(arg);
+    return ArgumentParser::Continue::No;
+  }
+
+  std::map<std::string, std::vector<std::string>> Func2_;
+  ArgumentParser::Continue Func2(cm::string_view key, cm::string_view arg)
+  {
+    Func2_[std::string(key)].emplace_back(arg);
+    return key == "FUNC_2b" ? ArgumentParser::Continue::Yes
+                            : ArgumentParser::Continue::No;
+  }
+
+  std::vector<std::string> Func3_;
+  ArgumentParser::Continue Func3(cm::string_view arg)
+  {
+    Func3_.emplace_back(arg);
+    return ArgumentParser::Continue::Yes;
+  }
+
+  std::map<std::string, std::vector<std::string>> Func4_;
+  ArgumentParser::Continue Func4(cm::string_view key, cm::string_view arg)
+  {
+    Func4_[std::string(key)].emplace_back(arg);
+    return key == "FUNC_4b" ? ArgumentParser::Continue::Yes
+                            : ArgumentParser::Continue::No;
+  }
+
+  ArgumentParser::Maybe<std::string> UnboundMaybe{ 'u', 'n', 'b', 'o',
+                                                   'u', 'n', 'd' };
+  ArgumentParser::MaybeEmpty<std::vector<std::string>> UnboundMaybeEmpty{
+    1, "unbound"
+  };
+  ArgumentParser::NonEmpty<std::vector<std::string>> UnboundNonEmpty{
+    1, "unbound"
+  };
+  ArgumentParser::NonEmpty<std::string> UnboundNonEmptyStr{ 'u', 'n', 'b', 'o',
+                                                            'u', 'n', 'd' };
+
+  std::vector<cm::string_view> ParsedKeywords;
 };
 
 std::initializer_list<cm::string_view> const args = {
   /* clang-format off */
+  "pos0",                    // position index 0
   "OPTION_1",                // option
+  "pos2",                    // position index 2, ignored because after keyword
+  // "OPTION_2",             // option that is not present
   "STRING_1",                // string arg missing value
-  "STRING_2", "foo", "bar",  // string arg + unparsed value
+  "STRING_2", "foo", "bar",  // string arg + unparsed value, presence captured
+  // "STRING_3",             // string arg that is not present
+  "STRING_4",                // string arg allowed to be missing value
+  "STRING_5", "foo",         // string arg that is not empty
+  "STRING_6", "",            // string arg that is empty
   "LIST_1",                  // list arg missing values
   "LIST_2", "foo", "bar",    // list arg with 2 elems
   "LIST_3", "bar",           // list arg ...
   "LIST_3", "foo",           // ... with continuation
+  "LIST_4",                  // list arg missing values, presence captured
+  // "LIST_5",               // list arg that is not present
+  "LIST_6",                  // list arg allowed to be empty
   "MULTI_2",                 // multi list with 0 lists
   "MULTI_3", "foo", "bar",   // multi list with first list with two elems
   "MULTI_3", "bar", "foo",   // multi list with second list with two elems
+  // "MULTI_4",              // multi list arg that is not present
+  "FUNC_0",                  // callback arg missing value
+  "FUNC_1", "foo", "ign1",   // callback with one arg + unparsed value
+  "FUNC_2a", "foo", "ign2",  // callback with keyword-dependent arg count
+  "FUNC_2b", "bar", "zot",   // callback with keyword-dependent arg count
+  "FUNC_3", "foo", "bar",    // callback with list arg ...
+  "FUNC_4a", "foo", "ign4",  // callback with keyword-dependent arg count
+  "FUNC_4b", "bar", "zot",   // callback with keyword-dependent arg count
   /* clang-format on */
 };
 
 bool verifyResult(Result const& result,
-                  std::vector<std::string> const& unparsedArguments,
-                  std::vector<std::string> const& keywordsMissingValue)
+                  std::vector<std::string> const& unparsedArguments)
 {
   static std::vector<std::string> const foobar = { "foo", "bar" };
   static std::vector<std::string> const barfoo = { "bar", "foo" };
-  static std::vector<std::string> const missing = { "STRING_1", "LIST_1" };
+  static std::vector<std::string> const unbound = { "unbound" };
+  static std::vector<cm::string_view> const parsedKeywords = {
+    /* clang-format off */
+    "OPTION_1",
+    "STRING_1",
+    "STRING_2",
+    "STRING_4",
+    "STRING_5",
+    "STRING_6",
+    "LIST_1",
+    "LIST_2",
+    "LIST_3",
+    "LIST_3",
+    "LIST_4",
+    "LIST_6",
+    "MULTI_2",
+    "MULTI_3",
+    "MULTI_3",
+    "FUNC_0",
+    "FUNC_1",
+    "FUNC_2a",
+    "FUNC_2b",
+    "FUNC_3",
+    "FUNC_4a",
+    "FUNC_4b",
+    /* clang-format on */
+  };
+  static std::map<std::string, std::vector<std::string>> const func2map = {
+    { "FUNC_2a", { "foo" } }, { "FUNC_2b", { "bar", "zot" } }
+  };
+  static std::map<std::string, std::vector<std::string>> const func4map = {
+    { "FUNC_4a", { "foo" } }, { "FUNC_4b", { "bar", "zot" } }
+  };
+  static std::map<cm::string_view, std::string> const keywordErrors = {
+    { "STRING_1"_s, "  missing required value\n" },
+    { "STRING_6"_s, "  empty string not allowed\n" },
+    { "LIST_1"_s, "  missing required value\n" },
+    { "LIST_4"_s, "  missing required value\n" },
+    { "FUNC_0"_s, "  missing required value\n" }
+  };
+  static std::vector<std::string> const unparsed = { "pos2", "bar", "ign1",
+                                                     "ign2", "ign4" };
 
 #define ASSERT_TRUE(x)                                                        \
   do {                                                                        \
@@ -61,26 +186,63 @@ bool verifyResult(Result const& result,
     }                                                                         \
   } while (false)
 
+  ASSERT_TRUE(!result);
+
   ASSERT_TRUE(result.Option1);
   ASSERT_TRUE(!result.Option2);
 
   ASSERT_TRUE(result.String1.empty());
-  ASSERT_TRUE(result.String2 == "foo");
+  ASSERT_TRUE(result.String2);
+  ASSERT_TRUE(*result.String2 == "foo");
+  ASSERT_TRUE(!result.String3);
+  ASSERT_TRUE(result.String4.empty());
+  ASSERT_TRUE(result.String5 == "foo");
+  ASSERT_TRUE(result.String6.empty());
 
   ASSERT_TRUE(result.List1.empty());
   ASSERT_TRUE(result.List2 == foobar);
-  ASSERT_TRUE(result.List3 == barfoo);
+  ASSERT_TRUE(result.List3);
+  ASSERT_TRUE(*result.List3 == barfoo);
+  ASSERT_TRUE(result.List4);
+  ASSERT_TRUE(result.List4->empty());
+  ASSERT_TRUE(!result.List5);
+  ASSERT_TRUE(result.List6);
+  ASSERT_TRUE(result.List6->empty());
 
   ASSERT_TRUE(result.Multi1.empty());
   ASSERT_TRUE(result.Multi2.size() == 1);
   ASSERT_TRUE(result.Multi2[0].empty());
-  ASSERT_TRUE(result.Multi3.size() == 2);
-  ASSERT_TRUE(result.Multi3[0] == foobar);
-  ASSERT_TRUE(result.Multi3[1] == barfoo);
+  ASSERT_TRUE(result.Multi3);
+  ASSERT_TRUE((*result.Multi3).size() == 2);
+  ASSERT_TRUE((*result.Multi3)[0] == foobar);
+  ASSERT_TRUE((*result.Multi3)[1] == barfoo);
+  ASSERT_TRUE(!result.Multi4);
+
+  ASSERT_TRUE(result.Pos0 == "pos0");
+  ASSERT_TRUE(!result.Pos1);
+  ASSERT_TRUE(!result.Pos2);
+
+  ASSERT_TRUE(result.Func0_ == false);
+  ASSERT_TRUE(result.Func1_ == "foo");
+  ASSERT_TRUE(result.Func2_ == func2map);
+  ASSERT_TRUE(result.Func3_ == foobar);
+  ASSERT_TRUE(result.Func4_ == func4map);
+
+  ASSERT_TRUE(unparsedArguments == unparsed);
 
-  ASSERT_TRUE(unparsedArguments.size() == 1);
-  ASSERT_TRUE(unparsedArguments[0] == "bar");
-  ASSERT_TRUE(keywordsMissingValue == missing);
+  ASSERT_TRUE(result.UnboundMaybe == "unbound");
+  ASSERT_TRUE(result.UnboundMaybeEmpty == unbound);
+  ASSERT_TRUE(result.UnboundNonEmpty == unbound);
+  ASSERT_TRUE(result.UnboundNonEmptyStr == "unbound");
+
+  ASSERT_TRUE(result.ParsedKeywords == parsedKeywords);
+
+  ASSERT_TRUE(result.GetKeywordErrors().size() == keywordErrors.size());
+  for (auto const& ke : result.GetKeywordErrors()) {
+    auto const ki = keywordErrors.find(ke.first);
+    ASSERT_TRUE(ki != keywordErrors.end());
+    ASSERT_TRUE(ke.second == ki->second);
+  }
 
   return true;
 }
@@ -89,45 +251,116 @@ bool testArgumentParserDynamic()
 {
   Result result;
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-
-  cmArgumentParser<void>{}
-    .Bind("OPTION_1"_s, result.Option1)
-    .Bind("OPTION_2"_s, result.Option2)
-    .Bind("STRING_1"_s, result.String1)
-    .Bind("STRING_2"_s, result.String2)
-    .Bind("LIST_1"_s, result.List1)
-    .Bind("LIST_2"_s, result.List2)
-    .Bind("LIST_3"_s, result.List3)
-    .Bind("MULTI_1"_s, result.Multi1)
-    .Bind("MULTI_2"_s, result.Multi2)
-    .Bind("MULTI_3"_s, result.Multi3)
-    .Parse(args, &unparsedArguments, &keywordsMissingValue);
-
-  return verifyResult(result, unparsedArguments, keywordsMissingValue);
+
+  std::function<ArgumentParser::Continue(cm::string_view, cm::string_view)>
+    func4 = [&result](cm::string_view key,
+                      cm::string_view arg) -> ArgumentParser::Continue {
+    return result.Func4(key, arg);
+  };
+
+  static_cast<ArgumentParser::ParseResult&>(result) =
+    cmArgumentParser<void>{}
+      .Bind(0, result.Pos0)
+      .Bind(1, result.Pos1)
+      .Bind(2, result.Pos2)
+      .Bind("OPTION_1"_s, result.Option1)
+      .Bind("OPTION_2"_s, result.Option2)
+      .Bind("STRING_1"_s, result.String1)
+      .Bind("STRING_2"_s, result.String2)
+      .Bind("STRING_3"_s, result.String3)
+      .Bind("STRING_4"_s, result.String4)
+      .Bind("STRING_5"_s, result.String5)
+      .Bind("STRING_6"_s, result.String6)
+      .Bind("LIST_1"_s, result.List1)
+      .Bind("LIST_2"_s, result.List2)
+      .Bind("LIST_3"_s, result.List3)
+      .Bind("LIST_4"_s, result.List4)
+      .Bind("LIST_5"_s, result.List5)
+      .Bind("LIST_6"_s, result.List6)
+      .Bind("MULTI_1"_s, result.Multi1)
+      .Bind("MULTI_2"_s, result.Multi2)
+      .Bind("MULTI_3"_s, result.Multi3)
+      .Bind("MULTI_4"_s, result.Multi4)
+      .Bind("FUNC_0"_s,
+            [&result](cm::string_view arg) -> ArgumentParser::Continue {
+              return result.Func0(arg);
+            })
+      .Bind("FUNC_1"_s,
+            [&result](cm::string_view arg) -> ArgumentParser::Continue {
+              return result.Func1(arg);
+            })
+      .Bind("FUNC_2a"_s,
+            [&result](cm::string_view key, cm::string_view arg)
+              -> ArgumentParser::Continue { return result.Func2(key, arg); })
+      .Bind("FUNC_2b"_s,
+            [&result](cm::string_view key, cm::string_view arg)
+              -> ArgumentParser::Continue { return result.Func2(key, arg); })
+      .Bind("FUNC_3"_s,
+            [&result](cm::string_view arg) -> ArgumentParser::Continue {
+              return result.Func3(arg);
+            })
+      .Bind("FUNC_4a"_s, func4)
+      .Bind("FUNC_4b"_s, func4)
+      .BindParsedKeywords(result.ParsedKeywords)
+      .Parse(args, &unparsedArguments);
+
+  return verifyResult(result, unparsedArguments);
 }
 
+static auto const parserStaticFunc4 =
+  [](Result& result, cm::string_view key,
+     cm::string_view arg) -> ArgumentParser::Continue {
+  return result.Func4(key, arg);
+};
+static auto const parserStatic = //
+  cmArgumentParser<Result>{}
+    .Bind(0, &Result::Pos0)
+    .Bind(1, &Result::Pos1)
+    .Bind(2, &Result::Pos2)
+    .Bind("OPTION_1"_s, &Result::Option1)
+    .Bind("OPTION_2"_s, &Result::Option2)
+    .Bind("STRING_1"_s, &Result::String1)
+    .Bind("STRING_2"_s, &Result::String2)
+    .Bind("STRING_3"_s, &Result::String3)
+    .Bind("STRING_4"_s, &Result::String4)
+    .Bind("STRING_5"_s, &Result::String5)
+    .Bind("STRING_6"_s, &Result::String6)
+    .Bind("LIST_1"_s, &Result::List1)
+    .Bind("LIST_2"_s, &Result::List2)
+    .Bind("LIST_3"_s, &Result::List3)
+    .Bind("LIST_4"_s, &Result::List4)
+    .Bind("LIST_5"_s, &Result::List5)
+    .Bind("LIST_6"_s, &Result::List6)
+    .Bind("MULTI_1"_s, &Result::Multi1)
+    .Bind("MULTI_2"_s, &Result::Multi2)
+    .Bind("MULTI_3"_s, &Result::Multi3)
+    .Bind("MULTI_4"_s, &Result::Multi4)
+    .Bind("FUNC_0"_s, &Result::Func0)
+    .Bind("FUNC_1"_s, &Result::Func1)
+    .Bind("FUNC_2a"_s, &Result::Func2)
+    .Bind("FUNC_2b"_s, &Result::Func2)
+    .Bind("FUNC_3"_s,
+          [](Result& result, cm::string_view arg) -> ArgumentParser::Continue {
+            return result.Func3(arg);
+          })
+    .Bind("FUNC_4a"_s, parserStaticFunc4)
+    .Bind("FUNC_4b"_s, parserStaticFunc4)
+    .BindParsedKeywords(&Result::ParsedKeywords)
+  /* keep semicolon on own line */;
+
 bool testArgumentParserStatic()
 {
-  static auto const parser = //
-    cmArgumentParser<Result>{}
-      .Bind("OPTION_1"_s, &Result::Option1)
-      .Bind("OPTION_2"_s, &Result::Option2)
-      .Bind("STRING_1"_s, &Result::String1)
-      .Bind("STRING_2"_s, &Result::String2)
-      .Bind("LIST_1"_s, &Result::List1)
-      .Bind("LIST_2"_s, &Result::List2)
-      .Bind("LIST_3"_s, &Result::List3)
-      .Bind("MULTI_1"_s, &Result::Multi1)
-      .Bind("MULTI_2"_s, &Result::Multi2)
-      .Bind("MULTI_3"_s, &Result::Multi3);
-
   std::vector<std::string> unparsedArguments;
-  std::vector<std::string> keywordsMissingValue;
-  Result const result =
-    parser.Parse(args, &unparsedArguments, &keywordsMissingValue);
+  Result const result = parserStatic.Parse(args, &unparsedArguments);
+  return verifyResult(result, unparsedArguments);
+}
 
-  return verifyResult(result, unparsedArguments, keywordsMissingValue);
+bool testArgumentParserStaticBool()
+{
+  std::vector<std::string> unparsedArguments;
+  Result result;
+  ASSERT_TRUE(parserStatic.Parse(result, args, &unparsedArguments) == false);
+  return verifyResult(result, unparsedArguments);
 }
 
 } // namespace
@@ -144,5 +377,10 @@ int testArgumentParser(int /*unused*/, char* /*unused*/ [])
     return -1;
   }
 
+  if (!testArgumentParserStaticBool()) {
+    std::cout << "While executing testArgumentParserStaticBool().\n";
+    return -1;
+  }
+
   return 0;
 }
index 64c437b..dbb0a54 100644 (file)
@@ -191,6 +191,15 @@ void testEdition()
       ++failed;
     }
   }
+  {
+    cm::enum_set<Test> testSet1;
+    cm::enum_set<Test> testSet2{ Test::A, Test::C, Test::B };
+
+    testSet1 = { Test::A, Test::C, Test::B };
+    if (testSet1.size() != 3 || testSet1 != testSet2) {
+      ++failed;
+    }
+  }
 }
 }
 
index 3e400c2..6e35df9 100644 (file)
@@ -40,10 +40,10 @@ set(ENV{HOME} \"${TEST_HOME}\")
 endif()
 
 # Suppress generator deprecation warnings in test suite.
-if(CMAKE_GENERATOR MATCHES "^Visual Studio 10 2010")
-  set(TEST_WARN_VS10_CODE "set(ENV{CMAKE_WARN_VS10} OFF)")
+if(CMAKE_GENERATOR MATCHES "^Visual Studio 11 2012")
+  set(TEST_WARN_VS11_CODE "set(ENV{CMAKE_WARN_VS11} OFF)")
 else()
-  set(TEST_WARN_VS10_CODE "")
+  set(TEST_WARN_VS11_CODE "")
 endif()
 
 # 3.9 or later provides a definitive answer to whether we are multi-config
@@ -89,7 +89,7 @@ if(BUILD_TESTING)
     endif()
   endif()
 
-  set(MAKE_IS_GNU )
+  set(MAKE_IS_GNU)
   if(CMAKE_MAKE_PROGRAM MATCHES make)
     execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} no_such_target --version
       RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_VARIABLE out)
@@ -101,7 +101,7 @@ if(BUILD_TESTING)
   endif()
 
   # some old versions of make simply cannot handle spaces in paths
-  if (MAKE_IS_GNU OR
+  if(MAKE_IS_GNU OR
       CMAKE_MAKE_PROGRAM MATCHES "nmake|gmake|wmake" OR
       CMAKE_GENERATOR MATCHES "Visual Studio|Xcode|Borland|Ninja")
     set(MAKE_SUPPORTS_SPACES 1)
@@ -181,7 +181,7 @@ if(BUILD_TESTING)
                     ERROR_VARIABLE my_err)
         string(REGEX REPLACE "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\Wow6432Node\\\\Microsoft\\\\Windows CE Tools\\\\SDKs\\\\"  ";" sdk_list "${sdk_reg}")
         list(LENGTH sdk_list sdk_list_len)
-        if (${sdk_list_len} GREATER 1)
+        if(${sdk_list_len} GREATER 1)
           list(GET sdk_list 1 _sdk) # The first entry is always empty due to the regex replace above
           string(STRIP ${_sdk} _sdk) # Make sure there is no newline in the SDK name
         endif()
@@ -205,7 +205,7 @@ if(BUILD_TESTING)
     select_wince_sdk(reg_wince wince_sdk)
     set(reg_tegra "[HKEY_LOCAL_MACHINE\\SOFTWARE\\NVIDIA Corporation\\Nsight Tegra;sdkRoot]")
     set(reg_nasm "[HKEY_CURRENT_USER\\SOFTWARE\\nasm]")
-    foreach(reg vs10 vs11 vs12 vs14 ws80 ws81 ws10_0 wp80 wp81 wince tegra nasm)
+    foreach(reg IN ITEMS vs10 vs11 vs12 vs14 ws80 ws81 ws10_0 wp80 wp81 wince tegra nasm)
       get_filename_component(r "${reg_${reg}}" ABSOLUTE)
       if(IS_DIRECTORY "${r}" AND NOT "${r}" STREQUAL "/registry")
         set(${reg} 1)
@@ -227,7 +227,7 @@ if(BUILD_TESTING)
           set(vs_versions vs15)
         endif()
       endif()
-      foreach(info ${vs_versions})
+      foreach(info IN LISTS vs_versions)
         cmake_host_system_information(RESULT found QUERY "${info_${info}}")
         if(found)
           set(${info} 1)
@@ -287,12 +287,12 @@ if(BUILD_TESTING)
     "Should the long tests be run (such as Bootstrap)." ON)
   mark_as_advanced(CMAKE_RUN_LONG_TESTS)
 
-  if (CMAKE_RUN_LONG_TESTS)
+  if(CMAKE_RUN_LONG_TESTS)
     option(CTEST_TEST_CTEST
       "Should the tests that run a full sub ctest process be run?"
       OFF)
     mark_as_advanced(CTEST_TEST_CTEST)
-  endif ()
+  endif()
 
   option(CTEST_TEST_CPACK
     "Should the tests that use '--build-target package' be run?"
@@ -367,9 +367,9 @@ if(BUILD_TESTING)
 
   if(CMake_TEST_RESOURCES)
     ADD_TEST_MACRO(VSResource VSResource)
-    if (CMAKE_GENERATOR MATCHES "Ninja")
+    if(CMAKE_GENERATOR MATCHES "Ninja")
       add_test_macro(VSResourceNinjaForceRSP VSResourceNinjaForceRSP)
-    endif ()
+    endif()
   endif()
   if(_isMultiConfig)
     set(MSManifest_CTEST_OPTIONS -C $<CONFIGURATION>)
@@ -394,6 +394,9 @@ if(BUILD_TESTING)
     if(CMake_TEST_XCODE_SWIFT)
       ADD_TEST_MACRO(SwiftMix SwiftMix)
     endif()
+    if(CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 5.1)
+      ADD_TEST_MACRO(SwiftMixLib Swifty)
+    endif()
   endif()
   if(CMAKE_Fortran_COMPILER)
     ADD_TEST_MACRO(FortranOnly FortranOnly)
@@ -431,7 +434,7 @@ if(BUILD_TESTING)
 
   if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
     ADD_TEST_MACRO(CSharpOnly CSharpOnly)
-    if(NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+    if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
       ADD_TEST_MACRO(CSharpLinkToCxx CSharpLinkToCxx)
       ADD_TEST_MACRO(CSharpLinkFromCxx CSharpLinkFromCxx)
     endif()
@@ -499,7 +502,7 @@ if(BUILD_TESTING)
   endif()
   ADD_TEST_MACRO(SourcesProperty SourcesProperty)
   ADD_TEST_MACRO(SourceFileProperty SourceFileProperty)
-  if (NOT CMAKE_GENERATOR STREQUAL "Xcode")
+  if(NOT CMAKE_GENERATOR STREQUAL "Xcode")
     ADD_TEST_MACRO(SourceFileIncludeDirProperty SourceFileIncludeDirProperty)
   endif()
   if(CMAKE_CXX_COMPILER_ID STREQUAL "LCC" OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
@@ -618,6 +621,11 @@ if(BUILD_TESTING)
   set(Module.CheckIPOSupported-CXX_BUILD_OPTIONS -DCMake_TEST_IPO_WORKS_CXX=${CMake_TEST_IPO_WORKS_CXX})
   ADD_TEST_MACRO(Module.CheckIPOSupported-CXX CheckIPOSupported-CXX)
 
+  if(CMake_TEST_CUDA)
+    ADD_TEST_MACRO(Module.CheckIPOSupported-CUDA CheckIPOSupported-CUDA)
+    set_property(TEST Module.CheckIPOSupported-CUDA APPEND PROPERTY LABELS "CUDA")
+  endif()
+
   if(CMAKE_Fortran_COMPILER)
     set(Module.CheckIPOSupported-Fortran_BUILD_OPTIONS -DCMake_TEST_IPO_WORKS_Fortran=${CMake_TEST_IPO_WORKS_Fortran})
     ADD_TEST_MACRO(Module.CheckIPOSupported-Fortran CheckIPOSupported-Fortran)
@@ -641,11 +649,11 @@ if(BUILD_TESTING)
 
   ADD_TEST_MACRO(Module.WriteCompilerDetectionHeader WriteCompilerDetectionHeader)
 
-  if (APPLE OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "LCC")
+  if(APPLE OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "LCC")
     include(CheckCXXCompilerFlag)
     check_cxx_compiler_flag(-fPIE run_pic_test)
   else()
-    if (CMAKE_CXX_COMPILER_ID MATCHES "PGI"
+    if(CMAKE_CXX_COMPILER_ID MATCHES "PGI"
         OR CMAKE_CXX_COMPILER_ID MATCHES "PathScale"
         OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
       set(run_pic_test 0)
@@ -654,7 +662,7 @@ if(BUILD_TESTING)
     endif()
   endif()
 
-  if (run_pic_test)
+  if(run_pic_test)
     ADD_TEST_MACRO(PositionIndependentTargets PositionIndependentTargets)
   endif()
 
@@ -725,13 +733,15 @@ if(BUILD_TESTING)
   # 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
+    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
@@ -786,7 +796,7 @@ if(BUILD_TESTING)
       --build-exe-dir "${CMake_BINARY_DIR}/Tests/SubProject/foo"
       --test-command foo
       )
-    set_tests_properties ( SubProject-Stage2 PROPERTIES DEPENDS SubProject)
+    set_tests_properties(SubProject-Stage2 PROPERTIES DEPENDS SubProject)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/SubProject")
   endif()
 
@@ -1056,8 +1066,11 @@ if(BUILD_TESTING)
       endif()
     endif()
     if(NSIS_MAKENSIS_EXECUTABLE)
-      set(CPackComponents_BUILD_OPTIONS ${CPackComponents_BUILD_OPTIONS}
-        -DCPACK_BINARY_NSIS:BOOL=ON)
+      execute_process(COMMAND ${NSIS_MAKENSIS_EXECUTABLE} "-VERSION" ERROR_QUIET OUTPUT_QUIET RESULT_VARIABLE NSIS_OK)
+      if("${NSIS_OK}" STREQUAL "0")
+        set(CPackComponents_BUILD_OPTIONS ${CPackComponents_BUILD_OPTIONS}
+          -DCPACK_BINARY_NSIS:BOOL=ON)
+      endif()
     endif()
 
     add_test(CPackComponents ${CMAKE_CTEST_COMMAND}
@@ -1119,7 +1132,7 @@ if(BUILD_TESTING)
         set(CPACK_GENERATOR_STRING_${CPackGen} ${CPackGen})
       endif()
       set(CPackRun_CPackGen  "-DCPackGen=${CPACK_GENERATOR_STRING_${CPackGen}}")
-      foreach(CPackComponentWay ${CWAYLST})
+      foreach(CPackComponentWay IN LISTS CWAYLST)
         set(CPackRun_CPackComponentWay "-DCPackComponentWay=${CPackComponentWay}")
         add_test(CPackComponentsForAll-${CPackGen}-${CPackComponentWay}
           ${CMAKE_CTEST_COMMAND} -C \${CTEST_CONFIGURATION_TYPE}
@@ -1153,7 +1166,6 @@ if(BUILD_TESTING)
                                      "components-source"
                                      "components-shlibdeps1"
                                      "components-depend1"
-                                     "components-depend2"
                                      "compression")
       # Run additional tests if dpkg-shlibdeps is available (and is new enough version)
       find_program(SHLIBDEPS_EXECUTABLE NAMES dpkg-shlibdeps)
@@ -1173,6 +1185,11 @@ if(BUILD_TESTING)
             list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-private-lib-failure"
                                                    "shlibdeps-with-private-lib-success")
         endif()
+        # Check if distro has symbols or shlibs data
+        file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols")
+        if(SHLIBS_FILES_EXIST)
+          list(APPEND DEB_CONFIGURATIONS_TO_TEST "components-depend2")
+        endif()
       endif()
 
       set(CPackGen "DEB")
@@ -1338,7 +1355,7 @@ if(BUILD_TESTING)
     --test-command complex)
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/ComplexOneConfig")
   # because of the registry write these tests depend on each other
-  set_tests_properties ( complex PROPERTIES DEPENDS complexOneConfig)
+  set_tests_properties(complex PROPERTIES DEPENDS complexOneConfig)
 
   add_test(Environment ${CMAKE_CTEST_COMMAND}
     --build-and-test
@@ -1421,62 +1438,66 @@ if(BUILD_TESTING)
   endif()
 
   # test for Find modules, simple cases
-  foreach(_mod IN ITEMS
-            ALSA
-            Boost
-            BLAS
-            BZip2
-            CURL
-            Cups
-            Doxygen
-            DevIL
-            EnvModules
-            EXPAT
-            Fontconfig
-            Freetype
-            GDAL
-            GIF
-            Git
-            GLEW
-            GLUT
-            GnuTLS
-            GSL
-            GTK2
-            Iconv
-            ICU
-            Intl
-            Jasper
-            JNI
-            JPEG
-            JsonCpp
-            LAPACK
-            LibArchive
-            LibLZMA
-            LibRHash
-            Libinput
-            LibUV
-            LibXml2
-            LibXslt
-            LTTngUST
-            ODBC
-            OpenACC
-            OpenCL
-            OpenGL
-            OpenMP
-            OpenSSL
-            MPI
-            PNG
-            Patch
-            PostgreSQL
-            Protobuf
-            SDL
-            SQLite3
-            TIFF
-            Vulkan
-            X11
-            XalanC
-            XercesC
-         )
+  foreach(
+  _mod
+  IN ITEMS
+      ALSA
+      BLAS
+      Boost
+      BZip2
+      Cups
+      CURL
+      DevIL
+      Doxygen
+      EnvModules
+      EXPAT
+      Fontconfig
+      Freetype
+      GDAL
+      GIF
+      Git
+      GLEW
+      GLUT
+      GnuTLS
+      GSL
+      GTK2
+      Iconv
+      ICU
+      Intl
+      Jasper
+      JNI
+      JPEG
+      JsonCpp
+      LAPACK
+      LibArchive
+      Libinput
+      LibLZMA
+      LibRHash
+      LibUV
+      LibXml2
+      LibXslt
+      LTTngUST
+      MPI
+      ODBC
+      OpenACC
+      OpenAL
+      OpenCL
+      OpenGL
+      OpenMP
+      OpenSP
+      OpenSSL
+      Patch
+      PNG
+      PostgreSQL
+      Protobuf
+      SDL
+      SQLite3
+      TIFF
+      Vulkan
+      X11
+      XalanC
+      XercesC
+    )
     if(CMake_TEST_Find${_mod})
       add_subdirectory(Find${_mod})
     endif()
@@ -1522,7 +1543,7 @@ if(BUILD_TESTING)
   # CMake_TEST_FindMatlab_MCR_ROOT_DIR: indicates an optional root directory for the MCR, required on Linux
   if(CMake_TEST_FindMatlab OR CMake_TEST_FindMatlab_ROOT_DIR OR
      CMake_TEST_FindMatlab_MCR OR CMake_TEST_FindMatlab_MCR_ROOT_DIR)
-    set(FindMatlab_additional_test_options )
+    set(FindMatlab_additional_test_options)
     if(CMake_TEST_FindMatlab_MCR OR CMake_TEST_FindMatlab_MCR_ROOT_DIR)
       set(FindMatlab_additional_test_options -DIS_MCR=TRUE)
     endif()
@@ -1556,7 +1577,7 @@ if(BUILD_TESTING)
   endif()
 
   set(ExternalProject_BUILD_OPTIONS "")
-  foreach(vcs CVS SVN GIT HG)
+  foreach(vcs IN ITEMS CVS SVN GIT HG)
     if(DEFINED CMake_TEST_ExternalProject_${vcs})
       list(APPEND ExternalProject_BUILD_OPTIONS -DEP_TEST_${vcs}=${CMake_TEST_ExternalProject_${vcs}})
     endif()
@@ -1656,7 +1677,8 @@ if(BUILD_TESTING)
     RUN_SERIAL 1
     TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT}
     WORKING_DIRECTORY ${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate
-    DEPENDS ExternalProjectUpdateSetup )
+    DEPENDS ExternalProjectUpdateSetup
+    )
 
   execute_process(
     COMMAND ${CMAKE_COMMAND}
@@ -1711,7 +1733,7 @@ if(BUILD_TESTING)
   function(add_tutorial_test step_name use_mymath tutorial_arg pass_regex)
     set(tutorial_test_name Tutorial${step_name})
     set(tutorial_build_dir "${CMake_BINARY_DIR}/Tests/Tutorial/${step_name}")
-    if (use_mymath)
+    if(use_mymath)
       set(tutorial_build_options "")
     else()
       set(tutorial_test_name ${tutorial_test_name}_MYMATH)
@@ -1735,7 +1757,7 @@ if(BUILD_TESTING)
 
   if(NOT CMake_TEST_EXTERNAL_CMAKE)
     foreach(STP RANGE 2 12)
-      if (STP EQUAL 6)
+      if(STP EQUAL 8)
         set(pass_regex ".*using log and exp")
       else()
         set(pass_regex "The square root of 25 is 5")
@@ -1994,11 +2016,11 @@ if(BUILD_TESTING)
     --test-command Exec2
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/LinkLineOrder")
-  set_tests_properties ( qtwrapping PROPERTIES DEPENDS wrapping)
-  set_tests_properties ( testdriver1 PROPERTIES DEPENDS qtwrapping)
-  set_tests_properties ( testdriver2 PROPERTIES DEPENDS testdriver1)
-  set_tests_properties ( testdriver3 PROPERTIES DEPENDS testdriver2)
-  set_tests_properties ( linkorder2 PROPERTIES DEPENDS linkorder1)
+  set_tests_properties(qtwrapping PROPERTIES DEPENDS wrapping)
+  set_tests_properties(testdriver1 PROPERTIES DEPENDS qtwrapping)
+  set_tests_properties(testdriver2 PROPERTIES DEPENDS testdriver1)
+  set_tests_properties(testdriver3 PROPERTIES DEPENDS testdriver2)
+  set_tests_properties(linkorder2 PROPERTIES DEPENDS linkorder1)
 
   # Test static linking on toolchains known to support it.
   if((CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "LCC")
@@ -2030,9 +2052,9 @@ if(BUILD_TESTING)
       "${CMake_BINARY_DIR}/Tests/SubDirSpaces/testfromsubdir.obj"
       )
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/SubDirSpaces")
-  endif ()
+  endif()
 
-  if (WIN32)
+  if(WIN32)
     add_test(SubDir ${CMAKE_CTEST_COMMAND}
       --build-and-test
       "${CMake_SOURCE_DIR}/Tests/SubDir"
@@ -2044,7 +2066,7 @@ if(BUILD_TESTING)
       "${CMake_BINARY_DIR}/Tests/SubDir/ShouldBeHere"
       "${CMake_BINARY_DIR}/Tests/SubDir/testfromsubdir.obj"
       )
-  else ()
+  else()
     add_test(SubDir ${CMAKE_CTEST_COMMAND}
       --build-and-test
       "${CMake_SOURCE_DIR}/Tests/SubDir"
@@ -2056,7 +2078,7 @@ if(BUILD_TESTING)
       "${CMake_BINARY_DIR}/Tests/SubDir/ShouldBeHere"
       "${CMake_BINARY_DIR}/Tests/SubDir/testfromsubdir.o"
       )
-  endif ()
+  endif()
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/SubDir")
 
   if(MSVC OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_SIMULATE_ID STREQUAL "MSVC"))
@@ -2067,6 +2089,15 @@ if(BUILD_TESTING)
     if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM")
       ADD_TEST_MACRO(PrecompiledHeader foo)
     endif()
+
+    set(MSVCDebugInformationFormat_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
+    if(CMAKE_Fortran_COMPILER)
+      list(APPEND MSVCDebugInformationFormat_BUILD_OPTIONS -DCMake_TEST_Fortran=1)
+    endif()
+    ADD_TEST_MACRO(MSVCDebugInformationFormat)
+    set_property(TEST MSVCDebugInformationFormat APPEND
+      PROPERTY LABELS "CUDA")
+
     set(MSVCRuntimeLibrary_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
     ADD_TEST_MACRO(MSVCRuntimeLibrary)
     set_property(TEST MSVCRuntimeLibrary APPEND
@@ -2080,7 +2111,7 @@ if(BUILD_TESTING)
     ADD_TEST_MACRO(ModuleDefinition example_exe)
   endif()
 
-  if (CMAKE_C_COMPILER_ID MATCHES "Watcom" AND WIN32)
+  if(CMAKE_C_COMPILER_ID MATCHES "Watcom" AND WIN32)
     ADD_TEST_MACRO(WatcomRuntimeLibrary)
   endif()
 
@@ -2116,7 +2147,7 @@ if(BUILD_TESTING)
   if(MSVC AND NOT MSVC_VERSION LESS 1310
      AND (NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 "
           OR CMAKE_SIZEOF_VOID_P EQUAL 4)
-     AND (NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+     AND (NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
       )
     ADD_TEST_MACRO(VSMASM VSMASM)
   endif()
@@ -2128,7 +2159,7 @@ if(BUILD_TESTING)
 
     if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio 9 "
         AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90"
-        AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+        AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
       ADD_TEST_MACRO(VSWindowsFormsResx VSWindowsFormsResx)
       ADD_TEST_MACRO(VSManagedCustomCommand)
     endif()
@@ -2157,7 +2188,7 @@ if(BUILD_TESTING)
       # The test (and tested property) works with .sln files, so it's skipped when:
       # * cmake --build is set up to use MSBuild, since the MSBuild invocation does not use the .sln file
       set(_last_test "")
-      foreach(config ${CMAKE_CONFIGURATION_TYPES})
+      foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
         add_test(NAME VSExcludeFromDefaultBuild-${config} COMMAND ${CMAKE_CTEST_COMMAND}
           --build-and-test
           "${CMake_SOURCE_DIR}/Tests/VSExcludeFromDefaultBuild"
@@ -2295,7 +2326,7 @@ if(BUILD_TESTING)
     ADD_TEST_MACRO(VSNASM VSNASM)
   endif()
 
-  if (CMake_TEST_GreenHillsMULTI)
+  if(CMake_TEST_GreenHillsMULTI)
     macro(add_test_GhsMulti test_name test_dir bin_sub_dir build_opts)
       separate_arguments(_ghs_build_opts UNIX_COMMAND ${build_opts})
       separate_arguments(_ghs_toolset_extra UNIX_COMMAND ${ghs_toolset_extra})
@@ -2356,7 +2387,7 @@ if(BUILD_TESTING)
         set(ghs_config_name "__default__")
       endif()
       # test integrity build
-      if (NOT ghs_skip_integrity AND (NOT ghs_target_platform OR ghs_target_platform MATCHES "integrity"))
+      if(NOT ghs_skip_integrity AND (NOT ghs_target_platform OR ghs_target_platform MATCHES "integrity"))
         add_test_GhsMulti(integrityDDInt GhsMultiIntegrity/GhsMultiIntegrityDDInt "" "")
         add_test_GhsMulti(integrityMonolith GhsMultiIntegrity/GhsMultiIntegrityMonolith "" "")
         add_test_GhsMulti(integrityDD GhsMultiIntegrity/GhsMultiIntegrityDD "" "")
@@ -2412,9 +2443,6 @@ if(BUILD_TESTING)
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSAndroid/${name}")
   endmacro()
   if(tegra AND NOT "${CMake_SOURCE_DIR};${CMake_BINARY_DIR}" MATCHES " ")
-    if(vs10)
-      add_test_VSAndroid(vs10 "Visual Studio 10 2010" "Tegra-Android")
-    endif()
     if(vs11)
       add_test_VSAndroid(vs11 "Visual Studio 11 2012" "Tegra-Android")
     endif()
@@ -2438,8 +2466,8 @@ if(BUILD_TESTING)
     add_test_VSAndroid(vs17 "Visual Studio 17 2022" "ARM")
   endif()
 
-  if (APPLE)
-    if (CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
+  if(APPLE)
+    if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
       set(BundleTestInstallDir
         "${CMake_BINARY_DIR}/Tests/BundleTest/InstallDirectory")
       add_test(BundleTest ${CMAKE_CTEST_COMMAND}
@@ -2475,8 +2503,8 @@ if(BUILD_TESTING)
 
       add_subdirectory(ObjC)
       add_subdirectory(ObjCXX)
-    endif ()
-  endif ()
+    endif()
+  endif()
 
   if(APPLE AND CTEST_TEST_CPACK)
     add_test(BundleGeneratorTest ${CMAKE_CTEST_COMMAND}
@@ -2667,7 +2695,10 @@ if(BUILD_TESTING)
   endif()
   if(NOT DEFINED CMake_TEST_CTestUpdate_HG AND HG_EXECUTABLE
       AND (UNIX OR NOT "${HG_EXECUTABLE}" MATCHES "cygwin"))
-    set(CMake_TEST_CTestUpdate_HG 1)
+    execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV)
+    if(HG_RV EQUAL 0)
+      set(CMake_TEST_CTestUpdate_HG 1)
+    endif()
   endif()
   if(CMake_TEST_CTestUpdate_HG)
     if(NOT HG_EXECUTABLE)
@@ -3195,24 +3226,24 @@ if(BUILD_TESTING)
     --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFdSetSize/testOutput.log"
     )
 
-  if (CMAKE_TESTS_CDASH_SERVER)
+  if(CMAKE_TESTS_CDASH_SERVER)
     set(regex "^([^:]+)://([^/]+)(.*)$")
 
-    if ("${CMAKE_TESTS_CDASH_SERVER}" MATCHES "${regex}")
+    if("${CMAKE_TESTS_CDASH_SERVER}" MATCHES "${regex}")
       set(protocol "${CMAKE_MATCH_1}")
       set(server "${CMAKE_MATCH_2}")
       set(path "${CMAKE_MATCH_3}")
-    else ()
+    else()
       set(protocol "http")
       set(server "open.cdash.org")
       set(path "")
       message("warning: CMAKE_TESTS_CDASH_SERVER does not match expected regex...")
       message("         ...using default url='${protocol}://${server}${path}' for CTestTest[23]")
-    endif ()
-  endif ()
+    endif()
+  endif()
 
 
-  if (CTEST_TEST_CTEST AND CMAKE_RUN_LONG_TESTS AND CMAKE_TESTS_CDASH_SERVER)
+  if(CTEST_TEST_CTEST AND CMAKE_RUN_LONG_TESTS AND CMAKE_TESTS_CDASH_SERVER)
     configure_file("${CMake_SOURCE_DIR}/Tests/CTestTest/test.cmake.in"
       "${CMake_BINARY_DIR}/Tests/CTestTest/test.cmake" @ONLY ESCAPE_QUOTES)
     add_test(CTestTest ${CMAKE_CTEST_COMMAND}
@@ -3254,19 +3285,19 @@ if(BUILD_TESTING)
     # these tests take a long time, make sure they have it
     # if timeouts have not already been set
     get_test_property(CTestTest TIMEOUT PREVIOUS_TIMEOUT)
-    if ("${PREVIOUS_TIMEOUT}" MATCHES NOTFOUND)
-      set_tests_properties ( CTestTest
+    if("${PREVIOUS_TIMEOUT}" MATCHES NOTFOUND)
+      set_tests_properties(CTestTest
         PROPERTIES TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT})
-    endif ()
+    endif()
 
     if(NOT CMake_TEST_EXTERNAL_CMAKE)
       get_test_property(CTestTest2 TIMEOUT PREVIOUS_TIMEOUT)
       if("${PREVIOUS_TIMEOUT}" MATCHES NOTFOUND)
-        set_tests_properties ( CTestTest2
+        set_tests_properties(CTestTest2
           PROPERTIES TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT})
       endif()
     endif()
-  endif ()
+  endif()
 
   if(CMake_TEST_EXTERNAL_CMAKE)
     set(CMAKE_SKIP_BOOTSTRAP_TEST 1)
@@ -3302,10 +3333,9 @@ if(BUILD_TESTING)
 
     # provide more time for the bootstrap test
     get_test_property(BootstrapTest TIMEOUT PREVIOUS_TIMEOUT)
-    if ("${PREVIOUS_TIMEOUT}" MATCHES NOTFOUND)
-      set_tests_properties ( BootstrapTest
-        PROPERTIES TIMEOUT 5400)
-    endif ()
+    if("${PREVIOUS_TIMEOUT}" MATCHES NOTFOUND)
+      set_tests_properties(BootstrapTest PROPERTIES TIMEOUT 5400)
+    endif()
   endif()
 
   if(CMAKE_Fortran_COMPILER)
@@ -3369,56 +3399,70 @@ if(BUILD_TESTING)
     set(JavaExportImport_BUILD_OPTIONS -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM})
     ADD_TEST_MACRO(JavaExportImport JavaExportImport)
 
-    get_filename_component(JNIPATH ${Java_JAVAC_EXECUTABLE} PATH)
+    get_filename_component(JAVACPATH ${Java_JAVAC_EXECUTABLE} REALPATH)
+    get_filename_component(JNIPATH ${JAVACPATH} PATH)
     find_file(JNI_H jni.h
       "${JNIPATH}/../include"
       "${JNIPATH}/../java/include")
     if(JNI_H AND EXISTS "${JNI_H}") # in case jni.h is a broken symlink
       file(READ "${JNI_H}" JNI_FILE)
       if("${JNI_FILE}" MATCHES "JDK1_2")
-        add_test(NAME Java.Jar COMMAND ${CMAKE_CTEST_COMMAND}
-          --build-and-test
-          "${CMake_SOURCE_DIR}/Tests/Java"
-          "${CMake_BINARY_DIR}/Tests/JavaJar"
-          ${build_generator_args}
-          --build-project hello
-          --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJar/"
-          --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
-        list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJar")
 
-        # For next tests, java tool must have same architecture as toolchain
-        math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8")
         execute_process(
           COMMAND "${Java_JAVA_EXECUTABLE}" -version
           OUTPUT_VARIABLE _version ERROR_VARIABLE _version RESULT_VARIABLE _result
           )
-        if(_result EQUAL 0 AND _version MATCHES "${_object_mode}-Bit")
-          ## next test is valid only if Java version is less than 1.10
-          if ("${Java_VERSION}" VERSION_LESS 1.10)
-            add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND}
-              --build-and-test
-              "${CMake_SOURCE_DIR}/Tests/JavaJavah"
-              "${CMake_BINARY_DIR}/Tests/JavaJavah"
-              ${build_generator_args}
-              --build-project helloJavah
-              --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/"
-              --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
-            list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah")
+
+        # E2K has broken Java RVM before 3.5.2
+        if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "e2k" AND _result EQUAL 0)
+          string(REGEX MATCH "RVM ([0-9.]+)" RVMVER "${_version}")
+          # Consider empty match a broken version too
+          if("${CMAKE_MATCH_1}" VERSION_LESS "3.5.2")
+            set(BROKEN_RVM TRUE)
           endif()
-          ## next test is valid only if Java is, at least, version 1.8
-          if (NOT "${Java_VERSION}" VERSION_LESS 1.8)
-            add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND}
-              --build-and-test
-              "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders"
-              "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders"
-              ${build_generator_args}
-              --build-project helloJavaNativeHeaders
-              --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/"
-              --build-target install
-              --build-options
-              "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/Install"
-              --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
-            list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders")
+        endif()
+
+        if(NOT BROKEN_RVM)
+          add_test(NAME Java.Jar COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG>
+            --build-and-test
+            "${CMake_SOURCE_DIR}/Tests/Java"
+            "${CMake_BINARY_DIR}/Tests/JavaJar"
+            ${build_generator_args}
+            --build-project hello
+            --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJar/"
+            --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
+          list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJar")
+
+          # For next tests, java tool must have same architecture as toolchain
+          math(EXPR _object_mode "${CMAKE_SIZEOF_VOID_P} * 8")
+          if(_result EQUAL 0 AND _version MATCHES "${_object_mode}-Bit")
+            ## next test is valid only if Java version is less than 1.10
+            if("${Java_VERSION}" VERSION_LESS 1.10)
+              add_test(NAME Java.Javah COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG>
+                --build-and-test
+                "${CMake_SOURCE_DIR}/Tests/JavaJavah"
+                "${CMake_BINARY_DIR}/Tests/JavaJavah"
+                ${build_generator_args}
+                --build-project helloJavah
+                --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaJavah/"
+                --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
+              list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaJavah")
+            endif()
+            ## next test is valid only if Java is, at least, version 1.8
+            if(NOT "${Java_VERSION}" VERSION_LESS 1.8)
+              add_test(NAME Java.NativeHeaders COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG>
+                --build-and-test
+                "${CMake_SOURCE_DIR}/Tests/JavaNativeHeaders"
+                "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders"
+                ${build_generator_args}
+                --build-project helloJavaNativeHeaders
+                --build-run-dir "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/"
+                --build-target install
+                --build-options
+                "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/JavaNativeHeaders/Install"
+                --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIG>)
+              list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/JavaNativeHeaders")
+            endif()
           endif()
         endif()
       endif()
@@ -3480,18 +3524,18 @@ if(BUILD_TESTING)
   endif()
 
   if(CMAKE_TEST_PLPLOT_DIR)
-    add_test(plplot ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_PLPLOT_DIR}/../../EasyDashboardScripts/plplot.cmake )
+    add_test(plplot ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_PLPLOT_DIR}/../../EasyDashboardScripts/plplot.cmake)
     set_tests_properties ( plplot PROPERTIES TIMEOUT 5400)
   endif()
 
   if(CMAKE_TEST_CHICKEN_DIR)
-    add_test(Chicken ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_CHICKEN_DIR}/../../EasyDashboardScripts/Chicken.cmake )
-    set_tests_properties ( Chicken PROPERTIES TIMEOUT 5400)
+    add_test(Chicken ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_CHICKEN_DIR}/../../EasyDashboardScripts/Chicken.cmake)
+    set_tests_properties(Chicken PROPERTIES TIMEOUT 5400)
   endif()
 
   if(CMAKE_TEST_KDELIBS_ALPHA_1_DIR)
-    add_test(KDELibsAlpha1 ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_KDELIBS_ALPHA_1_DIR}/../../EasyDashboardScripts/kdelibs.cmake )
-    set_tests_properties ( KDELibsAlpha1 PROPERTIES TIMEOUT 5400)
+    add_test(KDELibsAlpha1 ${CMAKE_CTEST_COMMAND} -V -S ${CMAKE_TEST_KDELIBS_ALPHA_1_DIR}/../../EasyDashboardScripts/kdelibs.cmake)
+    set_tests_properties(KDELibsAlpha1 PROPERTIES TIMEOUT 5400)
   endif()
 
   # Define a set of "contract" tests, each activated by a cache entry
@@ -3501,11 +3545,7 @@ if(BUILD_TESTING)
   # The directory should also contain a Configure.cmake file that
   # sets "CMake_TEST_CONTRACT_<project>_<var>" variables to configure
   # the code below.
-  foreach(project
-      PLplot
-      Trilinos
-      VTK
-      )
+  foreach(project IN ITEMS PLplot Trilinos VTK)
     if(CMake_TEST_CONTRACT_${project})
       include(Contracts/${project}/Configure.cmake)
       ADD_TEST_MACRO(Contracts.${project} ${CMake_TEST_CONTRACT_${project}_RUN_TEST})
@@ -3578,6 +3618,14 @@ if(BUILD_TESTING)
     add_subdirectory(CMakeGUI)
   endif()
 
+  # Run CheckSourceTree as the very last test in the CMake/CTest/CPack test
+  # suite. It detects if any changes have been made to the CMake source tree
+  # by any previous configure, build or test steps.
+  if(GIT_EXECUTABLE AND EXISTS "${CMake_SOURCE_DIR}/.git"
+      AND NOT "${CMake_SOURCE_DIR}" STREQUAL "${CMake_BINARY_DIR}")
+    add_subdirectory(CheckSourceTree)
+  endif()
+
   # If this is not an in-source build, provide a target to wipe out
   # all the test build directories. This must come at the end after
   # all the above logic has finished adding to TEST_BUILD_DIRS
index 49a4041..0907d03 100644 (file)
@@ -56,6 +56,15 @@ macro(check_version_string MODULE_NAME VERSION_VAR)
     if (NOT _exclude_pos EQUAL -1)
         message(STATUS "excluding check of ${VERSION_VAR}='${${VERSION_VAR}}' due to local configuration")
     elseif (${MODULE_NAME}_FOUND)
+
+        unset(SKIP_CHECK)
+        if(${MODULE_NAME} STREQUAL "HG")
+            execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV)
+            if(NOT HG_RV EQUAL 0)
+                message(WARNING "Broken HG executable detected, skipping")
+                set(SKIP_CHECK TRUE)
+            endif()
+        endif()
         if (DEFINED ${VERSION_VAR})
             message(STATUS "${VERSION_VAR}='${${VERSION_VAR}}'")
             if (NOT ${VERSION_VAR} MATCHES "^[0-9]")
@@ -71,7 +80,9 @@ macro(check_version_string MODULE_NAME VERSION_VAR)
                 message(SEND_ERROR "unexpected: ${VERSION_VAR} is NOT VERSION_GREATER 0")
             endif()
         else()
-            message(SEND_ERROR "${MODULE_NAME}_FOUND is set but version number variable ${VERSION_VAR} is NOT DEFINED")
+            if(NOT SKIP_CHECK)
+                message(SEND_ERROR "${MODULE_NAME}_FOUND is set but version number variable ${VERSION_VAR} is NOT DEFINED")
+            endif()
         endif()
     endif ()
 endmacro()
index 9e0b891..bd2dd7e 100644 (file)
@@ -59,18 +59,3 @@ if(GIT_EXECUTABLE)
     )
   AddCMakeTest(PolicyCheck "${PolicyCheck_PreArgs}")
 endif()
-
-# Run CheckSourceTree as the very last test in the CMake/CTest/CPack test
-# suite. It detects if any changes have been made to the CMake source tree
-# by any previous configure, build or test steps.
-#
-if(GIT_EXECUTABLE)
-  string(REPLACE "\\" "/" ENV_HOME "$ENV{HOME}")
-  set(CheckSourceTree_PreArgs
-    "-DCMake_BINARY_DIR:PATH=${CMake_BINARY_DIR}"
-    "-DCMake_SOURCE_DIR:PATH=${CMake_SOURCE_DIR}"
-    "-DGIT_EXECUTABLE:STRING=${GIT_EXECUTABLE}"
-    "-DHOME:STRING=${ENV_HOME}"
-    )
-  AddCMakeTest(CheckSourceTree "${CheckSourceTree_PreArgs}")
-endif()
diff --git a/Tests/CMakeTests/CheckSourceTreeTest.cmake.in b/Tests/CMakeTests/CheckSourceTreeTest.cmake.in
deleted file mode 100644 (file)
index 4f2aaea..0000000
+++ /dev/null
@@ -1,365 +0,0 @@
-# Check the CMake source tree and report anything suspicious...
-#
-message("=============================================================================")
-message("CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
-message("")
-message("CMake_BINARY_DIR='${CMake_BINARY_DIR}'")
-message("CMake_SOURCE_DIR='${CMake_SOURCE_DIR}'")
-message("GIT_EXECUTABLE='${GIT_EXECUTABLE}'")
-message("HOME='${HOME}'")
-message("ENV{DASHBOARD_TEST_FROM_CTEST}='$ENV{DASHBOARD_TEST_FROM_CTEST}'")
-message("")
-string(REPLACE "\\" "\\\\" HOME "${HOME}")
-
-
-# Is the build directory the same as or underneath the source directory?
-# (i.e. - is it an "in source" build?)
-#
-set(in_source_build 0)
-set(build_under_source 0)
-
-string(FIND "${CMake_BINARY_DIR}" "${CMake_SOURCE_DIR}/" pos)
-if(pos EQUAL 0)
-  message("build dir is *inside* source dir")
-  set(build_under_source 1)
-elseif(CMake_SOURCE_DIR STREQUAL "${CMake_BINARY_DIR}")
-  message("build dir *is* source dir")
-  set(in_source_build 1)
-else()
-  string(LENGTH "${CMake_SOURCE_DIR}" src_len)
-  string(LENGTH "${CMake_BINARY_DIR}" bin_len)
-
-  if(bin_len GREATER src_len)
-    math(EXPR substr_len "${src_len}+1")
-    string(SUBSTRING "${CMake_BINARY_DIR}" 0 ${substr_len} bin_dir)
-    if(bin_dir STREQUAL "${CMake_SOURCE_DIR}/")
-      message("build dir is under source dir")
-      set(in_source_build 1)
-    endif()
-  endif()
-endif()
-
-message("src_len='${src_len}'")
-message("bin_len='${bin_len}'")
-message("substr_len='${substr_len}'")
-message("bin_dir='${bin_dir}'")
-message("in_source_build='${in_source_build}'")
-message("build_under_source='${build_under_source}'")
-message("")
-
-if(build_under_source)
-  message(STATUS "Skipping rest of test because build tree is under source tree")
-  return()
-endif()
-
-# If this does not appear to be a git checkout, just pass the test here
-# and now. (Do not let the test fail if it is run in a tree *exported* from a
-# repository or unpacked from a .zip file source installer...)
-#
-set(is_git_checkout 0)
-if(EXISTS "${CMake_SOURCE_DIR}/.git")
-  set(is_git_checkout 1)
-endif()
-
-message("is_git_checkout='${is_git_checkout}'")
-message("")
-
-if(NOT is_git_checkout)
-  message("source tree is not a git checkout... test passes by early return...")
-  return()
-endif()
-
-# This test looks for the following types of changes in the source tree:
-#
-set(additions 0)
-set(conflicts 0)
-set(modifications 0)
-set(nonadditions 0)
-
-# ov == output variable... conditionally filled in by either git below:
-#
-set(cmd "")
-set(ov "")
-set(ev "")
-set(rv "")
-
-# If no GIT_EXECUTABLE, see if we can figure out which git was used
-# for the ctest_update step on this dashboard...
-#
-if(is_git_checkout AND NOT GIT_EXECUTABLE)
-  set(ctest_ini_file "")
-  set(exe "")
-
-  # Use the old name:
-  if(EXISTS "${CMake_BINARY_DIR}/DartConfiguration.tcl")
-    set(ctest_ini_file "${CMake_BINARY_DIR}/DartConfiguration.tcl")
-  endif()
-
-  # But if it exists, prefer the new name:
-  if(EXISTS "${CMake_BINARY_DIR}/CTestConfiguration.ini")
-    set(ctest_ini_file "${CMake_BINARY_DIR}/CTestConfiguration.ini")
-  endif()
-
-  # If there is a ctest ini file, read the update command or git command
-  # from it:
-  #
-  if(ctest_ini_file)
-    file(STRINGS "${ctest_ini_file}" line REGEX "^GITCommand: (.*)$")
-    string(REGEX REPLACE "^GITCommand: (.*)$" "\\1" line "${line}")
-    if("${line}" MATCHES "^\"")
-      string(REGEX REPLACE "^\"([^\"]+)\" *.*$" "\\1" line "${line}")
-    else()
-      string(REGEX REPLACE "^([^ ]+) *.*$" "\\1" line "${line}")
-    endif()
-    set(exe "${line}")
-    if("${exe}" STREQUAL "GITCOMMAND-NOTFOUND")
-      set(exe "")
-    endif()
-    if(exe)
-      message("info: GIT_EXECUTABLE set by 'GITCommand:' from '${ctest_ini_file}'")
-    endif()
-
-    if(NOT exe)
-      file(STRINGS "${ctest_ini_file}" line REGEX "^UpdateCommand: (.*)$")
-      string(REGEX REPLACE "^UpdateCommand: (.*)$" "\\1" line "${line}")
-      if("${line}" MATCHES "^\"")
-        string(REGEX REPLACE "^\"([^\"]+)\" *.*$" "\\1" line "${line}")
-      else()
-        string(REGEX REPLACE "^([^ ]+) *.*$" "\\1" line "${line}")
-      endif()
-      set(exe "${line}")
-      if("${exe}" STREQUAL "GITCOMMAND-NOTFOUND")
-        set(exe "")
-      endif()
-      if(exe)
-        message("info: GIT_EXECUTABLE set by 'UpdateCommand:' from '${ctest_ini_file}'")
-      endif()
-    endif()
-  else()
-    message("info: no DartConfiguration.tcl or CTestConfiguration.ini file...")
-  endif()
-
-  # If we have still not grokked the exe, look in the Update.xml file to see
-  # if we can parse it from there...
-  #
-  if(NOT exe)
-    file(GLOB_RECURSE update_xml_file "${CMake_BINARY_DIR}/Testing/Update.xml")
-    if(update_xml_file)
-      file(STRINGS "${update_xml_file}" line
-        REGEX "^.*<UpdateCommand>(.*)</UpdateCommand>$" LIMIT_COUNT 1)
-      string(REPLACE "&quot\;" "\"" line "${line}")
-      string(REGEX REPLACE "^.*<UpdateCommand>(.*)</UpdateCommand>$" "\\1" line "${line}")
-      if("${line}" MATCHES "^\"")
-        string(REGEX REPLACE "^\"([^\"]+)\" *.*$" "\\1" line "${line}")
-      else()
-        string(REGEX REPLACE "^([^ ]+) *.*$" "\\1" line "${line}")
-      endif()
-      if(line)
-        set(exe "${line}")
-      endif()
-      if(exe)
-        message("info: GIT_EXECUTABLE set by '<UpdateCommand>' from '${update_xml_file}'")
-      endif()
-    else()
-      message("info: no Update.xml file...")
-    endif()
-  endif()
-
-  if(exe)
-    set(GIT_EXECUTABLE "${exe}")
-    message("GIT_EXECUTABLE='${GIT_EXECUTABLE}'")
-    message("")
-
-    if(NOT EXISTS "${GIT_EXECUTABLE}")
-      message(FATAL_ERROR "GIT_EXECUTABLE does not exist...")
-    endif()
-  else()
-    message(FATAL_ERROR "could not determine GIT_EXECUTABLE...")
-  endif()
-endif()
-
-
-if(is_git_checkout AND GIT_EXECUTABLE)
-  # Check with "git status" if there are any local modifications to the
-  # CMake source tree:
-  #
-  message("=============================================================================")
-  message("This is a git checkout, using git to verify source tree....")
-  message("")
-
-  execute_process(COMMAND ${GIT_EXECUTABLE} --version
-    WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-    OUTPUT_VARIABLE version_output
-    OUTPUT_STRIP_TRAILING_WHITESPACE)
-  message("=== output of 'git --version' ===")
-  message("${version_output}")
-  message("=== end output ===")
-  message("")
-
-  execute_process(COMMAND ${GIT_EXECUTABLE} branch -a
-    WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-    OUTPUT_VARIABLE git_branch_output
-    OUTPUT_STRIP_TRAILING_WHITESPACE)
-  message("=== output of 'git branch -a' ===")
-  message("${git_branch_output}")
-  message("=== end output ===")
-  message("")
-
-  execute_process(COMMAND ${GIT_EXECUTABLE} log -1
-    WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-    OUTPUT_VARIABLE git_log_output
-    OUTPUT_STRIP_TRAILING_WHITESPACE)
-  message("=== output of 'git log -1' ===")
-  message("${git_log_output}")
-  message("=== end output ===")
-  message("")
-
-  message("Copy/paste this command to reproduce:")
-  message("cd \"${CMake_SOURCE_DIR}\" && \"${GIT_EXECUTABLE}\" status")
-  message("")
-
-  set(cmd ${GIT_EXECUTABLE} status)
-endif()
-
-
-if(cmd)
-  # Use the HOME value passed in to the script for calling git so it can
-  # find its user/global config settings...
-  #
-  set(original_ENV_HOME "$ENV{HOME}")
-  set(ENV{HOME} "${HOME}")
-
-  execute_process(COMMAND ${cmd}
-    WORKING_DIRECTORY ${CMake_SOURCE_DIR}
-    OUTPUT_VARIABLE ov
-    ERROR_VARIABLE ev
-    RESULT_VARIABLE rv)
-
-  set(ENV{HOME} "${original_ENV_HOME}")
-
-  message("Results of running ${cmd}")
-  message("rv='${rv}'")
-  message("ov='${ov}'")
-  message("ev='${ev}'")
-  message("")
-
-  if(NOT rv STREQUAL 0)
-    if(is_git_checkout AND (rv STREQUAL "1"))
-      # Many builds of git return "1" from a "nothing is changed" git status call...
-      # Do not fail with an error for rv==1 with git...
-    else()
-      message(FATAL_ERROR "error: ${cmd} attempt failed... (see output above)")
-    endif()
-  endif()
-else()
-  message(FATAL_ERROR "error: no COMMAND to run to analyze source tree...")
-endif()
-
-
-# Analyze output:
-#
-if(NOT ov STREQUAL "")
-  string(REPLACE ";" "\\\\;" lines "${ov}")
-  string(REPLACE "\n" "E;" lines "${lines}")
-
-  foreach(line ${lines})
-    message("'${line}'")
-
-    # But do not consider files that exist just because some user poked around
-    # the file system with Windows Explorer or with the Finder from a Mac...
-    # ('Thumbs.db' and '.DS_Store' files...)
-    #
-    set(consider 1)
-    set(ignore_files_regex "^(. |.*(/|\\\\))(\\.DS_Store|Thumbs.db)E$")
-    if(line MATCHES "${ignore_files_regex}")
-      message("   line matches '${ignore_files_regex}' -- not considered")
-      set(consider 0)
-    endif()
-
-    if(consider)
-      if(is_git_checkout)
-        if(line MATCHES "^#?[ \t]*modified:")
-          message("   locally modified file detected...")
-          set(modifications 1)
-        endif()
-
-        if(line MATCHES "^(# )?Untracked")
-          message("   locally non-added file/directory detected...")
-          set(nonadditions 1)
-        endif()
-      endif()
-    endif()
-  endforeach()
-endif()
-
-
-message("=============================================================================")
-message("additions='${additions}'")
-message("conflicts='${conflicts}'")
-message("modifications='${modifications}'")
-message("nonadditions='${nonadditions}'")
-message("")
-
-
-# Decide if the test passes or fails:
-#
-message("=============================================================================")
-
-if("$ENV{DASHBOARD_TEST_FROM_CTEST}" STREQUAL "")
-
-  # developers are allowed to have local additions and modifications...
-  message("interactive test run")
-  message("")
-
-else()
-
-  message("dashboard test run")
-  message("")
-
-  # but dashboard machines are not allowed to have local additions or modifications...
-  if(additions)
-    message(FATAL_ERROR "test fails: local source tree additions")
-  endif()
-
-  if(modifications)
-    message(FATAL_ERROR "test fails: local source tree modifications")
-  endif()
-
-  #
-  # It's a dashboard run if ctest was run with '-D ExperimentalTest' or some
-  # other -D arg on its command line or if ctest is running a -S script to run
-  # a dashboard... Running ctest like that sets the DASHBOARD_TEST_FROM_CTEST
-  # env var.
-  #
-
-endif()
-
-
-# ...and nobody is allowed to have local non-additions or conflicts...
-# Not even developers.
-#
-if(nonadditions)
-  if(in_source_build)
-    message("
-warning: test results confounded because this is an 'in-source' build - cannot
-distinguish between non-added files that are in-source build products and
-non-added source files that somebody forgot to 'git add'... - this is only ok
-if this is intentionally an in-source dashboard build... Developers should
-use out-of-source builds to verify a clean source tree with this test...
-
-Allowing test to pass despite the warning message...
-")
-  else()
-    message(FATAL_ERROR "test fails: local source tree non-additions: use git add before committing, or remove the files from the source tree")
-  endif()
-endif()
-
-if(conflicts)
-  message(FATAL_ERROR "test fails: local source tree conflicts: resolve before committing")
-endif()
-
-
-# Still here? Good then...
-#
-message("test passes")
-message("")
index 63c234a..7acfcd9 100644 (file)
@@ -13,72 +13,84 @@ set(linux64_gcc_text " /usr/lib/gcc/x86_64-linux-gnu/4.3.3/collect2 --eh-frame-h
 set(linux64_gcc_libs "gcc;gcc_s;c;gcc;gcc_s")
 set(linux64_gcc_dirs "/usr/lib/gcc/x86_64-linux-gnu/4.3.3;/usr/lib;/lib;/usr/lib/x86_64-linux-gnu")
 list(APPEND platforms linux64_gcc)
+list(APPEND langs C)
 
 # g++ dummy.cxx -v
 set(linux64_g++_text " /usr/lib/gcc/x86_64-linux-gnu/4.3.3/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../.. -L/usr/lib/x86_64-linux-gnu /tmp/ccalRBlq.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.3.3/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crtn.o")
 set(linux64_g++_libs "stdc++;m;gcc_s;gcc;c;gcc_s;gcc")
 set(linux64_g++_dirs "/usr/lib/gcc/x86_64-linux-gnu/4.3.3;/usr/lib;/lib;/usr/lib/x86_64-linux-gnu")
 list(APPEND platforms linux64_g++)
+list(APPEND langs CXX)
 
 # f95 dummy.f -v
 set(linux64_f95_text " /usr/lib/gcc/x86_64-linux-gnu/4.3.3/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3 -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../.. -L/usr/lib/x86_64-linux-gnu /tmp/ccAVcN7N.o -lgfortranbegin -lgfortran -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.3.3/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.3.3/../../../../lib/crtn.o")
 set(linux64_f95_libs "gfortranbegin;gfortran;m;gcc_s;gcc;c;gcc_s;gcc")
 set(linux64_f95_dirs "/usr/lib/gcc/x86_64-linux-gnu/4.3.3;/usr/lib;/lib;/usr/lib/x86_64-linux-gnu")
 list(APPEND platforms linux64_f95)
+list(APPEND langs Fortran)
 
 # suncc dummy.c '-#'
 set(linux64_suncc_text "/usr/bin/ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /opt/sun/sunstudio12/prod/lib/amd64/crti.o /opt/sun/sunstudio12/prod/lib/amd64/crt1x.o /opt/sun/sunstudio12/prod/lib/amd64/values-xa.o dummy.o -Y \"/opt/sun/sunstudio12/prod/lib/amd64:/lib64:/usr/lib64\" -Qy -lc /opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a /opt/sun/sunstudio12/prod/lib/amd64/crtn.o")
 set(linux64_suncc_libs "c;/opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a")
 set(linux64_suncc_dirs "/opt/sun/sunstudio12/prod/lib/amd64;/lib64;/usr/lib64")
 list(APPEND platforms linux64_suncc)
+list(APPEND langs C)
 
 # sunCC dummy.cxx -v
 set(linux64_sunCC_text "/opt/sun/sunstudio12/prod/lib/amd64/ld -u __1cH__CimplKcplus_init6F_v_ --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -R/opt/sun/sunstudio12/lib/rw7/amd64:/opt/sun/sunstudio12/lib/amd64:/opt/sun/sunstudio12/rtlibs/amd64:/opt/sun/lib/rtlibs/amd64:/opt/SUNWspro/lib/amd64:/lib64:/usr/lib64 -o a.out /opt/sun/sunstudio12/prod/lib/amd64/crti.o /opt/sun/sunstudio12/prod/lib/amd64/CCrti.o /opt/sun/sunstudio12/prod/lib/amd64/crt1x.o -Y P,/opt/sun/sunstudio12/lib/rw7/amd64:/opt/sun/sunstudio12/lib/amd64:/opt/sun/sunstudio12/rtlibs/amd64:/opt/sun/sunstudio12/prod/lib/rw7/amd64:/opt/sun/sunstudio12/prod/lib/amd64:/lib64:/usr/lib64 dummy.o -lCstd -lCrun -lm -lc /opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a /opt/sun/sunstudio12/prod/lib/amd64/CCrtn.o /opt/sun/sunstudio12/prod/lib/amd64/crtn.o >&/tmp/ld.04973.2.err")
 set(linux64_sunCC_libs "Cstd;Crun;m;c;/opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a")
 set(linux64_sunCC_dirs "/opt/sun/sunstudio12/lib/rw7/amd64;/opt/sun/sunstudio12/lib/amd64;/opt/sun/sunstudio12/rtlibs/amd64;/opt/sun/sunstudio12/prod/lib/rw7/amd64;/opt/sun/sunstudio12/prod/lib/amd64;/lib64;/usr/lib64")
 list(APPEND platforms linux64_sunCC)
+list(APPEND langs CXX)
 
 # sunf90 dummy.f -v
 set(linux64_sunf90_text "/usr/bin/ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -R/opt/sun/sunstudio12/lib/amd64:/opt/sun/sunstudio12/rtlibs/amd64:/opt/sun/lib/rtlibs/amd64 -o a.out /opt/sun/sunstudio12/prod/lib/amd64/crti.o /opt/sun/sunstudio12/prod/lib/amd64/crt1x.o /opt/sun/sunstudio12/prod/lib/amd64/values-xi.o -Y P,/opt/sun/sunstudio12/lib/amd64:/opt/sun/sunstudio12/rtlibs/amd64:/opt/sun/sunstudio12/prod/lib/amd64:/lib64:/usr/lib64 dummy.o -lfui -lfai -lfsu -Bdynamic -lmtsk -lpthread -lm -lc /opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a /opt/sun/sunstudio12/prod/lib/amd64/crtn.o")
 set(linux64_sunf90_libs "fui;fai;fsu;mtsk;pthread;m;c;/opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a")
 set(linux64_sunf90_dirs "/opt/sun/sunstudio12/lib/amd64;/opt/sun/sunstudio12/rtlibs/amd64;/opt/sun/sunstudio12/prod/lib/amd64;/lib64;/usr/lib64")
 list(APPEND platforms linux64_sunf90)
+list(APPEND langs Fortran)
 
 # icc dummy.c -v
 set(linux64_icc_text "ld    /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crt1.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crti.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o --eh-frame-hdr -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /tmp/iccBP8OfN.o -L/opt/compiler/intel/compiler/11.0/lib/intel64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../x86_64-suse-linux/lib -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../.. -L/lib64 -L/lib -L/usr/lib64 -L/usr/lib -Bstatic -limf -lsvml -Bdynamic -lm -Bstatic -lipgo -ldecimal -lirc -Bdynamic -lgcc_s -lgcc -Bstatic -lirc -Bdynamic -lc -lgcc_s -lgcc -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crtn.o")
 set(linux64_icc_libs "imf;svml;m;ipgo;decimal;irc;gcc_s;gcc;irc;c;gcc_s;gcc;irc_s;dl;c")
 set(linux64_icc_dirs "/opt/compiler/intel/compiler/11.0/lib/intel64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2;/usr/lib64;/lib64;/usr/x86_64-suse-linux/lib;/lib;/usr/lib")
 list(APPEND platforms linux64_icc)
+list(APPEND langs C)
 
 # icxx dummy.cxx -v
 set(linux64_icxx_text "ld    /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crt1.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crti.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o --eh-frame-hdr -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /tmp/icpc270GoT.o -L/opt/compiler/intel/compiler/11.0/lib/intel64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../x86_64-suse-linux/lib -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../.. -L/lib64 -L/lib -L/usr/lib64 -L/usr/lib -Bstatic -limf -lsvml -Bdynamic -lm -Bstatic -lipgo -ldecimal -Bdynamic -lstdc++ -Bstatic -lirc -Bdynamic -lgcc_s -lgcc -Bstatic -lirc -Bdynamic -lc -lgcc_s -lgcc -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crtn.o")
 set(linux64_icxx_libs "imf;svml;m;ipgo;decimal;stdc++;irc;gcc_s;gcc;irc;c;gcc_s;gcc;irc_s;dl;c")
 set(linux64_icxx_dirs "/opt/compiler/intel/compiler/11.0/lib/intel64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2;/usr/lib64;/lib64;/usr/x86_64-suse-linux/lib;/lib;/usr/lib")
 list(APPEND platforms linux64_icxx)
+list(APPEND langs CXX)
 
 # ifort dummy.f -v
 set(linux64_ifort_text "ld    --eh-frame-hdr -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out -L/opt/compiler/intel/compiler/11.0/lib/intel64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../x86_64-suse-linux/lib -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../.. -L/lib64 -L/lib -L/usr/lib64 -L/usr/lib /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crt1.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crti.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o /opt/compiler/intel/compiler/11.0/lib/intel64/for_main.o dum.cxx -Bstatic -lifport -lifcore -limf -lsvml -Bdynamic -lm -Bstatic -lipgo -lirc -Bdynamic -lpthread -lc -lgcc_s -lgcc -Bstatic -lirc_s -Bdynamic -ldl -lc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/../../../../lib64/crtn.o")
 set(linux64_ifort_libs "ifport;ifcore;imf;svml;m;ipgo;irc;pthread;c;gcc_s;gcc;irc_s;dl;c")
 set(linux64_ifort_dirs "/opt/compiler/intel/compiler/11.0/lib/intel64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2;/usr/lib64;/lib64;/usr/x86_64-suse-linux/lib;/lib;/usr/lib")
 list(APPEND platforms linux64_ifort)
+list(APPEND langs Fortran)
 
 # pgcc dummy.c -v
 set(linux64_pgcc_text "/usr/bin/ld /usr/lib64/crt1.o /usr/lib64/crti.o /opt/compiler/pgi/linux86-64/8.0-3/lib/trace_init.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /opt/compiler/pgi/linux86-64/8.0-3/lib/pgi.ld -L/opt/compiler/pgi/linux86-64/8.0-3/lib -L/usr/lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 /tmp/pgcc7OscXa5ur7Zk.o -rpath /opt/compiler/pgi/linux86-64/8.0-3/lib -lnspgc -lpgc -lm -lgcc -lc -lgcc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/crtn.o")
 set(linux64_pgcc_libs "nspgc;pgc;m;gcc;c;gcc")
 set(linux64_pgcc_dirs "/opt/compiler/pgi/linux86-64/8.0-3/lib;/usr/lib64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2")
 list(APPEND platforms linux64_pgcc)
+list(APPEND langs C)
 
 # pgCC dummy.cxx -v
 set(linux64_pgCC_text "/usr/bin/ld /usr/lib64/crt1.o /usr/lib64/crti.o /opt/compiler/pgi/linux86-64/8.0-3/lib/trace_init.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /opt/compiler/pgi/linux86-64/8.0-3/lib/pgi.ld -L/opt/compiler/pgi/linux86-64/8.0-3/lib -L/usr/lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 /tmp/pgCCFhjcDt1fs1Ki.o -rpath /opt/compiler/pgi/linux86-64/8.0-3/lib -lstd -lC -lnspgc -lpgc -lm -lgcc -lc -lgcc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/crtn.o")
 set(linux64_pgCC_libs "std;C;nspgc;pgc;m;gcc;c;gcc")
 set(linux64_pgCC_dirs "/opt/compiler/pgi/linux86-64/8.0-3/lib;/usr/lib64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2")
 list(APPEND platforms linux64_pgCC)
+list(APPEND langs CXX)
 
 # pgf90 dummy.f -v
 set(linux64_pgf90_text "/usr/bin/ld /usr/lib64/crt1.o /usr/lib64/crti.o /opt/compiler/pgi/linux86-64/8.0-3/lib/trace_init.o /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtbegin.o /opt/compiler/pgi/linux86-64/8.0-3/lib/f90main.o -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /opt/compiler/pgi/linux86-64/8.0-3/lib/pgi.ld -L/opt/compiler/pgi/linux86-64/8.0-3/lib -L/usr/lib64 -L/usr/lib64/gcc/x86_64-suse-linux/4.1.2 /tmp/pgf90QOIc_eB9xY5h.o -rpath /opt/compiler/pgi/linux86-64/8.0-3/lib -lpgf90 -lpgf90_rpm1 -lpgf902 -lpgf90rtl -lpgftnrtl -lnspgc -lpgc -lrt -lpthread -lm -lgcc -lc -lgcc /usr/lib64/gcc/x86_64-suse-linux/4.1.2/crtend.o /usr/lib64/crtn.o")
 set(linux64_pgf90_libs "pgf90;pgf90_rpm1;pgf902;pgf90rtl;pgftnrtl;nspgc;pgc;rt;pthread;m;gcc;c;gcc")
 set(linux64_pgf90_dirs "/opt/compiler/pgi/linux86-64/8.0-3/lib;/usr/lib64;/usr/lib64/gcc/x86_64-suse-linux/4.1.2")
 list(APPEND platforms linux64_pgf90)
+list(APPEND langs Fortran)
 
 # nagfor dummy.f -Wl,-v
 set(linux64_nagfor_text " /usr/libexec/gcc/x86_64-redhat-linux/4.4.5/collect2 --no-add-needed --eh-frame-hdr --build-id -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.4.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.4.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../.. /usr/local/NAG/lib/f90_init.o /usr/local/NAG/lib/quickfit.o dummy.o -rpath /usr/local/NAG/lib /usr/local/NAG/lib/libf53.so /usr/local/NAG/lib/libf53.a -lm -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.4.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crtn.o")
@@ -86,6 +98,7 @@ set(linux64_nagfor_libs "/usr/local/NAG/lib/f90_init.o;/usr/local/NAG/lib/quickf
 set(linux64_nagfor_dirs "/usr/lib/gcc/x86_64-redhat-linux/4.4.5;/usr/lib64;/lib64;/usr/lib")
 set(linux64_nagfor_obj_regex "^/usr/local/NAG/lib")
 list(APPEND platforms linux64_nagfor)
+list(APPEND langs Fortran)
 
 # absoft dummy.f -X -v
 set(linux64_absoft_text "collect2 version 4.4.5 (x86-64 Linux/ELF)
@@ -93,6 +106,7 @@ set(linux64_absoft_text "collect2 version 4.4.5 (x86-64 Linux/ELF)
 set(linux64_absoft_libs "af90math;afio;amisc;absoftmain;af77math;m;mv;pthread;gcc;gcc_s;c;gcc;gcc_s")
 set(linux64_absoft_dirs "/opt/absoft11.1/lib64;/usr/lib/gcc/x86_64-linux-gnu/4.4.5;/usr/lib;/lib")
 list(APPEND platforms linux64_absoft)
+list(APPEND langs Fortran)
 
 # gcc dummy.c -v # in strange path
 set(linux64_test1_text "
@@ -101,12 +115,14 @@ ${linux64_gcc_text}")
 set(linux64_test1_libs "${linux64_gcc_libs}")
 set(linux64_test1_dirs "${linux64_gcc_dirs}")
 list(APPEND platforms linux64_test1)
+list(APPEND langs C)
 
 # sunCC dummy.cxx -v # extra slashes
 set(linux64_test2_text "/usr/bin/ld --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /opt/sun/sunstudio12/prod/lib/amd64//crti.o /opt/sun/sunstudio12/prod/lib/amd64/crt1x.o /opt/sun/sunstudio12/prod/lib/amd64/values-xa.o dummy.o -Y \"/opt/sun/sunstudio12/prod/lib//amd64:/lib64:/usr//lib64\" -Qy -lc /opt/sun/sunstudio12/prod/lib/amd64//libc_supp.a /opt/sun/sunstudio12/prod/lib/amd64/crtn.o")
 set(linux64_test2_libs "c;/opt/sun/sunstudio12/prod/lib/amd64/libc_supp.a")
 set(linux64_test2_dirs "/opt/sun/sunstudio12/prod/lib/amd64;/lib64;/usr/lib64")
 list(APPEND platforms linux64_test2)
+list(APPEND langs CXX)
 
 # -specs=redhat-hardened-ld
 set(linux64_test3_text "COLLECT_GCC_OPTIONS='-specs=/usr/lib/rpm/redhat/redhat-hardened-ld' '-v' '-O2' '-g' '-pipe' '-Wall' '-Werror=format-security' '-fexceptions' '-fstack-protector-strong' '--param' 'ssp-buffer-size=4' '-grecord-gcc-switches' '-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1' '-m64' '-mtune=generic' '-I' '/usr/lib64/gfortran/modules' '-o' 'a.out' '-rdynamic' '-shared-libgcc' '-march=x86-64' '-pie'
@@ -114,12 +130,14 @@ set(linux64_test3_text "COLLECT_GCC_OPTIONS='-specs=/usr/lib/rpm/redhat/redhat-h
 set(linux64_test3_libs "gfortran;m;gcc_s;gcc;quadmath;m;gcc_s;gcc;c;gcc_s;gcc")
 set(linux64_test3_dirs "/usr/lib/gcc/x86_64-redhat-linux/5.1.1;/usr/lib64;/lib64;/usr/lib")
 list(APPEND platforms linux64_test3)
+list(APPEND langs Fortran)
 
 # clang -fsanitize=memory
 set(linux64_clang_sanitize_memory_text [[ "/usr/bin/ld" --hash-style=both --build-id --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o a.out /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../x86_64-linux-gnu/crt1.o /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../x86_64-linux-gnu/crti.o /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/crtbegin.o -L/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0 -L/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../x86_64-linux-gnu -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../.. -L/usr/lib/llvm-3.8/bin/../lib -L/lib -L/usr/lib -whole-archive /usr/lib/llvm-3.8/bin/../lib/clang/3.8.0/lib/linux/libclang_rt.msan-x86_64.a -no-whole-archive --dynamic-list=/usr/lib/llvm-3.8/bin/../lib/clang/3.8.0/lib/linux/libclang_rt.msan-x86_64.a.syms /tmp/dummy-27898d.o --no-as-needed -lpthread -lrt -lm -ldl -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/crtend.o /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../x86_64-linux-gnu/crtn.o]])
 set(linux64_clang_sanitize_memory_libs "pthread;rt;m;dl;gcc;gcc_s;c;gcc;gcc_s")
 set(linux64_clang_sanitize_memory_dirs "/usr/lib/gcc/x86_64-linux-gnu/5.4.0;/usr/lib/x86_64-linux-gnu;/lib/x86_64-linux-gnu;/lib64;/usr/lib;/usr/lib/llvm-3.8/lib;/lib")
 list(APPEND platforms linux64_clang_sanitize_memory)
+list(APPEND langs C)
 
 #-----------------------------------------------------------------------------
 # Mac
@@ -144,12 +162,14 @@ set(mac_i686_gcc_Wlv_libs "gcc")
 set(mac_i686_gcc_Wlv_dirs "/usr/lib/i686-apple-darwin10/4.2.1;/usr/lib/gcc/i686-apple-darwin10/4.2.1;/usr/lib;/usr/local/lib")
 set(mac_i686_gcc_Wlv_fwks "/Library/Frameworks;/System/Library/Frameworks")
 list(APPEND platforms mac_i686_gcc_Wlv)
+list(APPEND langs C)
 
 # gcc -arch i686 dummy.c -v
 set(mac_i686_gcc_text " /usr/libexec/gcc/i686-apple-darwin10/4.2.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.6.o -L/usr/lib/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../../i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../.. /var/tmp//ccnhXAGL.o -lSystem -lgcc -lSystem")
 set(mac_i686_gcc_libs "gcc")
 set(mac_i686_gcc_dirs "/usr/lib/i686-apple-darwin10/4.2.1;/usr/lib/gcc/i686-apple-darwin10/4.2.1;/usr/lib")
 list(APPEND platforms mac_i686_gcc)
+list(APPEND langs C)
 
 # g++ -arch i686 dummy.cxx -v -Wl,-v
 set(mac_i686_g++_Wlv_text " /usr/libexec/gcc/i686-apple-darwin10/4.2.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.6.o -L/usr/lib/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../../i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../.. /var/tmp//ccEXXICh.o -lstdc++ -lSystem -lgcc -lSystem
@@ -171,12 +191,14 @@ set(mac_i686_g++_Wlv_libs "stdc++;gcc")
 set(mac_i686_g++_Wlv_dirs "/usr/lib/i686-apple-darwin10/4.2.1;/usr/lib/gcc/i686-apple-darwin10/4.2.1;/usr/lib;/usr/local/lib")
 set(mac_i686_g++_Wlv_fwks "/Library/Frameworks;/System/Library/Frameworks")
 list(APPEND platforms mac_i686_g++_Wlv)
+list(APPEND langs CXX)
 
 # g++ -arch i686 dummy.cxx -v
 set(mac_i686_g++_text " /usr/libexec/gcc/i686-apple-darwin10/4.2.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.6.o -L/usr/lib/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../../i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../.. /var/tmp//ccEXXICh.o -lstdc++ -lSystem -lgcc -lSystem")
 set(mac_i686_g++_libs "stdc++;gcc")
 set(mac_i686_g++_dirs "/usr/lib/i686-apple-darwin10/4.2.1;/usr/lib/gcc/i686-apple-darwin10/4.2.1;/usr/lib")
 list(APPEND platforms mac_i686_g++)
+list(APPEND langs CXX)
 
 # gfortran dummy.f -v -Wl,-v
 set(mac_i686_gfortran_Wlv_text " /usr/local/libexec/gcc/i386-apple-darwin9.7.0/4.4.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/local/lib/gcc/i386-apple-darwin9.7.0/4.4.1 -L/usr/local/lib/gcc/i386-apple-darwin9.7.0/4.4.1/../../.. /var/tmp//cc9zXNax.o -v -lgfortranbegin -lgfortran -lgcc_s.10.5 -lgcc -lSystem
@@ -195,12 +217,14 @@ set(mac_i686_gfortran_Wlv_libs "gfortranbegin;gfortran;gcc_s.10.5;gcc")
 set(mac_i686_gfortran_Wlv_dirs "/usr/local/lib/gcc/i386-apple-darwin9.7.0/4.4.1;/usr/local/lib;/usr/lib")
 set(mac_i686_gfortran_Wlv_fwks "/Library/Frameworks;/System/Library/Frameworks")
 list(APPEND platforms mac_i686_gfortran_Wlv)
+list(APPEND langs Fortran)
 
 # gfortran dummy.f -v
 set(mac_i686_gfortran_text " /usr/libexec/gcc/i386-apple-darwin9.7.0/4.4.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/lib/gcc/i386-apple-darwin9.7.0/4.4.1 -L/usr/lib/gcc -L/usr/lib/gcc/i386-apple-darwin9.7.0/4.4.1/../../.. /var/tmp//ccgqbX5P.o -lgfortranbegin -lgfortran -lgcc_s.10.5 -lgcc -lSystem")
 set(mac_i686_gfortran_libs "gfortranbegin;gfortran;gcc_s.10.5;gcc")
 set(mac_i686_gfortran_dirs "/usr/lib/gcc/i386-apple-darwin9.7.0/4.4.1;/usr/lib/gcc;/usr/lib")
 list(APPEND platforms mac_i686_gfortran)
+list(APPEND langs Fortran)
 
 # gcc -arch ppc dummy.c -v -Wl,-v
 set(mac_ppc_gcc_Wlv_text " /usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/collect2 -dynamic -arch ppc -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/lib/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../../powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../.. /var/tmp//cclziQY4.o -v -lgcc -lSystemStubs -lSystem
@@ -222,12 +246,14 @@ set(mac_ppc_gcc_Wlv_libs "gcc")
 set(mac_ppc_gcc_Wlv_dirs "/usr/lib/powerpc-apple-darwin10/4.2.1;/usr/lib/gcc/powerpc-apple-darwin10/4.2.1;/usr/lib;/usr/local/lib")
 set(mac_ppc_gcc_Wlv_fwks "/Library/Frameworks;/System/Library/Frameworks")
 list(APPEND platforms mac_ppc_gcc_Wlv)
+list(APPEND langs C)
 
 # gcc -arch ppc dummy.c -v
 set(mac_ppc_gcc_text " /usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/collect2 -dynamic -arch ppc -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/lib/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../../powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../.. /var/tmp//ccdcolsP.o -lgcc -lSystemStubs -lSystem")
 set(mac_ppc_gcc_libs "gcc")
 set(mac_ppc_gcc_dirs "/usr/lib/powerpc-apple-darwin10/4.2.1;/usr/lib/gcc/powerpc-apple-darwin10/4.2.1;/usr/lib")
 list(APPEND platforms mac_ppc_gcc)
+list(APPEND langs C)
 
 # g++ -arch ppc dummy.cxx -v -Wl,-v
 set(mac_ppc_g++_Wlv_text " /usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/collect2 -dynamic -arch ppc -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/lib/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../../powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../.. /var/tmp//ccaFTkwq.o -v -lstdc++ -lgcc -lSystemStubs -lSystem
@@ -249,12 +275,14 @@ set(mac_ppc_g++_Wlv_libs "stdc++;gcc")
 set(mac_ppc_g++_Wlv_dirs "/usr/lib/powerpc-apple-darwin10/4.2.1;/usr/lib/gcc/powerpc-apple-darwin10/4.2.1;/usr/lib;/usr/local/lib")
 set(mac_ppc_g++_Wlv_fwks "/Library/Frameworks;/System/Library/Frameworks")
 list(APPEND platforms mac_ppc_g++_Wlv)
+list(APPEND langs CXX)
 
 # g++ -arch ppc dummy.cxx -v
 set(mac_ppc_g++_text " /usr/libexec/gcc/powerpc-apple-darwin10/4.2.1/collect2 -dynamic -arch ppc -macosx_version_min 10.6.0 -weak_reference_mismatches non-weak -o a.out -lcrt1.10.5.o -L/usr/lib/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../../powerpc-apple-darwin10/4.2.1 -L/usr/lib/gcc/powerpc-apple-darwin10/4.2.1/../../.. /var/tmp//ccbjB6Lj.o -lstdc++ -lgcc -lSystemStubs -lSystem")
 set(mac_ppc_g++_libs "stdc++;gcc")
 set(mac_ppc_g++_dirs "/usr/lib/powerpc-apple-darwin10/4.2.1;/usr/lib/gcc/powerpc-apple-darwin10/4.2.1;/usr/lib")
 list(APPEND platforms mac_ppc_g++)
+list(APPEND langs CXX)
 
 # absoft dummy.f -X -v
 set(mac_absoft_text "collect2 version 4.2.1 (Apple Inc. build 5664) (i686 Darwin)
@@ -263,12 +291,14 @@ set(mac_absoft_text "collect2 version 4.2.1 (Apple Inc. build 5664) (i686 Darwin
 set(mac_absoft_libs "af90math;afio;amisc;absoftmain;af77math;m;mv;gcc")
 set(mac_absoft_dirs "/Applications/Absoft11.1/lib;/usr/lib/i686-apple-darwin10/4.2.1;/usr/lib/gcc/i686-apple-darwin10/4.2.1;/usr/lib")
 list(APPEND platforms mac_absoft)
+list(APPEND langs Fortran)
 
 # Xcode 8.3: clang++ dummy.cpp -v
 set(mac_clang_v_text " \"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld\" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch x86_64 -macosx_version_min 10.12.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -o a.out /var/folders/hc/95l7dhnx459c57g4yg_6yd8c0000gp/T/dummy-384ea1.o -lc++ -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/lib/darwin/libclang_rt.osx.a")
 set(mac_clang_v_libs "c++")
 set(mac_clang_v_dirs "")
 list(APPEND platforms mac_clang_v)
+list(APPEND langs CXX)
 
 #-----------------------------------------------------------------------------
 # Sun
@@ -278,24 +308,28 @@ set(sun_cc_text "/usr/ccs/bin/ld /opt/SUNWspro/prod/lib/crti.o /opt/SUNWspro/pro
 set(sun_cc_libs "c")
 set(sun_cc_dirs "/opt/SUNWspro/prod/lib/v8plus;/opt/SUNWspro/prod/lib;/usr/ccs/lib;/lib;/usr/lib")
 list(APPEND platforms sun_cc)
+list(APPEND langs C)
 
 # CC dummy.cxx -v
 set(sun_CC_text "/usr/ccs/bin/ld -u __1cH__CimplKcplus_init6F_v_ -zld32=-S/opt/SUNWspro/prod/lib/libldstab_ws.so -zld64=-S/opt/SUNWspro/prod/lib/v9/libldstab_ws.so -zld32=-S/opt/SUNWspro/prod/lib/libCCexcept.so.1 -zld64=-S/opt/SUNWspro/prod/lib/v9/libCCexcept.so.1 -R/opt/SUNWspro/lib/rw7:/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/lib:/usr/ccs/lib:/lib:/usr/lib -o a.out /opt/SUNWspro/prod/lib/crti.o /opt/SUNWspro/prod/lib/CCrti.o /opt/SUNWspro/prod/lib/crt1.o /opt/SUNWspro/prod/lib/misalign.o /opt/SUNWspro/prod/lib/values-xa.o -Y P,/opt/SUNWspro/lib/rw7:/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/prod/lib/rw7:/opt/SUNWspro/prod/lib/v8plus:/opt/SUNWspro/lib:/opt/SUNWspro/prod/lib:/usr/ccs/lib:/lib:/usr/lib dummy.o -lCstd -lCrun -lm -lc /opt/SUNWspro/prod/lib/CCrtn.o /opt/SUNWspro/prod/lib/crtn.o >&/tmp/ld.14846.2.err")
 set(sun_CC_libs "Cstd;Crun;m;c")
 set(sun_CC_dirs "/opt/SUNWspro/lib/rw7;/opt/SUNWspro/lib/v8plus;/opt/SUNWspro/prod/lib/rw7;/opt/SUNWspro/prod/lib/v8plus;/opt/SUNWspro/lib;/opt/SUNWspro/prod/lib;/usr/ccs/lib;/lib;/usr/lib")
 list(APPEND platforms sun_CC)
+list(APPEND langs CXX)
 
 # f77 dummy.f -v
 set(sun_f77_text "/usr/ccs/bin/ld -t -R/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/lib -o a.out /opt/SUNWspro/prod/lib/crti.o /opt/SUNWspro/prod/lib/crt1.o /opt/SUNWspro/prod/lib/misalign.o /opt/SUNWspro/prod/lib/values-xi.o -Y P,/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/prod/lib/v8plus:/opt/SUNWspro/lib:/opt/SUNWspro/prod/lib:/usr/ccs/lib:/lib:/usr/lib dummy.o -lf77compat -zallextract -lompstubs -zdefaultextract -lfui -lfai -lfai2 -lfsumai -lfprodai -lfminlai -lfmaxlai -lfminvai -lfmaxvai -lfsu -lsunmath -lm -lc /opt/SUNWspro/prod/lib/crtn.o")
 set(sun_f77_libs "f77compat;-zallextract;ompstubs;-zdefaultextract;fui;fai;fai2;fsumai;fprodai;fminlai;fmaxlai;fminvai;fmaxvai;fsu;sunmath;m;c")
 set(sun_f77_dirs "/opt/SUNWspro/lib/v8plus;/opt/SUNWspro/prod/lib/v8plus;/opt/SUNWspro/lib;/opt/SUNWspro/prod/lib;/usr/ccs/lib;/lib;/usr/lib")
 list(APPEND platforms sun_f77)
+list(APPEND langs Fortran)
 
 # f90 dummy.f -v
 set(sun_f90_text "/usr/ccs/bin/ld -t -R/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/lib -o a.out /opt/SUNWspro/prod/lib/crti.o /opt/SUNWspro/prod/lib/crt1.o /opt/SUNWspro/prod/lib/misalign.o /opt/SUNWspro/prod/lib/values-xi.o -Y P,/opt/SUNWspro/lib/v8plus:/opt/SUNWspro/prod/lib/v8plus:/opt/SUNWspro/lib:/opt/SUNWspro/prod/lib:/usr/ccs/lib:/lib:/usr/lib dummy.o -zallextract -lompstubs -zdefaultextract -lfui -lfai -lfai2 -lfsumai -lfprodai -lfminlai -lfmaxlai -lfminvai -lfmaxvai -lfsu -lsunmath -lm -lc /opt/SUNWspro/prod/lib/crtn.o")
 set(sun_f90_libs "-zallextract;ompstubs;-zdefaultextract;fui;fai;fai2;fsumai;fprodai;fminlai;fmaxlai;fminvai;fmaxvai;fsu;sunmath;m;c")
 set(sun_f90_dirs "/opt/SUNWspro/lib/v8plus;/opt/SUNWspro/prod/lib/v8plus;/opt/SUNWspro/lib;/opt/SUNWspro/prod/lib;/usr/ccs/lib;/lib;/usr/lib")
 list(APPEND platforms sun_f90)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # AIX
@@ -305,12 +339,14 @@ set(aix_xlc_32_text "/bin/ld -b32 /lib/crt0.o -bpT:0x10000000 -bpD:0x20000000 du
 set(aix_xlc_32_libs "xlopt;xl;c")
 set(aix_xlc_32_dirs "/usr/vac/lib")
 list(APPEND platforms aix_xlc_32)
+list(APPEND langs C)
 
 # xlc -q64 dummy.c -V
 set(aix_xlc_64_text "/bin/ld -b64 /lib/crt0_64.o -bpT:0x100000000 -bpD:0x110000000 dummy.o -L/usr/vac/lib -lxlopt -lxl -lc")
 set(aix_xlc_64_libs "xlopt;xl;c")
 set(aix_xlc_64_dirs "/usr/vac/lib")
 list(APPEND platforms aix_xlc_64)
+list(APPEND langs C)
 
 # xlC -q32 dummy.cxx -V
 set(aix_xlC_32_text "
@@ -319,6 +355,7 @@ set(aix_xlC_32_text "
 set(aix_xlC_32_libs "xlopt;xl;C;m;c")
 set(aix_xlC_32_dirs "/usr/vac/lib;/usr/vacpp/lib")
 list(APPEND platforms aix_xlC_32)
+list(APPEND langs CXX)
 
 # xlC -q64 dummy.cxx -V
 set(aix_xlC_64_text "
@@ -327,36 +364,42 @@ set(aix_xlC_64_text "
 set(aix_xlC_64_libs "xlopt;xl;C;m;c")
 set(aix_xlC_64_dirs "/usr/vac/lib;/usr/vacpp/lib")
 list(APPEND platforms aix_xlC_64)
+list(APPEND langs CXX)
 
 # xlf -q32 dummy.f -V
 set(aix_xlf_32_text "/bin/ld -b32 /lib/crt0.o -bpT:0x10000000 -bpD:0x20000000 -bh:4 -bh:4 -bh:4 -bh:4 dummy.o -lxlf90 -L/usr/lpp/xlf/lib -lxlopt -lxlf -lxlomp_ser -lm -lc")
 set(aix_xlf_32_libs "xlf90;xlopt;xlf;xlomp_ser;m;c")
 set(aix_xlf_32_dirs "/usr/lpp/xlf/lib")
 list(APPEND platforms aix_xlf_32)
+list(APPEND langs Fortran)
 
 # xlf -q64 dummy.f -V
 set(aix_xlf_64_text "/bin/ld -b64 /lib/crt0_64.o -bpT:0x100000000 -bpD:0x110000000 -bh:4 -bh:4 -bh:4 -bh:4 dummy.o -lxlf90 -L/usr/lpp/xlf/lib -lxlopt -lxlf -lxlomp_ser -lm -lc")
 set(aix_xlf_64_libs "xlf90;xlopt;xlf;xlomp_ser;m;c")
 set(aix_xlf_64_dirs "/usr/lpp/xlf/lib")
 list(APPEND platforms aix_xlf_64)
+list(APPEND langs Fortran)
 
 # xlf90 -q32 dummy.f -V
 set(aix_xlf90_32_text "/bin/ld -b32 /lib/crt0.o -bpT:0x10000000 -bpD:0x20000000 -bh:4 dummy.o -lxlf90 -L/usr/lpp/xlf/lib -lxlopt -lxlf -lxlomp_ser -lm -lc")
 set(aix_xlf90_32_libs "xlf90;xlopt;xlf;xlomp_ser;m;c")
 set(aix_xlf90_32_dirs "/usr/lpp/xlf/lib")
 list(APPEND platforms aix_xlf90_32)
+list(APPEND langs Fortran)
 
 # xlf90 -q64 dummy.f -V
 set(aix_xlf90_64_text "/bin/ld -b64 /lib/crt0_64.o -bpT:0x100000000 -bpD:0x110000000 -bh:4 dummy.o -lxlf90 -L/usr/lpp/xlf/lib -lxlopt -lxlf -lxlomp_ser -lm -lc")
 set(aix_xlf90_64_libs "xlf90;xlopt;xlf;xlomp_ser;m;c")
 set(aix_xlf90_64_dirs "/usr/lpp/xlf/lib")
 list(APPEND platforms aix_xlf90_64)
+list(APPEND langs Fortran)
 
 # g++ dummy.c -v
 set(aix_g++_text " /prefix/libexec/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/collect2 -bpT:0x10000000 -bpD:0x20000000 -btextro /lib/crt0.o /prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/crtcxa.o /prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/crtdbase.o -L/prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0 -L/prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/../../.. /tmp//ccKROJ1f.o -lstdc++ -lm -lgcc_s /prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/libgcc.a -lc -lgcc_s /prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/libgcc.a")
 set(aix_g++_libs "stdc++;m;gcc_s;/prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/libgcc.a;c;gcc_s;/prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0/libgcc.a")
 set(aix_g++_dirs "/prefix/lib/gcc/powerpc-ibm-aix7.2.0.0/7.2.0;/prefix/lib")
 list(APPEND platforms aix_g++)
+list(APPEND langs CXX)
 
 #-----------------------------------------------------------------------------
 # HP
@@ -367,6 +410,7 @@ set(hp_cc_old_text "cc: LPATH is /usr/lib/pa1.1:/usr/lib:/opt/langtools/lib:
 set(hp_cc_old_libs "c")
 set(hp_cc_old_dirs "/usr/lib/pa1.1;/usr/lib;/opt/langtools/lib")
 list(APPEND platforms hp_cc_old)
+list(APPEND langs C)
 
 # aCC dummy.cxx -v
 set(hp_aCC_old_text "LPATH=/usr/lib/pa1.1:/usr/lib:/opt/langtools/lib
@@ -374,6 +418,7 @@ set(hp_aCC_old_text "LPATH=/usr/lib/pa1.1:/usr/lib:/opt/langtools/lib
 set(hp_aCC_old_libs "std;stream;Csup;m;cl;c")
 set(hp_aCC_old_dirs "/usr/lib/pa1.1;/usr/lib;/opt/langtools/lib;/opt/aCC/lib")
 list(APPEND platforms hp_aCC_old)
+list(APPEND langs CXX)
 
 # cc dummy.c -v
 set(hp_cc_32_text "LPATH=/usr/lib/hpux32:/opt/langtools/lib/hpux32
@@ -381,6 +426,7 @@ set(hp_cc_32_text "LPATH=/usr/lib/hpux32:/opt/langtools/lib/hpux32
 set(hp_cc_32_libs "c")
 set(hp_cc_32_dirs "/usr/lib/hpux32;/opt/langtools/lib/hpux32")
 list(APPEND platforms hp_cc_32)
+list(APPEND langs C)
 
 # cc +DD64 dummy.c -v
 set(hp_cc_64_text "LPATH=/usr/lib/hpux64:/opt/langtools/lib/hpux64
@@ -388,6 +434,7 @@ set(hp_cc_64_text "LPATH=/usr/lib/hpux64:/opt/langtools/lib/hpux64
 set(hp_cc_64_libs "c")
 set(hp_cc_64_dirs "/usr/lib/hpux64;/opt/langtools/lib/hpux64")
 list(APPEND platforms hp_cc_64)
+list(APPEND langs C)
 
 # aCC dummy.cxx -v
 set(hp_aCC_32_text "LPATH=/usr/lib/hpux32:/opt/langtools/lib/hpux32
@@ -395,6 +442,7 @@ set(hp_aCC_32_text "LPATH=/usr/lib/hpux32:/opt/langtools/lib/hpux32
 set(hp_aCC_32_libs "std_v2;Csup;m;unwind;Csup;c;dl")
 set(hp_aCC_32_dirs "/usr/lib/hpux32;/opt/langtools/lib/hpux32;/opt/aCC/lib/hpux32")
 list(APPEND platforms hp_aCC_32)
+list(APPEND langs CXX)
 
 # aCC +DD64 dummy.cxx -v
 set(hp_aCC_64_text "LPATH=/usr/lib/hpux64:/opt/langtools/lib/hpux64
@@ -402,6 +450,7 @@ set(hp_aCC_64_text "LPATH=/usr/lib/hpux64:/opt/langtools/lib/hpux64
 set(hp_aCC_64_libs "std_v2;Csup;m;unwind;Csup;c;dl")
 set(hp_aCC_64_dirs "/usr/lib/hpux64;/opt/langtools/lib/hpux64;/opt/aCC/lib/hpux64")
 list(APPEND platforms hp_aCC_64)
+list(APPEND langs CXX)
 
 # f90 dummy.f -v
 set(hp_f90_32_text "LPATH is: /usr/lib/hpux32/:/opt/langtools/lib/hpux32/
@@ -409,6 +458,7 @@ set(hp_f90_32_text "LPATH is: /usr/lib/hpux32/:/opt/langtools/lib/hpux32/
 set(hp_f90_32_libs "-l:libF90.a;-l:libIO77.a;m;c;unwind;uca")
 set(hp_f90_32_dirs "/usr/lib/hpux32;/opt/langtools/lib/hpux32")
 list(APPEND platforms hp_f90_32)
+list(APPEND langs Fortran)
 
 # f90 +DD64 dummy.f -v
 set(hp_f90_64_text "LPATH is: /usr/lib/hpux64/:/opt/langtools/lib/hpux64/
@@ -416,6 +466,7 @@ set(hp_f90_64_text "LPATH is: /usr/lib/hpux64/:/opt/langtools/lib/hpux64/
 set(hp_f90_64_libs "-l:libF90.a;-l:libIO77.a;m;c;unwind;uca")
 set(hp_f90_64_dirs "/usr/lib/hpux64;/opt/langtools/lib/hpux64")
 list(APPEND platforms hp_f90_64)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # Cygwin
@@ -425,12 +476,14 @@ set(cygwin_gcc_text " /usr/lib/gcc/i686-pc-cygwin/3.4.4/collect2.exe -Bdynamic -
 set(cygwin_gcc_libs "gcc;cygwin;user32;kernel32;advapi32;shell32;gcc")
 set(cygwin_gcc_dirs "/usr/lib/gcc/i686-pc-cygwin/3.4.4;/usr/lib")
 list(APPEND platforms cygwin_gcc)
+list(APPEND langs C)
 
 # g++ dummy.cxx -v
 set(cygwin_g++_text " /usr/lib/gcc/i686-pc-cygwin/3.4.4/collect2.exe -Bdynamic --dll-search-prefix=cyg /usr/lib/gcc/i686-pc-cygwin/3.4.4/../../../crt0.o -L/usr/lib/gcc/i686-pc-cygwin/3.4.4 -L/usr/lib/gcc/i686-pc-cygwin/3.4.4 -L/usr/lib/gcc/i686-pc-cygwin/3.4.4/../../.. /home/user/AppData/Local/Temp/ccsvcDO6.o -lstdc++ -lgcc -lcygwin -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc")
 set(cygwin_g++_libs "stdc++;gcc;cygwin;user32;kernel32;advapi32;shell32;gcc")
 set(cygwin_g++_dirs "/usr/lib/gcc/i686-pc-cygwin/3.4.4;/usr/lib")
 list(APPEND platforms cygwin_g++)
+list(APPEND langs CXX)
 
 # gfortran dummy.f -v
 set(cygwin_gfortran_text "Configured with: ... LD=/opt/gcc-tools/bin/ld.exe
@@ -439,6 +492,7 @@ set(cygwin_gfortran_text "Configured with: ... LD=/opt/gcc-tools/bin/ld.exe
 set(cygwin_gfortran_libs "gfortranbegin;gfortran;gcc_s;gcc_s;gcc;cygwin;user32;kernel32;advapi32;shell32;gcc_s;gcc_s;gcc")
 set(cygwin_gfortran_dirs "/usr/lib/gcc/i686-pc-cygwin/4.3.2;/usr/lib")
 list(APPEND platforms cygwin_gfortran)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # MSYS
@@ -448,18 +502,21 @@ set(msys_gcc_text " C:/some-mingw/bin/../libexec/gcc/mingw32/3.4.5/collect2.exe
 set(msys_gcc_libs "mingw32;gcc;moldname;mingwex;user32;kernel32;advapi32;shell32;mingw32;gcc;moldname;mingwex")
 set(msys_gcc_dirs "C:/some-mingw/lib/gcc/mingw32/3.4.5;C:/some-mingw/lib/gcc;/some-mingw/lib;C:/some-mingw/lib")
 list(APPEND platforms msys_gcc)
+list(APPEND langs C)
 
 # g++ dummy.cxx -v
 set(msys_g++_text " C:/some-mingw/bin/../libexec/gcc/mingw32/3.4.5/collect2.exe -Bdynamic /some-mingw/lib/crt2.o C:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/crtbegin.o -LC:/some-mingw/bin/../lib/gcc/mingw32/3.4.5 -LC:/some-mingw/bin/../lib/gcc -L/some-mingw/lib -LC:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/../../.. C:/home/user/AppData/Local/Temp/cci5hYPk.o -lstdc++ -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt C:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/crtend.o")
 set(msys_g++_libs "stdc++;mingw32;gcc;moldname;mingwex;user32;kernel32;advapi32;shell32;mingw32;gcc;moldname;mingwex")
 set(msys_g++_dirs "C:/some-mingw/lib/gcc/mingw32/3.4.5;C:/some-mingw/lib/gcc;/some-mingw/lib;C:/some-mingw/lib")
 list(APPEND platforms msys_g++)
+list(APPEND langs CXX)
 
 # g77 dummy.f -v
 set(msys_g77_text " C:/some-mingw/bin/../libexec/gcc/mingw32/3.4.5/collect2.exe -Bdynamic /some-mingw/lib/crt2.o C:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/crtbegin.o -LC:/some-mingw/bin/../lib/gcc/mingw32/3.4.5 -LC:/some-mingw/bin/../lib/gcc -L/some-mingw/lib -LC:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/../../.. C:/home/user/AppData/Local/Temp/ccabRxQ1.o -lfrtbegin -lg2c -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt -luser32 -lkernel32 -ladvapi32 -lshell32 -lmingw32 -lgcc -lmoldname -lmingwex -lmsvcrt C:/some-mingw/bin/../lib/gcc/mingw32/3.4.5/crtend.o")
 set(msys_g77_libs "frtbegin;g2c;mingw32;gcc;moldname;mingwex;user32;kernel32;advapi32;shell32;mingw32;gcc;moldname;mingwex")
 set(msys_g77_dirs "C:/some-mingw/lib/gcc/mingw32/3.4.5;C:/some-mingw/lib/gcc;/some-mingw/lib;C:/some-mingw/lib")
 list(APPEND platforms msys_g77)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # MSYS2-runtime
@@ -469,18 +526,21 @@ set(msys2rt_gcc_text " /usr/lib/gcc/i686-pc-msys/6.4.0/collect2.exe -Bdynamic --
 set(msys2rt_gcc_libs "msys-2.0;user32;kernel32;advapi32;shell32")
 set(msys2rt_gcc_dirs "/usr/lib/gcc/i686-pc-msys/6.4.0;/usr/lib")
 list(APPEND platforms msysrt_gcc)
+list(APPEND langs C)
 
 # g++ dummy.cxx -v
 set(msys2rt_g++_text " /usr/lib/gcc/i686-pc-msys/6.4.0/collect2.exe -Bdynamic --dll-search-prefix=msys- /usr/lib/gcc/i686-pc-msys/6.4.0/../../../crt0.o -L/usr/lib/gcc/i686-pc-msys/6.4.0 -L/usr/lib/gcc/i686-pc-msys/6.4.0 -L/usr/lib/gcc/i686-pc-msys/6.4.0/../../.. /home/user/AppData/Local/Temp/ccsvcDO6.o -lstdc++ -lgcc -lmsys-2.0 -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc")
 set(msys2rt_g++_libs "stdc++;msys-2.0;user32;kernel32;advapi32;shell32")
 set(msys2rt_g++_dirs "/usr/lib/gcc/i686-pc-msys/6.4.0;/usr/lib")
 list(APPEND platforms msysrt_g++)
+list(APPEND langs CXX)
 
 # g77 dummy.f -v
 set(msys2rt_g77_text "Configured with: ... LD=/opt/gcc-tools/bin/ld.exe /usr/lib/gcc/i686-pc-msys/6.4.0/collect2.exe -Bdynamic --dll-search-prefix=msys- -u ___register_frame_info -u ___deregister_frame_info /usr/lib/gcc/i686-pc-msys/6.4.0/../../../crt0.o /usr/lib/gcc/i686-pc-msys/6.4.0/crtbegin.o -L/usr/lib/gcc/i686-pc-msys/6.4.0 -L/usr/lib/gcc/i686-pc-msys/6.4.0 -L/usr/lib/gcc/i686-pc-msys/6.4.0/../../.. /home/user/AppData/Local/Temp/ccqRWKWg.o -lgfortranbegin -lgfortran -lgcc_s -lgcc_s -lgcc -lmsys-2.0 -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc_s -lgcc_s -lgcc /usr/lib/gcc/i686-pc-msys/6.4.0/crtend.o")
 set(msys2rt_g77_libs "stdc++;msys-2.0;user32;kernel32;advapi32;shell32")
 set(msys2rt_g77_dirs "/usr/lib/gcc/i686-pc-msys/6.4.0;/usr/lib")
 list(APPEND platforms msysrt_g77)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # MSYS2-mingw
@@ -490,18 +550,21 @@ set(msys2_gcc_text " C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/c
 set(msys2_gcc_libs "mingw32;gcc;moldname;mingwex;pthread;advapi32;shell32;user32;kernel32;mingw32;gcc;moldname;mingwex")
 set(msys2_gcc_dirs "C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/7.2.0;C:/msys64/mingw64/lib/gcc;C:/msys64/mingw64/x86_64-w64-mingw32/lib;C:/msys64/mingw64/lib")
 list(APPEND platforms msys2_gcc)
+list(APPEND langs C)
 
 # g++ dummy.cxx -v
 set(msys2_g++_text " C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/collect2.exe -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/liblto_plugin-0.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/ccJQgvbN.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -m i386pep -Bdynamic C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtbegin.o -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0 -LC:/msys64/mingw64/bin/../lib/gcc -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../.. C:/msys64/tmp/ccqPpuVS.o -lstdc++ -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/default-manifest.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtend.o")
 set(msys2_g++_libs "stdc++;mingw32;gcc_s;gcc;moldname;mingwex;pthread;advapi32;shell32;user32;kernel32;mingw32;gcc_s;gcc;moldname;mingwex")
 set(msys2_g++_dirs "C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/7.2.0;C:/msys64/mingw64/lib/gcc;C:/msys64/mingw64/x86_64-w64-mingw32/lib;C:/msys64/mingw64/lib")
 list(APPEND platforms msys2_g++)
+list(APPEND langs CXX)
 
 # gfortran dummy.f90 -v
 set(msys2_gfortran_text " C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/collect2.exe -plugin C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/liblto_plugin-0.dll -plugin-opt=C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/lto-wrapper.exe -plugin-opt=-fresolution=C:/msys64/tmp/cczOKIDy.res -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lquadmath -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -plugin-opt=-pass-through=-lpthread -plugin-opt=-pass-through=-ladvapi32 -plugin-opt=-pass-through=-lshell32 -plugin-opt=-pass-through=-luser32 -plugin-opt=-pass-through=-lkernel32 -plugin-opt=-pass-through=-lmingw32 -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lmoldname -plugin-opt=-pass-through=-lmingwex -plugin-opt=-pass-through=-lmsvcrt -m i386pep -Bdynamic C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/crt2.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtbegin.o -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0 -LC:/msys64/mingw64/bin/../lib/gcc -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib -LC:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../.. C:/msys64/tmp/ccyXuCgD.o -lgfortran -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -lquadmath -lm -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/../../../../x86_64-w64-mingw32/lib/../lib/default-manifest.o C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/7.2.0/crtend.o")
 set(msys2_gfortran_libs "gfortran;mingw32;gcc_s;gcc;moldname;mingwex;quadmath;m;mingw32;gcc_s;gcc;moldname;mingwex;pthread;advapi32;shell32;user32;kernel32;mingw32;gcc_s;gcc;moldname;mingwex")
 set(msys2_gfortran_dirs "C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/7.2.0;C:/msys64/mingw64/lib/gcc;C:/msys64/mingw64/x86_64-w64-mingw32/lib;C:/msys64/mingw64/lib")
 list(APPEND platforms msys2_gfortran)
+list(APPEND langs Fortran)
 
 #-----------------------------------------------------------------------------
 # MSVC from NVIDIA CUDA
@@ -510,6 +573,18 @@ set(nvcc_msvc_text [[cuda-fake-ld cl.exe -nologo "tmp/a_dlink.obj" "tmp/CMakeCUD
 set(nvcc_msvc_libs "cudadevrt.lib;cudart_static.lib")
 set(nvcc_msvc_dirs "C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/lib/x64")
 list(APPEND platforms nvcc_msvc)
+list(APPEND langs CUDA)
+
+#-----------------------------------------------------------------------------
+# MSVC from NVIDIA CUDA custom compiler
+
+set(nvcc_msvc_cl_text [[cuda-fake-ld cl -nologo "tmp/a_dlink.obj" "tmp/CMakeCUDACompilerId.obj" -link -INCREMENTAL:NO   "/LIBPATH:C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/bin/../lib/x64" cudadevrt.lib  cudart_static.lib -Fe"a.exe"]])
+set(nvcc_msvc_cl_libs "cudadevrt.lib;cudart_static.lib")
+set(nvcc_msvc_cl_dirs "C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v8.0/lib/x64")
+set(nvcc_msvc_cl_compiler_id NVIDIA)
+set(nvcc_msvc_cl_compiler_sim_id MSVC)
+list(APPEND platforms nvcc_msvc_cl)
+list(APPEND langs CUDA)
 
 #-----------------------------------------------------------------------------
 # PGI on Windows
@@ -527,6 +602,7 @@ set(windows_pgf95_startfile "pgimain[mx][xpt]+[.]obj")
 set(windows_pgf95_libs "libpgmp;pg;libpgf90rtl;libpgf90;libpgf90_rpm1;libpgf902;libpgf90rtl;libpgftnrtl;libpgc14;libnspgc;legacy_stdio_definitions;oldnames")
 set(windows_pgf95_dirs "X:/NOT-PROGRAM-FILES-86/Microsoft Visual Studio 14.0/VC/Lib/AMD64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/ucrt/x64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/um/x64;X:/NOT-PROGRAM-FILES/PGICE/win64/17.4/lib")
 list(APPEND platforms windows_pgf95)
+list(APPEND langs Fortran)
 
 # pgf77 dummy.f77 -v
 set(windows_pgf77_text [[Writing to file C:\temp\pgf776eC42buYRoCNJk.lnk
@@ -537,6 +613,7 @@ set(windows_pgf77_startfile "pgimain[mx][xpt]+[.]obj")
 set(windows_pgf77_libs "libpgmp;libpgftnrtl;libpgc14;libnspgc;legacy_stdio_definitions;oldnames")
 set(windows_pgf77_dirs "X:/NOT-PROGRAM-FILES-86/Microsoft Visual Studio 14.0/VC/Lib/AMD64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/ucrt/x64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/um/x64;X:/NOT-PROGRAM-FILES/PGICE/win64/17.4/lib")
 list(APPEND platforms windows_pgf77)
+list(APPEND langs Fortran)
 
 # pgcc dummy.c -v
 set(windows_pgcc_text [[Writing to file C:\temp\pgcc6esqW26_ZNKyL.lnk
@@ -547,6 +624,7 @@ set(windows_pgcc_startfile "pgimain[mx][xpt]+[.]obj")
 set(windows_pgcc_libs "libpgmp;libpgc14;libnspgc;legacy_stdio_definitions;oldnames")
 set(windows_pgcc_dirs "X:/NOT-PROGRAM-FILES-86/Microsoft Visual Studio 14.0/VC/Lib/AMD64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/ucrt/x64;X:/NOT-PROGRAM-FILES-86/Windows Kits/10/Lib/10.0.15063.0/um/x64;X:/NOT-PROGRAM-FILES/PGICE/win64/17.4/lib")
 list(APPEND platforms windows_pgcc)
+list(APPEND langs C)
 
 endif()
 
@@ -554,13 +632,20 @@ endif()
 # Test parsing for all above examples.
 set(CMAKE_LINKER "not-a-linker[]().*+^$?")
 
-foreach(p IN LISTS platforms)
+foreach(p lang IN ZIP_LISTS platforms langs)
   if(DEFINED ${p}_startfile)
     set(CMAKE_LINK_STARTFILE "${${p}_startfile}")
   else()
     unset(CMAKE_LINK_STARTFILE)
   endif()
-  cmake_parse_implicit_link_info("${${p}_text}" libs dirs fwks log "${${p}_obj_regex}")
+  if(DEFINED ${p}_compiler_id)
+    set(CMAKE_${lang}_ID ${${p}_compiler_id})
+  endif()
+  if(DEFINED ${p}_compiler_sim_id)
+    set(CMAKE_${lang}_SIMULATE_ID ${${p}_compiler_sim_id})
+  endif()
+
+  cmake_parse_implicit_link_info("${${p}_text}" libs dirs fwks log "${${p}_obj_regex}" LANGUAGE ${lang})
 
   foreach(v libs dirs fwks)
     if(DEFINED "${p}_${v}" AND NOT "${${v}}" STREQUAL "${${p}_${v}}")
index a36f374..7c7a3e5 100644 (file)
@@ -34,9 +34,10 @@ set(expected_out
 # then back to relative to get them in canonical form (or maybe this is a bug
 # in how the tarball is generated?)
 function(to_relative_paths real_paths paths)
+  file(REAL_PATH "${CTEST_BINARY_DIRECTORY}" base)
   foreach(file ${paths})
-    file(REAL_PATH "${file}" real_path BASE_DIRECTORY "${CTEST_BINARY_DIRECTORY}")
-    file(RELATIVE_PATH relative_path "${CTEST_BINARY_DIRECTORY}" "${real_path}")
+    file(REAL_PATH "${file}" real_path BASE_DIRECTORY "${base}")
+    file(RELATIVE_PATH relative_path "${base}" "${real_path}")
     list(APPEND local_real_paths "${relative_path}")
     message(DEBUG "${file} -> ${real_path} -> ${relative_path}")
   endforeach()
index 2fb6490..d55cec6 100644 (file)
@@ -1,7 +1,11 @@
 #if defined(_WIN32)
 #  include <windows.h>
-#else
+#elif _XOPEN_SOURCE >= 500 || defined(_ALL_SOURCE)
 #  include <unistd.h>
+#else
+#  include <time.h>
+
+#  include <sys/select.h>
 #endif
 
 /* sleeps for 0.1 second */
@@ -9,8 +13,14 @@ int main(int argc, char** argv)
 {
 #if defined(_WIN32)
   Sleep(100);
-#else
+#elif _XOPEN_SOURCE >= 500 || defined(_ALL_SOURCE)
   usleep(100 * 1000);
+#else
+  struct timeval tv;
+  tv.tv_sec = 0;
+  tv.tv_usec = 100 * 1000;
+
+  select(0, NULL, NULL, NULL, &tv);
 #endif
   return 0;
 }
index 0f8ec8e..467b41a 100644 (file)
@@ -73,7 +73,7 @@ function(check_updates build)
       string(REGEX REPLACE "${rev_regex}" "\\1" element "${r}")
       set(element_${element} 1)
     endforeach()
-    foreach(element ${rev_elements})
+    foreach(element IN LISTS rev_elements)
       if(NOT element_${element})
         list(APPEND MISSING "global <${element}> element")
       endif()
@@ -85,7 +85,7 @@ function(check_updates build)
   if(MISSING)
     # List the missing entries
     string(APPEND MSG "Update.xml is missing expected entries:\n")
-    foreach(f ${MISSING})
+    foreach(f IN LISTS MISSING)
       string(APPEND MSG "  ${f}\n")
     endforeach()
   else()
@@ -97,7 +97,7 @@ function(check_updates build)
   if(EXTRA)
     # List the extra entries
     string(APPEND MSG "Update.xml has extra unexpected entries:\n")
-    foreach(f ${EXTRA})
+    foreach(f IN LISTS EXTRA)
       string(APPEND MSG "  ${f}\n")
     endforeach()
   else()
index 8a99e67..2ac01e2 100644 (file)
@@ -109,7 +109,7 @@ list(APPEND P4CMD ${P4_CLIENT})
 
 message("Adding files to repository")
 file(GLOB_RECURSE files ${TOP}/user-source/*)
-foreach(filename ${files})
+foreach(filename IN LISTS files)
   run_child(
     WORKING_DIRECTORY ${TOP}/user-source
     COMMAND ${P4CMD} add ${filename}
@@ -140,14 +140,14 @@ run_child(
 # Make changes in the working tree.
 message("Changing content...")
 update_content(user-source files_added files_removed dirs_added)
-foreach(filename ${files_added})
+foreach(filename IN LISTS files_added)
   message("add: ${filename}")
   run_child(
     WORKING_DIRECTORY ${TOP}/user-source
     COMMAND ${P4CMD} add ${TOP}/user-source/${filename}
   )
 endforeach()
-foreach(filename ${files_removed})
+foreach(filename IN LISTS files_removed)
   run_child(
     WORKING_DIRECTORY ${TOP}/user-source
     COMMAND ${P4CMD} delete ${TOP}/user-source/${filename}
diff --git a/Tests/CheckSourceTree/CMakeLists.txt b/Tests/CheckSourceTree/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3abc6a9
--- /dev/null
@@ -0,0 +1,6 @@
+add_test(NAME CMake.CheckSourceTree
+  COMMAND ${CMAKE_CMAKE_COMMAND} -D GIT_EXECUTABLE=${GIT_EXECUTABLE}
+                                 -D CMake_SOURCE_DIR=${CMake_SOURCE_DIR}
+                                 -P ${CMAKE_CURRENT_LIST_DIR}/check.cmake
+  )
+set_property(TEST CMake.CheckSourceTree PROPERTY RUN_SERIAL 1)
diff --git a/Tests/CheckSourceTree/check.cmake b/Tests/CheckSourceTree/check.cmake
new file mode 100644 (file)
index 0000000..c2e3529
--- /dev/null
@@ -0,0 +1,22 @@
+# Give Git access to the real home directory to get user's settings.
+if(DEFINED ENV{CTEST_REAL_HOME})
+  set(ENV{HOME} "$ENV{CTEST_REAL_HOME}")
+endif()
+
+execute_process(
+  COMMAND "${GIT_EXECUTABLE}" status
+  WORKING_DIRECTORY "${CMake_SOURCE_DIR}"
+  OUTPUT_VARIABLE output
+  ERROR_VARIABLE output
+  RESULT_VARIABLE result
+  )
+string(REPLACE "\n" "\n  " output "  ${output}")
+if(NOT result EQUAL 0)
+  message(FATAL_ERROR "'git status' failed (${result}):\n${output}")
+endif()
+
+if(output MATCHES "\n[ \t#]*(Changes |new file:|modified:|Untracked )")
+  message(FATAL_ERROR "The source tree is not clean.  'git status' reports:\n${output}")
+endif()
+
+message(STATUS "The source tree is clean.")
index fcbae7e..099c298 100644 (file)
@@ -14,6 +14,7 @@ if(NOT DEFINED CMAKE_Swift_COMPILER)
 project(CheckSwift Swift)
 file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
   \"set(CMAKE_Swift_COMPILER \\\"\${CMAKE_Swift_COMPILER}\\\")\\n\"
+  \"set(CMAKE_Swift_COMPILER_VERSION \\\"\${CMAKE_Swift_COMPILER_VERSION}\\\")\\n\"
   \"set(CMAKE_Swift_FLAGS \\\"\${CMAKE_Swift_FLAGS}\\\")\\n\")
 ")
 
@@ -54,6 +55,7 @@ file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/result.cmake\"
   message(STATUS "${_desc} - ${CMAKE_Swift_COMPILER}")
 
   set(CMAKE_Swift_COMPILER "${CMAKE_Swift_COMPILER}" CACHE FILEPATH "Swift compiler")
+  set(CMAKE_Swift_COMPILER_VERSION "${CMAKE_Swift_COMPILER_VERSION}" CACHE FILEPATH "Swift compiler version")
   set(CMAKE_Swift_FLAGS "${CMAKE_Swift_FLAGS}" CACHE STRING "Swift flags")
 
   mark_as_advanced(CMAKE_Swift_COMPILER)
index c6d1e8a..f3d3a73 100644 (file)
@@ -356,6 +356,7 @@ else()
       HAVE_CXX_STD_17=$<COMPILE_FEATURES:cxx_std_17>
       HAVE_CXX_STD_20=$<COMPILE_FEATURES:cxx_std_20>
       HAVE_CXX_STD_23=$<COMPILE_FEATURES:cxx_std_23>
+      HAVE_CXX_STD_26=$<COMPILE_FEATURES:cxx_std_26>
     )
   endif()
 
index 9c3910e..048f3de 100644 (file)
@@ -27,6 +27,9 @@
 #  if HAVE_CXX_STD_23 && !defined(ALLOW_LATER_STANDARDS)
 #    error HAVE_CXX_STD_23 is true with CXX_STANDARD == 11
 #  endif
+#  if HAVE_CXX_STD_26 && !defined(ALLOW_LATER_STANDARDS)
+#    error HAVE_CXX_STD_26 is true with CXX_STANDARD == 11
+#  endif
 #endif
 
 #if !HAVE_OVERRIDE_CONTROL
index 5df22d2..9493a2f 100644 (file)
@@ -5,6 +5,14 @@ cmake_minimum_required(VERSION 2.4)
 cmake_policy(SET CMP0054 NEW)
 project (Complex)
 
+# Inform the test if the debug configuration is getting built.
+string(APPEND CMAKE_C_FLAGS_RELEASE " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELEASE " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_MINSIZEREL " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " -DCOMPLEX_NDEBUG")
+
 # Test that renaming a built-in works when configured multiple times.
 message("message")
 function(message)
index 49e97d5..67a1645 100644 (file)
@@ -62,7 +62,7 @@ void cmPassed(const char* Message, const char* m2 = "")
 #  error This is a problem. Looks like ADD_DEFINITIONS and REMOVE_DEFINITIONS does not work
 #endif
 
-#if defined(NDEBUG) && !defined(CMAKE_IS_FUN_IN_RELEASE_MODE)
+#if defined(COMPLEX_NDEBUG) && !defined(CMAKE_IS_FUN_IN_RELEASE_MODE)
 #  error Per-configuration directory-level definition not inherited.
 #endif
 
index 5a4134d..e4fdc68 100644 (file)
@@ -5,6 +5,14 @@ cmake_minimum_required(VERSION 2.4)
 cmake_policy(SET CMP0054 NEW)
 project (Complex)
 
+# Inform the test if the debug configuration is getting built.
+string(APPEND CMAKE_C_FLAGS_RELEASE " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELEASE " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_MINSIZEREL " -DCOMPLEX_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " -DCOMPLEX_NDEBUG")
+
 # Try setting a new policy.  The IF test is for coverage.
 if(POLICY CMP0003)
   cmake_policy(SET CMP0003 NEW)
index 54c18f4..097668b 100644 (file)
@@ -62,7 +62,7 @@ void cmPassed(const char* Message, const char* m2 = "")
 #  error This is a problem. Looks like ADD_DEFINITIONS and REMOVE_DEFINITIONS does not work
 #endif
 
-#if defined(NDEBUG) && !defined(CMAKE_IS_FUN_IN_RELEASE_MODE)
+#if defined(COMPLEX_NDEBUG) && !defined(CMAKE_IS_FUN_IN_RELEASE_MODE)
 #  error Per-configuration directory-level definition not inherited.
 #endif
 
index 97670e3..447e137 100644 (file)
@@ -1,3 +1,6 @@
-project(SeparableCompCXXOnly LANGUAGES CXX CUDA)
+# Set CMAKE_CUDA_SEPARABLE_COMPILATION before `project`
+# so we verify that compiler/linker verbose extraction
+# works as required when a `dlink` is part of it
 set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)
+project(SeparableCompCXXOnly LANGUAGES CXX CUDA)
 add_executable(SeparableCompCXXOnly main.cpp)
index aa4755d..39634ac 100644 (file)
@@ -7,13 +7,13 @@ endmacro ()
 add_cuda_test_macro(CudaOnly.Architecture Architecture)
 add_cuda_test_macro(CudaOnly.ArchSpecial CudaOnlyArchSpecial)
 add_cuda_test_macro(CudaOnly.CompileFlags CudaOnlyCompileFlags)
-
 add_cuda_test_macro(CudaOnly.EnableStandard CudaOnlyEnableStandard)
 add_cuda_test_macro(CudaOnly.ExportPTX CudaOnlyExportPTX)
 add_cuda_test_macro(CudaOnly.SharedRuntimePlusToolkit CudaOnlySharedRuntimePlusToolkit)
 add_cuda_test_macro(CudaOnly.Standard98 CudaOnlyStandard98)
 add_cuda_test_macro(CudaOnly.Toolkit CudaOnlyToolkit)
 add_cuda_test_macro(CudaOnly.ToolkitBeforeLang CudaOnlyToolkitBeforeLang)
+add_cuda_test_macro(CudaOnly.ToolkitMultipleDirs CudaOnlyToolkitMultipleDirs)
 add_cuda_test_macro(CudaOnly.WithDefs CudaOnlyWithDefs)
 add_cuda_test_macro(CudaOnly.CircularLinkLine CudaOnlyCircularLinkLine)
 add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols)
@@ -28,6 +28,19 @@ if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang")
   add_cuda_test_macro(CudaOnly.GPUDebugFlag CudaOnlyGPUDebugFlag)
 endif()
 
+# The CUDA only ships the shared version of the toolkit libraries
+# on windows
+if(NOT WIN32)
+  add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
+endif()
+
+add_cuda_test_macro(CudaOnly.DeviceLTO CudaOnlyDeviceLTO)
+
+if(MSVC)
+  # Tests for features that only work with MSVC
+  add_cuda_test_macro(CudaOnly.PDB CudaOnlyPDB)
+endif()
+
 add_test(NAME CudaOnly.DontResolveDeviceSymbols COMMAND
   ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
   --build-and-test
@@ -41,16 +54,6 @@ add_test(NAME CudaOnly.DontResolveDeviceSymbols COMMAND
 set_property(TEST "CudaOnly.DontResolveDeviceSymbols" APPEND
   PROPERTY LABELS "CUDA")
 
-# The CUDA only ships the shared version of the toolkit libraries
-# on windows
-if(NOT WIN32)
-  add_cuda_test_macro(CudaOnly.StaticRuntimePlusToolkit CudaOnlyStaticRuntimePlusToolkit)
-endif()
-
-if(MSVC)
-  add_cuda_test_macro(CudaOnly.PDB CudaOnlyPDB)
-endif()
-
 add_test(NAME CudaOnly.RuntimeControls COMMAND
   ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
   --build-and-test
diff --git a/Tests/CudaOnly/DeviceLTO/CMakeLists.txt b/Tests/CudaOnly/DeviceLTO/CMakeLists.txt
new file mode 100644 (file)
index 0000000..653b35d
--- /dev/null
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.18)
+project(DeviceLTO CUDA)
+
+# Goal:
+# Verify that we correctly compile with device LTO
+# Verify that device LTO requirements are propagated to
+# the final device link line
+
+add_library(CUDA_dlto STATIC file1.cu file2.cu file3.cu)
+add_executable(CudaOnlyDeviceLTO main.cu)
+
+set_target_properties(CUDA_dlto
+                      PROPERTIES
+                      CUDA_ARCHITECTURES "${CMAKE_CUDA_ARCHITECTURES_ALL}"
+                      CUDA_SEPARABLE_COMPILATION ON
+                      POSITION_INDEPENDENT_CODE ON)
+
+set_target_properties(CudaOnlyDeviceLTO
+                      PROPERTIES
+                      CUDA_SEPARABLE_COMPILATION ON
+                      CUDA_ARCHITECTURES "${CMAKE_CUDA_ARCHITECTURES_ALL}"
+                      )
+
+target_link_libraries(CudaOnlyDeviceLTO PRIVATE CUDA_dlto)
+
+include(CheckIPOSupported)
+check_ipo_supported(LANGUAGES CUDA RESULT ipo_supported)
+if(ipo_supported)
+  set_target_properties(CUDA_dlto
+                        PROPERTIES
+                        INTERPROCEDURAL_OPTIMIZATION ON)
+
+  # When non-LTO variants (i.e. virtual) are built together with LTO ones the
+  # linker warns about missing device LTO for the virtual architectures.
+  # Ignore these warnings.
+  target_link_options(CudaOnlyDeviceLTO PRIVATE "$<DEVICE_LINK:-w>")
+endif()
diff --git a/Tests/CudaOnly/DeviceLTO/file1.cu b/Tests/CudaOnly/DeviceLTO/file1.cu
new file mode 100644 (file)
index 0000000..703927c
--- /dev/null
@@ -0,0 +1,17 @@
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT
+#endif
+
+extern __device__ int file2_func(int);
+void __global__ kernel(int x)
+{
+  file2_func(x);
+}
+
+EXPORT int launch_kernel(int x)
+{
+  kernel<<<1, 1>>>(x);
+  return x;
+}
diff --git a/Tests/CudaOnly/DeviceLTO/file2.cu b/Tests/CudaOnly/DeviceLTO/file2.cu
new file mode 100644 (file)
index 0000000..73d6468
--- /dev/null
@@ -0,0 +1,5 @@
+extern __device__ int file3_func(int);
+int __device__ file2_func(int x)
+{
+  return x + file3_func(x);
+}
diff --git a/Tests/CudaOnly/DeviceLTO/file3.cu b/Tests/CudaOnly/DeviceLTO/file3.cu
new file mode 100644 (file)
index 0000000..235ac06
--- /dev/null
@@ -0,0 +1,4 @@
+int __device__ file3_func(int x)
+{
+  return x * x * x;
+}
diff --git a/Tests/CudaOnly/DeviceLTO/main.cu b/Tests/CudaOnly/DeviceLTO/main.cu
new file mode 100644 (file)
index 0000000..8ef4873
--- /dev/null
@@ -0,0 +1,62 @@
+#include <iostream>
+
+#include "cuda.h"
+
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
+IMPORT int launch_kernel(int x);
+
+int choose_cuda_device()
+{
+  int nDevices = 0;
+  cudaError_t err = cudaGetDeviceCount(&nDevices);
+  if (err != cudaSuccess) {
+    std::cerr << "Failed to retrieve the number of CUDA enabled devices"
+              << std::endl;
+    return 1;
+  }
+  for (int i = 0; i < nDevices; ++i) {
+    cudaDeviceProp prop;
+    cudaError_t err = cudaGetDeviceProperties(&prop, i);
+    if (err != cudaSuccess) {
+      std::cerr << "Could not retrieve properties from CUDA device " << i
+                << std::endl;
+      return 1;
+    }
+    std::cout << "prop.major: " << prop.major << std::endl;
+    err = cudaSetDevice(i);
+    if (err != cudaSuccess) {
+      std::cout << "Could not select CUDA device " << i << std::endl;
+    } else {
+      return 0;
+    }
+  }
+
+  std::cout << "Could not find a CUDA enabled card" << std::endl;
+
+  return 1;
+}
+
+int main()
+{
+  int ret = choose_cuda_device();
+  if (ret) {
+    return 0;
+  }
+
+  cudaError_t err;
+  launch_kernel(1);
+  err = cudaGetLastError();
+  if (err != cudaSuccess) {
+    std::cerr << "launch_kernel: kernel launch should have passed.\n "
+                 "Error message: "
+              << cudaGetErrorString(err) << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/Tests/CudaOnly/ToolkitMultipleDirs/CMakeLists.txt b/Tests/CudaOnly/ToolkitMultipleDirs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9255149
--- /dev/null
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.15)
+project(CudaOnlyToolkitMultipleDirs CUDA)
+
+find_package(CUDAToolkit REQUIRED)
+
+add_subdirectory(subdir)
+
+if(NOT DEFINED CUDAToolkit_VERSION)
+  message(FATAL_ERROR "expected CUDAToolkit variable CUDAToolkit_VERSION not found")
+endif()
+
+message(STATUS "CUDAToolkit_VERSION: ${CUDAToolkit_VERSION}")
+message(STATUS "CUDAToolkit_VERSION_MAJOR: ${CUDAToolkit_VERSION_MAJOR}")
+message(STATUS "CUDAToolkit_VERSION_MINOR: ${CUDAToolkit_VERSION_MINOR}")
+message(STATUS "CUDAToolkit_VERSION_PATCH: ${CUDAToolkit_VERSION_PATCH}")
+message(STATUS "CUDAToolkit_BIN_DIR: ${CUDAToolkit_BIN_DIR}")
+message(STATUS "CUDAToolkit_INCLUDE_DIRS: ${CUDAToolkit_INCLUDE_DIRS}")
+message(STATUS "CUDAToolkit_LIBRARY_DIR: ${CUDAToolkit_LIBRARY_DIR}")
+message(STATUS "CUDAToolkit_NVCC_EXECUTABLE ${CUDAToolkit_NVCC_EXECUTABLE}")
+
+add_executable(CudaOnlyToolkitMultipleDirs main.cu)
+target_link_libraries(CudaOnlyToolkitMultipleDirs PRIVATE CUDA::toolkit)
diff --git a/Tests/CudaOnly/ToolkitMultipleDirs/main.cu b/Tests/CudaOnly/ToolkitMultipleDirs/main.cu
new file mode 100644 (file)
index 0000000..0f3ccdc
--- /dev/null
@@ -0,0 +1,8 @@
+// Only thing we care about is that these headers are found
+#include <cuda.h>
+#include <cuda_runtime_api.h>
+
+int main(int argc, char** argv)
+{
+  return 0;
+}
diff --git a/Tests/CudaOnly/ToolkitMultipleDirs/subdir/CMakeLists.txt b/Tests/CudaOnly/ToolkitMultipleDirs/subdir/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f1f4b89
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_package(CUDAToolkit REQUIRED)
index 7c6f76a..7722d7d 100644 (file)
@@ -10,7 +10,7 @@ if(NOT CTEST_CONFIGURATION_TYPE)
   set(CTEST_CMD "@CMAKE_CTEST_COMMAND@@CMAKE_EXECUTABLE_SUFFIX@")
   get_filename_component(CTEST_DIR "${CTEST_CMD}" PATH)
   get_filename_component(CTEST_EXE "${CTEST_CMD}" NAME)
-  foreach(cfg Release Debug MinSizeRel RelWithDebInfo)
+  foreach(cfg IN ITEMS Release Debug MinSizeRel RelWithDebInfo)
     if(NOT CTEST_CONFIGURATION_TYPE)
       if(EXISTS "${CTEST_DIR}/${cfg}/${CTEST_EXE}")
         set(CTEST_CONFIGURATION_TYPE ${cfg})
@@ -36,4 +36,4 @@ unset(ENV{CMAKE_GENERATOR_TOOLSET})
 unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS})
 
 @TEST_HOME_ENV_CODE@
-@TEST_WARN_VS10_CODE@
+@TEST_WARN_VS11_CODE@
index c9e41f5..6f19c13 100644 (file)
@@ -155,6 +155,15 @@ target_include_directories(testInterfaceIncludeUser
     "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser>"
 )
 set_property(TARGET testInterfaceIncludeUser PROPERTY IMPORTED_NO_SYSTEM 1)
+
+add_library(testInterfaceIncludeUser2 INTERFACE)
+target_include_directories(testInterfaceIncludeUser2
+  INTERFACE
+    "$<INSTALL_INTERFACE:include/testInterfaceIncludeUser>"
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser>"
+)
+set_property(TARGET testInterfaceIncludeUser2 PROPERTY EXPORT_NO_SYSTEM 1)
+
 install(
   FILES
     "${CMAKE_CURRENT_SOURCE_DIR}/include/testInterfaceIncludeUser/testInterfaceInclude.h"
@@ -562,6 +571,7 @@ install(
   TopDirLib SubDirLinkA
   systemlib
   testInterfaceIncludeUser
+  testInterfaceIncludeUser2
   EXPORT exp
   RUNTIME DESTINATION $<1:bin>$<0:/wrong>
   LIBRARY DESTINATION $<1:lib>$<0:/wrong> NAMELINK_SKIP
@@ -622,6 +632,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3
   TopDirLib SubDirLinkA
   systemlib
   testInterfaceIncludeUser
+  testInterfaceIncludeUser2
   NAMESPACE bld_
   FILE ExportBuildTree.cmake
   )
index 272c7a9..5bafdf8 100644 (file)
@@ -75,6 +75,10 @@ add_library(imp_testInterfaceInclude1 STATIC imp_testInterfaceInclude1.c)
 target_include_directories(imp_testInterfaceInclude1 SYSTEM PRIVATE testInterfaceIncludeSystem)
 target_link_libraries(imp_testInterfaceInclude1 PRIVATE exp_testInterfaceIncludeUser)
 
+add_library(imp_testInterfaceInclude1b STATIC imp_testInterfaceInclude1.c)
+target_include_directories(imp_testInterfaceInclude1b SYSTEM PRIVATE testInterfaceIncludeSystem)
+target_link_libraries(imp_testInterfaceInclude1b PRIVATE exp_testInterfaceIncludeUser2)
+
 add_executable(imp_UseSharedLibWithHelper1 ../../../InterfaceLinkLibrariesDirect/UseSharedLibWithHelper.c)
 target_link_libraries(imp_UseSharedLibWithHelper1 PRIVATE exp_testSharedLibWithHelper testSharedLibHelperExclude)
 
@@ -122,9 +126,13 @@ target_link_libraries(imp_testExe1b
   bld_testStaticLibWithPlugin
   )
 
-add_library(imp_testInterfaceInclude1b STATIC imp_testInterfaceInclude1.c)
-target_include_directories(imp_testInterfaceInclude1b SYSTEM PRIVATE testInterfaceIncludeSystem)
-target_link_libraries(imp_testInterfaceInclude1b PRIVATE bld_testInterfaceIncludeUser)
+add_library(imp_testInterfaceInclude1c STATIC imp_testInterfaceInclude1.c)
+target_include_directories(imp_testInterfaceInclude1c SYSTEM PRIVATE testInterfaceIncludeSystem)
+target_link_libraries(imp_testInterfaceInclude1c PRIVATE bld_testInterfaceIncludeUser)
+
+add_library(imp_testInterfaceInclude1d STATIC imp_testInterfaceInclude1.c)
+target_include_directories(imp_testInterfaceInclude1d SYSTEM PRIVATE testInterfaceIncludeSystem)
+target_link_libraries(imp_testInterfaceInclude1d PRIVATE bld_testInterfaceIncludeUser2)
 
 add_custom_target(check_testLib1_genex ALL
   COMMAND ${CMAKE_COMMAND} -DtestLib1=$<TARGET_FILE:exp_testLib1>
index e4c6c66..81d31e7 100644 (file)
@@ -45,7 +45,11 @@ if(NOT DEFINED EP_TEST_HG OR EP_TEST_HG)
   find_package(Hg)
 endif()
 if(NOT DEFINED EP_TEST_HG AND Hg_FOUND)
-  set(EP_TEST_HG 1)
+  # Check if hg executable is working
+  execute_process(COMMAND "${HG_EXECUTABLE}" --version OUTPUT_QUIET ERROR_QUIET RESULT_VARIABLE HG_RV)
+  if(HG_RV EQUAL 0)
+    set(EP_TEST_HG 1)
+  endif()
 endif()
 
 message(STATUS "EP_TEST_CVS='${EP_TEST_CVS}' CVS_EXECUTABLE='${CVS_EXECUTABLE}'")
diff --git a/Tests/FindOpenAL/CMakeLists.txt b/Tests/FindOpenAL/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fc4803e
--- /dev/null
@@ -0,0 +1,10 @@
+add_test(NAME FindOpenAL.Test COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindOpenAL/Test"
+  "${CMake_BINARY_DIR}/Tests/FindOpenAL/Test"
+  ${build_generator_args}
+  --build-project TestFindOpenAL
+  --build-options ${build_options}
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
diff --git a/Tests/FindOpenAL/Test/CMakeLists.txt b/Tests/FindOpenAL/Test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fa3e263
--- /dev/null
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 3.21)
+project(TestFindOpenAL CXX)
+include(CTest)
+
+find_package(OpenAL REQUIRED)
+
+add_executable(test_tgt main.cxx)
+target_link_libraries(test_tgt OpenAL::OpenAL)
+add_test(NAME test_tgt COMMAND test_tgt)
+
+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)
diff --git a/Tests/FindOpenAL/Test/main.cxx b/Tests/FindOpenAL/Test/main.cxx
new file mode 100644 (file)
index 0000000..bb45faf
--- /dev/null
@@ -0,0 +1,13 @@
+#include <AL/al.h>
+#include <AL/alc.h>
+#include <stdio.h>
+
+int main()
+{
+  /* Reference an AL symbol without requiring a context at runtime.  */
+  printf("&alGetString = %p\n", &alGetString);
+
+  /* Reference an ALC symbol without requiring a context at runtime.  */
+  printf("&alcGetString = %p\n", &alcGetString);
+  return 0;
+}
diff --git a/Tests/FindOpenSP/CMakeLists.txt b/Tests/FindOpenSP/CMakeLists.txt
new file mode 100644 (file)
index 0000000..26826d3
--- /dev/null
@@ -0,0 +1,10 @@
+add_test(NAME FindOpenSP.Test COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindOpenSP/Test"
+  "${CMake_BINARY_DIR}/Tests/FindOpenSP/Test"
+  ${build_generator_args}
+  --build-project TestFindOpenSP
+  --build-options ${build_options}
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
diff --git a/Tests/FindOpenSP/Test/CMakeLists.txt b/Tests/FindOpenSP/Test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..d8992d9
--- /dev/null
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.4)
+project(TestFindOpenSP CXX)
+include(CTest)
+
+find_package(OpenSP REQUIRED)
+
+add_definitions(-DSP_MULTI_BYTE="${OpenSP_MULTI_BYTE}")
+
+add_executable(test_tgt main.cxx)
+target_link_libraries(test_tgt OpenSP::OpenSP)
+add_test(NAME test_tgt COMMAND test_tgt)
+
+add_executable(test_var main.cxx)
+target_include_directories(test_var PRIVATE ${OpenSP_INCLUDE_DIRS})
+target_link_libraries(test_var PRIVATE ${OpenSP_LIBRARIES})
+add_test(NAME test_var COMMAND test_var)
diff --git a/Tests/FindOpenSP/Test/main.cxx b/Tests/FindOpenSP/Test/main.cxx
new file mode 100644 (file)
index 0000000..ef8aa2c
--- /dev/null
@@ -0,0 +1,55 @@
+#include <cassert>
+#include <string>
+
+#include "ParserEventGeneratorKit.h"
+
+std::string CharStringtostring(const SGMLApplication::CharString source)
+{
+  // The CharString type might have multi-byte characters if SP_MULTI_BYTE was
+  // defined
+  std::string result;
+  result.resize(source.len);
+  for (size_t i = 0; i < source.len; i++) {
+    result[i] = static_cast<char>(source.ptr[i]);
+  }
+  return result;
+}
+
+class OutlineApplication : public SGMLApplication
+{
+public:
+  OutlineApplication()
+    : depth_(0)
+  {
+  }
+  void startElement(const StartElementEvent& event)
+  {
+    for (unsigned i = 0; i < depth_; i++)
+      parsedOutput += "\t";
+    parsedOutput += CharStringtostring(event.gi);
+    depth_++;
+  }
+  void endElement(const EndElementEvent&) { depth_--; }
+  std::string parsedOutput;
+
+private:
+  unsigned depth_;
+};
+
+int main()
+{
+  std::string expectedOutput = "TESTDOC\tTESTELEMENT";
+  char file_name[] = "test.sgml";
+  char* files[] = { file_name, 0 };
+
+  ParserEventGeneratorKit parserKit;
+  EventGenerator* egp = parserKit.makeEventGenerator(1, files);
+  OutlineApplication app;
+  unsigned nErrors = egp->run(app);
+
+  assert(nErrors == 0);
+  assert(app.parsedOutput.compare(expectedOutput) == 0);
+
+  delete egp;
+  return 0;
+}
diff --git a/Tests/FindOpenSP/Test/test.sgml b/Tests/FindOpenSP/Test/test.sgml
new file mode 100644 (file)
index 0000000..bbf0da6
--- /dev/null
@@ -0,0 +1,7 @@
+ <!DOCTYPE TESTDOC [
+<!ELEMENT TESTDOC - - (TESTELEMENT)+>
+<!ELEMENT TESTELEMENT - - (#PCDATA)>
+]>
+<TESTDOC>
+<TESTELEMENT>Hello</TESTELEMENT>
+</TESTDOC>
index c25b2c3..e4143b9 100644 (file)
@@ -20,7 +20,7 @@ find_package(OpenGL QUIET)
 find_package(NotAPackage QUIET)
 
 # Look for a package that has an advanced find module.
-find_package(VTK QUIET)
+find_package(Boost QUIET)
 
 add_executable(FindPackageTest FindPackageTest.cxx)
 
index 42543ac..dfcfc15 100644 (file)
@@ -7,7 +7,10 @@ set(components
   glslang
   shaderc_combined
   SPIRV-Tools
+  volk
+  dxc
 )
+
 if(APPLE)
   list(APPEND components MoltenVK)
 endif()
@@ -75,6 +78,14 @@ if(APPLE)
   add_test(NAME test_tgt_MoltenVK COMMAND test_tgt_MoltenVK)
 endif()
 
+add_executable(test_tgt_volk main-volk.cxx)
+target_link_libraries(test_tgt_volk Vulkan::volk)
+add_test(NAME test_tgt_volk COMMAND test_tgt_volk)
+
+add_executable(test_tgt_dxc_lib main-dxc_lib.cxx)
+target_link_libraries(test_tgt_dxc_lib Vulkan::dxc_lib)
+add_test(NAME test_tgt_dxc_lib COMMAND test_tgt_dxc_lib)
+
 if(Vulkan_GLSLC_EXECUTABLE)
   add_test(NAME test_glslc
     COMMAND ${CMAKE_COMMAND}
@@ -92,3 +103,12 @@ if(Vulkan_GLSLANG_VALIDATOR_EXECUTABLE)
     -P "${CMAKE_CURRENT_LIST_DIR}/Run-glslangValidator.cmake"
     )
 endif()
+
+if(Vulkan_dxc_EXECUTABLE)
+  add_test(NAME test_dxc_exe
+    COMMAND ${CMAKE_COMMAND}
+    "-DVULKAN_DXC_EXECUTABLE=${Vulkan_dxc_EXECUTABLE}"
+    "-DVULKAN_DXC_EXECUTABLE_TARGET=$<TARGET_FILE:Vulkan::dxc_exe>"
+    -P "${CMAKE_CURRENT_LIST_DIR}/Run-dxc_exe.cmake"
+    )
+endif()
diff --git a/Tests/FindVulkan/Test/Run-dxc_exe.cmake b/Tests/FindVulkan/Test/Run-dxc_exe.cmake
new file mode 100644 (file)
index 0000000..0d38855
--- /dev/null
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.12)
+
+function(run_dxc_exe exe exe_display)
+  execute_process(COMMAND ${exe} --help
+    OUTPUT_VARIABLE output
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    RESULT_VARIABLE result
+    )
+
+  if(NOT result EQUAL 0)
+    message(SEND_ERROR "Result of ${exe_display} --help is ${result}, should be 0")
+  endif()
+
+  if(NOT output MATCHES "^OVERVIEW: HLSL Compiler for ")
+    message(SEND_ERROR "Output of ${exe_display} --help is \"${output}\", should begin with \"OVERVIEW: HLSL Compiler for \"")
+  endif()
+endfunction()
+
+run_dxc_exe("${VULKAN_DXC_EXECUTABLE}" "\${VULKAN_DXC_EXECUTABLE}")
+run_dxc_exe("${VULKAN_DXC_EXECUTABLE_TARGET}" "Vulkan::dxc_exe")
diff --git a/Tests/FindVulkan/Test/main-dxc_lib.cxx b/Tests/FindVulkan/Test/main-dxc_lib.cxx
new file mode 100644 (file)
index 0000000..6ccb0de
--- /dev/null
@@ -0,0 +1,23 @@
+#include <cstdio>
+
+#include "dxc/dxcapi.h"
+#include "printf.h"
+
+int main()
+{
+  IDxcCompiler3* compiler;
+  DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&compiler));
+
+  assert(compiler);
+
+  IDxcVersionInfo* version;
+  compiler->QueryInterface(&version);
+
+  uint32_t major, minor;
+  version->GetVersion(&major, &minor);
+  printf("DirectX Shader Compiler: %u.%u\n", major, minor);
+  version->Release();
+  compiler->Release();
+
+  return 0;
+}
diff --git a/Tests/FindVulkan/Test/main-volk.cxx b/Tests/FindVulkan/Test/main-volk.cxx
new file mode 100644 (file)
index 0000000..2ec9fb4
--- /dev/null
@@ -0,0 +1,14 @@
+#include <iostream>
+
+#include <volk/volk.h>
+
+int main()
+{
+  if (volkInitialize() != VK_SUCCESS) {
+    std::cout << "volk initialization success!" << std::endl;
+  } else {
+    std::cout << "volk initialization failure!" << std::endl;
+  }
+
+  return 0;
+}
index ab8e0f9..783af7e 100644 (file)
@@ -16,6 +16,7 @@
 add_executable(submod
   main.f90
   parent.f90
+  obfuscated_parent.f90
   child.f90
   grandchild.f90
   greatgrandchild.f90
index 3cd2989..67ffba8 100644 (file)
@@ -1,7 +1,10 @@
 program main
   use parent, only : child_function,grandchild_subroutine
   use parent, only : sibling_function,GreatGrandChild_subroutine
+  ! Using a module without postfix
+  use obfuscated_parent
   implicit none
-  if (child_function()) call grandchild_subroutine
-  if (sibling_function()) call GreatGrandChild_subroutine
+  if (child_function())     call grandchild_subroutine
+  if (sibling_function())   call GreatGrandChild_subroutine
+  if (child_function_obf()) call grandchild_subroutine_obf
 end program
diff --git a/Tests/FortranModules/Submodules/obfuscated_parent.f90 b/Tests/FortranModules/Submodules/obfuscated_parent.f90
new file mode 100644 (file)
index 0000000..f3e68be
--- /dev/null
@@ -0,0 +1,33 @@
+! This module has two procedures from the "parent" module
+! but it has different combinations 'module <word>' phrases
+! in breaked lines for test of modules dependencies detection
+
+! Module declaration on breaked line with reminder
+module &
+         obfuscated_parent;  implicit none
+
+  interface
+
+    ! Boolean module function
+    module logical &
+                     function child_function_obf() result(child_stuff)
+    end function
+
+    ! Module subroutine
+    module subroutine &
+                     grandchild_subroutine_obf()
+    end subroutine
+
+  end interface
+
+  contains
+
+    module logical function child_function_obf() result(child_stuff)
+      child_stuff=.true.
+    end function
+
+    module subroutine grandchild_subroutine_obf()
+      print *,"Test passed."
+    end subroutine
+
+end module obfuscated_parent
index 6ac97fa..958d0a2 100644 (file)
@@ -1,4 +1,4 @@
 PROGRAM MAINF90
-  USE TEST_MODULE
+  USE TEST_MODULE, only : TEST_MODULE_FUNCTION
   PRINT *,'Sum is',TEST_MODULE_FUNCTION(1., 2.)
 END PROGRAM MAINF90
index 6450447..0660d0f 100644 (file)
@@ -51,7 +51,7 @@ track_find_variable(testvar is_changed)
 if ("${is_changed}" STREQUAL changed)
   pass("same argument name test")
 else ()
-  pass("same argument name test")
+  fail("same argument name test" "Got: ${is_changed}")
 endif ()
 
 include("Util.cmake")
@@ -59,7 +59,7 @@ tester()
 if (tester_res STREQUAL "${CMAKE_CURRENT_LIST_FILE}")
   pass("CMAKE_CURRENT_LIST_FILE test")
 else ()
-  pass("CMAKE_CURRENT_LIST_FILE test")
+  fail("CMAKE_CURRENT_LIST_FILE test" "Got: ${tester_res}")
 endif ()
 
 
index bb4b92c..e82cea2 100644 (file)
@@ -121,12 +121,66 @@ add_library(ordertest ordertest.cpp)
 target_include_directories(ordertest SYSTEM PUBLIC SystemIncludeDirectories/systemlib)
 target_include_directories(ordertest PUBLIC SystemIncludeDirectories/userlib)
 
+# Test "IMPORTED_NO_SYSTEM" property and its interaction with "SYSTEM"
 add_library(ordertest2 ordertest.cpp)
 target_include_directories(ordertest2 SYSTEM PRIVATE SystemIncludeDirectories/systemlib)
 target_link_libraries(ordertest2 PRIVATE ordertest2_userlib)
 add_library(ordertest2_userlib INTERFACE IMPORTED)
 target_include_directories(ordertest2_userlib INTERFACE SystemIncludeDirectories/userlib)
 set_property(TARGET ordertest2_userlib PROPERTY IMPORTED_NO_SYSTEM 1)
+get_property(system_prop_value TARGET ordertest2_userlib PROPERTY SYSTEM)
+if (NOT system_prop_value)
+  message(SEND_ERROR "ordertest2_userlib SYSTEM property should be ON.")
+endif()
+
+# Test "SYSTEM" property of non-imported libraries
+add_library(ordertest3_systemlib INTERFACE)
+target_include_directories(ordertest3_systemlib INTERFACE SystemIncludeDirectories/systemlib)
+set_property(TARGET ordertest3_systemlib PROPERTY SYSTEM 1)
+add_library(ordertest3_userlib INTERFACE)
+target_include_directories(ordertest3_userlib INTERFACE SystemIncludeDirectories/userlib)
+add_library(ordertest3 ordertest.cpp)
+target_link_libraries(ordertest3 PRIVATE ordertest3_systemlib ordertest3_userlib)
+
+# Test "SYSTEM" property of imported libraries and its interaction with "IMPORTED_NO_SYSTEM"
+add_library(ordertest4 ordertest.cpp)
+target_include_directories(ordertest4 SYSTEM PRIVATE SystemIncludeDirectories/systemlib)
+target_link_libraries(ordertest4 PRIVATE ordertest4_userlib)
+add_library(ordertest4_userlib INTERFACE IMPORTED)
+target_include_directories(ordertest4_userlib INTERFACE SystemIncludeDirectories/userlib)
+set_property(TARGET ordertest4_userlib PROPERTY SYSTEM 0)
+get_property(imported_no_system_prop_value TARGET ordertest4_userlib PROPERTY IMPORTED_NO_SYSTEM)
+if (imported_no_system_prop_value)
+  message(SEND_ERROR "ordertest4_userlib IMPORTED_NO_SYSTEM property should be OFF.")
+endif()
+
+# Test the interaction between "SYSTEM" and "INTERFACE_SYSTEM_INCLUDE_DIRECTORIES"
+add_library(ordertest5_systemlib INTERFACE)
+target_include_directories(ordertest5_systemlib SYSTEM INTERFACE SystemIncludeDirectories/systemlib)
+# The default value of `SYSTEM` is already `OFF`. Here we explicitly set it again.
+set_property(TARGET ordertest5_systemlib PROPERTY SYSTEM 0)
+add_library(ordertest5_userlib INTERFACE)
+target_include_directories(ordertest5_userlib INTERFACE SystemIncludeDirectories/userlib)
+add_library(ordertest5 ordertest.cpp)
+target_link_libraries(ordertest5 PRIVATE ordertest5_systemlib ordertest5_userlib)
+
+# Test that the include of imported executable is treated as system by default.
+add_executable(ordertest6_systemexe IMPORTED)
+target_include_directories(ordertest6_systemexe INTERFACE SystemIncludeDirectories/systemlib)
+set_property(TARGET ordertest6_systemexe PROPERTY ENABLE_EXPORTS 1)
+add_library(ordertest6_userlib INTERFACE)
+target_include_directories(ordertest6_userlib INTERFACE SystemIncludeDirectories/userlib)
+add_library(ordertest6 ordertest.cpp)
+target_link_libraries(ordertest6 PRIVATE ordertest6_systemexe ordertest6_userlib)
+
+# Test that the include of imported executable is not treated as system if "SYSTEM" property is OFF.
+add_library(ordertest7 ordertest.cpp)
+target_include_directories(ordertest7 SYSTEM PRIVATE SystemIncludeDirectories/systemlib)
+target_link_libraries(ordertest7 PRIVATE ordertest7_userexe)
+add_library(ordertest7_userexe INTERFACE IMPORTED)
+target_include_directories(ordertest7_userexe INTERFACE SystemIncludeDirectories/userlib)
+set_property(TARGET ordertest7_userexe PROPERTY ENABLE_EXPORTS 1)
+set_property(TARGET ordertest7_userexe PROPERTY SYSTEM 0)
 
 add_subdirectory(StandardIncludeDirectories)
 add_subdirectory(TargetIncludeDirectories)
index af7b092..7176ebe 100644 (file)
@@ -75,10 +75,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
   info->CAPI->DisplaySatus(mf, info->CAPI->GetStartOutputDirectory(mf));
   info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentDirectory(mf));
   info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentOutputDirectory(mf));
-  sprintf(buffer, "Cache version: %d.%d, CMake version: %d.%d",
-          info->CAPI->GetCacheMajorVersion(mf),
-          info->CAPI->GetCacheMinorVersion(mf),
-          info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf));
+  snprintf(
+    buffer, sizeof(buffer), "Cache version: %d.%d, CMake version: %d.%d",
+    info->CAPI->GetCacheMajorVersion(mf), info->CAPI->GetCacheMinorVersion(mf),
+    info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf));
   info->CAPI->DisplaySatus(mf, buffer);
   if (info->CAPI->CommandExists(mf, "SET")) {
     info->CAPI->DisplaySatus(mf, "Command SET exists");
@@ -91,10 +91,12 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
 
   source_file = info->CAPI->CreateNewSourceFile(mf);
   cstr = info->CAPI->SourceFileGetSourceName(source_file);
-  sprintf(buffer, "Should be empty (source file name): [%s]", cstr);
+  snprintf(buffer, sizeof(buffer), "Should be empty (source file name): [%s]",
+           cstr);
   info->CAPI->DisplaySatus(mf, buffer);
   cstr = info->CAPI->SourceFileGetFullPath(source_file);
-  sprintf(buffer, "Should be empty (source file full path): [%s]", cstr);
+  snprintf(buffer, sizeof(buffer),
+           "Should be empty (source file full path): [%s]", cstr);
   info->CAPI->DisplaySatus(mf, buffer);
   info->CAPI->DefineSourceFileProperty(mf, "SOME_PROPERTY", "unused old prop",
                                        "This property is no longer used", 0);
@@ -106,7 +108,8 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
                                        "This property is for testing.", 0);
   info->CAPI->SourceFileSetProperty(source_file, "SOME_PROPERTY2", "HERE");
   cstr = info->CAPI->SourceFileGetProperty(source_file, "ABSTRACT");
-  sprintf(buffer, "Should be 0 (source file abstract property): [%p]", cstr);
+  snprintf(buffer, sizeof(buffer),
+           "Should be 0 (source file abstract property): [%p]", cstr);
   info->CAPI->DisplaySatus(mf, buffer);
 
   info->CAPI->DestroySourceFile(source_file);
index af7b092..7176ebe 100644 (file)
@@ -75,10 +75,10 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
   info->CAPI->DisplaySatus(mf, info->CAPI->GetStartOutputDirectory(mf));
   info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentDirectory(mf));
   info->CAPI->DisplaySatus(mf, info->CAPI->GetCurrentOutputDirectory(mf));
-  sprintf(buffer, "Cache version: %d.%d, CMake version: %d.%d",
-          info->CAPI->GetCacheMajorVersion(mf),
-          info->CAPI->GetCacheMinorVersion(mf),
-          info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf));
+  snprintf(
+    buffer, sizeof(buffer), "Cache version: %d.%d, CMake version: %d.%d",
+    info->CAPI->GetCacheMajorVersion(mf), info->CAPI->GetCacheMinorVersion(mf),
+    info->CAPI->GetMajorVersion(mf), info->CAPI->GetMinorVersion(mf));
   info->CAPI->DisplaySatus(mf, buffer);
   if (info->CAPI->CommandExists(mf, "SET")) {
     info->CAPI->DisplaySatus(mf, "Command SET exists");
@@ -91,10 +91,12 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
 
   source_file = info->CAPI->CreateNewSourceFile(mf);
   cstr = info->CAPI->SourceFileGetSourceName(source_file);
-  sprintf(buffer, "Should be empty (source file name): [%s]", cstr);
+  snprintf(buffer, sizeof(buffer), "Should be empty (source file name): [%s]",
+           cstr);
   info->CAPI->DisplaySatus(mf, buffer);
   cstr = info->CAPI->SourceFileGetFullPath(source_file);
-  sprintf(buffer, "Should be empty (source file full path): [%s]", cstr);
+  snprintf(buffer, sizeof(buffer),
+           "Should be empty (source file full path): [%s]", cstr);
   info->CAPI->DisplaySatus(mf, buffer);
   info->CAPI->DefineSourceFileProperty(mf, "SOME_PROPERTY", "unused old prop",
                                        "This property is no longer used", 0);
@@ -106,7 +108,8 @@ static int CCONV InitialPass(void* inf, void* mf, int argc, char* argv[])
                                        "This property is for testing.", 0);
   info->CAPI->SourceFileSetProperty(source_file, "SOME_PROPERTY2", "HERE");
   cstr = info->CAPI->SourceFileGetProperty(source_file, "ABSTRACT");
-  sprintf(buffer, "Should be 0 (source file abstract property): [%p]", cstr);
+  snprintf(buffer, sizeof(buffer),
+           "Should be 0 (source file abstract property): [%p]", cstr);
   info->CAPI->DisplaySatus(mf, buffer);
 
   info->CAPI->DestroySourceFile(source_file);
diff --git a/Tests/MSVCDebugInformationFormat/CMakeLists.txt b/Tests/MSVCDebugInformationFormat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b09bc6c
--- /dev/null
@@ -0,0 +1,81 @@
+cmake_minimum_required(VERSION 3.24)
+cmake_policy(SET CMP0141 NEW)
+
+# The debug information format flags do not change preprocessor definitions,
+# so override our table of flags to artificially add a definition we can check.
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/override-C.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/override-CXX.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_CUDA ${CMAKE_CURRENT_SOURCE_DIR}/override-CUDA.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_Fortran ${CMAKE_CURRENT_SOURCE_DIR}/override-Fortran.cmake)
+
+project(MSVCDebugInformationFormat)
+if(CMake_TEST_CUDA)
+  enable_language(CUDA)
+endif()
+if(CMake_TEST_Fortran)
+  enable_language(Fortran)
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+if("${CMAKE_C_COMPILER_ID};${CMAKE_C_SIMULATE_ID};${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang;MSVC;GNU")
+  set(verify_default VERIFY_Z7)
+  set(NO_COMPILER_PDB 1)
+elseif("${CMAKE_C_COMPILER_ID};${CMAKE_C_SIMULATE_ID};${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang;MSVC;MSVC")
+  set(verify_default VERIFY_Zi)
+  set(NO_EDIT_AND_CONTINUE 1)
+else()
+  set(verify_default VERIFY_Zi)
+endif()
+
+set(verify_def_Embedded        -DVERIFY_Z7)
+set(verify_def_ProgramDatabase -DVERIFY_Zi)
+set(verify_def_EditAndContinue -DVERIFY_ZI)
+
+function(verify_combination format lang src)
+  # Test that try_compile builds with this debug format.
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "${format}")
+  set(CMAKE_TRY_COMPILE_CONFIGURATION "Debug")
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+  try_compile(${format}_COMPILES
+    ${CMAKE_CURRENT_BINARY_DIR}/try_compile/${format}
+    ${CMAKE_CURRENT_SOURCE_DIR}/${src}
+    COMPILE_DEFINITIONS ${verify_def_${format}}
+    CMAKE_FLAGS -DINCLUDE_DIRECTORIES=${CMAKE_CURRENT_SOURCE_DIR}
+    OUTPUT_VARIABLE ${format}_OUTPUT
+    )
+  if(${format}_COMPILES)
+    message(STATUS "try_compile ${lang} with ${format} worked")
+  else()
+    string(REPLACE "\n" "\n  " ${format}_OUTPUT "  ${${format}_OUTPUT}")
+    message(SEND_ERROR "try_compile ${lang} with ${format} failed:\n${${format}_OUTPUT}")
+  endif()
+
+  # Test that targets build with this debug format.
+  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${format}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+  add_library(${format}-${lang} ${src})
+  set_property(TARGET ${format}-${lang} PROPERTY BOOL_TRUE TRUE)
+  target_compile_definitions(${format}-${lang} PRIVATE ${verify_def_${format}})
+endfunction()
+
+function(verify lang src)
+  add_library(default-${lang} ${src})
+  target_compile_definitions(default-${lang} PRIVATE "$<$<CONFIG:Debug,RelWithDebInfo>:${verify_default}>")
+
+  verify_combination(Embedded ${lang} ${src})
+  if(NOT NO_COMPILER_PDB)
+    verify_combination(ProgramDatabase ${lang} ${src})
+    if(NOT NO_EDIT_AND_CONTINUE AND NOT lang MATCHES "^(Fortran)$")
+      verify_combination(EditAndContinue ${lang} ${src})
+    endif()
+  endif()
+endfunction()
+
+verify(C verify.c)
+verify(CXX verify.cxx)
+if(CMake_TEST_CUDA)
+  verify(CUDA verify.cu)
+endif()
+if(CMake_TEST_Fortran)
+  verify(Fortran verify.F90)
+endif()
diff --git a/Tests/MSVCDebugInformationFormat/override-C.cmake b/Tests/MSVCDebugInformationFormat/override-C.cmake
new file mode 100644 (file)
index 0000000..e8f5ae4
--- /dev/null
@@ -0,0 +1,7 @@
+set(var "CMAKE_C_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded")
+string(REPLACE "-Z7" "-Z7;-DTEST_Z7" "${var}" "${${var}}")
+string(REPLACE "-gcodeview" "-gcodeview;-DTEST_Z7" "${var}" "${${var}}")
+set(var "CMAKE_C_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase")
+string(REPLACE "-Zi" "-Zi;-DTEST_Zi" "${var}" "${${var}}")
+set(var "CMAKE_C_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue")
+string(REPLACE "-ZI" "-ZI;-DTEST_ZI" "${var}" "${${var}}")
diff --git a/Tests/MSVCDebugInformationFormat/override-CUDA.cmake b/Tests/MSVCDebugInformationFormat/override-CUDA.cmake
new file mode 100644 (file)
index 0000000..f870775
--- /dev/null
@@ -0,0 +1,6 @@
+set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded")
+string(REPLACE "-Z7" "-Z7;-DTEST_Z7" "${var}" "${${var}}")
+set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase")
+string(REPLACE "-Zi" "-Zi;-DTEST_Zi" "${var}" "${${var}}")
+set(var "CMAKE_CUDA_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue")
+string(REPLACE "-ZI" "-ZI;-DTEST_ZI" "${var}" "${${var}}")
diff --git a/Tests/MSVCDebugInformationFormat/override-CXX.cmake b/Tests/MSVCDebugInformationFormat/override-CXX.cmake
new file mode 100644 (file)
index 0000000..caa23fe
--- /dev/null
@@ -0,0 +1,7 @@
+set(var "CMAKE_CXX_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded")
+string(REPLACE "-Z7" "-Z7;-DTEST_Z7" "${var}" "${${var}}")
+string(REPLACE "-gcodeview" "-gcodeview;-DTEST_Z7" "${var}" "${${var}}")
+set(var "CMAKE_CXX_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase")
+string(REPLACE "-Zi" "-Zi;-DTEST_Zi" "${var}" "${${var}}")
+set(var "CMAKE_CXX_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue")
+string(REPLACE "-ZI" "-ZI;-DTEST_ZI" "${var}" "${${var}}")
diff --git a/Tests/MSVCDebugInformationFormat/override-Fortran.cmake b/Tests/MSVCDebugInformationFormat/override-Fortran.cmake
new file mode 100644 (file)
index 0000000..5d2db58
--- /dev/null
@@ -0,0 +1,4 @@
+set(var "CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded")
+string(REPLACE "-Z7" "-Z7;-DTEST_Z7" "${var}" "${${var}}")
+set(var "CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase")
+string(REPLACE "-Zi" "-Zi;-DTEST_Zi" "${var}" "${${var}}")
diff --git a/Tests/MSVCDebugInformationFormat/verify.F90 b/Tests/MSVCDebugInformationFormat/verify.F90
new file mode 100644 (file)
index 0000000..741bca6
--- /dev/null
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCDebugInformationFormat/verify.c b/Tests/MSVCDebugInformationFormat/verify.c
new file mode 100644 (file)
index 0000000..741bca6
--- /dev/null
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCDebugInformationFormat/verify.cu b/Tests/MSVCDebugInformationFormat/verify.cu
new file mode 100644 (file)
index 0000000..741bca6
--- /dev/null
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCDebugInformationFormat/verify.cxx b/Tests/MSVCDebugInformationFormat/verify.cxx
new file mode 100644 (file)
index 0000000..741bca6
--- /dev/null
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCDebugInformationFormat/verify.h b/Tests/MSVCDebugInformationFormat/verify.h
new file mode 100644 (file)
index 0000000..4bd6529
--- /dev/null
@@ -0,0 +1,29 @@
+#ifdef VERIFY_Z7
+#  ifndef TEST_Z7
+#    error "TEST_Z7 incorrectly not defined by debug format selection"
+#  endif
+#else
+#  ifdef TEST_Z7
+#    error "TEST_Z7 incorrectly defined by non-debug format selection"
+#  endif
+#endif
+
+#ifdef VERIFY_Zi
+#  ifndef TEST_Zi
+#    error "TEST_Zi incorrectly not defined by debug format selection"
+#  endif
+#else
+#  ifdef TEST_Zi
+#    error "TEST_Zi incorrectly defined by non-debug format selection"
+#  endif
+#endif
+
+#ifdef VERIFY_ZI
+#  ifndef TEST_ZI
+#    error "TEST_ZI incorrectly not defined by debug format selection"
+#  endif
+#else
+#  ifdef TEST_ZI
+#    error "TEST_ZI incorrectly defined by non-debug format selection"
+#  endif
+#endif
diff --git a/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt b/Tests/Module/CheckIPOSupported-CUDA/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9dd670e
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.8)
+project(CheckIPOSupported-CUDA LANGUAGES CUDA)
+
+cmake_policy(SET CMP0069 NEW)
+
+include(CheckIPOSupported)
+check_ipo_supported(RESULT ipo_supported OUTPUT ipo_output)
+if(ipo_supported)
+  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
+endif()
+
+if(NOT ipo_supported AND CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA"
+   AND CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2)
+  message(FATAL_ERROR "CheckIPOSupported failed to correctly identify NVIDIA CUDA IPO support")
+endif()
+
+set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)
+
+add_library(foo STATIC foo.cu)
+set_target_properties(foo PROPERTIES
+      WINDOWS_EXPORT_ALL_SYMBOLS ON
+      POSITION_INDEPENDENT_CODE ON)
+
+add_library(bar SHARED bar.cu)
+set_target_properties(bar PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
+target_link_libraries(bar PRIVATE foo)
+
+add_executable(CheckIPOSupported-CUDA main.cu)
+target_link_libraries(CheckIPOSupported-CUDA PUBLIC bar)
+
+enable_testing()
+add_test(NAME CheckIPOSupported-CUDA COMMAND CheckIPOSupported-CUDA)
diff --git a/Tests/Module/CheckIPOSupported-CUDA/bar.cu b/Tests/Module/CheckIPOSupported-CUDA/bar.cu
new file mode 100644 (file)
index 0000000..79b276d
--- /dev/null
@@ -0,0 +1,12 @@
+__device__ int foo_func(int);
+
+void __global__ bar_kernel(int x)
+{
+  foo_func(x);
+}
+
+int launch_kernel(int x)
+{
+  bar_kernel<<<1, 1>>>(x);
+  return x;
+}
diff --git a/Tests/Module/CheckIPOSupported-CUDA/foo.cu b/Tests/Module/CheckIPOSupported-CUDA/foo.cu
new file mode 100644 (file)
index 0000000..416607b
--- /dev/null
@@ -0,0 +1,4 @@
+extern __device__ int foo_func(int a)
+{
+  return a * 42 + 9;
+}
diff --git a/Tests/Module/CheckIPOSupported-CUDA/main.cu b/Tests/Module/CheckIPOSupported-CUDA/main.cu
new file mode 100644 (file)
index 0000000..8ef4873
--- /dev/null
@@ -0,0 +1,62 @@
+#include <iostream>
+
+#include "cuda.h"
+
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
+IMPORT int launch_kernel(int x);
+
+int choose_cuda_device()
+{
+  int nDevices = 0;
+  cudaError_t err = cudaGetDeviceCount(&nDevices);
+  if (err != cudaSuccess) {
+    std::cerr << "Failed to retrieve the number of CUDA enabled devices"
+              << std::endl;
+    return 1;
+  }
+  for (int i = 0; i < nDevices; ++i) {
+    cudaDeviceProp prop;
+    cudaError_t err = cudaGetDeviceProperties(&prop, i);
+    if (err != cudaSuccess) {
+      std::cerr << "Could not retrieve properties from CUDA device " << i
+                << std::endl;
+      return 1;
+    }
+    std::cout << "prop.major: " << prop.major << std::endl;
+    err = cudaSetDevice(i);
+    if (err != cudaSuccess) {
+      std::cout << "Could not select CUDA device " << i << std::endl;
+    } else {
+      return 0;
+    }
+  }
+
+  std::cout << "Could not find a CUDA enabled card" << std::endl;
+
+  return 1;
+}
+
+int main()
+{
+  int ret = choose_cuda_device();
+  if (ret) {
+    return 0;
+  }
+
+  cudaError_t err;
+  launch_kernel(1);
+  err = cudaGetLastError();
+  if (err != cudaSuccess) {
+    std::cerr << "launch_kernel: kernel launch should have passed.\n "
+                 "Error message: "
+              << cudaGetErrorString(err) << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
index 567fb4b..483bd8b 100644 (file)
@@ -15,7 +15,17 @@ add_custom_command(OUTPUT example_dll_gen.def
 add_library(example_dll_gen SHARED example_dll_gen.c example_dll_gen.def)
 
 # Test /DEF:<file> flag recognition for VS.
-if(MSVC OR CMAKE_C_COMPILER_ID STREQUAL "Intel")
+if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM")
+  # IntelLLVM for MSVC frontend variant needs the /DEF flag wrapped to be sent
+  # to the linker, which happens automatically when the DEF file is added
+  # to the sources.
+  add_library(example_dll_2 SHARED
+    example_dll_2.c
+    "${ModuleDefinition_SOURCE_DIR}/example_dll_2.def"
+    )
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS EXAMPLE_DLL_2)
+  set(example_dll_2 example_dll_2)
+elseif(MSVC OR CMAKE_C_COMPILER_ID STREQUAL "Intel")
   add_library(example_dll_2 SHARED example_dll_2.c)
   set_property(TARGET example_dll_2 PROPERTY LINK_FLAGS
     /DEF:"${ModuleDefinition_SOURCE_DIR}/example_dll_2.def")
index 4347459..84ca5e8 100644 (file)
@@ -197,9 +197,14 @@ endif()
 #-----------------------------------------------------------------------------
 
 # Inform the test if the debug configuration is getting built.
-# The NDEBUG definition takes care of this for release.
 string(APPEND CMAKE_C_FLAGS_DEBUG " -DPREPROCESS_DEBUG")
 string(APPEND CMAKE_CXX_FLAGS_DEBUG " -DPREPROCESS_DEBUG")
+string(APPEND CMAKE_C_FLAGS_RELEASE " -DPREPROCESS_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELEASE " -DPREPROCESS_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " -DPREPROCESS_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -DPREPROCESS_NDEBUG")
+string(APPEND CMAKE_C_FLAGS_MINSIZEREL " -DPREPROCESS_NDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " -DPREPROCESS_NDEBUG")
 
 # Inform the test if it built from Xcode.
 if(PP_XCODE)
index b3117da..88f9e97 100644 (file)
@@ -33,7 +33,7 @@ int check_defines_C(void)
       result = 0;
     }
   }
-#ifdef NDEBUG
+#ifdef PREPROCESS_NDEBUG
 #  ifdef FILE_DEF_DEBUG
   {
     fprintf(stderr, "FILE_DEF_DEBUG should not be defined in C\n");
index f2fffef..50150d1 100644 (file)
@@ -35,7 +35,7 @@ int check_defines_CXX()
       result = 0;
     }
   }
-#ifdef NDEBUG
+#ifdef PREPROCESS_NDEBUG
 #  ifdef FILE_DEF_DEBUG
   {
     fprintf(stderr, "FILE_DEF_DEBUG should not be defined in CXX\n");
index dbcf4b8..fe57d56 100644 (file)
@@ -7,7 +7,7 @@ set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
 add_library(autoexport SHARED hello.cxx world.cxx foo.c $<TARGET_OBJECTS:objlib>)
 add_library(autoexport3 SHARED cppCLI.cxx)
 if(MSVC AND NOT MSVC_VERSION VERSION_LESS 1600
-  AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+  AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
   set_property(TARGET autoexport3 PROPERTY COMMON_LANGUAGE_RUNTIME "")
 endif()
 
@@ -17,7 +17,7 @@ if(MSVC)
   add_library(autoexport_for_exec SHARED hello2.c)
   target_link_libraries(autoexport_for_exec say)
   if(NOT MSVC_VERSION VERSION_LESS 1600 AND
-    NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+    NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
     enable_language(ASM_MASM)
     target_sources(autoexport PRIVATE nop.asm)
     set_property(SOURCE nop.asm PROPERTY COMPILE_FLAGS /safeseh)
index 06f416b..8099079 100644 (file)
@@ -60,8 +60,7 @@ set(run_BuildDepends_skip_step_3 1)
 
 run_BuildDepends(C-Exe)
 if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
-  if(RunCMake_GENERATOR MATCHES "Visual Studio 10" OR
-      RunCMake_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100)$")
+  if(RunCMake_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100)$")
     # VS 10 forgets to re-link when a manifest changes
     set(run_BuildDepends_skip_step_2 1)
   endif()
diff --git a/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt b/Tests/RunCMake/CMP0102/CMP0102-OLD-stderr.txt
new file mode 100644 (file)
index 0000000..5d09fcf
--- /dev/null
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0102-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0102 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\)$
index 61e046f..074db65 100644 (file)
@@ -4,3 +4,4 @@ run_cmake_script(Regex-CMP0127-NEW)
 run_cmake_script(Regex-CMP0127-OLD)
 run_cmake_script(Parentheses-CMP0127-NEW)
 run_cmake_script(Parentheses-CMP0127-WARN)
+run_cmake_script(UseDotSymbol)
diff --git a/Tests/RunCMake/CMakeDependentOption/UseDotSymbol-stdout.txt b/Tests/RunCMake/CMakeDependentOption/UseDotSymbol-stdout.txt
new file mode 100644 (file)
index 0000000..15b56a1
--- /dev/null
@@ -0,0 +1 @@
+-- USE_FOO='ON'
diff --git a/Tests/RunCMake/CMakeDependentOption/UseDotSymbol.cmake b/Tests/RunCMake/CMakeDependentOption/UseDotSymbol.cmake
new file mode 100644 (file)
index 0000000..8f07c48
--- /dev/null
@@ -0,0 +1,4 @@
+include(CMakeDependentOption)
+
+cmake_dependent_option(USE_FOO "Use Foo" ON "CMAKE_VERSION VERSION_GREATER_EQUAL 3.08" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
index 09f248e..3038ed8 100644 (file)
@@ -197,6 +197,10 @@ if(CMAKE_GENERATOR MATCHES "Ninja")
       ${ninja_qt_args}
     )
   endif()
+  if(WIN32)
+    add_executable(showIncludes showIncludes.c)
+    list(APPEND Ninja_ARGS -DshowIncludes=$<TARGET_FILE:showIncludes>)
+  endif()
   add_RunCMake_test(Ninja)
   set(NinjaMultiConfig_ARGS
     -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}
@@ -295,6 +299,19 @@ if(UNIX AND "${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
   add_RunCMake_test(CompilerChange)
 endif()
 add_RunCMake_test(CompilerNotFound)
+if (APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
+  list(APPEND CompilerTest_ARGS -DCMake_TEST_OBJC=1)
+endif()
+if(CMAKE_Fortran_COMPILER)
+  list(APPEND CompilerTest_ARGS -DCMake_TEST_Fortran=1)
+endif()
+foreach(lang IN ITEMS CUDA HIP ISPC)
+  if(CMake_TEST_${lang})
+    list(APPEND CompilerTest_ARGS -DCMake_TEST_${lang}=1)
+  endif()
+endforeach()
+add_RunCMake_test(CompilerTest)
+set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC")
 add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
 add_RunCMake_test(DisallowedCommands)
 if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
@@ -305,6 +322,9 @@ add_RunCMake_test(ExportImport)
 add_RunCMake_test(ExternalData)
 add_RunCMake_test(FeatureSummary)
 add_RunCMake_test(FPHSA)
+if(CMAKE_USE_SYSTEM_JSONCPP)
+  list(APPEND FileAPI_ARGS -DJsonCpp_VERSION_STRING=${JsonCpp_VERSION_STRING})
+endif()
 add_RunCMake_test(FileAPI -DPython_EXECUTABLE=${Python_EXECUTABLE}
                           -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID})
 add_RunCMake_test(FindBoost)
@@ -356,6 +376,7 @@ if(MSVC)
   add_RunCMake_test(MSVCRuntimeLibrary)
   add_RunCMake_test(MSVCRuntimeTypeInfo)
   add_RunCMake_test(MSVCWarningFlags)
+  add_RunCMake_test(MSVCDebugInformationFormat)
 endif()
 if(XCODE_VERSION)
   set(ObjectLibrary_ARGS -DXCODE_VERSION=${XCODE_VERSION})
@@ -453,6 +474,7 @@ add_RunCMake_test(find_path)
 add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(foreach)
 add_RunCMake_test(function)
+add_RunCMake_test(block)
 add_RunCMake_test(get_filename_component)
 add_RunCMake_test(get_property)
 add_RunCMake_test(if)
@@ -522,7 +544,9 @@ function(add_RunCMake_test_try_compile)
       set(CMAKE_CXX_STANDARD_DEFAULT 14)
     endif()
   endif()
-  foreach(var
+  foreach(
+    var
+    IN ITEMS
       CMAKE_SYSTEM_NAME
       CMAKE_C_COMPILER_ID
       CMAKE_C_COMPILER_VERSION
@@ -536,7 +560,7 @@ function(add_RunCMake_test_try_compile)
       CMake_TEST_FILESYSTEM_1S
       CMAKE_OBJC_STANDARD_DEFAULT
       CMAKE_OBJCXX_STANDARD_DEFAULT
-      )
+    )
     if(DEFINED ${var})
       list(APPEND try_compile_ARGS -D${var}=${${var}})
     endif()
@@ -566,6 +590,9 @@ if(CMake_TEST_CUDA)
 endif()
 add_RunCMake_test(DependencyGraph -DCMAKE_Fortran_COMPILER=${CMAKE_Fortran_COMPILER})
 
+# Add C++ Module tests.
+add_RunCMake_test(CXXModules -DCMake_TEST_MODULE_COMPILATION=${CMake_TEST_MODULE_COMPILATION} -DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES})
+
 # ctresalloc links against CMakeLib and CTestLib, which means it can't be built
 # if CMake_TEST_EXTERNAL_CMAKE is activated (the compiler might be different.)
 # So, it has to be provided in the original build tree.
@@ -689,6 +716,8 @@ add_RunCMake_test(target_link_libraries-LINK_LIBRARY -DCMAKE_SYSTEM_NAME=${CMAKE
                                                      -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
                                                      -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
                                                      -DMSVC_VERSION=${MSVC_VERSION}
+                                                     -DXCODE=${XCODE}
+                                                     -DXCODE_VERSION=${XCODE_VERSION}
                                                      -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
                                                      -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX}
                                                      -DCMAKE_IMPORT_LIBRARY_PREFIX=${CMAKE_IMPORT_LIBRARY_PREFIX}
@@ -948,7 +977,7 @@ if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN)
   if(NOT "${CMAKE_GENERATOR}" MATCHES "Make|Ninja|Visual Studio 1[456]")
     message(FATAL_ERROR "Android tests supported only by Makefile, Ninja, and Visual Studio >= 14 generators")
   endif()
-  foreach(v TEST_ANDROID_NDK TEST_ANDROID_STANDALONE_TOOLCHAIN)
+  foreach(v IN ITEMS TEST_ANDROID_NDK TEST_ANDROID_STANDALONE_TOOLCHAIN)
     if(CMake_${v})
       string(REPLACE ";" "|" ${v} "${CMake_${v}}")
       list(APPEND Android_ARGS "-D${v}=${${v}}")
@@ -967,7 +996,7 @@ endif()
 
 if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])")
   add_RunCMake_test(CSharpCustomCommand)
-  if(NOT CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64")
+  if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
     add_RunCMake_test(CSharpReferenceImport)
   endif()
 endif()
@@ -992,6 +1021,14 @@ add_RunCMake_test(CMakePresetsTest
   -DPython_EXECUTABLE=${Python_EXECUTABLE}
   -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
   )
+add_RunCMake_test(CMakePresetsPackage
+  -DPython_EXECUTABLE=${Python_EXECUTABLE}
+  -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
+  )
+add_RunCMake_test(CMakePresetsWorkflow
+  -DPython_EXECUTABLE=${Python_EXECUTABLE}
+  -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}
+  )
 
 add_RunCMake_test(VerifyHeaderSets)
 
index 2f404bc..b3b6b66 100644 (file)
@@ -1,2 +1,9 @@
 ^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/Comment: JSON parse error$
+]*/Tests/RunCMake/CMakePresets/Comment: JSON parse error
+Errors:
+[^
+]*Comment\/CMakePresets.json:
+\* Line 1, Column 1
+  Syntax error: value, object or array expected\.
+\* Line 2, Column 1
+  Extra non-whitespace after JSON value\.$
diff --git a/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt b/Tests/RunCMake/CMakePresets/DocumentationExampleListAllPresets-stdout.txt
new file mode 100644 (file)
index 0000000..57b714d
--- /dev/null
@@ -0,0 +1,22 @@
+^Not searching for unused variables given on the command line\.
+Available configure presets:
+
+  "default"      ?- Default Config
+  "ninja-multi"  ?- Ninja Multi-Config(
+  "windows-only" - Windows-only configuration)?
+
+Available build presets:
+
+  "default"
+
+Available test presets:
+
+  "default"
+
+Available package presets:
+
+  "default"
+
+Available workflow presets:
+
+  "default"$
index 6970674..0d3c500 100644 (file)
@@ -1,2 +1,5 @@
 ^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/EmptyPresetName: Invalid preset$
+]*/Tests/RunCMake/CMakePresets/EmptyPresetName: Invalid preset
+Errors:
+[^
+]*/EmptyPresetName/CMakePresets.json$
index df58e72..d9e399f 100644 (file)
@@ -1,4 +1,5 @@
 include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
 
 get_filename_component(_parent "${CMAKE_SOURCE_DIR}" DIRECTORY)
+file(REAL_PATH "${_parent}" _parent)
 test_variable(CMAKE_BINARY_DIR "" "${_parent}/GoodNoSCachePrep-build")
index 7ccabab..85a2d78 100644 (file)
@@ -1,2 +1,5 @@
 ^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/IncludeNotFound: File not found$
+]*/Tests/RunCMake/CMakePresets/IncludeNotFound: File not found
+Errors:
+[^
+]*/IncludeNotFound/NotFound.json: Failed to read file$
index a43bf77..89eff9f 100644 (file)
@@ -1,2 +1,9 @@
 ^CMake Error: Could not read presets from [^
-]*/Tests/RunCMake/CMakePresets/JSONParseError: JSON parse error$
+]*/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\.$
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt b/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild-stdout.txt
new file mode 100644 (file)
index 0000000..38f52aa
--- /dev/null
@@ -0,0 +1,8 @@
+^Not searching for unused variables given on the command line.
+Available configure presets:
+
+  "default"
+
+Available test presets:
+
+  "default"$
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild.json.in b/Tests/RunCMake/CMakePresets/ListAllPresetsNoBuild.json.in
new file mode 100644 (file)
index 0000000..9259477
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt b/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest-stdout.txt
new file mode 100644 (file)
index 0000000..8cac0a8
--- /dev/null
@@ -0,0 +1,8 @@
+^Not searching for unused variables given on the command line.
+Available configure presets:
+
+  "default"
+
+Available build presets:
+
+  "default"$
diff --git a/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest.json.in b/Tests/RunCMake/CMakePresets/ListAllPresetsNoTest.json.in
new file mode 100644 (file)
index 0000000..33fd036
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/ListPresetsInvalidType-stderr.txt b/Tests/RunCMake/CMakePresets/ListPresetsInvalidType-stderr.txt
new file mode 100644 (file)
index 0000000..153abee
--- /dev/null
@@ -0,0 +1,3 @@
+^CMake Error: Invalid value specified for --list-presets\.
+Valid values are configure, build, test, package, or all\. When no value is passed the default is configure\.
+CMake Error: Run 'cmake --help' for all supported options\.$
index d097086..efa838e 100644 (file)
@@ -289,6 +289,7 @@ run_cmake_presets(UserInheritance)
 # Test listing presets
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/ListPresets.json.in")
 run_cmake_presets(ListPresets --list-presets)
+run_cmake_presets(ListPresetsInvalidType --list-presets=invalid-type)
 
 set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/ListPresetsWorkingDir")
 set(RunCMake_TEST_NO_CLEAN 1)
@@ -304,6 +305,12 @@ unset(RunCMake_TEST_BINARY_DIR)
 run_cmake_presets(ListPresetsNoSuchPreset)
 run_cmake_presets(ListPresetsHidden)
 
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/ListAllPresetsNoBuild.json.in")
+run_cmake_presets(ListAllPresetsNoBuild --list-presets=all)
+
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/ListAllPresetsNoTest.json.in")
+run_cmake_presets(ListAllPresetsNoTest --list-presets=all)
+
 # Test warning and error flags
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings.json.in")
 set(CMakePresets_WARN_UNUSED_CLI 1)
@@ -401,4 +408,5 @@ set(CMakePresets_EXTRA_FILES
   "${RunCMake_SOURCE_DIR}/moreThings.json.in"
 )
 run_cmake_presets(DocumentationExample --preset=default)
+run_cmake_presets(DocumentationExampleListAllPresets --list-presets=all)
 unset(CMakePresets_EXTRA_FILES)
diff --git a/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsPackage/CMakeLists.txt.in
new file mode 100644 (file)
index 0000000..129184a
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.19)
+project("@CASE_NAME@" NONE)
+include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-config-file-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-config-file-check.cmake
new file mode 100644 (file)
index 0000000..40240f9
--- /dev/null
@@ -0,0 +1,6 @@
+include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+set(filename "${RunCMake_TEST_BINARY_DIR}/default/_CPack_Packages/${CPACK_TOPLEVEL_TAG}/TGZ/config-file-alt.tar.gz")
+if(NOT EXISTS "${filename}")
+  set(RunCMake_TEST_FAILED "Expected ${filename} to exist but it does not")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-configurations-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-configurations-check.cmake
new file mode 100644 (file)
index 0000000..3d684af
--- /dev/null
@@ -0,0 +1,18 @@
+if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+  set(cpack_dir "${RunCMake_TEST_BINARY_DIR}/default/_CPack_Packages/${CPACK_TOPLEVEL_TAG}")
+  set(contents [[Debug
+Release
+]])
+
+  file(GLOB dirs RELATIVE "${cpack_dir}" "${cpack_dir}/*")
+  foreach(dir IN LISTS dirs)
+    set(configs_file "${cpack_dir}/${dir}/${CPACK_PACKAGE_FILE_NAME}/configs.txt")
+    file(READ "${configs_file}" actual_contents)
+    if(NOT contents STREQUAL actual_contents)
+      string(REPLACE "\n" "\n  " contents_formatted "${contents}")
+      string(REPLACE "\n" "\n  " actual_contents_formatted "${actual_contents}")
+      string(APPEND RunCMake_TEST_FAILED "Expected contents of ${configs_file}:\n  ${contents_formatted}\nActual contents:\n  ${actual_contents_formatted}\n")
+    endif()
+  endforeach()
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-debug-stdout.txt b/Tests/RunCMake/CMakePresetsPackage/Good-package-debug-stdout.txt
new file mode 100644 (file)
index 0000000..be885b4
--- /dev/null
@@ -0,0 +1,2 @@
+CPack: [^
+]* Enable Debug
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-generators-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-generators-check.cmake
new file mode 100644 (file)
index 0000000..aaa75e4
--- /dev/null
@@ -0,0 +1 @@
+check_cpack_packages("TBZ2;TXZ" "")
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-no-environment-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-no-environment-check.cmake
new file mode 100644 (file)
index 0000000..205e7b7
--- /dev/null
@@ -0,0 +1,7 @@
+check_cpack_packages("TGZ;TXZ" [[TEST_ENV not defined
+TEST_ENV_REF=xx
+TEST_ENV_OVERRIDE not defined
+TEST_ENV_OVERRIDE_REF not defined
+]])
+
+include("${RunCMake_SOURCE_DIR}/check.cmake")
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-package-directory-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-package-directory-check.cmake
new file mode 100644 (file)
index 0000000..8f4b771
--- /dev/null
@@ -0,0 +1,6 @@
+include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+set(filename "${RunCMake_TEST_BINARY_DIR}/default/package-directory/_CPack_Packages/${CPACK_TOPLEVEL_TAG}/TGZ/${CPACK_PACKAGE_FILE_NAME}.tar.gz")
+if(NOT EXISTS "${filename}")
+  set(RunCMake_TEST_FAILED "Expected ${filename} to exist but it does not")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-package-name-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-package-name-check.cmake
new file mode 100644 (file)
index 0000000..fdc4824
--- /dev/null
@@ -0,0 +1,7 @@
+include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/default/${CPACK_PACKAGE_FILE_NAME}.json" contents)
+string(JSON package_name GET "${contents}" packageName)
+if(NOT package_name STREQUAL "package-name")
+  set(RunCMake_TEST_FAILED "Expected package name to be \"package-name\" but it was \"${package_name}\"")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-package-version-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-package-version-check.cmake
new file mode 100644 (file)
index 0000000..dba9110
--- /dev/null
@@ -0,0 +1,7 @@
+include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/default/${CPACK_PACKAGE_FILE_NAME}.json" contents)
+string(JSON package_version GET "${contents}" packageVersion)
+if(NOT package_version STREQUAL "1.0")
+  set(RunCMake_TEST_FAILED "Expected package version to be \"1.0\" but it was \"${package_version}\"")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-variables-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-variables-check.cmake
new file mode 100644 (file)
index 0000000..2858170
--- /dev/null
@@ -0,0 +1,6 @@
+include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+set(filename "${RunCMake_TEST_BINARY_DIR}/default/_CPack_Packages/${CPACK_TOPLEVEL_TAG}/TGZ/variables-package.tar.gz")
+if(NOT EXISTS "${filename}")
+  set(RunCMake_TEST_FAILED "Expected ${filename} to exist but it does not")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-verbose-stdout.txt b/Tests/RunCMake/CMakePresetsPackage/Good-package-verbose-stdout.txt
new file mode 100644 (file)
index 0000000..22fd115
--- /dev/null
@@ -0,0 +1 @@
+CPack: Enable Verbose
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good-package-with-environment-check.cmake b/Tests/RunCMake/CMakePresetsPackage/Good-package-with-environment-check.cmake
new file mode 100644 (file)
index 0000000..a775e4d
--- /dev/null
@@ -0,0 +1,7 @@
+check_cpack_packages("TGZ;TXZ" [[TEST_ENV=Environment variable
+TEST_ENV_REF=xEnvironment variablex
+TEST_ENV_OVERRIDE=Override
+TEST_ENV_OVERRIDE_REF=xOverridex
+]])
+
+include("${RunCMake_SOURCE_DIR}/check.cmake")
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good.cmake b/Tests/RunCMake/CMakePresetsPackage/Good.cmake
new file mode 100644 (file)
index 0000000..d019443
--- /dev/null
@@ -0,0 +1,31 @@
+set(CPACK_PACKAGE_NAME Good)
+set(CPACK_GENERATOR "TGZ;TXZ")
+
+include(CPack)
+
+install(CODE [[
+function(print_env name)
+  if(DEFINED ENV{${name}})
+    file(APPEND $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/env.txt "${name}=$ENV{${name}}\n")
+  else()
+    file(APPEND $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/env.txt "${name} not defined\n")
+  endif()
+endfunction()
+
+file(REMOVE $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/env.txt)
+print_env(TEST_ENV)
+print_env(TEST_ENV_REF)
+print_env(TEST_ENV_OVERRIDE)
+print_env(TEST_ENV_OVERRIDE_REF)
+
+file(APPEND $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/configs.txt "$<CONFIG>\n")
+]])
+
+file(WRITE "${CMAKE_BINARY_DIR}/CPackConfigAlt.cmake" [[include(${CMAKE_CURRENT_LIST_DIR}/CPackConfig.cmake)
+set(CPACK_PACKAGE_FILE_NAME "config-file-alt")
+]])
+
+file(WRITE "${CMAKE_BINARY_DIR}/external_package.cmake" [[if(NOT CPACK_PACKAGE_VENDOR STREQUAL "some-vendor")
+  message(FATAL_ERROR "Expected vendor to be \"some-vendor\" but it was \"${CPACK_PACKAGE_VENDOR}\"")
+endif()
+]])
diff --git a/Tests/RunCMake/CMakePresetsPackage/Good.json.in b/Tests/RunCMake/CMakePresetsPackage/Good.json.in
new file mode 100644 (file)
index 0000000..0c0e7d9
--- /dev/null
@@ -0,0 +1,135 @@
+{
+    "version": 6,
+    "configurePresets": [
+        {
+            "name": "default",
+            "generator": "@RunCMake_GENERATOR@",
+            "binaryDir": "${sourceDir}/build/${presetName}",
+            "environment": {
+                "TEST_ENV": "Environment variable",
+                "TEST_ENV_OVERRIDE": "Overridden environment variable"
+            }
+        }
+    ],
+    "buildPresets": [
+        {
+            "name": "build-default-debug",
+            "configurePreset": "default",
+            "configuration": "Debug"
+        },
+        {
+            "name": "build-default-release",
+            "inherits": "build-default-debug",
+            "configuration": "Release"
+        }
+    ],
+    "packagePresets": [
+        {
+            "name": "minimal",
+            "configurePreset": "default"
+        },
+        {
+            "name": "defaults",
+            "hidden": false,
+            "inherits": [],
+            "vendor": {},
+            "displayName": "",
+            "description": "",
+            "environment": {},
+            "configurePreset": "default",
+            "inheritConfigureEnvironment": true
+        },
+        {
+            "name": "no-environment",
+            "configurePreset": "default",
+            "inheritConfigureEnvironment": false,
+            "environment": {
+                "TEST_ENV_REF": "x$env{TEST_ENV}x"
+            }
+        },
+        {
+            "name": "with-environment",
+            "inherits": "no-environment",
+            "inheritConfigureEnvironment": true,
+            "environment": {
+                "TEST_ENV_OVERRIDE": "Override",
+                "TEST_ENV_OVERRIDE_REF": "x$env{TEST_ENV_OVERRIDE}x",
+                "TEST_ENV_REF": "x$env{TEST_ENV}x"
+            }
+        },
+        {
+            "name": "generators",
+            "inherits": "minimal",
+            "generators": [
+                "TBZ2",
+                "TXZ"
+            ]
+        },
+        {
+            "name": "configurations",
+            "inherits": "minimal",
+            "configurations": [
+                "Debug",
+                "Release"
+            ]
+        },
+        {
+            "name": "variables",
+            "inherits": "minimal",
+            "variables": {
+                "CPACK_PACKAGE_FILE_NAME": "variables-package"
+            }
+        },
+        {
+            "name": "config-file",
+            "inherits": "minimal",
+            "configFile": "CPackConfigAlt.cmake"
+        },
+        {
+            "name": "debug",
+            "inherits": "minimal",
+            "output": {
+                "debug": true
+            }
+        },
+        {
+            "name": "verbose",
+            "inherits": "minimal",
+            "output": {
+                "verbose": true
+            }
+        },
+        {
+            "name": "package-name",
+            "inherits": "minimal",
+            "generators": [
+                "External"
+            ],
+            "packageName": "package-name"
+        },
+        {
+            "name": "package-version",
+            "inherits": "minimal",
+            "generators": [
+                "External"
+            ],
+            "packageVersion": "1.0"
+        },
+        {
+            "name": "package-directory",
+            "inherits": "minimal",
+            "packageDirectory": "${sourceDir}/build/default/package-directory"
+        },
+        {
+            "name": "vendor-name",
+            "inherits": "minimal",
+            "generators": [
+                "External"
+            ],
+            "variables": {
+                "CPACK_EXTERNAL_PACKAGE_SCRIPT": "${sourceDir}/build/default/external_package.cmake"
+            },
+            "vendorName": "some-vendor"
+        }
+    ]
+}
diff --git a/Tests/RunCMake/CMakePresetsPackage/ListPresets-package-x-stdout.txt b/Tests/RunCMake/CMakePresetsPackage/ListPresets-package-x-stdout.txt
new file mode 100644 (file)
index 0000000..307ec0a
--- /dev/null
@@ -0,0 +1,4 @@
+^Available package presets:
+
+  "default"
+  "with-description" - With Description$
diff --git a/Tests/RunCMake/CMakePresetsPackage/ListPresets.json.in b/Tests/RunCMake/CMakePresetsPackage/ListPresets.json.in
new file mode 100644 (file)
index 0000000..5f3cf48
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "packagePresets": [
+    {
+      "name": "default",
+      "configurePreset": "default"
+    },
+    {
+      "name": "with-description",
+      "displayName": "With Description",
+      "description": "This preset has a description",
+      "configurePreset": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsPackage/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..269fb6e
--- /dev/null
@@ -0,0 +1,102 @@
+include(RunCMake)
+
+# Presets do not support legacy VS generator name architecture suffix.
+if(RunCMake_GENERATOR MATCHES "^(Visual Studio [0-9]+ [0-9]+) ")
+  set(RunCMake_GENERATOR "${CMAKE_MATCH_1}")
+endif()
+
+function(run_cmake_package_presets name CMakePresetsPackage_CONFIGURE_PRESETS CMakePresetsPackage_BUILD_PRESETS CMakePresetsPackage_PACKAGE_PRESETS)
+  set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_TEST_SOURCE_DIR}/build")
+  set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
+
+  set(RunCMake_TEST_NO_CLEAN TRUE)
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  set(CASE_NAME "${name}")
+  set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}")
+  configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY)
+
+  if(NOT CMakePresetsPackage_FILE)
+    set(CMakePresetsPackage_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in")
+  endif()
+  if(EXISTS "${CMakePresetsPackage_FILE}")
+    configure_file("${CMakePresetsPackage_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY)
+  endif()
+
+  if(NOT CMakeUserPresets_FILE)
+    set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in")
+  endif()
+  if(EXISTS "${CMakeUserPresets_FILE}")
+    configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY)
+  endif()
+
+  foreach(ASSET ${CMakePresetsPackage_ASSETS})
+    configure_file("${RunCMake_SOURCE_DIR}/${ASSET}" "${RunCMake_TEST_SOURCE_DIR}" COPYONLY)
+  endforeach()
+
+  if (NOT CMakePresetsPackage_NO_CONFIGURE)
+    foreach(CONFIGURE_PRESET ${CMakePresetsPackage_CONFIGURE_PRESETS})
+      run_cmake_command("${name}-configure-${CONFIGURE_PRESET}"
+        "${CMAKE_COMMAND}" "--preset" "${CONFIGURE_PRESET}")
+    endforeach()
+  endif()
+
+  if (NOT CMakePresetsPackage_NO_BUILD)
+    foreach(BUILD_PRESET ${CMakePresetsPackage_BUILD_PRESETS})
+      run_cmake_command("${name}-build-${BUILD_PRESET}"
+        "${CMAKE_COMMAND}" "--build" "--preset" "${BUILD_PRESET}")
+    endforeach()
+  endif()
+
+  set(eq 0)
+  foreach(PACKAGE_PRESET ${CMakePresetsPackage_PACKAGE_PRESETS})
+    file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}/default/_CPack_Packages")
+
+    if (EXISTS "${RunCMake_SOURCE_DIR}/${name}-package-${PACKAGE_PRESET}-check.cmake")
+      set(RunCMake-check-file "${name}-package-${PACKAGE_PRESET}-check.cmake")
+    else()
+      set(RunCMake-check-file "check.cmake")
+    endif()
+
+    if(eq)
+      run_cmake_command(${name}-package-${PACKAGE_PRESET}
+        ${CMAKE_CPACK_COMMAND} "--preset=${PACKAGE_PRESET}" ${ARGN})
+      set(eq 0)
+    else()
+      run_cmake_command(${name}-package-${PACKAGE_PRESET}
+        ${CMAKE_CPACK_COMMAND} "--preset" "${PACKAGE_PRESET}" ${ARGN})
+      set(eq 1)
+    endif()
+  endforeach()
+endfunction()
+
+function(check_cpack_packages generators contents)
+  include("${RunCMake_TEST_BINARY_DIR}/default/CPackConfig.cmake")
+
+  set(cpack_dir "${RunCMake_TEST_BINARY_DIR}/default/_CPack_Packages/${CPACK_TOPLEVEL_TAG}")
+  file(GLOB dirs RELATIVE "${cpack_dir}" "${cpack_dir}/*")
+  if(NOT dirs STREQUAL generators)
+    string(APPEND RunCMake_TEST_FAILED "Expected CPack generators: ${generators}\nActual CPack generators: ${dirs}\n")
+  endif()
+
+  if(contents)
+    foreach(dir IN LISTS dirs)
+      set(env_file "${cpack_dir}/${dir}/${CPACK_PACKAGE_FILE_NAME}/env.txt")
+      file(READ "${env_file}" actual_contents)
+      if(NOT contents STREQUAL actual_contents)
+        string(REPLACE "\n" "\n  " contents_formatted "${contents}")
+        string(REPLACE "\n" "\n  " actual_contents_formatted "${actual_contents}")
+        string(APPEND RunCMake_TEST_FAILED "Expected contents of ${env_file}:\n  ${contents_formatted}\nActual contents:\n  ${actual_contents_formatted}\n")
+      endif()
+    endforeach()
+  endif()
+
+  set(RunCMake_TEST_FAILED ${RunCMake_TEST_FAILED} PARENT_SCOPE)
+endfunction()
+
+run_cmake_package_presets(UnsupportedVersion "x" "" "")
+run_cmake_package_presets(Good "default" "build-default-debug" "no-environment;with-environment;generators;configurations;variables;config-file;debug;verbose;package-name;package-version;package-directory;vendor-name")
+run_cmake_package_presets(ListPresets "default" "" "x" "--list-presets")
diff --git a/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt b/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion-configure-x-stderr.txt
new file mode 100644 (file)
index 0000000..4c461e3
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion: File version must be 6 or higher for package preset support$
diff --git a/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion.json.in b/Tests/RunCMake/CMakePresetsPackage/UnsupportedVersion.json.in
new file mode 100644 (file)
index 0000000..e5f7240
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "version": 5,
+    "packagePresets": []
+}
diff --git a/Tests/RunCMake/CMakePresetsPackage/check.cmake b/Tests/RunCMake/CMakePresetsPackage/check.cmake
new file mode 100644 (file)
index 0000000..e79c4f1
--- /dev/null
@@ -0,0 +1,3 @@
+set(CMakePresets_VALIDATE_SCRIPT_PATH "${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.py")
+include("${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.cmake")
+include("${RunCMake_SOURCE_DIR}/../CMakePresets/check.cmake")
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-outputJUnit-check.cmake b/Tests/RunCMake/CMakePresetsTest/Good-test-outputJUnit-check.cmake
new file mode 100644 (file)
index 0000000..e1788cb
--- /dev/null
@@ -0,0 +1,4 @@
+include("${CMAKE_CURRENT_LIST_DIR}/check.cmake")
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/default/output.xml")
+  string(APPEND RunCMake_TEST_FAILED "Expected ${RunCMake_TEST_BINARY_DIR}/default/output.xml to exist but it does not\n")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsTest/Good-test-outputLog-check.cmake b/Tests/RunCMake/CMakePresetsTest/Good-test-outputLog-check.cmake
new file mode 100644 (file)
index 0000000..e860d42
--- /dev/null
@@ -0,0 +1,4 @@
+include("${CMAKE_CURRENT_LIST_DIR}/check.cmake")
+if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/default/output.log")
+  string(APPEND RunCMake_TEST_FAILED "Expected ${RunCMake_TEST_BINARY_DIR}/default/output.log to exist but it does not\n")
+endif()
index d484a19..a4b875a 100644 (file)
@@ -1,5 +1,5 @@
 {
-    "version": 5,
+    "version": 6,
     "configurePresets": [
         {
             "name": "default",
             "execution": {
                 "showOnly": "human"
             }
+        },
+        {
+            "name": "outputLog",
+            "inherits": "minimal",
+            "output": {
+                "outputLogFile": "${sourceDir}/build/default/output.log"
+            }
+        },
+        {
+            "name": "outputJUnit",
+            "inherits": "minimal",
+            "output": {
+                "outputJUnitFile": "${sourceDir}/build/default/output.xml"
+            }
         }
     ]
 }
diff --git a/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported-test-x-stderr.txt
new file mode 100644 (file)
index 0000000..acd5785
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported: File version must be 6 or higher for CTest JUnit output support$
diff --git a/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported.json.in b/Tests/RunCMake/CMakePresetsTest/OutputJUnitUnsupported.json.in
new file mode 100644 (file)
index 0000000..93f6b0c
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "version": 5,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "unsupported",
+      "configurePreset": "default",
+      "output": {
+        "outputJUnitFile": "junit.xml"
+      }
+    }
+  ]
+}
index bec0dd9..1c7b836 100644 (file)
@@ -78,7 +78,7 @@ set(CMakePresetsTest_ASSETS "Good-indexFile.txt")
 set(GoodTestPresets
   "minimal;defaults;noEnvironment;withEnvironment"
   "config-debug;config-release"
-  "exclude;index;indexFile;showOnly")
+  "exclude;index;indexFile;showOnly;outputLog;outputJUnit")
 run_cmake_test_presets(Good
                        "default"
                        ""
@@ -105,6 +105,8 @@ run_cmake_test_presets(Invalid "" "" "hidden;vendorMacro")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 run_cmake_test_presets(PresetsUnsupported "" "" "x")
 run_cmake_test_presets(ConditionFuture "" "" "x")
+run_cmake_test_presets(TestOutputTruncationUnsupported "" "" "x")
+run_cmake_test_presets(OutputJUnitUnsupported "" "" "x")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 run_cmake_test_presets(ConfigurePresetUnreachable "" "" "x")
 set(CMakePresetsTest_NO_CONFIGURE 0)
diff --git a/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported-test-x-stderr.txt
new file mode 100644 (file)
index 0000000..90ea7c3
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported: File version must be 5 or higher for testOutputTruncation preset support\.$
diff --git a/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported.json.in b/Tests/RunCMake/CMakePresetsTest/TestOutputTruncationUnsupported.json.in
new file mode 100644 (file)
index 0000000..c116039
--- /dev/null
@@ -0,0 +1,17 @@
+{
+  "version": 4,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "output": {
+        "testOutputTruncation": "tail"
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-result.txt
new file mode 100644 (file)
index 0000000..45a4fb7
--- /dev/null
@@ -0,0 +1 @@
+8
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt
new file mode 100644 (file)
index 0000000..0690c69
--- /dev/null
@@ -0,0 +1,4 @@
+^Errors while running CTest
+Output from these tests are in: [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode/build/Testing/Temporary/LastTest\.log
+Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stdout.txt
new file mode 100644 (file)
index 0000000..2f23f88
--- /dev/null
@@ -0,0 +1,17 @@
+^Executing workflow step 1 of 4: configure preset "default"
+
+.*Testing the configure step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode/build.*
+
+Executing workflow step 2 of 4: build preset "default"
+
+.*Testing the build step at [^
+]*[\\/]Tests[\\/]RunCMake[\\/]CMakePresetsWorkflow[\\/]BadExitCode[\\/]build.*
+
+Executing workflow step 3 of 4: test preset "default"
+
+.*Testing the test step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode/build.*
+
+The following tests FAILED:
+.* +1 - EchoTest \(Failed\)$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode.cmake b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode.cmake
new file mode 100644 (file)
index 0000000..10b46e3
--- /dev/null
@@ -0,0 +1,8 @@
+message(STATUS "Testing the configure step at ${CMAKE_BINARY_DIR}")
+
+add_custom_target(echo_test ALL COMMAND ${CMAKE_COMMAND} -E echo "Testing the build step at ${CMAKE_BINARY_DIR}")
+
+enable_testing()
+add_test(NAME EchoTest COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/BadExitCodeTest.cmake")
+
+include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCodeTest.cmake b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCodeTest.cmake
new file mode 100644 (file)
index 0000000..59f683e
--- /dev/null
@@ -0,0 +1 @@
+message(FATAL_ERROR "  Testing the test step at ${CMAKE_BINARY_DIR}")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in b/Tests/RunCMake/CMakePresetsWorkflow/CMakeLists.txt.in
new file mode 100644 (file)
index 0000000..129184a
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.19)
+project("@CASE_NAME@" NONE)
+include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch-stderr.txt
new file mode 100644 (file)
index 0000000..22ca94d
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch: Invalid workflow steps$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch.json.in b/Tests/RunCMake/CMakePresetsWorkflow/ConfigureStepMismatch.json.in
new file mode 100644 (file)
index 0000000..0864149
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default"
+    },
+    {
+      "name": "mismatch"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "mismatch",
+      "configurePreset": "mismatch"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "build",
+          "name": "mismatch"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt
new file mode 100644 (file)
index 0000000..cbfee5a
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure: Invalid workflow steps$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure.json.in b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure.json.in
new file mode 100644 (file)
index 0000000..2c121a8
--- /dev/null
@@ -0,0 +1,27 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "binaryDir": "${sourceDir}/build",
+      "generator": "@RunCMake_GENERATOR@"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "build",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/Fresh.cmake b/Tests/RunCMake/CMakePresetsWorkflow/Fresh.cmake
new file mode 100644 (file)
index 0000000..4cf999f
--- /dev/null
@@ -0,0 +1,4 @@
+option(FRESH_CONFIGURE "" ON)
+if(NOT FRESH_CONFIGURE)
+  message(FATAL_ERROR "FRESH_CONFIGURE is ${FRESH_CONFIGURE}, should be ON")
+endif()
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/Fresh.json.in b/Tests/RunCMake/CMakePresetsWorkflow/Fresh.json.in
new file mode 100644 (file)
index 0000000..4ce0ca5
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "Fresh",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/Good-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/Good-stdout.txt
new file mode 100644 (file)
index 0000000..a04d7ea
--- /dev/null
@@ -0,0 +1,19 @@
+^Executing workflow step 1 of 4: configure preset "default"
+
+.*Testing the configure step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/Good/build.*
+
+Executing workflow step 2 of 4: build preset "default"
+
+.*Testing the build step at [^
+]*[\\/]Tests[\\/]RunCMake[\\/]CMakePresetsWorkflow[\\/]Good[\\/]build.*
+
+Executing workflow step 3 of 4: test preset "default"
+
+.*Testing the test step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/Good/build.*
+
+Executing workflow step 4 of 4: package preset "default"
+
+.*Testing the package step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/Good/build.*
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/Good.cmake b/Tests/RunCMake/CMakePresetsWorkflow/Good.cmake
new file mode 100644 (file)
index 0000000..31ce7ff
--- /dev/null
@@ -0,0 +1,8 @@
+message(STATUS "Testing the configure step at ${CMAKE_BINARY_DIR}")
+
+add_custom_target(echo_test ALL COMMAND ${CMAKE_COMMAND} -E echo "Testing the build step at ${CMAKE_BINARY_DIR}")
+
+enable_testing()
+add_test(NAME EchoTest COMMAND ${CMAKE_COMMAND} -E echo "Testing the test step at ${CMAKE_BINARY_DIR}")
+
+include(CPack)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/Good.json.in b/Tests/RunCMake/CMakePresetsWorkflow/Good.json.in
new file mode 100644 (file)
index 0000000..87e2936
--- /dev/null
@@ -0,0 +1,87 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "binaryDir": "${sourceDir}/build",
+      "generator": "@RunCMake_GENERATOR@"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "configuration": "Debug"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "output": {
+        "verbosity": "verbose"
+      },
+      "configuration": "Debug"
+    }
+  ],
+  "packagePresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "generators": [
+        "External"
+      ],
+      "variables": {
+        "CPACK_EXTERNAL_PACKAGE_SCRIPT": "${sourceDir}/cpack_staging.cmake"
+      },
+      "configurations": ["Debug"]
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "Good",
+      "displayName": "Good Workflow Preset",
+      "description": "This workflow preset works properly.",
+      "vendor": {},
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "build",
+          "name": "default"
+        },
+        {
+          "type": "test",
+          "name": "default"
+        },
+        {
+          "type": "package",
+          "name": "default"
+        }
+      ]
+    },
+    {
+      "name": "BadExitCode",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "build",
+          "name": "default"
+        },
+        {
+          "type": "test",
+          "name": "default"
+        },
+        {
+          "type": "package",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/GoodUser-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/GoodUser-stdout.txt
new file mode 100644 (file)
index 0000000..1014915
--- /dev/null
@@ -0,0 +1,2 @@
+-- Testing the configure step at [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/GoodUser/build
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/GoodUser.cmake b/Tests/RunCMake/CMakePresetsWorkflow/GoodUser.cmake
new file mode 100644 (file)
index 0000000..9143e00
--- /dev/null
@@ -0,0 +1 @@
+message(STATUS "Testing the configure step at ${CMAKE_BINARY_DIR}")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/GoodUser.json.in b/Tests/RunCMake/CMakePresetsWorkflow/GoodUser.json.in
new file mode 100644 (file)
index 0000000..e71b4ea
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "version": 6,
+  "workflowPresets": [
+    {
+      "name": "GoodUser",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stderr.txt
new file mode 100644 (file)
index 0000000..049ff54
--- /dev/null
@@ -0,0 +1,6 @@
+^Unknown argument -DINVALID_OPTION
+Usage: cmake --workflow \[options\]
+Options:
+  --preset <preset> = Workflow preset to execute\.
+  --list-presets    = List available workflow presets\.
+  --fresh           = Configure a fresh build tree, removing any existing cache file\.$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/InvalidOption-stdout.txt
new file mode 100644 (file)
index 0000000..10f3293
--- /dev/null
@@ -0,0 +1 @@
+^$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/ListPresets-stdout.txt b/Tests/RunCMake/CMakePresetsWorkflow/ListPresets-stdout.txt
new file mode 100644 (file)
index 0000000..57f30a4
--- /dev/null
@@ -0,0 +1,4 @@
+^Available workflow presets:
+
+  "default"
+  "with-description" - With Description$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/ListPresets.json.in b/Tests/RunCMake/CMakePresetsWorkflow/ListPresets.json.in
new file mode 100644 (file)
index 0000000..9a7d5a6
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    },
+    {
+      "name": "with-description",
+      "displayName": "With Description",
+      "description": "This preset has a description.",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps-stderr.txt
new file mode 100644 (file)
index 0000000..049ed6b
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps: Invalid workflow steps$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps.json.in b/Tests/RunCMake/CMakePresetsWorkflow/NoWorkflowSteps.json.in
new file mode 100644 (file)
index 0000000..2757197
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "version": 6,
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": []
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt
new file mode 100644 (file)
index 0000000..c522b84
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep: Invalid workflow steps$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep.json.in b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep.json.in
new file mode 100644 (file)
index 0000000..235398b
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "version": 6,
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsWorkflow/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..550600a
--- /dev/null
@@ -0,0 +1,89 @@
+include(RunCMake)
+
+# Presets do not support legacy VS generator name architecture suffix.
+if(RunCMake_GENERATOR MATCHES "^(Visual Studio [0-9]+ [0-9]+) ")
+  set(RunCMake_GENERATOR "${CMAKE_MATCH_1}")
+endif()
+
+function(run_cmake_workflow_presets name)
+  set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_TEST_SOURCE_DIR}/build")
+  set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
+
+  if(NOT RunCMake_TEST_NO_CLEAN)
+    file(REMOVE_RECURSE "${RunCMake_TEST_SOURCE_DIR}")
+    file(MAKE_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
+  endif()
+
+  set(RunCMake_TEST_NO_CLEAN TRUE)
+
+  set(CASE_NAME "${name}")
+  set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}")
+  configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt.in" "${RunCMake_TEST_SOURCE_DIR}/CMakeLists.txt" @ONLY)
+
+  if(NOT CMakePresets_FILE)
+    set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/${name}.json.in")
+  endif()
+  if(EXISTS "${CMakePresets_FILE}")
+    configure_file("${CMakePresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" @ONLY)
+  endif()
+
+  if(NOT CMakeUserPresets_FILE)
+    set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/${name}User.json.in")
+  endif()
+  if(EXISTS "${CMakeUserPresets_FILE}")
+    configure_file("${CMakeUserPresets_FILE}" "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" @ONLY)
+  endif()
+
+  foreach(ASSET ${CMakePresets_ASSETS})
+    configure_file("${RunCMake_SOURCE_DIR}/${ASSET}.in" "${RunCMake_TEST_SOURCE_DIR}/${ASSET}" @ONLY)
+  endforeach()
+
+  if(EXISTS "${RunCMake_SOURCE_DIR}/${name}-check.cmake")
+    set(RunCMake-check-file "${name}-check.cmake")
+  else()
+    set(RunCMake-check-file "check.cmake")
+  endif()
+
+  if(eq)
+    set(eq 0 PARENT_SCOPE)
+    set(preset_arg "--preset=${name}")
+  else()
+    set(eq 1 PARENT_SCOPE)
+    set(preset_arg "--preset" "${name}")
+  endif()
+  run_cmake_command("${name}" "${CMAKE_COMMAND}" "--workflow" ${preset_arg} ${ARGN})
+endfunction()
+
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
+run_cmake_workflow_presets(UnsupportedVersion)
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
+run_cmake_workflow_presets(NoWorkflowSteps)
+run_cmake_workflow_presets(FirstStepNotConfigure)
+run_cmake_workflow_presets(SecondStepConfigure)
+run_cmake_workflow_presets(NonexistentStep)
+run_cmake_workflow_presets(UnreachableStep)
+run_cmake_workflow_presets(WorkflowStepHidden)
+run_cmake_workflow_presets(WorkflowStepDisabled)
+run_cmake_workflow_presets(WorkflowStepInvalidMacro)
+run_cmake_workflow_presets(ConfigureStepMismatch)
+
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Good.json.in")
+set(CMakeUserPresets_FILE "${RunCMake_SOURCE_DIR}/GoodUser.json.in")
+set(CMakePresets_ASSETS cpack_staging.cmake)
+run_cmake_workflow_presets(Good)
+run_cmake_workflow_presets(GoodUser)
+run_cmake_workflow_presets(BadExitCode)
+unset(CMakePresets_FILE)
+unset(CMakeUserPresets_FILE)
+unset(CMakePresets_ASSETS)
+
+run_cmake_workflow_presets(ListPresets --list-presets)
+run_cmake_workflow_presets(InvalidOption -DINVALID_OPTION)
+
+set(RunCMake_TEST_NO_CLEAN TRUE)
+file(REMOVE_RECURSE "${RunCMake_BINARY_DIR}/Fresh")
+file(MAKE_DIRECTORY "${RunCMake_BINARY_DIR}/Fresh/build")
+file(WRITE "${RunCMake_BINARY_DIR}/Fresh/build/CMakeCache.txt" "FRESH_CONFIGURE:BOOL=OFF\n")
+run_cmake_workflow_presets(Fresh --fresh)
+unset(RunCMake_TEST_NO_CLEAN)
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt
new file mode 100644 (file)
index 0000000..b0ad7d5
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure: Invalid workflow steps$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure.json.in b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure.json.in
new file mode 100644 (file)
index 0000000..44e1582
--- /dev/null
@@ -0,0 +1,25 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "binaryDir": "${sourceDir}/build",
+      "generator": "@RunCMake_GENERATOR@"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        },
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt
new file mode 100644 (file)
index 0000000..425e719
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep: Workflow step is unreachable from preset's file$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep.json.in b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep.json.in
new file mode 100644 (file)
index 0000000..235398b
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "version": 6,
+  "workflowPresets": [
+    {
+      "name": "default",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStepUser.json.in b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStepUser.json.in
new file mode 100644 (file)
index 0000000..39b6835
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default"
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion-stderr.txt
new file mode 100644 (file)
index 0000000..5cf01aa
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion: File version must be 6 or higher for workflow preset support$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion.json.in b/Tests/RunCMake/CMakePresetsWorkflow/UnsupportedVersion.json.in
new file mode 100644 (file)
index 0000000..4ebaa8e
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "version": 5,
+  "workflowPresets": []
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt
new file mode 100644 (file)
index 0000000..b598b27
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Cannot use disabled configure preset in [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled: "default"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled.json.in b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled.json.in
new file mode 100644 (file)
index 0000000..a3b6783
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "condition": {
+        "type": "const",
+        "value": false
+      }
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "WorkflowStepDisabled",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt
new file mode 100644 (file)
index 0000000..838ded5
--- /dev/null
@@ -0,0 +1,2 @@
+^CMake Error: Cannot use hidden configure preset in [^
+]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden: "default"$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden.json.in b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden.json.in
new file mode 100644 (file)
index 0000000..07c4105
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "hidden": true
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "WorkflowStepHidden",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-result.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt
new file mode 100644 (file)
index 0000000..f132a93
--- /dev/null
@@ -0,0 +1 @@
+^CMake Error: Could not evaluate configure preset "default": Invalid macro expansion$
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro.json.in b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro.json.in
new file mode 100644 (file)
index 0000000..6aec0e3
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "version": 6,
+  "configurePresets": [
+    {
+      "name": "default",
+      "binaryDir": "$vendor{invalidMacro}"
+    }
+  ],
+  "workflowPresets": [
+    {
+      "name": "WorkflowStepInvalidMacro",
+      "steps": [
+        {
+          "type": "configure",
+          "name": "default"
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/check.cmake b/Tests/RunCMake/CMakePresetsWorkflow/check.cmake
new file mode 100644 (file)
index 0000000..e79c4f1
--- /dev/null
@@ -0,0 +1,3 @@
+set(CMakePresets_VALIDATE_SCRIPT_PATH "${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.py")
+include("${RunCMake_SOURCE_DIR}/../CMakePresets/validate_schema.cmake")
+include("${RunCMake_SOURCE_DIR}/../CMakePresets/check.cmake")
diff --git a/Tests/RunCMake/CMakePresetsWorkflow/cpack_staging.cmake.in b/Tests/RunCMake/CMakePresetsWorkflow/cpack_staging.cmake.in
new file mode 100644 (file)
index 0000000..4030dfb
--- /dev/null
@@ -0,0 +1 @@
+message(STATUS "Testing the package step at @RunCMake_TEST_BINARY_DIR@")
index 9b98ed4..8904c69 100644 (file)
@@ -97,7 +97,7 @@ function(getMissingShlibsErrorExtra FILE RESULT_VAR)
         string(APPEND error_extra "; errors \"${deb_install_files_errors}\"")
       endif()
 
-      if(READELF_EXECUTABLE)
+      if(CPACK_READELF_EXECUTABLE)
         string(APPEND error_extra "; readelf \"\n")
 
         # Only dynamically linked ELF files are included
@@ -106,7 +106,7 @@ function(getMissingShlibsErrorExtra FILE RESULT_VAR)
           if(_FILE MATCHES "ELF.*shared object")
             string(REGEX MATCH "(^.*):" _FILE_NAME "${_FILE}")
 
-            execute_process(COMMAND ${READELF_EXECUTABLE} -d "${CMAKE_MATCH_1}"
+            execute_process(COMMAND ${CPACK_READELF_EXECUTABLE} -d "${CMAKE_MATCH_1}"
               WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
               RESULT_VARIABLE result
               OUTPUT_VARIABLE output
index 60d02e7..3b02579 100644 (file)
@@ -14,9 +14,9 @@ function(get_test_prerequirements found_var config_file)
   endif()
 
   # optional tool for some tests
-  find_program(READELF_EXECUTABLE NAMES readelf)
-  if(READELF_EXECUTABLE)
+  find_program(CPACK_READELF_EXECUTABLE NAMES readelf)
+  if(CPACK_READELF_EXECUTABLE)
     file(APPEND "${config_file}"
-      "\nset(READELF_EXECUTABLE \"${READELF_EXECUTABLE}\")")
+      "\nset(CPACK_READELF_EXECUTABLE \"${CPACK_READELF_EXECUTABLE}\")")
   endif()
 endfunction()
index e95cd15..a4741ba 100644 (file)
@@ -15,9 +15,9 @@ function(get_test_prerequirements found_var config_file)
   endif()
 
   # optional tool for some tests
-  find_program(OBJDUMP_EXECUTABLE objdump)
-  if(OBJDUMP_EXECUTABLE)
+  find_program(CPACK_OBJDUMP_EXECUTABLE objdump)
+  if(CPACK_OBJDUMP_EXECUTABLE)
     file(APPEND "${config_file}"
-      "\nset(OBJDUMP_EXECUTABLE \"${OBJDUMP_EXECUTABLE}\")")
+      "\nset(CPACK_OBJDUMP_EXECUTABLE \"${CPACK_OBJDUMP_EXECUTABLE}\")")
   endif()
 endfunction()
index be44b2e..b0bfc9d 100644 (file)
@@ -1,7 +1,7 @@
 function(get_test_prerequirements found_var config_file)
   include(${config_file})
 
-  if(READELF_EXECUTABLE)
+  if(CPACK_READELF_EXECUTABLE)
     set(${found_var} true PARENT_SCOPE)
   endif()
 endfunction()
index be44b2e..b0bfc9d 100644 (file)
@@ -1,7 +1,7 @@
 function(get_test_prerequirements found_var config_file)
   include(${config_file})
 
-  if(READELF_EXECUTABLE)
+  if(CPACK_READELF_EXECUTABLE)
     set(${found_var} true PARENT_SCOPE)
   endif()
 endfunction()
index 90cfe44..f5df8a2 100644 (file)
@@ -2,7 +2,7 @@ function(get_test_prerequirements found_var config_file)
   if(SUBTEST_SUFFIX MATCHES ".*single_debug_info")
     include(${config_file})
 
-    if(OBJDUMP_EXECUTABLE)
+    if(CPACK_OBJDUMP_EXECUTABLE)
       set(${found_var} true PARENT_SCOPE)
     endif()
   else()
index 5a008a7..0d534fc 100644 (file)
@@ -1,6 +1,7 @@
 #if defined(_WIN32)
 #  include <windows.h>
 #else
+#  include <sched.h>
 #  include <unistd.h>
 #endif
 
index 7b6eb53..ce40a7a 100644 (file)
@@ -1,4 +1,4 @@
 ^(CMake Warning in [^
-]*/Tests/RunCMake/CUDA_architectures/architectures-suffix-build/CMakeFiles/CMakeTmp/CMakeLists.txt:
+]*/Tests/RunCMake/CUDA_architectures/architectures-suffix-build/CMakeFiles/CMakeScratch/TryCompile-[^/]*/CMakeLists.txt:
   Clang doesn't support disabling CUDA real code generation.
 *)*$
diff --git a/Tests/RunCMake/CXXModules/CMakeLists.txt b/Tests/RunCMake/CXXModules/CMakeLists.txt
new file mode 100644 (file)
index 0000000..708d92c
--- /dev/null
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.23)
+project(${RunCMake_TEST} NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-check.cmake
new file mode 100644 (file)
index 0000000..cb6f6bd
--- /dev/null
@@ -0,0 +1,40 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake" export_script)
+
+if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules\.cmake"\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find C++ module property script inclusion")
+endif ()
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules.cmake" trampoline_script)
+
+if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-[^.]*\.cmake" OPTIONAL\)]])
+    list(APPEND RunCMake_TEST_FAILED
+      "Could not find C++ module property per-config script inclusion(s)")
+  endif ()
+else ()
+  if (NOT trampoline_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-[^.]*\.cmake"\)]])
+    list(APPEND RunCMake_TEST_FAILED
+      "Could not find C++ module property per-config script inclusion(s)")
+  endif ()
+endif ()
+
+set(any_exists 0)
+foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel)
+  if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-${config}.cmake")
+    continue ()
+  endif ()
+  set(any_exists 1)
+
+  file(READ "${RunCMake_TEST_BINARY_DIR}/lib/cmake/export-modules/cxx-modules/cxx-modules-${config}.cmake" config_script)
+
+  if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)")
+    list(APPEND RunCMake_TEST_FAILED
+      "Could not find C++ module per-target property script inclusion")
+  endif ()
+endforeach ()
+
+if (NOT any_exists)
+  list(APPEND RunCMake_TEST_FAILED
+    "No per-configuration target files exist.")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt
new file mode 100644 (file)
index 0000000..c05b0b4
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at ExportBuildCxxModules.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake
new file mode 100644 (file)
index 0000000..850f8dc
--- /dev/null
@@ -0,0 +1,22 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(export-modules)
+target_sources(export-modules
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(export-modules
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET export-modules
+  PROPERTY EXPORT_NAME export-name)
+
+install(TARGETS export-modules
+  EXPORT exp
+  FILE_SET fs DESTINATION "include/cxx/export-modules")
+
+export(EXPORT exp
+  FILE "${CMAKE_BINARY_DIR}/lib/cmake/export-modules/export-modules-targets.cmake"
+  CXX_MODULES_DIRECTORY "cxx-modules")
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-check.cmake
new file mode 100644 (file)
index 0000000..9e83fd8
--- /dev/null
@@ -0,0 +1,35 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/exp.cmake" export_script)
+
+if (NOT export_script MATCHES [[include\("\${CMAKE_CURRENT_LIST_DIR}/cxx-modules/cxx-modules\.cmake"\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find C++ module property script inclusion")
+endif ()
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules.cmake" trampoline_script)
+
+if (NOT trampoline_script MATCHES [[file\(GLOB _cmake_cxx_module_includes "\${CMAKE_CURRENT_LIST_DIR}/cxx-modules-\*\.cmake"\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find C++ module property per-config script inclusion(s)")
+endif ()
+
+set(any_exists 0)
+foreach (config IN ITEMS noconfig Debug Release RelWithDebInfo MinSizeRel)
+  if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-${config}.cmake")
+    continue ()
+  endif ()
+  set(any_exists 1)
+
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/eee57a7e91412f1be699e9b63fa9d601/cxx-modules/cxx-modules-${config}.cmake" config_script)
+
+  if (NOT config_script MATCHES "include\\(\"\\\${CMAKE_CURRENT_LIST_DIR}/target-export-name-${config}\\.cmake\"\\)")
+    list(APPEND RunCMake_TEST_FAILED
+      "Could not find C++ module per-target property script inclusion")
+  endif ()
+endforeach ()
+
+if (NOT any_exists)
+  list(APPEND RunCMake_TEST_FAILED
+    "No per-configuration target files exist.")
+endif ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt
new file mode 100644 (file)
index 0000000..4fe27a9
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at ExportInstallCxxModules.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake
new file mode 100644 (file)
index 0000000..234a4b5
--- /dev/null
@@ -0,0 +1,22 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(export-modules)
+target_sources(export-modules
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(export-modules
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET export-modules
+  PROPERTY EXPORT_NAME export-name)
+
+install(TARGETS export-modules
+  EXPORT exp
+  FILE_SET fs DESTINATION "include/cxx/export-modules")
+
+install(EXPORT exp
+  DESTINATION "lib/cmake/export-modules"
+  CXX_MODULES_DIRECTORY "cxx-modules")
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface-stderr.txt
new file mode 100644 (file)
index 0000000..d573a02
--- /dev/null
@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetModuleHeaderUnitsInterface.cmake:2 \(target_sources\):
+  target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS"
+  may not have "INTERFACE" visibility
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterface.cmake
new file mode 100644 (file)
index 0000000..03ca17e
--- /dev/null
@@ -0,0 +1,8 @@
+add_library(module-header)
+target_sources(module-header
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported-stderr.txt
new file mode 100644 (file)
index 0000000..1b4ba5d
--- /dev/null
@@ -0,0 +1,6 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsInterfaceImported.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsInterfaceImported.cmake
new file mode 100644 (file)
index 0000000..9ff5606
--- /dev/null
@@ -0,0 +1,8 @@
+add_library(module-header SHARED IMPORTED)
+target_sources(module-header
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  INTERFACE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate-stderr.txt
new file mode 100644 (file)
index 0000000..a7ac88e
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsPrivate.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPrivate.cmake
new file mode 100644 (file)
index 0000000..ebf9853
--- /dev/null
@@ -0,0 +1,13 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module-header
+  sources/cxx-anchor.cxx)
+target_sources(module-header
+  PRIVATE
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic-stderr.txt
new file mode 100644 (file)
index 0000000..a5b4ede
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModuleHeaderUnitsPublic.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModuleHeaderUnitsPublic.cmake
new file mode 100644 (file)
index 0000000..3dfccbb
--- /dev/null
@@ -0,0 +1,13 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module-header
+  sources/cxx-anchor.cxx)
+target_sources(module-header
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/module-header.h)
+target_compile_features(module-header
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
new file mode 100644 (file)
index 0000000..81a35e8
--- /dev/null
@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at FileSetModulesInterface.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetModulesInterface.cmake:2 \(target_sources\):
+  target_sources File set TYPEs "CXX_MODULES" and "CXX_MODULE_HEADER_UNITS"
+  may not have "INTERFACE" visibility
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake b/Tests/RunCMake/CXXModules/FileSetModulesInterface.cmake
new file mode 100644 (file)
index 0000000..24cec3e
--- /dev/null
@@ -0,0 +1,8 @@
+add_library(module)
+target_sources(module
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt
new file mode 100644 (file)
index 0000000..4420bbc
--- /dev/null
@@ -0,0 +1,6 @@
+CMake Warning \(dev\) at FileSetModulesInterfaceImported.cmake:2 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported.cmake
new file mode 100644 (file)
index 0000000..6640ae9
--- /dev/null
@@ -0,0 +1,8 @@
+add_library(module SHARED IMPORTED)
+target_sources(module
+  INTERFACE
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  INTERFACE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt
new file mode 100644 (file)
index 0000000..03e06cc
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModulesPrivate.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
new file mode 100644 (file)
index 0000000..ca18982
--- /dev/null
@@ -0,0 +1,12 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module)
+target_sources(module
+  PRIVATE
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt
new file mode 100644 (file)
index 0000000..0c110c3
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at FileSetModulesPublic.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
new file mode 100644 (file)
index 0000000..58de174
--- /dev/null
@@ -0,0 +1,12 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(module)
+target_sources(module
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(module
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/InstallBMI-check.cmake b/Tests/RunCMake/CXXModules/InstallBMI-check.cmake
new file mode 100644 (file)
index 0000000..f891c80
--- /dev/null
@@ -0,0 +1,24 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script)
+
+if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi" OR NOT CMAKE_INSTALL_COMPONENT\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find BMI install script component for `bmi`")
+endif ()
+
+if (NOT install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find BMI install script inclusion")
+endif ()
+
+if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi-optional"\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find BMI install script component for `bmi-optional`")
+endif ()
+
+if (NOT install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi-only-debug" OR NOT CMAKE_INSTALL_COMPONENT\)
+  if\(CMAKE_INSTALL_CONFIG_NAME MATCHES "\^\(\[Dd\]\[Ee\]\[Bb\]\[Uu\]\[Gg\]\)\$"\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find BMI install script component for `bmi-only-debug`")
+endif ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt
new file mode 100644 (file)
index 0000000..fc3c7db
--- /dev/null
@@ -0,0 +1,6 @@
+CMake Warning \(dev\) at InstallBMI.cmake:8 \(install\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/InstallBMI.cmake b/Tests/RunCMake/CXXModules/InstallBMI.cmake
new file mode 100644 (file)
index 0000000..f0947b4
--- /dev/null
@@ -0,0 +1,23 @@
+enable_language(CXX)
+
+add_library(install-bmi)
+target_sources(install-bmi
+  PRIVATE
+    sources/cxx-anchor.cxx)
+
+install(TARGETS install-bmi
+  CXX_MODULES_BMI
+    DESTINATION "lib/bmi"
+    COMPONENT "bmi")
+
+install(TARGETS install-bmi
+  CXX_MODULES_BMI
+    DESTINATION "lib/bmi"
+    EXCLUDE_FROM_ALL
+    COMPONENT "bmi-optional")
+
+install(TARGETS install-bmi
+  CXX_MODULES_BMI
+    DESTINATION "lib/bmi"
+    CONFIGURATIONS Debug
+    COMPONENT "bmi-only-debug")
diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-check.cmake
new file mode 100644 (file)
index 0000000..32a37ad
--- /dev/null
@@ -0,0 +1,8 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script)
+
+if (NOT install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-generic-args\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Could not find BMI install script inclusion")
+endif ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt
new file mode 100644 (file)
index 0000000..44c961f
--- /dev/null
@@ -0,0 +1,6 @@
+CMake Warning \(dev\) at InstallBMIGenericArgs.cmake:8 \(install\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs.cmake
new file mode 100644 (file)
index 0000000..8f17143
--- /dev/null
@@ -0,0 +1,9 @@
+enable_language(CXX)
+
+add_library(install-bmi-generic-args)
+target_sources(install-bmi-generic-args
+  PRIVATE
+    sources/cxx-anchor.cxx)
+
+install(TARGETS install-bmi-generic-args
+  DESTINATION "bin")
diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake b/Tests/RunCMake/CXXModules/InstallBMIIgnore-check.cmake
new file mode 100644 (file)
index 0000000..7d13ef0
--- /dev/null
@@ -0,0 +1,13 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script)
+
+if (install_script MATCHES [[\(CMAKE_INSTALL_COMPONENT STREQUAL "bmi" OR NOT CMAKE_INSTALL_COMPONENT\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Found BMI install script component for `bmi`")
+endif ()
+
+if (install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-ignore\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Found BMI install script inclusion")
+endif ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt
new file mode 100644 (file)
index 0000000..d9d2c2d
--- /dev/null
@@ -0,0 +1,6 @@
+CMake Warning \(dev\) at InstallBMIIgnore.cmake:5 \(install\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake b/Tests/RunCMake/CXXModules/InstallBMIIgnore.cmake
new file mode 100644 (file)
index 0000000..f339511
--- /dev/null
@@ -0,0 +1,9 @@
+enable_language(CXX)
+
+add_library(install-bmi-ignore INTERFACE)
+
+install(TARGETS install-bmi-ignore
+  CXX_MODULES_BMI
+    # An empty destination ignores BMI installation.
+    DESTINATION ""
+    COMPONENT "bmi")
diff --git a/Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake b/Tests/RunCMake/CXXModules/InstallBMINoGenericArgs-check.cmake
new file mode 100644 (file)
index 0000000..412e260
--- /dev/null
@@ -0,0 +1,8 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/cmake_install.cmake" install_script)
+
+if (install_script MATCHES [[include\("[^)]*/CMakeFiles/install-bmi-generic-args\.dir/install-cxx-module-bmi-[^.]*\.cmake" OPTIONAL\)]])
+  list(APPEND RunCMake_TEST_FAILED
+    "Found BMI install script inclusion")
+endif ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake
new file mode 100644 (file)
index 0000000..0d08c44
--- /dev/null
@@ -0,0 +1,34 @@
+include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake")
+
+if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(have_file 0)
+  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json")
+      continue ()
+    endif ()
+    set(have_file 1)
+
+    set(CMAKE_BUILD_TYPE "${config}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+  endforeach ()
+
+  if (NOT have_file)
+    list(APPEND RunCMake_TEST_FAILED
+      "No recognized build configurations found.")
+  endif ()
+else ()
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt
new file mode 100644 (file)
index 0000000..ebf7be5
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at NinjaDependInfoBMIInstall.cmake:14 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake
new file mode 100644 (file)
index 0000000..32dc42d
--- /dev/null
@@ -0,0 +1,76 @@
+# Fake out that we have dyndep; we only need to generate, not actually build
+# here.
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+enable_language(CXX)
+
+if (NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(FATAL_ERROR
+    "This test requires a 'Ninja' generator to be used.")
+endif ()
+
+add_library(ninja-bmi-install-public)
+target_sources(ninja-bmi-install-public
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-bmi-install-public
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET ninja-bmi-install-public
+  PROPERTY EXPORT_NAME "with-public")
+
+install(TARGETS ninja-bmi-install-public
+  FILE_SET modules
+    DESTINATION "lib/cxx"
+    COMPONENT "modules"
+  FILE_SET internal_partitions
+    DESTINATION "lib/cxx/internals"
+    COMPONENT "modules-internal"
+  CXX_MODULES_BMI
+    DESTINATION "lib/cxx/modules/$<CONFIG>"
+    COMPONENT "bmi")
+
+add_library(ninja-bmi-install-private)
+target_sources(ninja-bmi-install-private
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PRIVATE
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-bmi-install-private
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET ninja-bmi-install-private
+  PROPERTY EXPORT_NAME "with-private")
+
+set(CMAKE_INSTALL_MESSAGE LAZY)
+install(TARGETS ninja-bmi-install-private
+  CXX_MODULES_BMI
+    DESTINATION "lib/cxx/modules/private/$<CONFIG>"
+    PERMISSIONS
+      OWNER_READ OWNER_WRITE
+      GROUP_READ
+      WORLD_READ
+    COMPONENT "bmi")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake
new file mode 100644 (file)
index 0000000..7720257
--- /dev/null
@@ -0,0 +1,34 @@
+include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake")
+
+if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(have_file 0)
+  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json")
+      continue ()
+    endif ()
+    set(have_file 1)
+
+    set(CMAKE_BUILD_TYPE "${config}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+  endforeach ()
+
+  if (NOT have_file)
+    list(APPEND RunCMake_TEST_FAILED
+      "No recognized build configurations found.")
+  endif ()
+else ()
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt
new file mode 100644 (file)
index 0000000..e328223
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at NinjaDependInfoExport.cmake:14 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake
new file mode 100644 (file)
index 0000000..05e7ef7
--- /dev/null
@@ -0,0 +1,85 @@
+# Fake out that we have dyndep; we only need to generate, not actually build
+# here.
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+enable_language(CXX)
+
+if (NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(FATAL_ERROR
+    "This test requires a 'Ninja' generator to be used.")
+endif ()
+
+add_library(ninja-exports-public)
+target_sources(ninja-exports-public
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-exports-public
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET ninja-exports-public
+  PROPERTY EXPORT_NAME "with-public")
+
+install(TARGETS ninja-exports-public
+  EXPORT exp
+  FILE_SET modules
+    DESTINATION "lib/cxx"
+    COMPONENT "modules"
+  FILE_SET internal_partitions
+    DESTINATION "lib/cxx/internals"
+    COMPONENT "modules-internal")
+
+add_library(ninja-exports-private)
+target_sources(ninja-exports-private
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PRIVATE
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-exports-private
+  PRIVATE
+    cxx_std_20)
+set_property(TARGET ninja-exports-private
+  PROPERTY EXPORT_NAME "with-private")
+
+install(TARGETS ninja-exports-private
+  EXPORT exp)
+
+# Test multiple build exports.
+export(EXPORT exp
+  FILE "${CMAKE_BINARY_DIR}/lib/cmake/export1/export1-targets.cmake"
+  NAMESPACE export1::
+  CXX_MODULES_DIRECTORY "cxx-modules")
+export(EXPORT exp
+  FILE "${CMAKE_BINARY_DIR}/lib/cmake/export2/export2-targets.cmake"
+  CXX_MODULES_DIRECTORY "cxx-modules")
+
+# Test multiple install exports.
+install(EXPORT exp
+  DESTINATION "lib/cmake/export1"
+  NAMESPACE export1::
+  CXX_MODULES_DIRECTORY "cxx-modules")
+install(EXPORT exp
+  DESTINATION "lib/cmake/export2"
+  CXX_MODULES_DIRECTORY "cxx-modules")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake
new file mode 100644 (file)
index 0000000..b9a1315
--- /dev/null
@@ -0,0 +1,34 @@
+include("${CMAKE_CURRENT_LIST_DIR}/check-json.cmake")
+
+if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+  set(have_file 0)
+  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json")
+      continue ()
+    endif ()
+    set(have_file 1)
+
+    set(CMAKE_BUILD_TYPE "${config}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents)
+    check_json("${actual_contents}" "${expect_contents}")
+  endforeach ()
+
+  if (NOT have_file)
+    list(APPEND RunCMake_TEST_FAILED
+      "No recognized build configurations found.")
+  endif ()
+else ()
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+
+  file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/CXXDependInfo.json" actual_contents)
+  file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents)
+  check_json("${actual_contents}" "${expect_contents}")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt
new file mode 100644 (file)
index 0000000..ca430cc
--- /dev/null
@@ -0,0 +1,11 @@
+CMake Warning \(dev\) at NinjaDependInfoFileSet.cmake:14 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake
new file mode 100644 (file)
index 0000000..74e729e
--- /dev/null
@@ -0,0 +1,59 @@
+# Fake out that we have dyndep; we only need to generate, not actually build
+# here.
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+enable_language(CXX)
+
+if (NOT CMAKE_GENERATOR MATCHES "Ninja")
+  message(FATAL_ERROR
+    "This test requires a 'Ninja' generator to be used.")
+endif ()
+
+add_library(ninja-file-sets-public)
+target_sources(ninja-file-sets-public
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-file-sets-public
+  PRIVATE
+    cxx_std_20)
+
+install(TARGETS ninja-file-sets-public
+  FILE_SET modules
+    DESTINATION "lib/cxx"
+    COMPONENT "modules"
+  FILE_SET internal_partitions
+    DESTINATION "lib/cxx/internals"
+    COMPONENT "modules-internal")
+
+add_library(ninja-file-sets-private)
+target_sources(ninja-file-sets-private
+  PRIVATE
+    sources/module-impl.cxx
+    sources/module-internal-part-impl.cxx
+    sources/module-part-impl.cxx
+    sources/module-use.cxx
+  PRIVATE
+    FILE_SET modules TYPE CXX_MODULES
+    BASE_DIRS
+      "${CMAKE_CURRENT_SOURCE_DIR}/sources"
+    FILES
+      sources/module.cxx
+      sources/module-part.cxx
+    FILE_SET internal_partitions TYPE CXX_MODULES FILES
+      sources/module-internal-part.cxx)
+target_compile_features(ninja-file-sets-private
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/NoCXX-result.txt b/Tests/RunCMake/CXXModules/NoCXX-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt
new file mode 100644 (file)
index 0000000..aa7f406
--- /dev/null
@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX.cmake:4 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
+  been enabled
+
+(
+CMake Error in CMakeLists.txt:
+(  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
+  been enabled
+|  The "nocxx" target contains C\+\+ module sources which are not supported by
+  the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX.cmake b/Tests/RunCMake/CXXModules/NoCXX.cmake
new file mode 100644 (file)
index 0000000..3c46f9d
--- /dev/null
@@ -0,0 +1,9 @@
+enable_language(C)
+
+add_library(nocxx)
+target_sources(nocxx
+  PRIVATE
+    sources/c-anchor.c
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20-result.txt b/Tests/RunCMake/CXXModules/NoCXX20-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
new file mode 100644 (file)
index 0000000..95d73b1
--- /dev/null
@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX20.cmake:4 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "nocxx20" target has C\+\+ module sources but is not using at least
+  "cxx_std_20"
+
+(
+CMake Error in CMakeLists.txt:
+(  The "nocxx20" target has C\+\+ module sources but is not using at least
+  "cxx_std_20"
+|  The "nocxx20" target contains C\+\+ module sources which are not supported by
+  the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20.cmake b/Tests/RunCMake/CXXModules/NoCXX20.cmake
new file mode 100644 (file)
index 0000000..b7372e8
--- /dev/null
@@ -0,0 +1,11 @@
+enable_language(CXX)
+
+add_library(nocxx20)
+target_sources(nocxx20
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+set_target_properties(nocxx20
+  PROPERTIES
+  CXX_STANDARD 17
+  CXX_STANDARD_REQUIRED ON)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt
new file mode 100644 (file)
index 0000000..aa99af0
--- /dev/null
@@ -0,0 +1,20 @@
+CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  The "noexperimentalflag" target has C\+\+ module sources but its experimental
+  support has not been requested
+
+(
+CMake Error in CMakeLists.txt:
+(  The "noexperimentalflag" target has C\+\+ module sources but its experimental
+  support has not been requested
+|  The "noexperimentalflag" target contains C\+\+ module sources which are not
+  supported by the generator
+)
+)*
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake
new file mode 100644 (file)
index 0000000..79892ee
--- /dev/null
@@ -0,0 +1,12 @@
+enable_language(CXX)
+
+unset(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP)
+
+add_library(noexperimentalflag)
+target_sources(noexperimentalflag
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(noexperimentalflag
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
new file mode 100644 (file)
index 0000000..52f781f
--- /dev/null
@@ -0,0 +1,30 @@
+CMake Warning \(dev\) at NoDyndepSupport.cmake:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+(CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error:
+  The Ninja generator does not support C\+\+20 modules using Ninja version
+
+    .*
+
+  due to lack of required features.  Ninja 1.10 or higher is required.
+
+|CMake Error in CMakeLists.txt:
+  The "nodyndep" target contains C\+\+ module sources which are not supported
+  by the generator
+
+(
+CMake Error in CMakeLists.txt:
+  The "nodyndep" target contains C\+\+ module sources which are not supported
+  by the generator
+
+)*)
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
new file mode 100644 (file)
index 0000000..0954400
--- /dev/null
@@ -0,0 +1,16 @@
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+if (NOT CMAKE_CXX_STANDARD_DEFAULT)
+  set(CMAKE_CXX_STANDARD_DEFAULT "11")
+endif ()
+
+add_library(nodyndep)
+target_sources(nodyndep
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/module.cxx)
+target_compile_features(nodyndep
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits-stderr.txt
new file mode 100644 (file)
index 0000000..a93eb40
--- /dev/null
@@ -0,0 +1,22 @@
+CMake Warning \(dev\) at NotCXXSourceModuleHeaderUnits.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  Target "not-cxx-source" contains the source
+
+    .*/Tests/RunCMake/CXXModules/sources/c-anchor.c
+
+  in a file set of type "CXX_MODULE_HEADER_UNITS" but the source is not
+  classified as a "CXX" source.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModuleHeaderUnits.cmake
new file mode 100644 (file)
index 0000000..af4ddac
--- /dev/null
@@ -0,0 +1,15 @@
+enable_language(C)
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(not-cxx-source)
+target_sources(not-cxx-source
+  PRIVATE
+    sources/cxx-anchor.cxx
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULE_HEADER_UNITS FILES
+      sources/c-anchor.c)
+target_compile_features(not-cxx-source
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
new file mode 100644 (file)
index 0000000..d341c1f
--- /dev/null
@@ -0,0 +1,17 @@
+CMake Warning \(dev\) at NotCXXSourceModules.cmake:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:6 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error in CMakeLists.txt:
+  Target "not-cxx-source" contains the source
+
+    .*/Tests/RunCMake/CXXModules/sources/c-anchor.c
+
+  in a file set of type "CXX_MODULES" but the source is not classified as a
+  "CXX" source.
+
+
+CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
new file mode 100644 (file)
index 0000000..f7a6060
--- /dev/null
@@ -0,0 +1,13 @@
+enable_language(C)
+enable_language(CXX)
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
+set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+
+add_library(not-cxx-source)
+target_sources(not-cxx-source
+  PUBLIC
+    FILE_SET fs TYPE CXX_MODULES FILES
+      sources/c-anchor.c)
+target_compile_features(not-cxx-source
+  PRIVATE
+    cxx_std_20)
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..3f17c1f
--- /dev/null
@@ -0,0 +1,170 @@
+include(RunCMake)
+
+# For `if (IN_LIST)`
+cmake_policy(SET CMP0057 NEW)
+
+run_cmake(compiler_introspection)
+include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake")
+
+# Test negative cases where C++20 modules do not work.
+run_cmake(NoCXX)
+if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+  # This test requires that the compiler be told to compile in an older-than-20
+  # standard. If the compiler forces a standard to be used, skip it.
+  if (NOT forced_cxx_standard)
+    run_cmake(NoCXX20)
+  endif ()
+
+  # This test uses C++20, but another prerequisite is missing, so forced
+  # standards don't matter.
+  run_cmake(NoCXX20ModuleFlag)
+endif ()
+
+if (RunCMake_GENERATOR MATCHES "Ninja")
+  execute_process(
+    COMMAND "${CMAKE_MAKE_PROGRAM}" --version
+    RESULT_VARIABLE res
+    OUTPUT_VARIABLE ninja_version
+    ERROR_VARIABLE err
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    ERROR_STRIP_TRAILING_WHITESPACE)
+
+  if (res)
+    message(WARNING
+      "Failed to determine `ninja` version: ${err}")
+    set(ninja_version "0")
+  endif ()
+endif ()
+
+# Test behavior when the generator does not support C++20 modules.
+if (NOT RunCMake_GENERATOR MATCHES "Ninja" OR
+    ninja_version VERSION_LESS "1.10" OR
+    NOT "cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+  if ("cxx_std_20" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
+    run_cmake(NoDyndepSupport)
+  endif ()
+
+  # Bail; the remaining tests require the generator to successfully generate
+  # with C++20 modules in the source list.
+  return ()
+endif ()
+
+set(fileset_types
+  Modules
+  ModuleHeaderUnits)
+set(scopes
+  Interface
+  Private
+  Public)
+foreach (fileset_type IN LISTS fileset_types)
+  foreach (scope IN LISTS scopes)
+    run_cmake("FileSet${fileset_type}${scope}")
+  endforeach ()
+  run_cmake("FileSet${fileset_type}InterfaceImported")
+
+  # Test the error message when a non-C++ source file is found in the source
+  # list.
+  run_cmake("NotCXXSource${fileset_type}")
+endforeach ()
+
+run_cmake(InstallBMI)
+run_cmake(InstallBMIGenericArgs)
+run_cmake(InstallBMIIgnore)
+
+run_cmake(ExportBuildCxxModules)
+run_cmake(ExportInstallCxxModules)
+
+# Generator-specific tests.
+if (RunCMake_GENERATOR MATCHES "Ninja")
+  run_cmake(NinjaDependInfoFileSet)
+  run_cmake(NinjaDependInfoExport)
+  run_cmake(NinjaDependInfoBMIInstall)
+else ()
+  message(FATAL_ERROR
+    "Please add 'DependInfo' tests for the '${RunCMake_GENERATOR}' generator.")
+endif ()
+
+# Actual compilation tests.
+if (NOT CMake_TEST_MODULE_COMPILATION)
+  return ()
+endif ()
+
+function (run_cxx_module_test directory)
+  set(test_name "${directory}")
+  if (NOT ARGN STREQUAL "")
+    list(POP_FRONT ARGN test_name)
+  endif ()
+
+  set(RunCMake_TEST_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/examples/${directory}")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/examples/${test_name}-build")
+
+  if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(RunCMake_TEST_OPTIONS -DCMAKE_CONFIGURATION_TYPES=Debug)
+  else ()
+    set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
+  endif ()
+
+  if (RunCMake_CXXModules_INSTALL)
+    set(prefix "${RunCMake_BINARY_DIR}/examples/${test_name}-install")
+    file(REMOVE_RECURSE "${prefix}")
+    list(APPEND RunCMake_TEST_OPTIONS
+      "-DCMAKE_INSTALL_PREFIX=${prefix}")
+  endif ()
+
+  list(APPEND RunCMake_TEST_OPTIONS
+    "-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}"
+    ${ARGN})
+  run_cmake("examples/${test_name}")
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command("examples/${test_name}-build" "${CMAKE_COMMAND}" --build . --config Debug)
+  if (RunCMake_CXXModules_INSTALL)
+    run_cmake_command("examples/${test_name}-install" "${CMAKE_COMMAND}" --build . --target install --config Debug)
+  endif ()
+  run_cmake_command("examples/${test_name}-test" "${CMAKE_CTEST_COMMAND}" -C Debug --output-on-failure)
+endfunction ()
+
+string(REPLACE "," ";" CMake_TEST_MODULE_COMPILATION "${CMake_TEST_MODULE_COMPILATION}")
+
+# Tests which use named modules.
+if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(simple)
+  run_cxx_module_test(library library-static -DBUILD_SHARED_LIBS=OFF)
+  run_cxx_module_test(generated)
+  run_cxx_module_test(public-req-private)
+  run_cxx_module_test(deep-chain)
+endif ()
+
+# Tests which use named modules in shared libraries.
+if ("shared" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(library library-shared -DBUILD_SHARED_LIBS=ON)
+endif ()
+
+# Tests which use partitions.
+if ("partitions" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(partitions)
+endif ()
+
+# Tests which use internal partitions.
+if ("internal_partitions" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(internal-partitions)
+endif ()
+
+# Tests which install BMIs
+if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(export-interface-build)
+  run_cxx_module_test(export-bmi-and-interface-build)
+endif ()
+
+# All of the following tests perform installation.
+set(RunCMake_CXXModules_INSTALL 1)
+
+# Tests which install BMIs
+if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
+  run_cxx_module_test(install-bmi)
+  run_cxx_module_test(install-bmi-and-interfaces)
+
+  if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
+    run_cxx_module_test(export-interface-install)
+    run_cxx_module_test(export-bmi-and-interface-install)
+  endif ()
+endif ()
diff --git a/Tests/RunCMake/CXXModules/check-json.cmake b/Tests/RunCMake/CXXModules/check-json.cmake
new file mode 100644 (file)
index 0000000..19d0c8a
--- /dev/null
@@ -0,0 +1,160 @@
+cmake_policy(PUSH)
+cmake_policy(SET CMP0057 NEW)
+
+function (json_placeholders in out)
+  string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" in "${in}")
+  if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    string(REPLACE "<CONFIG_DIR>" "${CMAKE_BUILD_TYPE}/" in "${in}")
+  else ()
+    string(REPLACE "<CONFIG_DIR>" "" in "${in}")
+  endif ()
+  if (CMAKE_BUILD_TYPE)
+    string(REPLACE "<CONFIG_FORCE>" "${CMAKE_BUILD_TYPE}" in "${in}")
+  else ()
+    string(REPLACE "<CONFIG_FORCE>" "noconfig" in "${in}")
+  endif ()
+  string(REPLACE "<SOURCE_DIR>" "${RunCMake_SOURCE_DIR}" in "${in}")
+  string(REPLACE "<BINARY_DIR>" "${RunCMake_TEST_BINARY_DIR}" in "${in}")
+  set("${out}" "${in}" PARENT_SCOPE)
+endfunction ()
+
+function (check_json_value path actual_type expect_type actual_value expect_value)
+  if (NOT actual_type STREQUAL expect_type)
+    list(APPEND RunCMake_TEST_FAILED
+      "Type mismatch at ${path}: ${actual_type} vs. ${expect_type}")
+    return ()
+  endif ()
+
+  if (actual_type STREQUAL NULL)
+    # Nothing to check
+  elseif (actual_type STREQUAL BOOLEAN)
+    if (NOT actual_value STREQUAL expect_value)
+      list(APPEND RunCMake_TEST_FAILED
+        "Boolean mismatch at ${path}: ${actual_value} vs. ${expect_value}")
+    endif ()
+  elseif (actual_type STREQUAL NUMBER)
+    if (NOT actual_value EQUAL expect_value)
+      list(APPEND RunCMake_TEST_FAILED
+        "Number mismatch at ${path}: ${actual_value} vs. ${expect_value}")
+    endif ()
+  elseif (actual_type STREQUAL STRING)
+    # Allow some values to be ignored.
+    if (expect_value STREQUAL "<IGNORE>")
+      return ()
+    endif ()
+
+    json_placeholders("${expect_value}" expect_value_expanded)
+    if (NOT actual_value STREQUAL expect_value_expanded)
+      list(APPEND RunCMake_TEST_FAILED
+        "String mismatch at ${path}: ${actual_value} vs. ${expect_value_expanded}")
+    endif ()
+  elseif (actual_type STREQUAL ARRAY)
+    check_json_array("${path}" "${actual_value}" "${expect_value}")
+  elseif (actual_type STREQUAL OBJECT)
+    check_json_object("${path}" "${actual_value}" "${expect_value}")
+  endif ()
+endfunction ()
+
+# Check that two arrays are the same.
+function (check_json_array path actual expect)
+  string(JSON actual_len LENGTH "${actual}")
+  string(JSON expect_len LENGTH "${expect}")
+
+  set(iter_len "${actual_len}")
+  if (actual_len LESS expect_len)
+    list(APPEND RunCMake_TEST_FAILED
+      "Missing array items at ${path}")
+  elseif (expect_len LESS actual_len)
+    list(APPEND RunCMake_TEST_FAILED
+      "Extra array items at ${path}")
+    set(iter_len "${expect_len}")
+  endif ()
+
+  foreach (idx RANGE "${iter_len}")
+    if (idx EQUAL iter_len)
+      break ()
+    endif ()
+
+    set(new_path "${path}[${idx}]")
+    string(JSON actual_type TYPE "${actual}" "${idx}")
+    string(JSON expect_type TYPE "${expect}" "${idx}")
+    string(JSON actual_value GET "${actual}" "${idx}")
+    string(JSON expect_value GET "${expect}" "${idx}")
+    check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
+  endforeach ()
+endfunction ()
+
+# Check that two inner objects are the same.
+function (check_json_object path actual expect)
+  string(JSON actual_len LENGTH "${actual}")
+  string(JSON expect_len LENGTH "${expect}")
+
+  set(actual_keys "")
+  set(expect_keys "")
+  foreach (idx RANGE "${actual_len}")
+    if (idx EQUAL actual_len)
+      break ()
+    endif ()
+
+    string(JSON actual_key MEMBER "${actual}" "${idx}")
+    list(APPEND actual_keys "${actual_key}")
+  endforeach ()
+  foreach (idx RANGE "${expect_len}")
+    if (idx EQUAL expect_len)
+      break ()
+    endif ()
+
+    string(JSON expect_key MEMBER "${expect}" "${idx}")
+    list(APPEND expect_keys "${expect_key}")
+  endforeach ()
+
+  json_placeholders("${expect_keys}" expect_keys_expanded)
+
+  set(actual_keys_missed "${actual_keys}")
+  set(expect_keys_missed "${expect_keys}")
+
+  set(common_keys "")
+  set(expect_keys_stack "${expect_keys}")
+  while (expect_keys_stack)
+    list(POP_BACK expect_keys_stack expect_key)
+    json_placeholders("${expect_key}" expect_key_expanded)
+
+    if (expect_key_expanded IN_LIST actual_keys_missed AND
+        expect_key IN_LIST expect_keys_missed)
+      list(APPEND common_keys "${expect_key}")
+    endif ()
+
+    list(REMOVE_ITEM actual_keys_missed "${expect_key_expanded}")
+    list(REMOVE_ITEM expect_keys_missed "${expect_key}")
+  endwhile ()
+
+  if (actual_keys_missed)
+    string(REPLACE ";" ", " actual_keys_missed_text "${actual_keys_missed}")
+    list(APPEND RunCMake_TEST_FAILED
+      "Missing expected members at ${path}: ${actual_keys_missed_text}")
+  endif ()
+  if (expect_keys_missed)
+    string(REPLACE ";" ", " expect_keys_missed_text "${expect_keys_missed}")
+    list(APPEND RunCMake_TEST_FAILED
+      "Extra unexpected members at ${path}: ${expect_keys_missed_text}")
+  endif ()
+
+  foreach (key IN LISTS common_keys)
+    json_placeholders("${key}" key_expanded)
+    set(new_path "${path}.${key_expanded}")
+    string(JSON actual_type TYPE "${actual}" "${key_expanded}")
+    string(JSON expect_type TYPE "${expect}" "${key}")
+    string(JSON actual_value GET "${actual}" "${key_expanded}")
+    string(JSON expect_value GET "${expect}" "${key}")
+    check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
+  endforeach ()
+endfunction ()
+
+# Check that two JSON objects are the same.
+function (check_json actual expect)
+  check_json_object("" "${actual}" "${expect}")
+endfunction ()
+
+string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
+
+cmake_policy(POP)
diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/compiler_introspection.cmake
new file mode 100644 (file)
index 0000000..7a2df3d
--- /dev/null
@@ -0,0 +1,25 @@
+enable_language(CXX)
+
+set(info "")
+
+# See `Modules/Compiler/MSVC-CXX.cmake` for this. If there is explicitly no
+# default, the feature list is populated to be everything.
+if (DEFINED CMAKE_CXX_STANDARD_DEFAULT AND
+    CMAKE_CXX_STANDARD_DEFAULT STREQUAL "")
+  set(CMAKE_CXX_COMPILE_FEATURES "")
+endif ()
+
+# Detect if the environment forces a C++ standard, let the test selection know.
+set(forced_cxx_standard 0)
+if (CMAKE_CXX_FLAGS MATCHES "-std=")
+  set(forced_cxx_standard 1)
+endif ()
+
+# Forward information about the C++ compile features.
+string(APPEND info "\
+set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
+set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
+set(forced_cxx_standard \"${forced_cxx_standard}\")
+")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi-and-interfaces.cmake
new file mode 100644 (file)
index 0000000..f99455b
--- /dev/null
@@ -0,0 +1,22 @@
+function (check_for_bmi prefix destination name)
+  set(found 0)
+  foreach (ext IN ITEMS gcm)
+    if (EXISTS "${prefix}/${destination}/${name}.${ext}")
+      set(found 1)
+      break ()
+    endif ()
+  endforeach ()
+
+  if (NOT found)
+    message(SEND_ERROR
+      "Failed to find the ${name} BMI")
+  endif ()
+endfunction ()
+
+function (check_for_interface prefix destination subdir name)
+  set(found 0)
+  if (NOT EXISTS "${prefix}/${destination}/${subdir}/${name}")
+    message(SEND_ERROR
+      "Failed to find the ${name} module interface")
+  endif ()
+endfunction ()
diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-find-bmi.cmake
new file mode 100644 (file)
index 0000000..91f3995
--- /dev/null
@@ -0,0 +1,27 @@
+function (check_for_bmi prefix destination name)
+  set(found 0)
+  foreach (ext IN ITEMS gcm ifc)
+    if (EXISTS "${prefix}/${destination}/${name}.${ext}")
+      set(found 1)
+      break ()
+    endif ()
+  endforeach ()
+
+  if (NOT found)
+    message(SEND_ERROR
+      "Failed to find the ${name} BMI")
+  endif ()
+endfunction ()
+
+function (check_for_interface prefix destination subdir name)
+  set(found 0)
+  if (NOT EXISTS "${prefix}/${destination}/${subdir}/${name}")
+    message(SEND_ERROR
+      "Failed to find the ${name} module interface")
+  endif ()
+endfunction ()
+
+function (report_dirs prefix destination)
+  message("prefix: ${prefix}")
+  message("destination: ${destination}")
+endfunction ()
diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
new file mode 100644 (file)
index 0000000..381094e
--- /dev/null
@@ -0,0 +1,18 @@
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+if (NOT EXISTS "${CMake_TEST_MODULE_COMPILATION_RULES}")
+  message(FATAL_ERROR
+    "The `CMake_TEST_MODULE_COMPILATION_RULES` file must be specified "
+    "for these tests to operate.")
+endif ()
+
+include("${CMake_TEST_MODULE_COMPILATION_RULES}")
+
+if (NOT CMake_TEST_CXXModules_UUID STREQUAL "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
+  message(FATAL_ERROR
+    "The compilation rule file needs updated for changes in the test "
+    "suite. Please see the history for what needs to be updated.")
+endif ()
+
+include(CTest)
+enable_testing()
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt
new file mode 100644 (file)
index 0000000..515b240
--- /dev/null
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_deep_chain CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(a STATIC)
+target_sources(a
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        a.cxx)
+target_compile_features(a PUBLIC cxx_std_20)
+
+add_library(b STATIC)
+target_sources(b
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        b.cxx)
+target_compile_features(b PUBLIC cxx_std_20)
+target_link_libraries(b PUBLIC a)
+
+add_library(c STATIC)
+target_sources(c
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        c.cxx)
+target_compile_features(c PUBLIC cxx_std_20)
+target_link_libraries(c PUBLIC b)
+
+add_library(d STATIC)
+target_sources(d
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        d.cxx)
+target_compile_features(d PUBLIC cxx_std_20)
+target_link_libraries(d PUBLIC c)
+
+add_library(e STATIC)
+target_sources(e
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        e.cxx)
+target_compile_features(e PUBLIC cxx_std_20)
+target_link_libraries(e PUBLIC d)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE e)
+target_sources(exe
+  PRIVATE
+    main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/a.cxx
new file mode 100644 (file)
index 0000000..9edaec9
--- /dev/null
@@ -0,0 +1,6 @@
+export module a;
+
+export int a()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/b.cxx
new file mode 100644 (file)
index 0000000..38ab0c2
--- /dev/null
@@ -0,0 +1,7 @@
+export module b;
+import a;
+
+export int b()
+{
+  return a();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/c.cxx
new file mode 100644 (file)
index 0000000..580a458
--- /dev/null
@@ -0,0 +1,7 @@
+export module c;
+import b;
+
+export int c()
+{
+  return b();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/d.cxx
new file mode 100644 (file)
index 0000000..78bc5ba
--- /dev/null
@@ -0,0 +1,7 @@
+export module d;
+import c;
+
+export int d()
+{
+  return c();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/e.cxx
new file mode 100644 (file)
index 0000000..e019440
--- /dev/null
@@ -0,0 +1,7 @@
+export module e;
+import d;
+
+export int e()
+{
+  return d();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx b/Tests/RunCMake/CXXModules/examples/deep-chain/main.cxx
new file mode 100644 (file)
index 0000000..0b7c15d
--- /dev/null
@@ -0,0 +1,6 @@
+import e;
+
+int main(int argc, char* argv[])
+{
+  return e();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a450b7e
--- /dev/null
@@ -0,0 +1,56 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_export_bmi_and_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_bmi_and_interfaces STATIC)
+target_sources(export_bmi_and_interfaces
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_bmi_and_interfaces PUBLIC cxx_std_20)
+
+install(TARGETS export_bmi_and_interfaces
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu"
+  CXX_MODULES_BMI DESTINATION "lib/cxx/bmi")
+export(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-targets.cmake"
+  CXX_MODULES_DIRECTORY "export_bmi_and_interfaces-cxx-modules")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_bmi_and_interfaces-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_bmi_and_interfaces_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_source_dir=${CMAKE_CURRENT_SOURCE_DIR}"
+    "-Dexpected_binary_dir=${CMAKE_CURRENT_BINARY_DIR}"
+    "-Dexport_bmi_and_interfaces_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/forward.cxx
new file mode 100644 (file)
index 0000000..7f53271
--- /dev/null
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/importable.cxx
new file mode 100644 (file)
index 0000000..e0b1872
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/private.cxx
new file mode 100644 (file)
index 0000000..c5b719a
--- /dev/null
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b814b3b
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+find_package(export_bmi_and_interfaces REQUIRED)
+
+if (NOT TARGET CXXModules::export_bmi_and_interfaces)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(file_sets TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY INTERFACE_CXX_MODULE_SETS)
+if (NOT file_sets STREQUAL "modules")
+  message(FATAL_ERROR
+    "Incorrect exported file sets in `CXXModules::export_bmi_and_interfaces`: `${file_sets}`")
+endif ()
+
+get_property(file_set_files TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY CXX_MODULE_SET_modules)
+if (NOT file_set_files STREQUAL "${expected_source_dir}/importable.cxx")
+  message(FATAL_ERROR
+    "Incorrect exported file set paths in CXXModules::export_bmi_and_interfaces`: `${file_set_files}`")
+endif ()
+
+get_property(imported_modules TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY IMPORTED_CXX_MODULES_DEBUG)
+if (NOT imported_modules MATCHES "importable=${expected_source_dir}/importable.cxx,${expected_binary_dir}/CMakeFiles/export_bmi_and_interfaces.dir(/Debug)?/importable.(gcm|pcm|ifc)")
+  message(FATAL_ERROR
+    "Incorrect exported modules in CXXModules::export_bmi_and_interfaces`: `${imported_modules}`")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a5574fe
--- /dev/null
@@ -0,0 +1,59 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_export_bmi_and_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_bmi_and_interfaces STATIC)
+target_sources(export_bmi_and_interfaces
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_bmi_and_interfaces PUBLIC cxx_std_20)
+
+install(TARGETS export_bmi_and_interfaces
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu"
+  CXX_MODULES_BMI DESTINATION "lib/cxx/bmi")
+install(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  DESTINATION "lib/cmake/export_bmi_and_interfaces"
+  FILE "export_bmi_and_interfaces-targets.cmake"
+  CXX_MODULES_DIRECTORY "export_bmi_and_interfaces-cxx-modules")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_bmi_and_interfaces-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_bmi_and_interfaces-config.cmake"
+  DESTINATION "lib/cmake/export_bmi_and_interfaces")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_bmi_and_interfaces_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_source_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu"
+    "-Dexpected_binary_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/bmi"
+    "-Dexport_bmi_and_interfaces_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_bmi_and_interfaces"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/forward.cxx
new file mode 100644 (file)
index 0000000..7f53271
--- /dev/null
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/importable.cxx
new file mode 100644 (file)
index 0000000..e0b1872
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/private.cxx
new file mode 100644 (file)
index 0000000..c5b719a
--- /dev/null
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..db0484d
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+find_package(export_bmi_and_interfaces REQUIRED)
+
+if (NOT TARGET CXXModules::export_bmi_and_interfaces)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(file_sets TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY INTERFACE_CXX_MODULE_SETS)
+if (NOT file_sets STREQUAL "modules")
+  message(FATAL_ERROR
+    "Incorrect exported file sets in `CXXModules::export_bmi_and_interfaces`: `${file_sets}`")
+endif ()
+
+get_property(file_set_files TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY CXX_MODULE_SET_modules)
+if (NOT file_set_files STREQUAL "${expected_source_dir}/importable.cxx")
+  message(FATAL_ERROR
+    "Incorrect exported file set paths in CXXModules::export_bmi_and_interfaces`: `${file_set_files}`")
+endif ()
+
+get_property(imported_modules TARGET CXXModules::export_bmi_and_interfaces
+  PROPERTY IMPORTED_CXX_MODULES_DEBUG)
+if (NOT imported_modules MATCHES "importable=${expected_source_dir}/importable.cxx,${expected_binary_dir}/importable.(gcm|pcm|ifc)")
+  message(FATAL_ERROR
+    "Incorrect exported modules in CXXModules::export_bmi_and_interfaces`: `${imported_modules}`")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt
new file mode 100644 (file)
index 0000000..80ddaf8
--- /dev/null
@@ -0,0 +1,53 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_export_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_interfaces STATIC)
+target_sources(export_interfaces
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_interfaces PUBLIC cxx_std_20)
+
+install(TARGETS export_interfaces
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+export(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-targets.cmake")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interfaces-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_interfaces_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}"
+    "-Dexport_interfaces_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/forward.cxx
new file mode 100644 (file)
index 0000000..7f53271
--- /dev/null
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/importable.cxx
new file mode 100644 (file)
index 0000000..e0b1872
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-build/private.cxx
new file mode 100644 (file)
index 0000000..c5b719a
--- /dev/null
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6145210
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+find_package(export_interfaces REQUIRED)
+
+if (NOT TARGET CXXModules::export_interfaces)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(file_sets TARGET CXXModules::export_interfaces
+  PROPERTY INTERFACE_CXX_MODULE_SETS)
+if (NOT file_sets STREQUAL "modules")
+  message(FATAL_ERROR
+    "Incorrect exported file sets in `CXXModules::export_interfaces`: `${file_sets}`")
+endif ()
+
+get_property(file_set_files TARGET CXXModules::export_interfaces
+  PROPERTY CXX_MODULE_SET_modules)
+if (NOT file_set_files STREQUAL "${expected_dir}/importable.cxx")
+  message(FATAL_ERROR
+    "Incorrect exported file set paths in CXXModules::export_interfaces`: `${file_set_files}`")
+endif ()
+
+get_property(imported_modules_set TARGET CXXModules::export_interfaces
+  PROPERTY IMPORTED_CXX_MODULES_DEBUG SET)
+if (imported_modules_set)
+  message(FATAL_ERROR
+    "Unexpected C++ modules specified.")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1dfb6da
--- /dev/null
@@ -0,0 +1,56 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_export_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_interfaces STATIC)
+target_sources(export_interfaces
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_interfaces PUBLIC cxx_std_20)
+
+install(TARGETS export_interfaces
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+install(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  DESTINATION "lib/cmake/export_interfaces"
+  FILE "export_interfaces-targets.cmake")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_interfaces-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_interfaces-config.cmake"
+  DESTINATION "lib/cmake/export_interfaces")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_interfaces_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu"
+    "-Dexport_interfaces_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_interfaces"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/forward.cxx
new file mode 100644 (file)
index 0000000..7f53271
--- /dev/null
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/importable.cxx
new file mode 100644 (file)
index 0000000..e0b1872
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable;
+
+int forwarding();
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-interface-install/private.cxx
new file mode 100644 (file)
index 0000000..c5b719a
--- /dev/null
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6145210
--- /dev/null
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library NONE)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+find_package(export_interfaces REQUIRED)
+
+if (NOT TARGET CXXModules::export_interfaces)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(file_sets TARGET CXXModules::export_interfaces
+  PROPERTY INTERFACE_CXX_MODULE_SETS)
+if (NOT file_sets STREQUAL "modules")
+  message(FATAL_ERROR
+    "Incorrect exported file sets in `CXXModules::export_interfaces`: `${file_sets}`")
+endif ()
+
+get_property(file_set_files TARGET CXXModules::export_interfaces
+  PROPERTY CXX_MODULE_SET_modules)
+if (NOT file_set_files STREQUAL "${expected_dir}/importable.cxx")
+  message(FATAL_ERROR
+    "Incorrect exported file set paths in CXXModules::export_interfaces`: `${file_set_files}`")
+endif ()
+
+get_property(imported_modules_set TARGET CXXModules::export_interfaces
+  PROPERTY IMPORTED_CXX_MODULES_DEBUG SET)
+if (imported_modules_set)
+  message(FATAL_ERROR
+    "Unexpected C++ modules specified.")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt
new file mode 100644 (file)
index 0000000..b9bbf34
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:12 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
new file mode 100644 (file)
index 0000000..73f7ff7
--- /dev/null
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_generated CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+configure_file(
+  "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in"
+  "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx"
+  COPYONLY)
+
+add_executable(generated)
+target_sources(generated
+  PRIVATE
+    main.cxx
+  PRIVATE
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_BINARY_DIR}"
+      FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx")
+target_compile_features(generated PUBLIC cxx_std_20)
+
+add_test(NAME generated COMMAND generated)
diff --git a/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in b/Tests/RunCMake/CXXModules/examples/generated/importable.cxx.in
new file mode 100644 (file)
index 0000000..a9287d7
--- /dev/null
@@ -0,0 +1,5 @@
+export module importable;
+
+export int from_import() {
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/generated/main.cxx b/Tests/RunCMake/CXXModules/examples/generated/main.cxx
new file mode 100644 (file)
index 0000000..feb38d2
--- /dev/null
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt
new file mode 100644 (file)
index 0000000..efaca0e
--- /dev/null
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_install_bmi_and_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(install_bmi_and_interfaces STATIC)
+target_sources(install_bmi_and_interfaces
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(install_bmi_and_interfaces PUBLIC cxx_std_20)
+
+install(TARGETS install_bmi_and_interfaces
+  ARCHIVE DESTINATION "lib"
+  CXX_MODULES_BMI DESTINATION "lib/cxx/bmi"
+  FILE_SET CXX_MODULES DESTINATION "lib/cxx/miu")
+
+add_test(NAME check-for-bmi
+  COMMAND
+    "${CMAKE_COMMAND}"
+      "-Dprefix=${CMAKE_INSTALL_PREFIX}"
+      "-Dbmi_destination=lib/cxx/bmi"
+      "-Dfs_destination=lib/cxx/miu"
+      -P "${CMAKE_CURRENT_SOURCE_DIR}/check-for-bmi.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/check-for-bmi.cmake
new file mode 100644 (file)
index 0000000..a8ff1ad
--- /dev/null
@@ -0,0 +1,7 @@
+include("${CMAKE_CURRENT_LIST_DIR}/../cxx-modules-find-bmi.cmake")
+
+report_dirs("${prefix}" "${bmi_destination}")
+check_for_bmi("${prefix}" "${bmi_destination}" importable)
+
+report_dirs("${prefix}" "${fs_destination}")
+check_for_interface("${prefix}" "${fs_destination}" "" importable.cxx)
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/importable.cxx
new file mode 100644 (file)
index 0000000..607680a
--- /dev/null
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4e039f9
--- /dev/null
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_install_bmi CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(install_bmi STATIC)
+target_sources(install_bmi
+  PUBLIC
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(install_bmi PUBLIC cxx_std_20)
+
+install(TARGETS install_bmi
+  ARCHIVE DESTINATION "lib"
+  CXX_MODULES_BMI DESTINATION "lib/cxx/bmi")
+
+add_test(NAME check-for-bmi
+  COMMAND
+    "${CMAKE_COMMAND}"
+      "-Dprefix=${CMAKE_INSTALL_PREFIX}"
+      "-Ddestination=lib/cxx/bmi"
+      -P "${CMAKE_CURRENT_SOURCE_DIR}/check-for-bmi.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake b/Tests/RunCMake/CXXModules/examples/install-bmi/check-for-bmi.cmake
new file mode 100644 (file)
index 0000000..ff84ed6
--- /dev/null
@@ -0,0 +1,4 @@
+include("${CMAKE_CURRENT_LIST_DIR}/../cxx-modules-find-bmi.cmake")
+
+report_dirs("${prefix}" "${destination}")
+check_for_bmi("${prefix}" "${destination}" importable)
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx b/Tests/RunCMake/CXXModules/examples/install-bmi/importable.cxx
new file mode 100644 (file)
index 0000000..607680a
--- /dev/null
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt
new file mode 100644 (file)
index 0000000..4652aec
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f5e9d94
--- /dev/null
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_internal_partitions CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(internal-partitions)
+generate_export_header(internal-partitions)
+target_sources(internal-partitions
+  PUBLIC
+    FILE_SET HEADERS
+      BASE_DIRS
+        "${CMAKE_CURRENT_BINARY_DIR}"
+      FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/internal-partitions_export.h"
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx
+        partition.cxx)
+target_compile_features(internal-partitions PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE internal-partitions)
+target_sources(exe
+  PRIVATE
+    main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/importable.cxx
new file mode 100644 (file)
index 0000000..b872ae9
--- /dev/null
@@ -0,0 +1,9 @@
+export module importable;
+import : internal_partition;
+
+#include "internal-partitions_export.h"
+
+export INTERNAL_PARTITIONS_EXPORT int from_import()
+{
+  return from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/main.cxx
new file mode 100644 (file)
index 0000000..feb38d2
--- /dev/null
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/internal-partitions/partition.cxx
new file mode 100644 (file)
index 0000000..b15f53c
--- /dev/null
@@ -0,0 +1,6 @@
+module importable : internal_partition;
+
+int from_partition()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt
new file mode 100644 (file)
index 0000000..4652aec
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt
new file mode 100644 (file)
index 0000000..4652aec
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
new file mode 100644 (file)
index 0000000..27fd94f
--- /dev/null
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(library)
+generate_export_header(library)
+target_sources(library
+  PUBLIC
+    FILE_SET HEADERS
+      BASE_DIRS
+        "${CMAKE_CURRENT_BINARY_DIR}"
+      FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/library_export.h"
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(library PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE library)
+target_sources(exe
+  PRIVATE
+    main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/library/importable.cxx b/Tests/RunCMake/CXXModules/examples/library/importable.cxx
new file mode 100644 (file)
index 0000000..72ed0df
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable;
+
+#include "library_export.h"
+
+export LIBRARY_EXPORT int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/library/main.cxx b/Tests/RunCMake/CXXModules/examples/library/main.cxx
new file mode 100644 (file)
index 0000000..feb38d2
--- /dev/null
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt
new file mode 100644 (file)
index 0000000..4652aec
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3a7b0d4
--- /dev/null
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_partitions CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+include(GenerateExportHeader)
+
+add_library(partitions)
+generate_export_header(partitions)
+target_sources(partitions
+  PUBLIC
+    FILE_SET HEADERS
+      BASE_DIRS
+        "${CMAKE_CURRENT_BINARY_DIR}"
+      FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/partitions_export.h"
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx
+        partition.cxx)
+target_compile_features(partitions PUBLIC cxx_std_20)
+
+add_executable(exe)
+target_link_libraries(exe PRIVATE partitions)
+target_sources(exe
+  PRIVATE
+    main.cxx)
+
+add_test(NAME exe COMMAND exe)
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx b/Tests/RunCMake/CXXModules/examples/partitions/importable.cxx
new file mode 100644 (file)
index 0000000..d0ac2f4
--- /dev/null
@@ -0,0 +1,9 @@
+export module importable;
+export import : partition;
+
+#include "partitions_export.h"
+
+export PARTITIONS_EXPORT int from_import()
+{
+  return from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/main.cxx b/Tests/RunCMake/CXXModules/examples/partitions/main.cxx
new file mode 100644 (file)
index 0000000..c5b78c9
--- /dev/null
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import() + from_partition();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx b/Tests/RunCMake/CXXModules/examples/partitions/partition.cxx
new file mode 100644 (file)
index 0000000..a47a4fd
--- /dev/null
@@ -0,0 +1,8 @@
+export module importable : partition;
+
+#include "partitions_export.h"
+
+export PARTITIONS_EXPORT int from_partition()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-build-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-build-stdout.txt
new file mode 100644 (file)
index 0000000..b5f1c55
--- /dev/null
@@ -0,0 +1 @@
+CMake Error: Public C\+\+ module source `.*/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx` requires the `priv` C\+\+ module which is provided by a private source
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt
new file mode 100644 (file)
index 0000000..600fec4
--- /dev/null
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_public_req_private CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(public_req_private)
+target_sources(public_req_private
+  PRIVATE
+    FILE_SET private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        priv.cxx
+  PUBLIC
+    FILE_SET public TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        pub.cxx)
+target_compile_features(public_req_private PUBLIC cxx_std_20)
+
+add_test(NAME cmake-version COMMAND "${CMAKE_COMMAND}" --version)
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx b/Tests/RunCMake/CXXModules/examples/public-req-private/priv.cxx
new file mode 100644 (file)
index 0000000..7c000b7
--- /dev/null
@@ -0,0 +1,6 @@
+export module priv;
+
+export int g()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx b/Tests/RunCMake/CXXModules/examples/public-req-private/pub.cxx
new file mode 100644 (file)
index 0000000..6ff2d23
--- /dev/null
@@ -0,0 +1,8 @@
+export module pub;
+
+import priv;
+
+export int f()
+{
+  return g();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt
new file mode 100644 (file)
index 0000000..5e4392a
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\):
+  C\+\+20 modules support via CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP is
+  experimental.  It is meant only for compiler developers to try.
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
new file mode 100644 (file)
index 0000000..442e425
--- /dev/null
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_simple CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_executable(simple)
+target_sources(simple
+  PRIVATE
+    main.cxx
+  PRIVATE
+    FILE_SET CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(simple PUBLIC cxx_std_20)
+
+add_test(NAME simple COMMAND simple)
diff --git a/Tests/RunCMake/CXXModules/examples/simple/importable.cxx b/Tests/RunCMake/CXXModules/examples/simple/importable.cxx
new file mode 100644 (file)
index 0000000..607680a
--- /dev/null
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/simple/main.cxx b/Tests/RunCMake/CXXModules/examples/simple/main.cxx
new file mode 100644 (file)
index 0000000..feb38d2
--- /dev/null
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json
new file mode 100644 (file)
index 0000000..65f0759
--- /dev/null
@@ -0,0 +1,45 @@
+{
+  "bmi-installation": {
+    "destination": "lib/cxx/modules/private/<CONFIG>",
+    "message-level": "MESSAGE_LAZY",
+    "permissions": " OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ",
+    "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake"
+  },
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-bmi-install-private.dir/sources/module-internal-part.cxx.o": {
+      "destination": null,
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-bmi-install-private.dir/sources/module-part.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-bmi-install-private.dir/sources/module.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json
new file mode 100644 (file)
index 0000000..9c8a895
--- /dev/null
@@ -0,0 +1,45 @@
+{
+  "bmi-installation": {
+    "destination": "lib/cxx/modules/<CONFIG>",
+    "message-level": "",
+    "permissions": "",
+    "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-noconfig.cmake"
+  },
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-bmi-install-public.dir/sources/module-internal-part.cxx.o": {
+      "destination": "lib/cxx/internals",
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-bmi-install-public.dir/sources/module-part.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-bmi-install-public.dir/sources/module.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json
new file mode 100644 (file)
index 0000000..0545981
--- /dev/null
@@ -0,0 +1,73 @@
+{
+  "bmi-installation": null,
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-exports-private.dir/sources/module-internal-part.cxx.o": {
+      "destination": null,
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-exports-private.dir/sources/module-part.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-exports-private.dir/sources/module.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "lib/cmake/export1",
+      "export-name" : "with-private",
+      "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/d2e2673818fd2bd8c45c0e3ed0e38fcd",
+      "install" : true,
+      "namespace" : "export1::"
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "lib/cmake/export2",
+      "export-name" : "with-private",
+      "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/28cd47cb4c96ad5cadaa3fb1b0201ae8",
+      "install" : true,
+      "namespace" : ""
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "<BINARY_DIR>/lib/cmake/export1",
+      "export-name" : "with-private",
+      "export-prefix" : "<BINARY_DIR>/lib/cmake/export1",
+      "install" : false,
+      "namespace" : "export1::"
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "<BINARY_DIR>/lib/cmake/export2",
+      "export-name" : "with-private",
+      "export-prefix" : "<BINARY_DIR>/lib/cmake/export2",
+      "install" : false,
+      "namespace" : ""
+    }
+  ],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json
new file mode 100644 (file)
index 0000000..adc3ae3
--- /dev/null
@@ -0,0 +1,73 @@
+{
+  "bmi-installation": null,
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-exports-public.dir/sources/module-internal-part.cxx.o": {
+      "destination": "lib/cxx/internals",
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-exports-public.dir/sources/module-part.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-exports-public.dir/sources/module.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "lib/cmake/export1",
+      "export-name" : "with-public",
+      "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/d2e2673818fd2bd8c45c0e3ed0e38fcd",
+      "install" : true,
+      "namespace" : "export1::"
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "lib/cmake/export2",
+      "export-name" : "with-public",
+      "export-prefix" : "<BINARY_DIR>/CMakeFiles/Export/28cd47cb4c96ad5cadaa3fb1b0201ae8",
+      "install" : true,
+      "namespace" : ""
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "<BINARY_DIR>/lib/cmake/export1",
+      "export-name" : "with-public",
+      "export-prefix" : "<BINARY_DIR>/lib/cmake/export1",
+      "install" : false,
+      "namespace" : "export1::"
+    },
+    {
+      "cxx-module-info-dir" : "cxx-modules",
+      "destination" : "<BINARY_DIR>/lib/cmake/export2",
+      "export-name" : "with-public",
+      "export-prefix" : "<BINARY_DIR>/lib/cmake/export2",
+      "install" : false,
+      "namespace" : ""
+    }
+  ],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json
new file mode 100644 (file)
index 0000000..9ba6568
--- /dev/null
@@ -0,0 +1,40 @@
+{
+  "bmi-installation": null,
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-file-sets-private.dir/sources/module-internal-part.cxx.o": {
+      "destination": null,
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-file-sets-private.dir/sources/module-part.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    },
+    "CMakeFiles/ninja-file-sets-private.dir/sources/module.cxx.o": {
+      "destination": null,
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PRIVATE"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json
new file mode 100644 (file)
index 0000000..46e2cbf
--- /dev/null
@@ -0,0 +1,40 @@
+{
+  "bmi-installation": null,
+  "compiler-id": "<IGNORE>",
+  "config": "<CONFIG>",
+  "cxx-modules": {
+    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-internal-part.cxx.o": {
+      "destination": "lib/cxx/internals",
+      "name": "internal_partitions",
+      "relative-directory": "sources",
+      "source": "<SOURCE_DIR>/sources/module-internal-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-part.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module-part.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    },
+    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module.cxx.o": {
+      "destination": "lib/cxx",
+      "name": "modules",
+      "relative-directory": "",
+      "source": "<SOURCE_DIR>/sources/module.cxx",
+      "type": "CXX_MODULES",
+      "visibility": "PUBLIC"
+    }
+  },
+  "dir-cur-bld": "<BINARY_DIR>",
+  "dir-cur-src": "<SOURCE_DIR>",
+  "dir-top-bld": "<BINARY_DIR>",
+  "dir-top-src": "<SOURCE_DIR>",
+  "exports": [],
+  "include-dirs": [],
+  "language": "CXX",
+  "linked-target-dirs": [],
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir"
+}
diff --git a/Tests/RunCMake/CXXModules/sources/c-anchor.c b/Tests/RunCMake/CXXModules/sources/c-anchor.c
new file mode 100644 (file)
index 0000000..c782188
--- /dev/null
@@ -0,0 +1,4 @@
+int c_anchor()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx b/Tests/RunCMake/CXXModules/sources/cxx-anchor.cxx
new file mode 100644 (file)
index 0000000..9c94ec1
--- /dev/null
@@ -0,0 +1,4 @@
+int cxx_anchor()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-header.h b/Tests/RunCMake/CXXModules/sources/module-header.h
new file mode 100644 (file)
index 0000000..982617e
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef module_header_h
+#define module_header_h
+
+inline int h()
+{
+  return 0;
+}
+
+#endif
diff --git a/Tests/RunCMake/CXXModules/sources/module-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-impl.cxx
new file mode 100644 (file)
index 0000000..4718999
--- /dev/null
@@ -0,0 +1,6 @@
+module M;
+
+int f()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part-impl.cxx
new file mode 100644 (file)
index 0000000..be77b0d
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef _MSC_VER
+// Only MSVC supports this pattern.
+module M : internal_part;
+#else
+module M;
+#endif
+
+int i()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx b/Tests/RunCMake/CXXModules/sources/module-internal-part.cxx
new file mode 100644 (file)
index 0000000..fa82afb
--- /dev/null
@@ -0,0 +1,3 @@
+module M : internal_part;
+
+int i();
diff --git a/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx b/Tests/RunCMake/CXXModules/sources/module-part-impl.cxx
new file mode 100644 (file)
index 0000000..46d5d9f
--- /dev/null
@@ -0,0 +1,13 @@
+#ifdef _MSC_VER
+// Only MSVC supports this pattern.
+module M : part;
+#else
+module M;
+#endif
+
+import M : internal_part;
+
+int p()
+{
+  return i();
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module-part.cxx b/Tests/RunCMake/CXXModules/sources/module-part.cxx
new file mode 100644 (file)
index 0000000..137c16f
--- /dev/null
@@ -0,0 +1,3 @@
+export module M : part;
+
+int p();
diff --git a/Tests/RunCMake/CXXModules/sources/module-use.cxx b/Tests/RunCMake/CXXModules/sources/module-use.cxx
new file mode 100644 (file)
index 0000000..2d060cd
--- /dev/null
@@ -0,0 +1,6 @@
+import M;
+
+int main(int argc, char* argv[])
+{
+  return f() + p();
+}
diff --git a/Tests/RunCMake/CXXModules/sources/module.cxx b/Tests/RunCMake/CXXModules/sources/module.cxx
new file mode 100644 (file)
index 0000000..a631354
--- /dev/null
@@ -0,0 +1,5 @@
+export module M;
+export import M : part;
+import M : internal_part;
+
+int f();
index 276158c..bfb6f72 100644 (file)
@@ -1,33 +1,27 @@
 
 enable_language (C)
-include(CheckCompilerFlag)
+include(CheckCCompilerFlag)
 
 set(C 1) # test that this is tolerated
 
-# test that the check uses an isolated locale
-set(_env_LC_ALL "${LC_ALL}")
-set(ENV{LC_ALL} "BAD")
-
-check_compiler_flag(C "-_this_is_not_a_flag_" SHOULD_FAIL)
-if(SHOULD_FAIL)
-  message(SEND_ERROR "invalid C compile flag didn't fail.")
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "PathScale")
+  set(DD --)
 endif()
 
-if(CMAKE_C_COMPILER_ID MATCHES "GNU|LCC|Clang" AND NOT "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
-  check_compiler_flag(C "-x c" SHOULD_WORK)
-  if(NOT SHOULD_WORK)
-    message(SEND_ERROR "${CMAKE_C_COMPILER_ID} compiler flag '-x c' check failed")
-  endif()
+check_c_compiler_flag("${DD}-_this_is_not_a_flag_" C_BOGUS_FLAG)
+if(C_BOGUS_FLAG)
+  message(SEND_ERROR "CHECK_C_COMPILER_FLAG() succeeded, but should have failed")
 endif()
-
-if(CMAKE_C_COMPILER_ID STREQUAL "GNU") # LCC C compiler silently ignore -frtti instead of failing, so skip it here.
-  check_compiler_flag(C "-frtti" SHOULD_FAIL_RTTI)
-  if(SHOULD_FAIL_RTTI)
-    message(SEND_ERROR "${CMAKE_C_COMPILER_ID} compiler flag '-frtti' check passed but should have failed")
-  endif()
+unset(C_BOGUS_FLAG CACHE)
+if(DEFINED C_BOGUS_FLAG)
+  # Verify that CHECK_C_COMPILER_FLAG didn't construct a normal variable
+  message(SEND_ERROR "CHECK_C_COMPILER_FLAG shouldn't construct C_BOGUS_FLAG as a normal variable")
 endif()
 
-if(NOT "$ENV{LC_ALL}" STREQUAL "BAD")
-  message(SEND_ERROR "ENV{LC_ALL} was not preserved by check_compiler_flag")
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "LCC")
+  unset(C_STRICT_PROTOTYPES CACHE)
+  CHECK_C_COMPILER_FLAG("-Werror;-Wstrict-prototypes" C_STRICT_PROTOTYPES)
+  if(NOT C_STRICT_PROTOTYPES)
+    message(SEND_ERROR "CHECK_C_COMPILER_FLAG failed -Werror -Wstrict-prototypes")
+  endif()
 endif()
-set(ENV{LC_ALL} ${_env_LC_ALL})
index dec31ec..eb20eb4 100644 (file)
@@ -1,26 +1,19 @@
 
 enable_language (CXX)
-include(CheckCompilerFlag)
+include(CheckCXXCompilerFlag)
 
 set(CXX 1) # test that this is tolerated
 
-# test that the check uses an isolated locale
-set(_env_LC_ALL "${LC_ALL}")
-set(ENV{LC_ALL} "BAD")
-
-check_compiler_flag(CXX "-_this_is_not_a_flag_" SHOULD_FAIL)
-if(SHOULD_FAIL)
-  message(SEND_ERROR "invalid CXX compile flag didn't fail.")
+if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "PathScale")
+  set(DD --)
 endif()
 
-if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC|Clang" AND NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
-  check_compiler_flag(CXX "-x c++" SHOULD_WORK)
-  if(NOT SHOULD_WORK)
-    message(SEND_ERROR "${CMAKE_CXX_COMPILER_ID} compiler flag '-x c++' check failed")
-  endif()
+check_cxx_compiler_flag("${DD}-_this_is_not_a_flag_" CXX_BOGUS_FLAG)
+if(CXX_BOGUS_FLAG)
+  message(SEND_ERROR "CHECK_CXX_COMPILER_FLAG() succeeded, but should have failed")
 endif()
-
-if(NOT "$ENV{LC_ALL}" STREQUAL "BAD")
-  message(SEND_ERROR "ENV{LC_ALL} was not preserved by check_compiler_flag")
+unset(CXX_BOGUS_FLAG CACHE)
+if(DEFINED CXX_BOGUS_FLAG)
+  # Verify that CHECK_CXX_COMPILER_FLAG didn't construct a normal variable
+  message(SEND_ERROR "CHECK_CXX_COMPILER_FLAG shouldn't construct CXX_BOGUS_FLAG as a normal variable")
 endif()
-set(ENV{LC_ALL} ${_env_LC_ALL})
diff --git a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagC.cmake b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagC.cmake
new file mode 100644 (file)
index 0000000..276158c
--- /dev/null
@@ -0,0 +1,33 @@
+
+enable_language (C)
+include(CheckCompilerFlag)
+
+set(C 1) # test that this is tolerated
+
+# test that the check uses an isolated locale
+set(_env_LC_ALL "${LC_ALL}")
+set(ENV{LC_ALL} "BAD")
+
+check_compiler_flag(C "-_this_is_not_a_flag_" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid C compile flag didn't fail.")
+endif()
+
+if(CMAKE_C_COMPILER_ID MATCHES "GNU|LCC|Clang" AND NOT "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC")
+  check_compiler_flag(C "-x c" SHOULD_WORK)
+  if(NOT SHOULD_WORK)
+    message(SEND_ERROR "${CMAKE_C_COMPILER_ID} compiler flag '-x c' check failed")
+  endif()
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU") # LCC C compiler silently ignore -frtti instead of failing, so skip it here.
+  check_compiler_flag(C "-frtti" SHOULD_FAIL_RTTI)
+  if(SHOULD_FAIL_RTTI)
+    message(SEND_ERROR "${CMAKE_C_COMPILER_ID} compiler flag '-frtti' check passed but should have failed")
+  endif()
+endif()
+
+if(NOT "$ENV{LC_ALL}" STREQUAL "BAD")
+  message(SEND_ERROR "ENV{LC_ALL} was not preserved by check_compiler_flag")
+endif()
+set(ENV{LC_ALL} ${_env_LC_ALL})
diff --git a/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagCXX.cmake b/Tests/RunCMake/CheckCompilerFlag/CheckCompilerFlagCXX.cmake
new file mode 100644 (file)
index 0000000..dec31ec
--- /dev/null
@@ -0,0 +1,26 @@
+
+enable_language (CXX)
+include(CheckCompilerFlag)
+
+set(CXX 1) # test that this is tolerated
+
+# test that the check uses an isolated locale
+set(_env_LC_ALL "${LC_ALL}")
+set(ENV{LC_ALL} "BAD")
+
+check_compiler_flag(CXX "-_this_is_not_a_flag_" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid CXX compile flag didn't fail.")
+endif()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC|Clang" AND NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
+  check_compiler_flag(CXX "-x c++" SHOULD_WORK)
+  if(NOT SHOULD_WORK)
+    message(SEND_ERROR "${CMAKE_CXX_COMPILER_ID} compiler flag '-x c++' check failed")
+  endif()
+endif()
+
+if(NOT "$ENV{LC_ALL}" STREQUAL "BAD")
+  message(SEND_ERROR "ENV{LC_ALL} was not preserved by check_compiler_flag")
+endif()
+set(ENV{LC_ALL} ${_env_LC_ALL})
index df2b667..b0e025c 100644 (file)
@@ -5,28 +5,30 @@ run_cmake(NonExistentLanguage)
 
 run_cmake(CheckCCompilerFlag)
 run_cmake(CheckCXXCompilerFlag)
+run_cmake(CheckCompilerFlagC)
+run_cmake(CheckCompilerFlagCXX)
 
 if (APPLE)
-  run_cmake(CheckOBJCCompilerFlag)
-  run_cmake(CheckOBJCXXCompilerFlag)
+  run_cmake(CheckCompilerFlagOBJC)
+  run_cmake(CheckCompilerFlagOBJCXX)
 endif()
 
 if (CMAKE_Fortran_COMPILER_ID)
-  run_cmake(CheckFortranCompilerFlag)
+  run_cmake(CheckCompilerFlagFortran)
 endif()
 
 if (CMake_TEST_CUDA)
-  run_cmake(CheckCUDACompilerFlag)
+  run_cmake(CheckCompilerFlagCUDA)
 endif()
 
 if(CMake_TEST_ISPC)
-  run_cmake(CheckISPCCompilerFlag)
+  run_cmake(CheckCompilerFlagISPC)
 endif()
 
 if(CMake_TEST_HIP)
-  run_cmake(CheckHIPCompilerFlag)
+  run_cmake(CheckCompilerFlagHIP)
 endif()
 
 if(APPLE)
-  run_cmake_with_options(HeaderpadWorkaround --debug-trycompile)
+  run_cmake(HeaderpadWorkaround)
 endif()
index dc2c3ad..9a1ba04 100644 (file)
@@ -1,6 +1,6 @@
 ^CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\):
-  IPO is not supported \(no C/CXX/Fortran languages found in ENABLED_LANGUAGES
-  global property\)\.
+  IPO is not supported \(no C/CXX/CUDA/Fortran languages found in
+  ENABLED_LANGUAGES global property\)\.
 Call Stack \(most recent call first\):
   .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(_ipo_not_supported\)
   default-lang-none\.cmake:[0-9]+ \(check_ipo_supported\)
index 39fc430..5915ba1 100644 (file)
@@ -1,22 +1,22 @@
 include(RunCMake)
 
 if (CMAKE_C_COMPILER_ID MATCHES "Clang|GNU|LCC")
-  run_cmake(CheckCLinkerFlag)
-  run_cmake(CheckCXXLinkerFlag)
+  run_cmake(CheckLinkerFlagC)
+  run_cmake(CheckLinkerFlagCXX)
   if (APPLE)
-    run_cmake(CheckOBJCLinkerFlag)
-    run_cmake(CheckOBJCXXLinkerFlag)
+    run_cmake(CheckLinkerFlagOBJC)
+    run_cmake(CheckLinkerFlagOBJCXX)
   endif()
 endif()
 
 if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU|LCC")
-  run_cmake(CheckFortranLinkerFlag)
+  run_cmake(CheckLinkerFlagFortran)
 endif()
 
 if (CMake_TEST_CUDA)
-  run_cmake(CheckCUDALinkerFlag)
+  run_cmake(CheckLinkerFlagCUDA)
 endif()
 
 if (CMake_TEST_HIP)
-  run_cmake(CheckHIPLinkerFlag)
+  run_cmake(CheckLinkerFlagHIP)
 endif()
index 4f73622..7104650 100644 (file)
@@ -1,15 +1,15 @@
 
 enable_language (C)
-include(CheckSourceCompiles)
+include(CheckCSourceCompiles)
 
 set(C 1) # test that this is tolerated
 
-check_source_compiles(C "I don't build" SHOULD_FAIL)
+check_c_source_compiles("I don't build" SHOULD_FAIL)
 if(SHOULD_FAIL)
   message(SEND_ERROR "invalid C source didn't fail.")
 endif()
 
-check_source_compiles(C "int main() {return 0;}" SHOULD_BUILD)
+check_c_source_compiles("int main() {return 0;}" SHOULD_BUILD)
 if(NOT SHOULD_BUILD)
   message(SEND_ERROR "Test fail for valid C source.")
 endif()
index 97c9c30..7c9830a 100644 (file)
@@ -1,15 +1,15 @@
 
 enable_language (CXX)
-include(CheckSourceCompiles)
+include(CheckCXXSourceCompiles)
 
 set(CXX 1) # test that this is tolerated
 
-check_source_compiles(CXX "I don't build" SHOULD_FAIL)
+check_cxx_source_compiles("I don't build" SHOULD_FAIL)
 if(SHOULD_FAIL)
   message(SEND_ERROR "invalid CXX source didn't fail.")
 endif()
 
-check_source_compiles(CXX [=[
+check_cxx_source_compiles([=[
   #include <vector>
   int main() {
     return 0;
@@ -20,8 +20,8 @@ if(NOT SHOULD_BUILD)
   message(SEND_ERROR "Test fail for valid CXX source.")
 endif()
 
-check_source_compiles(CXX "void l(char const (&x)[2]){}; int main() { l(\"\\n\"); return 0;}"
- SHOULD_BUILD_COMPLEX)
+CHECK_CXX_SOURCE_COMPILES("void l(char const (&x)[2]){}; int main() { l(\"\\\\n\"); return 0;}"
 SHOULD_BUILD_COMPLEX)
 
 if(NOT SHOULD_BUILD_COMPLEX)
   message(SEND_ERROR "Test fail for valid CXX complex source.")
index 76a5009..ac43a1e 100644 (file)
@@ -1,9 +1,14 @@
 enable_language (OBJC)
-include(CheckSourceCompiles)
+include(CheckOBJCSourceCompiles)
 
 set(OBJC 1) # test that this is tolerated
 
-check_source_compiles(OBJC [[
+check_objc_source_compiles("I don't build in Objective-C" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid OBJC source didn't fail.")
+endif()
+
+check_objc_source_compiles([[
   #import <Foundation/Foundation.h>
   int main() {
     NSObject *foo;
index 814237e..0088956 100644 (file)
@@ -1,9 +1,14 @@
 enable_language (OBJCXX)
-include(CheckSourceCompiles)
+include(CheckOBJCXXSourceCompiles)
 
 set(OBJCXX 1) # test that this is tolerated
 
-check_source_compiles(OBJCXX [[
+check_objcxx_source_compiles("I don't build in Objective-C++" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid OBJCXX source didn't fail.")
+endif()
+
+check_objcxx_source_compiles([[
   #include <vector>
   #import <Foundation/Foundation.h>
   int main() {
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesC.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesC.cmake
new file mode 100644 (file)
index 0000000..4f73622
--- /dev/null
@@ -0,0 +1,15 @@
+
+enable_language (C)
+include(CheckSourceCompiles)
+
+set(C 1) # test that this is tolerated
+
+check_source_compiles(C "I don't build" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid C source didn't fail.")
+endif()
+
+check_source_compiles(C "int main() {return 0;}" SHOULD_BUILD)
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for valid C source.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesCXX.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesCXX.cmake
new file mode 100644 (file)
index 0000000..97c9c30
--- /dev/null
@@ -0,0 +1,28 @@
+
+enable_language (CXX)
+include(CheckSourceCompiles)
+
+set(CXX 1) # test that this is tolerated
+
+check_source_compiles(CXX "I don't build" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "invalid CXX source didn't fail.")
+endif()
+
+check_source_compiles(CXX [=[
+  #include <vector>
+  int main() {
+    return 0;
+  }
+]=]
+ SHOULD_BUILD)
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for valid CXX source.")
+endif()
+
+check_source_compiles(CXX "void l(char const (&x)[2]){}; int main() { l(\"\\n\"); return 0;}"
+ SHOULD_BUILD_COMPLEX)
+
+if(NOT SHOULD_BUILD_COMPLEX)
+  message(SEND_ERROR "Test fail for valid CXX complex source.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJC.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJC.cmake
new file mode 100644 (file)
index 0000000..76a5009
--- /dev/null
@@ -0,0 +1,16 @@
+enable_language (OBJC)
+include(CheckSourceCompiles)
+
+set(OBJC 1) # test that this is tolerated
+
+check_source_compiles(OBJC [[
+  #import <Foundation/Foundation.h>
+  int main() {
+    NSObject *foo;
+    return 0;
+  }
+]] SHOULD_BUILD)
+
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for valid OBJC source.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJCXX.cmake b/Tests/RunCMake/CheckSourceCompiles/CheckSourceCompilesOBJCXX.cmake
new file mode 100644 (file)
index 0000000..814237e
--- /dev/null
@@ -0,0 +1,19 @@
+enable_language (OBJCXX)
+include(CheckSourceCompiles)
+
+set(OBJCXX 1) # test that this is tolerated
+
+check_source_compiles(OBJCXX [[
+  #include <vector>
+  #import <Foundation/Foundation.h>
+  int main() {
+    std::vector<int> v;
+    NSObject *foo;
+    return 0;
+  }
+]] SHOULD_BUILD)
+
+
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for OBJCXX source.")
+endif()
index 530f133..df77d3d 100644 (file)
@@ -6,24 +6,28 @@ run_cmake(UnknownArgument)
 
 run_cmake(CheckCSourceCompiles)
 run_cmake(CheckCXXSourceCompiles)
+run_cmake(CheckSourceCompilesC)
+run_cmake(CheckSourceCompilesCXX)
 
 if (APPLE)
   run_cmake(CheckOBJCSourceCompiles)
   run_cmake(CheckOBJCXXSourceCompiles)
+  run_cmake(CheckSourceCompilesOBJC)
+  run_cmake(CheckSourceCompilesOBJCXX)
 endif()
 
 if (CMAKE_Fortran_COMPILER_ID)
-  run_cmake(CheckFortranSourceCompiles)
+  run_cmake(CheckSourceCompilesFortran)
 endif()
 
 if (CMake_TEST_CUDA)
-  run_cmake(CheckCUDASourceCompiles)
+  run_cmake(CheckSourceCompilesCUDA)
 endif()
 
 if(CMake_TEST_ISPC)
-  run_cmake(CheckISPCSourceCompiles)
+  run_cmake(CheckSourceCompilesISPC)
 endif()
 
 if(CMake_TEST_HIP)
-  run_cmake(CheckHIPSourceCompiles)
+  run_cmake(CheckSourceCompilesHIP)
 endif()
index 96a1027..c0f9463 100644 (file)
@@ -1,15 +1,15 @@
 
 enable_language (C)
-include(CheckSourceRuns)
+include(CheckCSourceRuns)
 
 set(C 1) # test that this is tolerated
 
-check_source_runs(C "int main() {return 2;}" SHOULD_FAIL)
+check_c_source_runs("int main() {return 2;}" SHOULD_FAIL)
 if(SHOULD_FAIL)
-  message(SEND_ERROR "C check_source_runs succeeded, but should have failed.")
+  message(SEND_ERROR "check_c_source_runs succeeded, but should have failed.")
 endif()
 
-check_source_runs(C "int main() {return 0;}" SHOULD_RUN)
+check_c_source_runs("int main() {return 0;}" SHOULD_RUN)
 if(NOT SHOULD_RUN)
-  message(SEND_ERROR "C check_source_runs failed for valid C executable.")
+  message(SEND_ERROR "check_c_source_runs failed for valid C executable.")
 endif()
index c4bef6e..c4c9026 100644 (file)
@@ -1,15 +1,15 @@
 
 enable_language (CXX)
-include(CheckSourceRuns)
+include(CheckCXXSourceRuns)
 
 set(CXX 1) # test that this is tolerated
 
-check_source_runs(CXX "int main() {return 2;}" SHOULD_FAIL)
+check_cxx_source_runs("int main() {return 2;}" SHOULD_FAIL)
 if(SHOULD_FAIL)
-  message(SEND_ERROR "CXX check_source_runs succeeded, but should have failed.")
+  message(SEND_ERROR "check_cxx_source_runs succeeded, but should have failed.")
 endif()
 
-check_source_runs(CXX
+check_cxx_source_runs(
 [=[
   #include <vector>
   int main() {
@@ -18,5 +18,5 @@ check_source_runs(CXX
 ]=]
  SHOULD_RUN)
 if(NOT SHOULD_RUN)
-  message(SEND_ERROR "CXX check_source_runs failed for valid C executable.")
+  message(SEND_ERROR "check_cxx_source_runs failed for valid C executable.")
 endif()
index b828352..604b2a1 100644 (file)
@@ -1,9 +1,14 @@
 enable_language (OBJC)
-include(CheckSourceRuns)
+include(CheckOBJCSourceRuns)
 
 set(OBJC 1) # test that this is tolerated
 
-check_source_runs(OBJC [[
+check_objc_source_runs("int main() {return 2;}" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "check_objc_source_runs succeeded, but should have failed.")
+endif()
+
+check_objc_source_runs([[
   #import <Foundation/Foundation.h>
   int main() {
     NSObject *foo;
index 8a56f13..b220166 100644 (file)
@@ -1,9 +1,14 @@
 enable_language (OBJCXX)
-include(CheckSourceRuns)
+include(CheckOBJCXXSourceRuns)
 
 set(OBJCXX 1) # test that this is tolerated
 
-check_source_runs(OBJCXX [[
+check_objcxx_source_runs("int main() {return 2;}" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "check_objcxx_source_runs succeeded, but should have failed.")
+endif()
+
+check_objcxx_source_runs([[
   #include <vector>
   #import <Foundation/Foundation.h>
   int main() {
diff --git a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsC.cmake b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsC.cmake
new file mode 100644 (file)
index 0000000..96a1027
--- /dev/null
@@ -0,0 +1,15 @@
+
+enable_language (C)
+include(CheckSourceRuns)
+
+set(C 1) # test that this is tolerated
+
+check_source_runs(C "int main() {return 2;}" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "C check_source_runs succeeded, but should have failed.")
+endif()
+
+check_source_runs(C "int main() {return 0;}" SHOULD_RUN)
+if(NOT SHOULD_RUN)
+  message(SEND_ERROR "C check_source_runs failed for valid C executable.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsCXX.cmake b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsCXX.cmake
new file mode 100644 (file)
index 0000000..c4bef6e
--- /dev/null
@@ -0,0 +1,22 @@
+
+enable_language (CXX)
+include(CheckSourceRuns)
+
+set(CXX 1) # test that this is tolerated
+
+check_source_runs(CXX "int main() {return 2;}" SHOULD_FAIL)
+if(SHOULD_FAIL)
+  message(SEND_ERROR "CXX check_source_runs succeeded, but should have failed.")
+endif()
+
+check_source_runs(CXX
+[=[
+  #include <vector>
+  int main() {
+    return 0;
+  }
+]=]
+ SHOULD_RUN)
+if(NOT SHOULD_RUN)
+  message(SEND_ERROR "CXX check_source_runs failed for valid C executable.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJC.cmake b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJC.cmake
new file mode 100644 (file)
index 0000000..b828352
--- /dev/null
@@ -0,0 +1,16 @@
+enable_language (OBJC)
+include(CheckSourceRuns)
+
+set(OBJC 1) # test that this is tolerated
+
+check_source_runs(OBJC [[
+  #import <Foundation/Foundation.h>
+  int main() {
+    NSObject *foo;
+    return 0;
+  }
+]] SHOULD_BUILD)
+
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for valid OBJC source.")
+endif()
diff --git a/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJCXX.cmake b/Tests/RunCMake/CheckSourceRuns/CheckSourceRunsOBJCXX.cmake
new file mode 100644 (file)
index 0000000..8a56f13
--- /dev/null
@@ -0,0 +1,19 @@
+enable_language (OBJCXX)
+include(CheckSourceRuns)
+
+set(OBJCXX 1) # test that this is tolerated
+
+check_source_runs(OBJCXX [[
+  #include <vector>
+  #import <Foundation/Foundation.h>
+  int main() {
+    std::vector<int> v;
+    NSObject *foo;
+    return 0;
+  }
+]] SHOULD_BUILD)
+
+
+if(NOT SHOULD_BUILD)
+  message(SEND_ERROR "Test fail for OBJCXX source.")
+endif()
index 4784103..64cecfc 100644 (file)
@@ -6,20 +6,24 @@ run_cmake(UnknownArgument)
 
 run_cmake(CheckCSourceRuns)
 run_cmake(CheckCXXSourceRuns)
+run_cmake(CheckSourceRunsC)
+run_cmake(CheckSourceRunsCXX)
 
 if (APPLE)
   run_cmake(CheckOBJCSourceRuns)
   run_cmake(CheckOBJCXXSourceRuns)
+  run_cmake(CheckSourceRunsOBJC)
+  run_cmake(CheckSourceRunsOBJCXX)
 endif()
 
 if (CMAKE_Fortran_COMPILER_ID)
-  run_cmake(CheckFortranSourceRuns)
+  run_cmake(CheckSourceRunsFortran)
 endif()
 
 if (CMake_TEST_CUDA)
-  run_cmake(CheckCUDASourceRuns)
+  run_cmake(CheckSourceRunsCUDA)
 endif()
 
 if (CMake_TEST_HIP)
-  run_cmake(CheckHIPSourceRuns)
+  run_cmake(CheckSourceRunsHIP)
 endif()
index ee41d94..5e3fbc4 100644 (file)
@@ -29,3 +29,4 @@ if (NOT RunCMake_GENERATOR STREQUAL "Watcom WMake")
   endif()
 endif()
 run_tidy(C-bad)
+run_tidy(compdb)
diff --git a/Tests/RunCMake/ClangTidy/compdb.cmake b/Tests/RunCMake/ClangTidy/compdb.cmake
new file mode 100644 (file)
index 0000000..f83e0ae
--- /dev/null
@@ -0,0 +1,7 @@
+enable_language(C)
+
+# Include a --checks option to confirm that we don't match options that start
+# with --, only a standalone --
+set(CMAKE_C_CLANG_TIDY "${PSEUDO_TIDY}" -p ${CMAKE_BINARY_DIR} --checks=*)
+
+add_executable(main main.c)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt
deleted file mode 100644 (file)
index 202ef80..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-^CMake Warning:
-  The "Visual Studio 10 2010" generator is deprecated and will be removed in
-  a future version of CMake.
-
-  Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt
new file mode 100644 (file)
index 0000000..9080942
--- /dev/null
@@ -0,0 +1,5 @@
+^CMake Warning:
+  The "Visual Studio 11 2012" generator is deprecated and will be removed in
+  a future version of CMake.
+
+  Add CMAKE_WARN_VS11=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake
new file mode 100644 (file)
index 0000000..e69de29
index 6a932f1..1452c9b 100644 (file)
@@ -1 +1 @@
-^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":4}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$
+^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":4}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
diff --git a/Tests/RunCMake/CommandLine/E_env-equal.cmake b/Tests/RunCMake/CommandLine/E_env-equal.cmake
new file mode 100644 (file)
index 0000000..3f18bb6
--- /dev/null
@@ -0,0 +1,15 @@
+if (NOT DEFINED ENV{TEST_ENV_EXPECTED})
+    if (NOT DEFINED ENV{TEST_ENV})
+        message(STATUS "TEST_ENV is correctly not set in environment")
+    else ()
+        message(FATAL_ERROR "TEST_ENV is incorrectly set in environment")
+    endif ()
+else ()
+    if (NOT DEFINED ENV{TEST_ENV})
+        message(FATAL_ERROR "TEST_ENV is incorrectly not set in environment")
+    elseif ("$ENV{TEST_ENV}" STREQUAL "$ENV{TEST_ENV_EXPECTED}")
+        message(STATUS "TEST_ENV is correctly set in environment: $ENV{TEST_ENV}")
+    else ()
+        message(FATAL_ERROR "TEST_ENV is incorrectly set in environment!\n\tactual: $ENV{TEST_ENV}\n\texpected: $ENV{TEST_ENV_EXPECTED}")
+    endif ()
+endif ()
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-bad-operation-result.txt b/Tests/RunCMake/CommandLine/E_env_modify-bad-operation-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-bad-operation-stderr.txt b/Tests/RunCMake/CommandLine/E_env_modify-bad-operation-stderr.txt
new file mode 100644 (file)
index 0000000..ccfdeab
--- /dev/null
@@ -0,0 +1,3 @@
+^CMake Error: Error: Unrecognized environment manipulation argument: unknown
+
+cmake -E env: invalid parameter to --modify: TEST_ENV=unknown:$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-cmake_list-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-cmake_list-stdout.txt
new file mode 100644 (file)
index 0000000..ad42f56
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly set in environment: exp;ect;ed$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-path_list-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-path_list-stdout.txt
new file mode 100644 (file)
index 0000000..49572a3
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly set in environment: exp[;:]ect[;:]ed$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-reset-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-reset-stdout.txt
new file mode 100644 (file)
index 0000000..a60f1bf
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly set in environment: expected$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-reset-to-unset-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-reset-to-unset-stdout.txt
new file mode 100644 (file)
index 0000000..a1d5c01
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly not set in environment$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-set-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-set-stdout.txt
new file mode 100644 (file)
index 0000000..feff117
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly set in environment: 1$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-string-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-string-stdout.txt
new file mode 100644 (file)
index 0000000..a60f1bf
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly set in environment: expected$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-unset-stdout.txt b/Tests/RunCMake/CommandLine/E_env_modify-unset-stdout.txt
new file mode 100644 (file)
index 0000000..a1d5c01
--- /dev/null
@@ -0,0 +1 @@
+^-- TEST_ENV is correctly not set in environment$
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-with-double-dash-result.txt b/Tests/RunCMake/CommandLine/E_env_modify-with-double-dash-result.txt
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-result.txt b/Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-stderr.txt b/Tests/RunCMake/CommandLine/E_env_modify-without-double-dash-stderr.txt
new file mode 100644 (file)
index 0000000..8d98f9d
--- /dev/null
@@ -0,0 +1 @@
+.*
index 4ea0293..e2c0378 100644 (file)
@@ -9,6 +9,11 @@ foreach(d CMAKE_BINARY_DIR CMAKE_CURRENT_BINARY_DIR CMAKE_SOURCE_DIR CMAKE_CURRE
   if(EXPECTED_WORKING_DIR STREQUAL "${${d}}")
     message(STATUS "${d} is the expected working directory (${EXPECTED_WORKING_DIR})")
   else()
-    message(FATAL_ERROR "${d} = \"${${d}}\" is not the expected working directory (${EXPECTED_WORKING_DIR})")
+    get_filename_component(resolved "${EXPECTED_WORKING_DIR}" REALPATH)
+    if(resolved STREQUAL "${${d}}")
+      message(STATUS "${d} is the expected working directory (${resolved}) after symlink resolution")
+    else()
+      message(FATAL_ERROR "${d} = \"${${d}}\" is not the expected working directory (${EXPECTED_WORKING_DIR})")
+    endif()
   endif()
 endforeach()
index fc05b2d..a2eeddf 100644 (file)
@@ -744,6 +744,10 @@ run_cmake_command(E_cat-without-double-dash ${CMAKE_COMMAND} -E cat "-file-start
 unset(RunCMake_TEST_COMMAND_WORKING_DIRECTORY)
 unset(out)
 
+# Unset environment variables that are used for testing cmake -E
+unset(ENV{TEST_ENV})
+unset(ENV{TEST_ENV_EXPECTED})
+
 run_cmake_command(E_env-no-command0 ${CMAKE_COMMAND} -E env)
 run_cmake_command(E_env-no-command1 ${CMAKE_COMMAND} -E env TEST_ENV=1)
 run_cmake_command(E_env-bad-arg1 ${CMAKE_COMMAND} -E env -bad-arg1)
@@ -758,6 +762,56 @@ file(COPY_FILE "${EXIT_CODE_EXE}" "${RunCMake_BINARY_DIR}/env=${exit_code}")
 run_cmake_command(E_env-with-double-dash ${CMAKE_COMMAND} -E env TEST_ENV=1 -- "${RunCMake_BINARY_DIR}/env=${exit_code}" zero_exit)
 run_cmake_command(E_env-without-double-dash ${CMAKE_COMMAND} -E env TEST_ENV=1 "${RunCMake_BINARY_DIR}/env=${exit_code}" zero_exit)
 
+## Tests of env --modify
+# Repeat the same tests as above
+run_cmake_command(E_env_modify-set   ${CMAKE_COMMAND} -E env --modify TEST_ENV=set:1 ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-set.cmake)
+run_cmake_command(E_env_modify-unset ${CMAKE_COMMAND} -E env --modify TEST_ENV=set:1 ${CMAKE_COMMAND} -E env --modify TEST_ENV=unset: ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-unset.cmake)
+run_cmake_command(E_env_modify-with-double-dash ${CMAKE_COMMAND} -E env --modify TEST_ENV=set:1 -- "${RunCMake_BINARY_DIR}/env=${exit_code}" zero_exit)
+run_cmake_command(E_env_modify-without-double-dash ${CMAKE_COMMAND} -E env --modify TEST_ENV=set:1 "${RunCMake_BINARY_DIR}/env=${exit_code}" zero_exit)
+
+# Test environment modification commands
+run_cmake_command(E_env_modify-reset
+                  ${CMAKE_COMMAND} -E env TEST_ENV=expected
+                  ${CMAKE_COMMAND} -E env TEST_ENV_EXPECTED=expected TEST_ENV=bad_value --modify TEST_ENV=reset:
+                  ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-equal.cmake)
+
+run_cmake_command(E_env_modify-reset-to-unset
+                  ${CMAKE_COMMAND} -E env --unset=TEST_ENV --unset=TEST_ENV_EXPECTED
+                  ${CMAKE_COMMAND} -E env TEST_ENV=bad_value --modify TEST_ENV=reset:
+                  ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-equal.cmake)
+
+run_cmake_command(E_env_modify-string
+                  ${CMAKE_COMMAND} -E env TEST_ENV_EXPECTED=expected
+                  --modify TEST_ENV=unset:
+                  --modify TEST_ENV=string_append:ect
+                  --modify TEST_ENV=string_prepend:exp
+                  --modify TEST_ENV=string_append:ed
+                  ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-equal.cmake)
+
+if (WIN32)
+  set(SEP "\\;")
+else ()
+  set(SEP ":")
+endif ()
+
+run_cmake_command(E_env_modify-path_list
+                  ${CMAKE_COMMAND} -E env "TEST_ENV_EXPECTED=exp${SEP}ect${SEP}ed"
+                  --modify TEST_ENV=unset:
+                  --modify TEST_ENV=path_list_append:ect
+                  --modify TEST_ENV=path_list_prepend:exp
+                  --modify TEST_ENV=path_list_append:ed
+                  ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-equal.cmake)
+
+run_cmake_command(E_env_modify-cmake_list
+                  ${CMAKE_COMMAND} -E env "TEST_ENV_EXPECTED=exp\\;ect\\;ed"
+                  --modify TEST_ENV=unset:
+                  --modify TEST_ENV=cmake_list_append:ect
+                  --modify TEST_ENV=cmake_list_prepend:exp
+                  --modify TEST_ENV=cmake_list_append:ed
+                  ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/E_env-equal.cmake)
+
+run_cmake_command(E_env_modify-bad-operation ${CMAKE_COMMAND} -E env --modify TEST_ENV=unknown:)
+
 run_cmake_command(E_md5sum-dir ${CMAKE_COMMAND} -E md5sum .)
 run_cmake_command(E_sha1sum-dir ${CMAKE_COMMAND} -E sha1sum .)
 run_cmake_command(E_sha224sum-dir ${CMAKE_COMMAND} -E sha224sum .)
@@ -924,6 +978,8 @@ set(RunCMake_TEST_OPTIONS --debug-trycompile)
 run_cmake(debug-trycompile)
 unset(RunCMake_TEST_OPTIONS)
 
+run_cmake(trycompile-clean)
+
 function(run_cmake_depends)
   set(RunCMake_TEST_SOURCE_DIR "${RunCMake_SOURCE_DIR}/cmake_depends")
   set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/cmake_depends-build")
@@ -941,8 +997,8 @@ set(CMAKE_DEPENDS_CHECK_C
 set(CMAKE_RELATIVE_PATH_TOP_SOURCE \"${RunCMake_TEST_SOURCE_DIR}\")
 set(CMAKE_RELATIVE_PATH_TOP_BINARY \"${RunCMake_TEST_BINARY_DIR}\")
 ")
-  run_cmake_command(cmake_depends ${CMAKE_COMMAND} -E cmake_depends
-    "Unix Makefiles"
+  run_cmake_command(cmake_depends ${CMAKE_COMMAND} -E env VERBOSE=1
+    ${CMAKE_COMMAND} -E cmake_depends "Unix Makefiles"
     ${RunCMake_TEST_SOURCE_DIR} ${RunCMake_TEST_SOURCE_DIR}
     ${RunCMake_TEST_BINARY_DIR} ${RunCMake_TEST_BINARY_DIR}
     ${RunCMake_TEST_BINARY_DIR}/CMakeFiles/DepTarget.dir/DependInfo.cmake
@@ -1016,9 +1072,9 @@ set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${P
 run_cmake(ProfilingTest)
 unset(RunCMake_TEST_OPTIONS)
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 10 2010")
-  run_cmake_with_options(DeprecateVS10-WARN-ON -DCMAKE_WARN_VS10=ON)
-  unset(ENV{CMAKE_WARN_VS10})
-  run_cmake(DeprecateVS10-WARN-ON)
-  run_cmake_with_options(DeprecateVS10-WARN-OFF -DCMAKE_WARN_VS10=OFF)
+if(RunCMake_GENERATOR MATCHES "^Visual Studio 11 2012")
+  run_cmake_with_options(DeprecateVS11-WARN-ON -DCMAKE_WARN_VS11=ON)
+  unset(ENV{CMAKE_WARN_VS11})
+  run_cmake(DeprecateVS11-WARN-ON)
+  run_cmake_with_options(DeprecateVS11-WARN-OFF -DCMAKE_WARN_VS11=OFF)
 endif()
index 8fe092b..cf972a8 100644 (file)
@@ -1 +1 @@
-^Scanning dependencies of target DepTarget$
+Scanning dependencies of target DepTarget$
diff --git a/Tests/RunCMake/CommandLine/debug-trycompile-stderr.txt b/Tests/RunCMake/CommandLine/debug-trycompile-stderr.txt
new file mode 100644 (file)
index 0000000..e987300
--- /dev/null
@@ -0,0 +1,11 @@
+^CMake Debug Log at [^
+]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\):
+  Executing try_compile \(CMAKE_C_ABI_COMPILED\) in:
+
+    [^
+]*/Tests/RunCMake/CommandLine/debug-trycompile-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+
+Call Stack \(most recent call first\):
+  [^
+]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)
+  debug-trycompile.cmake:[0-9]+ \(enable_language\)
+  CMakeLists.txt:[0-9]+ \(include\)
index a3835a7..9619ba8 100644 (file)
@@ -1,5 +1,8 @@
 enable_language(C)
+
 # Look for a source tree left by enable_language internal checks.
-if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/CMakeLists.txt)
+set(scratch ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeScratch)
+file(GLOB_RECURSE remnants ${scratch}/TryCompile-*/CMakeLists.txt)
+if(NOT remnants)
   message(FATAL_ERROR "--debug-trycompile should leave the source behind")
 endif()
diff --git a/Tests/RunCMake/CommandLine/trycompile-clean.cmake b/Tests/RunCMake/CommandLine/trycompile-clean.cmake
new file mode 100644 (file)
index 0000000..11ec2b9
--- /dev/null
@@ -0,0 +1,8 @@
+enable_language(C)
+
+# Look for a source tree left by enable_language internal checks.
+set(scratch ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeScratch)
+file(GLOB_RECURSE remnants ${scratch}/TryCompile-*/*)
+if(remnants)
+  message(FATAL_ERROR "try_compile should not leave artifacts behind")
+endif()
index 84d0479..e6a2605 100644 (file)
@@ -17,7 +17,8 @@ endfunction()
 
 function(run_compiler_launcher_env lang)
   string(REGEX REPLACE "-.*" "" core_lang "${lang}")
-  set(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER} "${CMAKE_COMMAND};-E;env;USED_LAUNCHER=1")
+  # 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")
   run_compiler_launcher(${lang})
   unset(ENV{CMAKE_${core_lang}_COMPILER_LAUNCHER})
 endfunction()
diff --git a/Tests/RunCMake/CompilerTest/C-stdout.txt b/Tests/RunCMake/CompilerTest/C-stdout.txt
new file mode 100644 (file)
index 0000000..ce5d80e
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working C compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/C.cmake b/Tests/RunCMake/CompilerTest/C.cmake
new file mode 100644 (file)
index 0000000..8e9d70c
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_C_ABI_COMPILED FALSE)
+enable_language(C)
diff --git a/Tests/RunCMake/CompilerTest/CMakeLists.txt b/Tests/RunCMake/CompilerTest/CMakeLists.txt
new file mode 100644 (file)
index 0000000..aba1016
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.24)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CompilerTest/CUDA-stdout.txt b/Tests/RunCMake/CompilerTest/CUDA-stdout.txt
new file mode 100644 (file)
index 0000000..00d35df
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working CUDA compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/CUDA.cmake b/Tests/RunCMake/CompilerTest/CUDA.cmake
new file mode 100644 (file)
index 0000000..e8c19a0
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_CUDA_ABI_COMPILED FALSE)
+enable_language(CUDA)
diff --git a/Tests/RunCMake/CompilerTest/CXX-stdout.txt b/Tests/RunCMake/CompilerTest/CXX-stdout.txt
new file mode 100644 (file)
index 0000000..513fd85
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working CXX compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/CXX.cmake b/Tests/RunCMake/CompilerTest/CXX.cmake
new file mode 100644 (file)
index 0000000..e17cc9d
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_CXX_ABI_COMPILED FALSE)
+enable_language(CXX)
diff --git a/Tests/RunCMake/CompilerTest/Fortran-stdout.txt b/Tests/RunCMake/CompilerTest/Fortran-stdout.txt
new file mode 100644 (file)
index 0000000..d871fea
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working Fortran compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/Fortran.cmake b/Tests/RunCMake/CompilerTest/Fortran.cmake
new file mode 100644 (file)
index 0000000..8c83bd7
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_Fortran_ABI_COMPILED FALSE)
+enable_language(Fortran)
diff --git a/Tests/RunCMake/CompilerTest/HIP-stdout.txt b/Tests/RunCMake/CompilerTest/HIP-stdout.txt
new file mode 100644 (file)
index 0000000..8b3baa7
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working HIP compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/HIP.cmake b/Tests/RunCMake/CompilerTest/HIP.cmake
new file mode 100644 (file)
index 0000000..e56b77a
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_HIP_ABI_COMPILED FALSE)
+enable_language(HIP)
diff --git a/Tests/RunCMake/CompilerTest/ISPC-stdout.txt b/Tests/RunCMake/CompilerTest/ISPC-stdout.txt
new file mode 100644 (file)
index 0000000..7e45c91
--- /dev/null
@@ -0,0 +1,3 @@
+-- The ISPC compiler identification is [^
+]*
+-- Configuring done
diff --git a/Tests/RunCMake/CompilerTest/ISPC.cmake b/Tests/RunCMake/CompilerTest/ISPC.cmake
new file mode 100644 (file)
index 0000000..3ce7b28
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_ISPC_ABI_COMPILED FALSE)
+enable_language(ISPC)
diff --git a/Tests/RunCMake/CompilerTest/OBJC-stdout.txt b/Tests/RunCMake/CompilerTest/OBJC-stdout.txt
new file mode 100644 (file)
index 0000000..23f5120
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working OBJC compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/OBJC.cmake b/Tests/RunCMake/CompilerTest/OBJC.cmake
new file mode 100644 (file)
index 0000000..5ec842c
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_OBJC_ABI_COMPILED FALSE)
+enable_language(OBJC)
diff --git a/Tests/RunCMake/CompilerTest/OBJCXX-stdout.txt b/Tests/RunCMake/CompilerTest/OBJCXX-stdout.txt
new file mode 100644 (file)
index 0000000..8291904
--- /dev/null
@@ -0,0 +1,2 @@
+-- Check for working OBJCXX compiler: [^
+]* - works
diff --git a/Tests/RunCMake/CompilerTest/OBJCXX.cmake b/Tests/RunCMake/CompilerTest/OBJCXX.cmake
new file mode 100644 (file)
index 0000000..49df0aa
--- /dev/null
@@ -0,0 +1,3 @@
+# Pretend the ABI check failed in order to force the fall-back test to run.
+set(CMAKE_OBJCXX_ABI_COMPILED FALSE)
+enable_language(OBJCXX)
diff --git a/Tests/RunCMake/CompilerTest/RunCMakeTest.cmake b/Tests/RunCMake/CompilerTest/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..2dd9c7f
--- /dev/null
@@ -0,0 +1,25 @@
+include(RunCMake)
+
+run_cmake(C)
+run_cmake(CXX)
+
+if(CMake_TEST_CUDA)
+  run_cmake(CUDA)
+endif()
+
+if(CMake_TEST_Fortran)
+  run_cmake(Fortran)
+endif()
+
+if(CMake_TEST_HIP)
+  run_cmake(HIP)
+endif()
+
+if(CMake_TEST_ISPC)
+  run_cmake(ISPC)
+endif()
+
+if(CMake_TEST_OBJC)
+  run_cmake(OBJC)
+  run_cmake(OBJCXX)
+endif()
@@ -1,5 +1,6 @@
 # Change the executable suffix that try_compile will use for
-# COPY_FILE but not inside the test project.  This forces failure.
+# COPY_FILE but not inside the test project, to verify
+# we can handle envs that try and break everything
 get_property(in_try_compile GLOBAL PROPERTY IN_TRY_COMPILE)
 if(NOT in_try_compile)
   set(CMAKE_EXECUTABLE_SUFFIX .missing)
@@ -1,4 +1,4 @@
 -- Detecting C compiler ABI info
--- Detecting C compiler ABI info - failed.*
+-- Detecting C compiler ABI info - done.*
 -- Configuring done
 -- Generating done
similarity index 71%
rename from Tests/RunCMake/Configure/FailCopyFileABI.cmake
rename to Tests/RunCMake/Configure/CopyFileABI.cmake
index 74efd97..4eee100 100644 (file)
@@ -1,2 +1,2 @@
-set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/FailCopyFileABI-override.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/CopyFileABI-override.cmake)
 enable_language(C)
diff --git a/Tests/RunCMake/Configure/FailCopyFileABI-check.cmake b/Tests/RunCMake/Configure/FailCopyFileABI-check.cmake
deleted file mode 100644 (file)
index db0cb0a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-set(log "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/CMakeError.log")
-if(EXISTS "${log}")
-  file(READ "${log}" error_log)
-else()
-  set(error_log "")
-endif()
-string(REPLACE "\r\n" "\n" regex "Cannot copy output executable.*
-to destination specified by COPY_FILE:.*
-Unable to find the executable at any of:
-  .*\\.missing")
-if(NOT error_log MATCHES "${regex}")
-  string(REGEX REPLACE "\n" "\n  " error_log "  ${error_log}")
-  set(RunCMake_TEST_FAILED "Log file:\n ${log}\ndoes not have expected COPY_FILE failure message:\n${error_log}")
-endif()
index 9fd4499..750fa3c 100644 (file)
@@ -1,9 +1,9 @@
 include(RunCMake)
 
 run_cmake(ContinueAfterError)
+run_cmake(CopyFileABI)
 run_cmake(CustomTargetAfterError)
 run_cmake(ErrorLogs)
-run_cmake(FailCopyFileABI)
 
 # Use a single build tree for a few tests without cleaning.
 set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/RerunCMake-build)
diff --git a/Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build1-stdout.txt b/Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build1-stdout.txt
new file mode 100644 (file)
index 0000000..2c223f7
--- /dev/null
@@ -0,0 +1,6 @@
+.*-- once: configure
+.*-- once: build
+.*-- once: install
+.*-- always: configure
+.*-- always: build
+.*-- always: install
diff --git a/Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build2-stdout.txt b/Tests/RunCMake/ExternalProject/BUILD_ALWAYS-build2-stdout.txt
new file mode 100644 (file)
index 0000000..d697490
--- /dev/null
@@ -0,0 +1,2 @@
+.*-- always: build
+.*-- always: install
diff --git a/Tests/RunCMake/ExternalProject/BUILD_ALWAYS.cmake b/Tests/RunCMake/ExternalProject/BUILD_ALWAYS.cmake
new file mode 100644 (file)
index 0000000..2e5fc6f
--- /dev/null
@@ -0,0 +1,20 @@
+include(ExternalProject)
+
+ExternalProject_Add(once
+  DOWNLOAD_COMMAND ""
+  CONFIGURE_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/once-configure.cmake
+  BUILD_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/once-build.cmake
+  INSTALL_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/once-install.cmake
+  )
+
+ExternalProject_Add(always
+  DEPENDS once
+  DOWNLOAD_COMMAND ""
+  CONFIGURE_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/always-configure.cmake
+  BUILD_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/always-build.cmake
+        COMMAND "${CMAKE_COMMAND}" -E copy_if_different ${CMAKE_CURRENT_LIST_FILE}
+                   "${CMAKE_CURRENT_BINARY_DIR}/byproduct.txt"
+  BUILD_BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/byproduct.txt"
+  BUILD_ALWAYS 1
+  INSTALL_COMMAND "${CMAKE_COMMAND}" -P ${CMAKE_CURRENT_BINARY_DIR}/always-install.cmake
+  )
index 2588d6c..f152f5b 100644 (file)
@@ -177,6 +177,28 @@ if(doSubstitutionTest)
     __ep_test_with_build(Substitutions)
 endif()
 
+function(__ep_test_BUILD_ALWAYS)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/BUILD_ALWAYS-build)
+  run_cmake(BUILD_ALWAYS)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-configure.cmake" [[message(STATUS "once: configure")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-build.cmake" [[message(STATUS "once: build")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-install.cmake" [[message(STATUS "once: install")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-configure.cmake" [[message(STATUS "always: configure")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-build.cmake" [[message(STATUS "always: build")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-install.cmake" [[message(STATUS "always: install")]])
+  run_cmake_command(BUILD_ALWAYS-build1 ${CMAKE_COMMAND} --build . --target always)
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-configure.cmake" [[message(FATAL_ERROR "once: configure should not run again")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-build.cmake" [[message(FATAL_ERROR "once: build should not run again")]])
+  file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-install.cmake" [[message(FATAL_ERROR "once: install should not run again")]])
+  if(NOT RunCMake_GENERATOR MATCHES "^(Xcode|Visual Studio 9 )")
+    # The Xcode and VS 9 build systems decide to run this every time.
+    file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-configure.cmake" [[message(FATAL_ERROR "always: configure should not run again")]])
+  endif()
+  run_cmake_command(BUILD_ALWAYS-build2 ${CMAKE_COMMAND} --build . --target always)
+endfunction()
+__ep_test_BUILD_ALWAYS()
+
 function(__ep_test_CONFIGURE_HANDLED_BY_BUILD)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CONFIGURE_HANDLED_BY_BUILD-build)
   run_cmake(CONFIGURE_HANDLED_BY_BUILD)
index 28b8570..3b095a6 100644 (file)
@@ -38,9 +38,15 @@ run_cmake(exact_1.2.3.5)
 unset(RunCMake_DEFAULT_stderr)
 
 # check if searching for a version 0 works
-list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DPseudo_VERSION=0")
+set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DPseudo_VERSION=0")
 run_cmake(exact_0_matching)
 
+set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DPseudo_VERSION=")
+run_cmake(empty_version)
+
+set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}")
+run_cmake(exact_1_no_version_var)
+
 # check custom error message
 set(RunCMake_TEST_OPTIONS "-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_LIST_DIR}" "-DCustomMessage_VERSION=1.2.3.4")
 run_cmake(custom_message_1)
diff --git a/Tests/RunCMake/FPHSA/empty_version-result.txt b/Tests/RunCMake/FPHSA/empty_version-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/FPHSA/empty_version-stderr.txt b/Tests/RunCMake/FPHSA/empty_version-stderr.txt
new file mode 100644 (file)
index 0000000..3b60c7a
--- /dev/null
@@ -0,0 +1,9 @@
+^CMake Error at [^
+]*/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(message\):
+  Could NOT find Pseudo: \(Required is at least version "1"\) \(found TRUE\)
+Call Stack \(most recent call first\):
+  [^
+]*/Modules/FindPackageHandleStandardArgs.cmake:[0-9]+ \(_FPHSA_FAILURE_MESSAGE\)
+  FindPseudo.cmake:[0-9]+ \(find_package_handle_standard_args\)
+  empty_version.cmake:[0-9]+ \(find_package\)
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/FPHSA/empty_version.cmake b/Tests/RunCMake/FPHSA/empty_version.cmake
new file mode 100644 (file)
index 0000000..7b7ff34
--- /dev/null
@@ -0,0 +1 @@
+find_package(Pseudo 1 REQUIRED)
diff --git a/Tests/RunCMake/FPHSA/exact_1_no_version_var-stdout.txt b/Tests/RunCMake/FPHSA/exact_1_no_version_var-stdout.txt
new file mode 100644 (file)
index 0000000..7cbdf6e
--- /dev/null
@@ -0,0 +1 @@
+-- Found Pseudo: TRUE \(Required is at least version "1"\)
diff --git a/Tests/RunCMake/FPHSA/exact_1_no_version_var.cmake b/Tests/RunCMake/FPHSA/exact_1_no_version_var.cmake
new file mode 100644 (file)
index 0000000..7b7ff34
--- /dev/null
@@ -0,0 +1 @@
+find_package(Pseudo 1 REQUIRED)
diff --git a/Tests/RunCMake/FetchContent/IncludesNonSystem/CMakeLists.txt b/Tests/RunCMake/FetchContent/IncludesNonSystem/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f319e01
--- /dev/null
@@ -0,0 +1,2 @@
+add_library(barnonsys STATIC bar.cpp)
+add_executable(foononsys foo.cpp)
diff --git a/Tests/RunCMake/FetchContent/IncludesNonSystem/bar.cpp b/Tests/RunCMake/FetchContent/IncludesNonSystem/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesNonSystem/foo.cpp b/Tests/RunCMake/FetchContent/IncludesNonSystem/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/CMakeLists.txt b/Tests/RunCMake/FetchContent/IncludesSystem/CMakeLists.txt
new file mode 100644 (file)
index 0000000..60c6820
--- /dev/null
@@ -0,0 +1,20 @@
+project(SystemSub NONE)
+
+FetchContent_Declare(
+  SubSub1
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/SubSub1
+  SYSTEM
+)
+FetchContent_Declare(
+  SubSub2
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/SubSub2
+)
+
+FetchContent_MakeAvailable(SubSub1 SubSub2)
+
+add_library(bar STATIC bar.cpp)
+
+add_library(foo STATIC foo.cpp)
+set_target_properties(foo PROPERTIES SYSTEM OFF)
+
+add_executable(zot zot.cpp)
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/CMakeLists.txt b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..291339b
--- /dev/null
@@ -0,0 +1,6 @@
+add_library(subsub1bar STATIC bar.cpp)
+
+add_library(subsub1foo STATIC foo.cpp)
+set_target_properties(subsub1foo PROPERTIES SYSTEM OFF)
+
+add_executable(subsub1zot zot.cpp)
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/bar.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/foo.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/zot.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub1/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/CMakeLists.txt b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5755742
--- /dev/null
@@ -0,0 +1,6 @@
+add_library(subsub2bar STATIC bar.cpp)
+
+add_library(subsub2foo STATIC foo.cpp)
+set_target_properties(subsub2foo PROPERTIES SYSTEM OFF)
+
+add_executable(subsub2zot zot.cpp)
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/bar.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/foo.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/zot.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/SubSub2/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/bar.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/foo.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/FetchContent/IncludesSystem/zot.cpp b/Tests/RunCMake/FetchContent/IncludesSystem/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
index a7ccf83..bb27491 100644 (file)
@@ -8,6 +8,7 @@ run_cmake(FirstDetailsWin)
 run_cmake(DownloadTwice)
 run_cmake(DownloadFile)
 run_cmake(SameGenerator)
+run_cmake(System)
 run_cmake(VarDefinitions)
 run_cmake(VarPassthroughs)
 run_cmake(GetProperties)
diff --git a/Tests/RunCMake/FetchContent/System.cmake b/Tests/RunCMake/FetchContent/System.cmake
new file mode 100644 (file)
index 0000000..c10e2af
--- /dev/null
@@ -0,0 +1,37 @@
+enable_language(CXX)
+
+include(FetchContent)
+
+FetchContent_Declare(
+  IncludesSystem
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/IncludesSystem
+  SYSTEM
+)
+FetchContent_MakeAvailable(IncludesSystem)
+
+FetchContent_Declare(
+  IncludesNonSystem
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/IncludesNonSystem
+)
+FetchContent_MakeAvailable(IncludesNonSystem)
+
+function(check_target_system target expected_value)
+  get_target_property(var ${target} SYSTEM)
+  if ((var AND NOT expected_value) OR (NOT var AND expected_value))
+    message(SEND_ERROR "\
+The 'SYSTEM' property of ${target} should be ${expected_value}, \
+but got ${var}")
+  endif()
+endfunction()
+
+check_target_system(foo OFF)
+check_target_system(bar ON)
+check_target_system(zot ON)
+check_target_system(subsub1foo OFF)
+check_target_system(subsub1bar ON)
+check_target_system(subsub1zot ON)
+check_target_system(subsub2foo OFF)
+check_target_system(subsub2bar ON)
+check_target_system(subsub2zot ON)
+check_target_system(foononsys OFF)
+check_target_system(barnonsys OFF)
index 44025d3..9a66cde 100644 (file)
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.13)
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
index f3d20d1..28679bb 100644 (file)
@@ -108,7 +108,11 @@ def check_query_json_empty(q):
     check_error_re(q, "value, object or array expected")
 
 def check_query_json_extra(q):
-    check_error_re(q, "Extra non-whitespace after JSON value")
+    if bool(os.environ.get("CMake_JSONCPP_PRE_1_7_5", "")) and is_dict(q) and sorted(q.keys()) == ["responses"]:
+        # jsoncpp < 1.7.5 did not diagnose extra non-whitespace characters
+        check_error(q["responses"], "'requests' member missing")
+    else:
+        check_error_re(q, "Extra non-whitespace after JSON value")
 
 def check_query_not_file(q):
     check_error_re(q, "failed to read from file")
index 61dce17..961b73a 100644 (file)
@@ -39,6 +39,10 @@ if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
   set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release\\;MinSizeRel\\;RelWithDebInfo")
 endif()
 
+if(JsonCpp_VERSION_STRING AND JsonCpp_VERSION_STRING VERSION_LESS 1.7.5)
+  set(ENV{CMake_JSONCPP_PRE_1_7_5} 1)
+endif()
+
 run_cmake(Nothing)
 run_cmake(Empty)
 run_cmake(EmptyClient)
index d5f596e..b7623de 100644 (file)
@@ -213,6 +213,16 @@ def check_directory(c):
                 assert is_int(at["index"])
                 assert c["targets"][at["index"]]["name"] == et["index"]
 
+            if e.get("cxxModuleBmiTarget", None) is not None:
+                expected_keys.append("cxxModuleBmiTarget")
+                et = e["cxxModuleBmiTarget"]
+                at = a["cxxModuleBmiTarget"]
+                assert is_dict(at)
+                assert sorted(at.keys()) == ["id", "index"]
+                assert matches(at["id"], et["id"])
+                assert is_int(at["index"])
+                assert c["targets"][at["index"]]["name"] == et["index"]
+
             if e["backtrace"] is not None:
                 expected_keys.append("backtrace")
                 check_backtrace(d, a["backtrace"], e["backtrace"])
@@ -654,6 +664,7 @@ def gen_check_directories(c, g):
         read_codemodel_json_data("directories/dir_dir.json"),
         read_codemodel_json_data("directories/external.json"),
         read_codemodel_json_data("directories/fileset.json"),
+        read_codemodel_json_data("directories/subdir.json"),
     ]
 
     if matches(g["name"], "^Visual Studio "):
@@ -712,6 +723,7 @@ def gen_check_targets(c, g, inSource):
         read_codemodel_json_data("targets/c_shared_exe.json"),
         read_codemodel_json_data("targets/c_static_lib.json"),
         read_codemodel_json_data("targets/c_static_exe.json"),
+        read_codemodel_json_data("targets/c_subdir.json"),
 
         read_codemodel_json_data("targets/all_build_cxx.json"),
         read_codemodel_json_data("targets/zero_check_cxx.json"),
index 6514910..de8b177 100644 (file)
@@ -10,7 +10,7 @@
         "^cxx_alias_exe::@53632cba2752272bb008$"
     ],
     "projectName": "Alias",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": null,
     "installers": []
 }
index c89e4f9..e57191c 100644 (file)
@@ -10,7 +10,7 @@
         "^custom_tgt::@c11385ffed57b860da63$"
     ],
     "projectName": "Custom",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": null,
     "installers": []
 }
index 8052c1a..28f2b99 100644 (file)
@@ -16,7 +16,7 @@
         "^cxx_static_lib::@a56b12a3f5c0529fb296$"
     ],
     "projectName": "Cxx",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": true,
     "installers": [
         {
index 8509f08..2a3756e 100644 (file)
@@ -7,7 +7,7 @@
     ],
     "targetIds": null,
     "projectName": "codemodel-v2",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": null,
     "installers": []
 }
index 27184cd..12677f2 100644 (file)
@@ -5,7 +5,7 @@
     "childSources": null,
     "targetIds": null,
     "projectName": "codemodel-v2",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": null,
     "installers": []
 }
index 6d2952d..f1199c3 100644 (file)
@@ -9,7 +9,7 @@
         "^generated_exe::@[0-9a-f]+$"
     ],
     "projectName": "External",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": true,
     "installers": [
         {
index 4774a13..c4df2ec 100644 (file)
@@ -8,7 +8,7 @@
     "^c_headers_2::@6b8db101d64c125f29fe$"
   ],
   "projectName": "codemodel-v2",
-  "minimumCMakeVersion": "3.12",
+  "minimumCMakeVersion": "3.13",
   "hasInstallRule": true,
   "installers": [
     {
index 92b9526..8210d7f 100644 (file)
@@ -13,7 +13,7 @@
         "^link_imported_static_exe::@ba7eb709d0b48779c6c8$"
     ],
     "projectName": "Imported",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": true,
     "installers": [
         {
index 90664dc..08edd64 100644 (file)
@@ -9,7 +9,7 @@
         "^iface_srcs::@25b7fa8ea00134654b85$"
     ],
     "projectName": "Interface",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": null,
     "installers": []
 }
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/subdir.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/subdir.json
new file mode 100644 (file)
index 0000000..996da47
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "source": "^subdir$",
+    "build": "^subdir$",
+    "parentSource": "^\\.$",
+    "childSources": null,
+    "targetIds": null,
+    "projectName": "codemodel-v2",
+    "minimumCMakeVersion": "3.13",
+    "hasInstallRule": null,
+    "installers": []
+}
index e7b146f..aed07e2 100644 (file)
@@ -11,7 +11,8 @@
         "^object$",
         "^.*/Tests/RunCMake/FileAPIExternalSource$",
         "^dir$",
-        "^fileset$"
+        "^fileset$",
+        "^subdir$"
     ],
     "targetIds": [
         "^ALL_BUILD::@6890427a1f51a3e7e1df$",
         "^c_shared_lib::@6890427a1f51a3e7e1df$",
         "^c_static_exe::@6890427a1f51a3e7e1df$",
         "^c_static_lib::@6890427a1f51a3e7e1df$",
+        "^c_subdir::@6890427a1f51a3e7e1df$",
         "^interface_exe::@6890427a1f51a3e7e1df$"
     ],
     "projectName": "codemodel-v2",
-    "minimumCMakeVersion": "3.12",
+    "minimumCMakeVersion": "3.13",
     "hasInstallRule": true,
     "installers": [
         {
@@ -48,7 +50,7 @@
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 39,
+                    "line": 42,
                     "command": "install",
                     "hasParent": true
                 },
@@ -93,7 +95,7 @@
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 42,
+                    "line": 45,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 42,
+                    "line": 45,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 42,
+                    "line": 45,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 42,
+                    "line": 45,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 47,
+                    "line": 50,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 49,
+                    "line": 52,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 50,
+                    "line": 53,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 51,
+                    "line": 54,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 52,
+                    "line": 55,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 53,
+                    "line": 56,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 54,
+                    "line": 57,
                     "command": "install",
                     "hasParent": true
                 },
             "backtrace": [
                 {
                     "file": "^codemodel-v2\\.cmake$",
-                    "line": 55,
+                    "line": 58,
                     "command": "install",
                     "hasParent": true
                 },
index 0d6c4a1..151c0a8 100644 (file)
@@ -14,7 +14,8 @@
         "^\\.$",
         "^dir$",
         "^dir/dir$",
-        "^fileset$"
+        "^fileset$",
+        "^subdir$"
     ],
     "targetIds": [
         "^ALL_BUILD::@6890427a1f51a3e7e1df$",
@@ -26,6 +27,7 @@
         "^c_shared_exe::@6890427a1f51a3e7e1df$",
         "^c_static_lib::@6890427a1f51a3e7e1df$",
         "^c_static_exe::@6890427a1f51a3e7e1df$",
+        "^c_subdir::@6890427a1f51a3e7e1df$",
         "^c_headers_1::@6b8db101d64c125f29fe$",
         "^c_headers_2::@6b8db101d64c125f29fe$"
     ]
index 4e772a7..0d45d07 100644 (file)
             "backtrace": null
         },
         {
+            "id": "^c_subdir::@6890427a1f51a3e7e1df$",
+            "backtrace": null
+        },
+        {
             "id": "^c_static_exe::@6890427a1f51a3e7e1df$",
             "backtrace": null
         },
index 3c9ace3..3392404 100644 (file)
@@ -27,7 +27,7 @@
             ]
         },
         {
-            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$",
+            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/c_object_lib\\.build)/.*/empty(\\.c)?\\.o(bj)?$",
             "isGenerated": true,
             "sourceGroupName": "Object Libraries",
             "compileGroupLanguage": null,
@@ -57,7 +57,7 @@
         {
             "name": "Object Libraries",
             "sourcePaths": [
-                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.c)?\\.o(bj)?$"
+                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/c_object_lib\\.build)/.*/empty(\\.c)?\\.o(bj)?$"
             ]
         }
     ],
index e3a20df..1917f92 100644 (file)
@@ -64,7 +64,7 @@
     "nameOnDisk": null,
     "artifacts": [
         {
-            "path": "^object/.*/empty(\\.c)?\\.o(bj)?$",
+            "path": "^(object|build/c_object_lib\\.build)/.*/empty(\\.c)?\\.o(bj)?$",
             "_dllExtra": false
         }
     ],
index b4318dd..9a210ff 100644 (file)
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 42,
+                        "line": 45,
                         "command": "install",
                         "hasParent": true
                     },
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 42,
+                        "line": 45,
                         "command": "install",
                         "hasParent": true
                     },
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 47,
+                        "line": 50,
                         "command": "install",
                         "hasParent": true
                     },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json
new file mode 100644 (file)
index 0000000..12ec917
--- /dev/null
@@ -0,0 +1,150 @@
+{
+    "name": "c_subdir",
+    "id": "^c_subdir::@6890427a1f51a3e7e1df$",
+    "directorySource": "^\\.$",
+    "projectName": "codemodel-v2",
+    "type": "STATIC_LIBRARY",
+    "isGeneratorProvided": null,
+    "sources": [
+        {
+            "path": "^subdir/empty\\.c$",
+            "isGenerated": null,
+            "sourceGroupName": "Source Files",
+            "compileGroupLanguage": "C",
+            "backtrace": [
+                {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": 4,
+                    "command": "target_sources",
+                    "hasParent": true
+                },
+                {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        }
+    ],
+    "sourceGroups": [
+        {
+            "name": "Source Files",
+            "sourcePaths": [
+                "^subdir/empty\\.c$"
+            ]
+        }
+    ],
+    "compileGroups": [
+        {
+            "language": "C",
+            "sourcePaths": [
+                "^subdir/empty\\.c$"
+            ],
+            "includes": [
+              {
+                "path": "^.*/Tests/RunCMake/FileAPI/subdir$",
+                "isSystem": null,
+                "backtrace": [
+                  {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": 2,
+                    "command": "target_include_directories",
+                    "hasParent": true
+                  },
+                  {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                  }
+                ]
+              }
+            ],
+            "defines": [
+                {
+                    "define": "SUBDIR",
+                    "backtrace": [
+                        {
+                            "file": "^subdir/CMakeLists\\.txt$",
+                            "line": 1,
+                            "command": "target_compile_definitions",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^subdir/CMakeLists\\.txt$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": false
+                        }
+                    ]
+                }
+            ],
+            "compileCommandFragments": null
+        }
+    ],
+    "backtrace": [
+        {
+            "file": "^codemodel-v2\\.cmake$",
+            "line": 17,
+            "command": "add_library",
+            "hasParent": true
+        },
+        {
+            "file": "^codemodel-v2\\.cmake$",
+            "line": null,
+            "command": null,
+            "hasParent": true
+        },
+        {
+            "file": "^CMakeLists\\.txt$",
+            "line": 3,
+            "command": "include",
+            "hasParent": true
+        },
+        {
+            "file": "^CMakeLists\\.txt$",
+            "line": null,
+            "command": null,
+            "hasParent": false
+        }
+    ],
+    "folder": null,
+    "nameOnDisk": "^(lib)?c_subdir\\.(a|lib)$",
+    "artifacts": [
+        {
+            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_subdir\\.(a|lib)$",
+            "_dllExtra": false
+        }
+    ],
+    "build": "^\\.$",
+    "source": "^\\.$",
+    "install": null,
+    "link": null,
+    "archive": {
+        "lto": null
+    },
+    "dependencies": [
+        {
+            "id": "^c_lib::@6890427a1f51a3e7e1df$",
+            "backtrace": [
+                {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": 3,
+                    "command": "target_link_libraries",
+                    "hasParent": true
+                },
+                {
+                    "file": "^subdir/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "id": "^ZERO_CHECK::@6890427a1f51a3e7e1df$",
+            "backtrace": null
+        }
+    ]
+}
index 5769f0c..16d074a 100644 (file)
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 39,
+                        "line": 42,
                         "command": "install",
                         "hasParent": true
                     },
index 119c91d..e8d6218 100644 (file)
@@ -27,7 +27,7 @@
             ]
         },
         {
-            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$",
+            "path": "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/cxx_object_lib\\.build)/.*/empty(\\.cxx)?\\.o(bj)?$",
             "isGenerated": true,
             "sourceGroupName": "Object Libraries",
             "compileGroupLanguage": null,
@@ -57,7 +57,7 @@
         {
             "name": "Object Libraries",
             "sourcePaths": [
-                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/object/.*/empty(\\.cxx)?\\.o(bj)?$"
+                "^.*/Tests/RunCMake/FileAPI/codemodel-v2-build/(object|build/cxx_object_lib\\.build)/.*/empty(\\.cxx)?\\.o(bj)?$"
             ]
         }
     ],
index 8e99f7d..24b391b 100644 (file)
@@ -64,7 +64,7 @@
     "nameOnDisk": null,
     "artifacts": [
         {
-            "path": "^object/.*/empty(\\.cxx)?\\.o(bj)?$",
+            "path": "^(object|build/cxx_object_lib\\.build)/.*/empty(\\.cxx)?\\.o(bj)?$",
             "_dllExtra": false
         }
     ],
index 1fe4d67..03f4cb9 100644 (file)
@@ -91,7 +91,7 @@
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 42,
+                        "line": 45,
                         "command": "install",
                         "hasParent": true
                     },
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 42,
+                        "line": 45,
                         "command": "install",
                         "hasParent": true
                     },
                 "backtrace": [
                     {
                         "file": "^codemodel-v2\\.cmake$",
-                        "line": 47,
+                        "line": 50,
                         "command": "install",
                         "hasParent": true
                     },
index 019eb87..09db216 100644 (file)
@@ -14,6 +14,9 @@ add_library(c_static_lib STATIC empty.c)
 add_executable(c_static_exe empty.c)
 target_link_libraries(c_static_exe PRIVATE c_static_lib)
 
+add_library(c_subdir STATIC)
+add_subdirectory(subdir)
+
 add_subdirectory(cxx)
 add_subdirectory(alias)
 add_subdirectory(object)
diff --git a/Tests/RunCMake/FileAPI/subdir/CMakeLists.txt b/Tests/RunCMake/FileAPI/subdir/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b8f4550
--- /dev/null
@@ -0,0 +1,4 @@
+target_compile_definitions(c_subdir PRIVATE SUBDIR)
+target_include_directories(c_subdir PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(c_subdir PRIVATE c_lib)
+target_sources(c_subdir PRIVATE empty.c)
diff --git a/Tests/RunCMake/FileAPI/subdir/empty.c b/Tests/RunCMake/FileAPI/subdir/empty.c
new file mode 100644 (file)
index 0000000..e69de29
index 3908f42..dad0dd3 100644 (file)
@@ -13,6 +13,9 @@ run_cmake(zip)
 # Extracting only selected files or directories
 run_cmake(zip-filtered)
 
+run_cmake(create-missing-args)
+run_cmake(extract-missing-args)
+
 run_cmake(unsupported-format)
 run_cmake(zip-with-bad-compression)
 run_cmake(7zip-with-bad-compression)
diff --git a/Tests/RunCMake/File_Archive/create-missing-args-result.txt b/Tests/RunCMake/File_Archive/create-missing-args-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/create-missing-args-stderr.txt
new file mode 100644 (file)
index 0000000..fd026f9
--- /dev/null
@@ -0,0 +1,19 @@
+^CMake Error at create-missing-args.cmake:[0-9]+ \(file\):
+  Error after keyword "COMPRESSION":
+
+    missing required value
+
+  Error after keyword "COMPRESSION_LEVEL":
+
+    missing required value
+
+  Error after keyword "FORMAT":
+
+    missing required value
+
+  Error after keyword "OUTPUT":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Archive/create-missing-args.cmake b/Tests/RunCMake/File_Archive/create-missing-args.cmake
new file mode 100644 (file)
index 0000000..a0c84d2
--- /dev/null
@@ -0,0 +1,8 @@
+file(ARCHIVE_CREATE
+  OUTPUT              # missing output path
+  FORMAT              # missing output format
+  COMPRESSION         # missing compression type
+  COMPRESSION_LEVEL   # missing compression level
+  MTIME               # missing modification time
+  PATHS               # no paths
+  )
diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-result.txt b/Tests/RunCMake/File_Archive/extract-missing-args-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt b/Tests/RunCMake/File_Archive/extract-missing-args-stderr.txt
new file mode 100644 (file)
index 0000000..0c93ece
--- /dev/null
@@ -0,0 +1,11 @@
+^CMake Error at extract-missing-args.cmake:[0-9]+ \(file\):
+  Error after keyword "DESTINATION":
+
+    missing required value
+
+  Error after keyword "INPUT":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Archive/extract-missing-args.cmake b/Tests/RunCMake/File_Archive/extract-missing-args.cmake
new file mode 100644 (file)
index 0000000..21c5d99
--- /dev/null
@@ -0,0 +1,5 @@
+file(ARCHIVE_EXTRACT
+  INPUT         # missing input
+  DESTINATION   # missing destination
+  PATTERNS      # no patterns
+  )
index a6ea314..72292f9 100644 (file)
@@ -1,4 +1,7 @@
 CMake Error at BadArgContent.cmake:[0-9]+ \(file\):
-  file CONFIGURE CONTENT option needs a value.
+  Error after keyword "CONTENT":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index b5a924c..d793f48 100644 (file)
@@ -1,4 +1,7 @@
 CMake Error at BadArgOutput.cmake:[0-9]+ \(file\):
-  file CONFIGURE OUTPUT option needs a value.
+  Error after keyword "OUTPUT":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/File_Configure/NoArgContent-result.txt b/Tests/RunCMake/File_Configure/NoArgContent-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/NoArgContent-stderr.txt b/Tests/RunCMake/File_Configure/NoArgContent-stderr.txt
new file mode 100644 (file)
index 0000000..2e8dd9a
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at NoArgContent.cmake:[0-9]+ \(file\):
+  file CONFIGURE CONTENT option is mandatory.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Configure/NoArgContent.cmake b/Tests/RunCMake/File_Configure/NoArgContent.cmake
new file mode 100644 (file)
index 0000000..cf52c46
--- /dev/null
@@ -0,0 +1 @@
+file(CONFIGURE OUTPUT "")
diff --git a/Tests/RunCMake/File_Configure/NoArgOutput-result.txt b/Tests/RunCMake/File_Configure/NoArgOutput-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt b/Tests/RunCMake/File_Configure/NoArgOutput-stderr.txt
new file mode 100644 (file)
index 0000000..53de48b
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at NoArgOutput.cmake:[0-9]+ \(file\):
+  file CONFIGURE OUTPUT option is mandatory.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/File_Configure/NoArgOutput.cmake b/Tests/RunCMake/File_Configure/NoArgOutput.cmake
new file mode 100644 (file)
index 0000000..77e9cdc
--- /dev/null
@@ -0,0 +1 @@
+file(CONFIGURE CONTENT "")
index 5022985..008ce67 100644 (file)
@@ -9,6 +9,8 @@ run_cmake(DirOutput)
 run_cmake(NewLineStyle-NoArg)
 run_cmake(NewLineStyle-ValidArg)
 run_cmake(NewLineStyle-WrongArg)
+run_cmake(NoArgOutput)
+run_cmake(NoArgContent)
 run_cmake(SubDir)
 run_cmake(AtOnly)
 run_cmake(EscapeQuotes)
index e823b25..708e6be 100644 (file)
@@ -1,4 +1,7 @@
 CMake Error at EmptyCondition1.cmake:2 \(file\):
-  file Incorrect arguments to GENERATE subcommand.
+  Error after keyword "CONDITION":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/File_Generate/InputAndContent-check.cmake b/Tests/RunCMake/File_Generate/InputAndContent-check.cmake
new file mode 100644 (file)
index 0000000..5c9b803
--- /dev/null
@@ -0,0 +1,8 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/output-INPUT.txt" input)
+if(NOT input MATCHES "INPUT file")
+  string(APPEND RunCMake_TEST_FAILED "INPUT incorrectly overridden by CONTENT")
+endif()
+file(READ "${RunCMake_TEST_BINARY_DIR}/output-CONTENT.txt" content)
+if(NOT content MATCHES "CONTENT argument")
+  string(APPEND RunCMake_TEST_FAILED "CONTENT incorrectly overridden by INPUT")
+endif()
diff --git a/Tests/RunCMake/File_Generate/InputAndContent-input.txt b/Tests/RunCMake/File_Generate/InputAndContent-input.txt
new file mode 100644 (file)
index 0000000..73f162b
--- /dev/null
@@ -0,0 +1 @@
+INPUT file
diff --git a/Tests/RunCMake/File_Generate/InputAndContent.cmake b/Tests/RunCMake/File_Generate/InputAndContent.cmake
new file mode 100644 (file)
index 0000000..9c3977a
--- /dev/null
@@ -0,0 +1,10 @@
+file(GENERATE
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-INPUT.txt"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/InputAndContent-input.txt"
+  CONTENT "CONTENT argument"
+)
+file(GENERATE
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output-CONTENT.txt"
+  CONTENT "CONTENT argument"
+  INPUT "${CMAKE_CURRENT_SOURCE_DIR}/InputAndContent-input.txt"
+)
index bc71f2f..b1b7f80 100644 (file)
@@ -1,4 +1,7 @@
 CMake Error at NewLineStyle-NoArg.cmake:[0-9]+ \(file\):
-  file Incorrect arguments to GENERATE subcommand.
+  Error after keyword "NEWLINE_STYLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
index be3bf04..5a670ae 100644 (file)
@@ -17,6 +17,7 @@ run_cmake(EmptyCondition2)
 run_cmake(BadCondition)
 run_cmake(DebugEvaluate)
 run_cmake(GenerateSource)
+run_cmake(InputAndContent)
 run_cmake(OutputNameMatchesSources)
 run_cmake(OutputNameMatchesObjects)
 run_cmake(OutputNameMatchesOtherSources)
index f479dcf..661ae3f 100644 (file)
@@ -24,6 +24,27 @@ endif()
 # We need a real pkg-config to run the test for get_variable.
 find_package(PkgConfig)
 if (PKG_CONFIG_FOUND)
+  string(FIND "${CMAKE_CURRENT_BINARY_DIR}" " " IS_SPACES_IN_PATH)
+  if(IS_SPACES_IN_PATH GREATER -1)
+    string(REPLACE " " "\\ " ESCAPED_ROOT "${CMAKE_CURRENT_BINARY_DIR}")
+    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test_spaces.pc" "
+libdir=${ESCAPED_ROOT}
+Name: test_spaces.pc
+Version: 0.0
+Description: test spaces
+Libs: -L\${libdir}
+")
+    set(PKG_CONFIG_PATH_SAVED "$ENV{PKG_CONFIG_PATH}")
+    set(ENV{PKG_CONFIG_PATH} "${CMAKE_CURRENT_BINARY_DIR}")
+    execute_process(COMMAND "${PKG_CONFIG_EXECUTABLE}" --libs test_spaces
+                    ERROR_QUIET COMMAND_ERROR_IS_FATAL ANY
+                    OUTPUT_VARIABLE test_spaces_LIBS)
+    set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH_SAVED}")
+    string(STRIP "${test_spaces_LIBS}" test_spaces_LIBS_STRIPPED)
+    if(NOT "${test_spaces_LIBS_STRIPPED}" STREQUAL "-L${ESCAPED_ROOT}")
+      set(PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH TRUE)
+    endif()
+  endif()
   run_cmake(FindPkgConfig_GET_VARIABLE)
   run_cmake(FindPkgConfig_GET_VARIABLE_PREFIX_PATH)
   run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH)
@@ -32,5 +53,7 @@ if (PKG_CONFIG_FOUND)
   run_cmake(FindPkgConfig_VERSION_OPERATORS)
   run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME)
   run_cmake(FindPkgConfig_empty_target)
-  run_cmake(FindPkgConfig_LIBRARY_PATH)
+  if(NOT PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH)
+    run_cmake(FindPkgConfig_LIBRARY_PATH)
+  endif()
 endif ()
index 4663166..58b70a3 100644 (file)
@@ -13,3 +13,21 @@ set_target_properties(Gui PROPERTIES
 add_executable(app main.c)
 
 target_link_libraries(app PRIVATE Gui)
+
+
+# Same test but with generation done in custom directories
+add_library(Gui2 SHARED Gui.c "${input_header}")
+set_target_properties(Gui2 PROPERTIES
+    PUBLIC_HEADER "${input_header}"
+    FRAMEWORK TRUE
+    LIBRARY_OUTPUT_DIRECTORY lib
+)
+
+add_executable(app2 main2.c)
+set_target_properties(Gui2 PROPERTIES
+    PUBLIC_HEADER "${input_header}"
+    FRAMEWORK TRUE
+    RUNTIME_OUTPUT_DIRECTORY bin
+)
+
+target_link_libraries(app2 PRIVATE Gui2)
diff --git a/Tests/RunCMake/Framework/main2.c b/Tests/RunCMake/Framework/main2.c
new file mode 100644 (file)
index 0000000..11f4e4d
--- /dev/null
@@ -0,0 +1,9 @@
+
+#include <Gui2/Gui.h>
+
+int main()
+{
+  foo();
+
+  return 0;
+}
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-result.txt b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-stderr.txt b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW-stderr.txt
new file mode 100644 (file)
index 0000000..fb61d4b
--- /dev/null
@@ -0,0 +1,5 @@
+^CMake Error in CMakeLists.txt:
+  MSVC_DEBUG_INFORMATION_FORMAT value 'BogusValue' not known for this (C|CXX)
+  compiler.
++
+CMake Generate step failed\.  Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NEW.cmake
new file mode 100644 (file)
index 0000000..165ea38
--- /dev/null
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 NEW)
+include(CMP0141-common.cmake)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NoEffect.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-NoEffect.cmake
new file mode 100644 (file)
index 0000000..82754a9
--- /dev/null
@@ -0,0 +1,4 @@
+include(CMP0141-common.cmake)
+
+# Setting this policy after enable_language command has no effect.
+cmake_policy(SET CMP0141 NEW)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-OLD.cmake
new file mode 100644 (file)
index 0000000..7bbe586
--- /dev/null
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 OLD)
+include(CMP0141-common.cmake)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-WARN.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-WARN.cmake
new file mode 100644 (file)
index 0000000..912112a
--- /dev/null
@@ -0,0 +1,2 @@
+
+include(CMP0141-common.cmake)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-common.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/CMP0141-common.cmake
new file mode 100644 (file)
index 0000000..8e43a25
--- /dev/null
@@ -0,0 +1,31 @@
+enable_language(CXX)
+
+cmake_policy(GET CMP0141 cmp0141)
+if(cmp0141 STREQUAL "NEW")
+  if(NOT CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT not set under NEW behavior")
+  endif()
+else()
+  if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT is set under OLD behavior")
+  endif()
+endif()
+
+if(cmp0141 STREQUAL "NEW")
+  if(CMAKE_CXX_FLAGS_DEBUG MATCHES "[/-]Zi( |$)")
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_DEBUG has -Zi flags under NEW behavior.")
+  endif()
+  if(CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "[/-]Zi( |$)")
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_RELWITHDEBINFO has -Zi flags under NEW behavior.")
+  endif()
+else()
+  if(NOT (CMAKE_CXX_FLAGS_DEBUG MATCHES "[/-]Zi( |$)"))
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_DEBUG does not have -Zi flags under OLD behavior.")
+  endif()
+  if(NOT (CMAKE_CXX_FLAGS_RELWITHDEBINFO MATCHES "[/-]Zi( |$)"))
+    message(SEND_ERROR "CMAKE_CXX_FLAGS_RELWITHDEBINFO does not have -Zi flags under OLD behavior.")
+  endif()
+endif()
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT BogusValue)
+add_library(foo empty.cxx)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/CMakeLists.txt b/Tests/RunCMake/MSVCDebugInformationFormat/CMakeLists.txt
new file mode 100644 (file)
index 0000000..aba1016
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.24)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/RunCMakeTest.cmake b/Tests/RunCMake/MSVCDebugInformationFormat/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..f678acf
--- /dev/null
@@ -0,0 +1,6 @@
+include(RunCMake)
+
+run_cmake(CMP0141-WARN)
+run_cmake(CMP0141-OLD)
+run_cmake(CMP0141-NEW)
+run_cmake(CMP0141-NoEffect)
diff --git a/Tests/RunCMake/MSVCDebugInformationFormat/empty.cxx b/Tests/RunCMake/MSVCDebugInformationFormat/empty.cxx
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/Ninja/QtAutoMocSkipPch.cmake b/Tests/RunCMake/Ninja/QtAutoMocSkipPch.cmake
new file mode 100644 (file)
index 0000000..059b917
--- /dev/null
@@ -0,0 +1,17 @@
+enable_language(CXX)
+
+set(QtX Qt${with_qt_version})
+
+find_package(${QtX} REQUIRED COMPONENTS Core)
+
+set(CMAKE_AUTOMOC ON)
+
+add_library(simple_lib SHARED simple_lib.cpp)
+add_executable(app_with_qt app.cpp app_qt.cpp)
+
+target_link_libraries(app_with_qt PRIVATE simple_lib ${QtX}::Core)
+
+set_source_files_properties(app.cpp app_qt.cpp
+    PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+
+target_precompile_headers(app_with_qt PRIVATE [["QObject"]])
index 0825666..b94466c 100644 (file)
@@ -42,6 +42,15 @@ function(run_Intl)
 endfunction()
 run_Intl()
 
+if(WIN32)
+  if(RunCMake_MAKE_PROGRAM)
+    set(maybe_MAKE_PROGRAM "-DRunCMake_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}")
+  endif()
+  run_cmake_script(ShowIncludes-54936 -DshowIncludes=${showIncludes} ${maybe_MAKE_PROGRAM})
+  run_cmake_script(ShowIncludes-65001 -DshowIncludes=${showIncludes} ${maybe_MAKE_PROGRAM})
+  unset(maybe_MAKE_PROGRAM)
+endif()
+
 function(run_NoWorkToDo)
   run_cmake(NoWorkToDo)
   set(RunCMake_TEST_NO_CLEAN 1)
@@ -366,6 +375,22 @@ function(run_QtAutoMocDeps)
     run_ninja("${RunCMake_TEST_BINARY_DIR}")
   endif()
 endfunction()
+
+function(run_QtAutoMocSkipPch)
+  set(QtX Qt${CMake_TEST_Qt_version})
+  if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocSkipPch-build)
+    run_cmake_with_options(QtAutoMocSkipPch
+      "-Dwith_qt_version=${CMake_TEST_Qt_version}"
+      "-D${QtX}_DIR=${${QtX}_DIR}"
+      "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
+      "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
+    )
+    # Build the project.
+    run_ninja("${RunCMake_TEST_BINARY_DIR}")
+  endif()
+endfunction()
 if(CMake_TEST_Qt_version)
   run_QtAutoMocDeps()
+  run_QtAutoMocSkipPch()
 endif()
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-54936-check.cmake b/Tests/RunCMake/Ninja/ShowIncludes-54936-check.cmake
new file mode 100644 (file)
index 0000000..40bb68f
--- /dev/null
@@ -0,0 +1,3 @@
+# 'cl /showIncludes' prefix with 'VSLANG=2052' and 'chcp 54936'.
+string(ASCII 215 162 210 226 58 32 176 252 186 172 206 196 188 254 58 expect)
+include(${CMAKE_CURRENT_LIST_DIR}/ShowIncludes-check.cmake)
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-54936-stdout.txt b/Tests/RunCMake/Ninja/ShowIncludes-54936-stdout.txt
new file mode 100644 (file)
index 0000000..42a2f35
--- /dev/null
@@ -0,0 +1 @@
+-- showIncludes='注意: 包含文件:'
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-54936.cmake b/Tests/RunCMake/Ninja/ShowIncludes-54936.cmake
new file mode 100644 (file)
index 0000000..07b4192
--- /dev/null
@@ -0,0 +1,2 @@
+set(CODEPAGE 54936)
+include(${CMAKE_CURRENT_LIST_DIR}/ShowIncludes.cmake)
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-65001-check.cmake b/Tests/RunCMake/Ninja/ShowIncludes-65001-check.cmake
new file mode 100644 (file)
index 0000000..c73b734
--- /dev/null
@@ -0,0 +1,3 @@
+# 'cl /showIncludes' prefix with 'VSLANG=2052' and 'chcp 65001'.
+string(ASCII 230 179 168 230 132 143 58 32 229 140 133 229 144 171 230 150 135 228 187 182 58 expect)
+include(${CMAKE_CURRENT_LIST_DIR}/ShowIncludes-check.cmake)
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-65001-stdout.txt b/Tests/RunCMake/Ninja/ShowIncludes-65001-stdout.txt
new file mode 100644 (file)
index 0000000..42a2f35
--- /dev/null
@@ -0,0 +1 @@
+-- showIncludes='注意: 包含文件:'
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-65001.cmake b/Tests/RunCMake/Ninja/ShowIncludes-65001.cmake
new file mode 100644 (file)
index 0000000..0eebd61
--- /dev/null
@@ -0,0 +1,2 @@
+set(CODEPAGE 65001)
+include(${CMAKE_CURRENT_LIST_DIR}/ShowIncludes.cmake)
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-check.cmake b/Tests/RunCMake/Ninja/ShowIncludes-check.cmake
new file mode 100644 (file)
index 0000000..304a7f6
--- /dev/null
@@ -0,0 +1,17 @@
+set(rules_ninja "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/rules.ninja")
+if(NOT EXISTS "${rules_ninja}")
+  set(RunCMake_TEST_FAILED "Generator output file is missing:\n ${rules_ninja}")
+  return()
+endif()
+
+file(READ "${rules_ninja}" rules_ninja)
+if(rules_ninja MATCHES "msvc_deps_prefix = ([^\r\n]*)\n")
+  set(actual "${CMAKE_MATCH_1}")
+endif()
+
+if(NOT actual STREQUAL expect)
+  string(HEX "${actual}" actual_hex)
+  string(HEX "${expect}" expect_hex)
+  set(RunCMake_TEST_FAILED "Expected byte sequence\n '${expect}' (${expect_hex})\nbut got\n '${actual}' (${actual_hex})")
+  return()
+endif()
diff --git a/Tests/RunCMake/Ninja/ShowIncludes-cmake.cmake b/Tests/RunCMake/Ninja/ShowIncludes-cmake.cmake
new file mode 100644 (file)
index 0000000..672a89f
--- /dev/null
@@ -0,0 +1,7 @@
+# Set the console code page.
+execute_process(COMMAND cmd /c chcp "${CODEPAGE}")
+
+if(RunCMake_MAKE_PROGRAM)
+  set(maybe_MAKE_PROGRAM "-DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}")
+endif()
+execute_process(COMMAND "${CMAKE_COMMAND}" . -G Ninja ${maybe_MAKE_PROGRAM})
diff --git a/Tests/RunCMake/Ninja/ShowIncludes.cmake b/Tests/RunCMake/Ninja/ShowIncludes.cmake
new file mode 100644 (file)
index 0000000..b9f89fe
--- /dev/null
@@ -0,0 +1,22 @@
+# Create a project to do showIncludes prefix detection.
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/CMakeLists.txt" "
+cmake_minimum_required(VERSION 3.25)
+project(ShowIncludes NONE)
+include(CMakeDetermineCompilerId)
+set(CMAKE_dummy_COMPILER \"${showIncludes}\")
+CMAKE_DETERMINE_MSVC_SHOWINCLUDES_PREFIX(dummy \"\")
+set(CMAKE_CL_SHOWINCLUDES_PREFIX \"\${CMAKE_dummy_CL_SHOWINCLUDES_PREFIX}\")
+file(WRITE \"\${CMAKE_CURRENT_BINARY_DIR}/showIncludes.txt\" \"\${CMAKE_CL_SHOWINCLUDES_PREFIX}\")
+")
+
+if(RunCMake_MAKE_PROGRAM)
+  set(maybe_MAKE_PROGRAM "-DRunCMake_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}")
+endif()
+
+# Run cmake in a new Window to isolate its console code page.
+execute_process(COMMAND cmd /c start /min /wait ""
+  ${CMAKE_COMMAND} -DCODEPAGE=${CODEPAGE} ${maybe_MAKE_PROGRAM} -P ${CMAKE_CURRENT_LIST_DIR}/ShowIncludes-cmake.cmake)
+
+# Print our internal UTF-8 representation of the showIncludes prefix.
+file(READ "${CMAKE_CURRENT_BINARY_DIR}/showIncludes.txt" showIncludes_txt)
+message(STATUS "showIncludes='${showIncludes_txt}'")
diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
new file mode 100644 (file)
index 0000000..a1ae6ac
--- /dev/null
@@ -0,0 +1,28 @@
+set(expected_compile_commands
+[==[^\[
+{
+  "directory": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)",
+  "command": "[^
+]*Debug[^
+]*",
+  "file": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)"
+},
+{
+  "directory": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/CompileCommands-build|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\CompileCommands-build)",
+  "command": "[^
+]*Release[^
+]*",
+  "file": "[^
+]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)"
+}
+]$]==])
+
+file(READ "${RunCMake_TEST_BINARY_DIR}/compile_commands.json" actual_compile_commands)
+if(NOT actual_compile_commands MATCHES "${expected_compile_commands}")
+  string(REPLACE "\n" "\n  " expected_compile_commands_formatted "${expected_compile_commands}")
+  string(REPLACE "\n" "\n  " actual_compile_commands_formatted "${actual_compile_commands}")
+  string(APPEND RunCMake_TEST_FAILED "Expected compile_commands.json to match:\n  ${expected_compile_commands_formatted}\nActual compile_commands.json:\n  ${actual_compile_commands_formatted}\n")
+endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands.cmake
new file mode 100644 (file)
index 0000000..fc44d5a
--- /dev/null
@@ -0,0 +1,3 @@
+enable_language(C)
+
+add_executable(exe main.c)
index 738bc6c..c040e8f 100644 (file)
@@ -453,6 +453,11 @@ run_cmake_command(NoUnusedVariables ${CMAKE_COMMAND} ${CMAKE_CURRENT_LIST_DIR}
   "-DCMAKE_DEFAULT_BUILD_TYPE=Debug"
   "-DCMAKE_DEFAULT_CONFIGS=all"
   )
+unset(RunCMake_TEST_BINARY_DIR)
+
+set(RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=Debug\\;Release;-DCMAKE_CROSS_CONFIGS=all;-DCMAKE_EXPORT_COMPILE_COMMANDS=ON")
+run_cmake(CompileCommands)
+unset(RunCMake_TEST_OPTIONS)
 
 # CudaSimple uses separable compilation, which is currently only supported on NVCC.
 if(CMake_TEST_CUDA)
index 1410eae..df4ef1f 100644 (file)
@@ -155,6 +155,7 @@ foreach(t ${targets})
 
   cmake_parse_implicit_link_info("${input}" implicit_libs idirs implicit_fwks log
       "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
+      LANGUAGE ${lang}
       COMPUTE_IMPLICIT_OBJECTS implicit_objs)
 
   set(library_arch)
index 59ee14b..18b09c1 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(DisabledPch C)
+enable_language(C)
 
 add_library(foo foo.c)
 target_include_directories(foo PUBLIC include)
index 854689f..039a546 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchDebugGenex C)
+enable_language(C)
 
 add_library(foo foo.c)
 target_include_directories(foo PUBLIC include)
index a455410..eef189a 100644 (file)
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchIncludedAllLanguages C CXX)
+enable_language(C)
+enable_language(CXX)
 
 if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH)
   add_definitions(-DHAVE_PCH_SUPPORT)
index dd582ac..6c5e89d 100644 (file)
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.16)
-project(PchIncludedAllLanguages C CXX)
+enable_language(C)
+enable_language(CXX)
 
 if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH)
   add_definitions(-DHAVE_PCH_SUPPORT)
index aab20d8..c031660 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchInterface C)
+enable_language(C)
 
 add_library(foo foo.c)
 target_include_directories(foo PUBLIC include)
index b4fdb71..ad8a328 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.16)
-project(PchLibObjLibExe CXX)
+enable_language(CXX)
 
 foreach(i 1 2 3)
     file(WRITE ${CMAKE_BINARY_DIR}/empty${i}.cpp "void nothing${i}() {}\n")
index bb18a64..d8abf8e 100644 (file)
@@ -1,5 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchMultilanguage C CXX)
+enable_language(C)
+enable_language(CXX)
 
 add_executable(foobar
   foo.c
index 3e27620..988c4c6 100644 (file)
@@ -1,6 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-
-project(PchPrologueEpilogue)
+enable_language(C)
+enable_language(CXX)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW-empty.cmake b/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW-empty.cmake
new file mode 100644 (file)
index 0000000..41c7d22
--- /dev/null
@@ -0,0 +1,4 @@
+cmake_policy(SET CMP0141 NEW)
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "")
+string(APPEND CMAKE_C_FLAGS_DEBUG_INIT " -Zi")
+include(PchReuseFrom-common.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW.cmake b/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-NEW.cmake
new file mode 100644 (file)
index 0000000..daf7a38
--- /dev/null
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 NEW)
+include(PchReuseFrom-common.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-OLD.cmake b/Tests/RunCMake/PrecompileHeaders/PchReuseFrom-CMP0141-OLD.cmake
new file mode 100644 (file)
index 0000000..9586887
--- /dev/null
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0141 OLD)
+include(PchReuseFrom-common.cmake)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchReuseFrom C)
+enable_language(C)
 
 if(CMAKE_C_COMPILE_OPTIONS_USE_PCH)
   add_definitions(-DHAVE_PCH_SUPPORT)
index ba504a3..510877f 100644 (file)
@@ -1,6 +1,5 @@
-cmake_minimum_required(VERSION 3.18)
-
-project(PchReuseFromObjLib)
+enable_language(C)
+enable_language(CXX)
 
 set(CMAKE_PCH_WARN_INVALID OFF)
 
index e306d8e..14703e3 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchReuseFromPrefixed C)
+enable_language(C)
 
 if(CMAKE_C_COMPILE_OPTIONS_USE_PCH)
   add_definitions(-DHAVE_PCH_SUPPORT)
index fff74dc..fefb4ee 100644 (file)
@@ -1,5 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
-project(PchReuseFromSubdir C)
+enable_language(C)
 
 add_library(empty empty.c)
 target_precompile_headers(empty PUBLIC
index fd41e2f..b163369 100644 (file)
@@ -15,7 +15,11 @@ run_test(PchInterface)
 run_cmake(PchPrologueEpilogue)
 run_test(SkipPrecompileHeaders)
 run_test(CXXnotC)
-run_test(PchReuseFrom)
+run_test(PchReuseFrom-CMP0141-OLD)
+run_test(PchReuseFrom-CMP0141-NEW)
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+  run_test(PchReuseFrom-CMP0141-NEW-empty)
+endif()
 run_test(PchReuseFromPrefixed)
 run_test(PchReuseFromSubdir)
 run_cmake(PchMultilanguage)
index 49efbfb..7405e65 100644 (file)
@@ -1,6 +1,5 @@
-cmake_minimum_required(VERSION 3.15)
-
-project(SkipPrecompileHeaders)
+enable_language(C)
+enable_language(CXX)
 
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
index 1c92ca0..ba9cc3b 100644 (file)
@@ -1,8 +1,10 @@
-foreach(arg
+foreach(
+  arg
+  IN ITEMS
     RunCMake_GENERATOR
     RunCMake_SOURCE_DIR
     RunCMake_BINARY_DIR
-    )
+  )
   if(NOT DEFINED ${arg})
     message(FATAL_ERROR "${arg} not given!")
   endif()
@@ -31,7 +33,7 @@ function(run_cmake test)
     set(platform_name msys)
   endif()
 
-  foreach(o out err)
+  foreach(o IN ITEMS out err)
     if(RunCMake-std${o}-file AND EXISTS ${top_src}/${RunCMake-std${o}-file})
       file(READ ${top_src}/${RunCMake-std${o}-file} expect_std${o})
       string(REGEX REPLACE "\n+$" "" expect_std${o} "${expect_std${o}}")
@@ -162,6 +164,7 @@ function(run_cmake test)
     "|Your license to use PGI[^\n]*expired"
     "|Please obtain a new version at"
     "|contact PGI Sales at"
+    "|icp?c: remark #10441: The Intel\\(R\\) C\\+\\+ Compiler Classic \\(ICC\\) is deprecated"
 
     "|[^\n]*install_name_tool: warning: changes being made to the file will invalidate the code signature in:"
     "|[^\n]*xcodebuild[^\n]*DVTPlugInManager"
@@ -176,7 +179,7 @@ function(run_cmake test)
     "|[^\n]*Bullseye Testing Technology"
     ")[^\n]*\n)+"
     )
-  foreach(o out err)
+  foreach(o IN ITEMS out err)
     string(REGEX REPLACE "\r\n" "\n" actual_std${o} "${actual_std${o}}")
     string(REGEX REPLACE "${ignore_line_regex}" "\\1" actual_std${o} "${actual_std${o}}")
     string(REGEX REPLACE "\n+$" "" actual_std${o} "${actual_std${o}}")
index 97c3394..0d8e4c9 100644 (file)
@@ -36,6 +36,7 @@
    \* CMP0113
    \* CMP0119
    \* CMP0131
+   \* CMP0142
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/VS10Project/DebugInformationFormat-check.cmake b/Tests/RunCMake/VS10Project/DebugInformationFormat-check.cmake
new file mode 100644 (file)
index 0000000..46af974
--- /dev/null
@@ -0,0 +1,46 @@
+macro(DebugInformationFormat_check tgt Debug_expect Release_expect MinSizeRel_expect RelWithDebInfo_expect)
+  set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
+  if(NOT EXISTS "${vcProjectFile}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
+    return()
+  endif()
+
+  set(Debug_actual "")
+  set(Release_actual "")
+  set(MinSizeRel_actual "")
+  set(RelWithDebInfo_actual "")
+
+  file(STRINGS "${vcProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    if(line MATCHES "^ *<ItemDefinitionGroup Condition=\"'\\$\\(Configuration\\)\\|\\$\\(Platform\\)'=='([^<>]+)\\|[A-Za-z0-9_]+'\">")
+      set(Configuration "${CMAKE_MATCH_1}")
+    endif()
+    if(line MATCHES "^ *<DebugInformationFormat>([^<>]+)</DebugInformationFormat>")
+      set(${Configuration}_actual "${CMAKE_MATCH_1}")
+    endif()
+  endforeach()
+
+  if (NOT "${Debug_actual}" STREQUAL "${Debug_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj Debug Configuration has DebugInformationFormat '${Debug_actual}', not '${Debug_expect}'.")
+  endif()
+  if (NOT "${Release_actual}" STREQUAL "${Release_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj Release Configuration has DebugInformationFormat '${Release_actual}', not '${Release_expect}'.")
+  endif()
+  if (NOT "${MinSizeRel_actual}" STREQUAL "${MinSizeRel_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj MinSizeRel Configuration has DebugInformationFormat '${MinSizeRel_actual}', not '${MinSizeRel_expect}'.")
+  endif()
+  if (NOT "${RelWithDebInfo_actual}" STREQUAL "${RelWithDebInfo_expect}")
+    set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj RelWithDebInfo Configuration has DebugInformationFormat '${RelWithDebInfo_actual}', not '${RelWithDebInfo_expect}'.")
+  endif()
+endmacro()
+
+DebugInformationFormat_check(default-C ProgramDatabase "" "" ProgramDatabase)
+DebugInformationFormat_check(default-CXX ProgramDatabase "" "" ProgramDatabase)
+DebugInformationFormat_check(empty-C "" "" "" "")
+DebugInformationFormat_check(empty-CXX "" "" "" "")
+DebugInformationFormat_check(Embedded-C OldStyle OldStyle OldStyle OldStyle)
+DebugInformationFormat_check(Embedded-CXX OldStyle OldStyle OldStyle OldStyle)
+DebugInformationFormat_check(ProgramDatabase-C ProgramDatabase ProgramDatabase ProgramDatabase ProgramDatabase)
+DebugInformationFormat_check(ProgramDatabase-CXX ProgramDatabase ProgramDatabase ProgramDatabase ProgramDatabase)
+DebugInformationFormat_check(EditAndContinue-C EditAndContinue EditAndContinue EditAndContinue EditAndContinue)
+DebugInformationFormat_check(EditAndContinue-CXX EditAndContinue EditAndContinue EditAndContinue EditAndContinue)
diff --git a/Tests/RunCMake/VS10Project/DebugInformationFormat.cmake b/Tests/RunCMake/VS10Project/DebugInformationFormat.cmake
new file mode 100644 (file)
index 0000000..f670166
--- /dev/null
@@ -0,0 +1,24 @@
+set(CMAKE_CONFIGURATION_TYPES Debug Release MinSizeRel RelWithDebInfo)
+cmake_policy(SET CMP0141 NEW)
+enable_language(C)
+enable_language(CXX)
+
+add_library(default-C empty.c)
+add_library(default-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "")
+add_library(empty-C empty.c)
+add_library(empty-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "Embedded")
+add_library(Embedded-C empty.c)
+add_library(Embedded-CXX empty.cxx)
+
+set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "ProgramDatabase")
+add_library(ProgramDatabase-C empty.c)
+add_library(ProgramDatabase-CXX empty.cxx)
+
+add_library(EditAndContinue-C empty.c)
+set_property(TARGET EditAndContinue-C PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
+add_library(EditAndContinue-CXX empty.cxx)
+set_property(TARGET EditAndContinue-CXX PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue")
index bcdc101..1701a36 100644 (file)
@@ -5,6 +5,7 @@ if(NOT EXISTS "${vcProjectFile}")
 endif()
 
 set(found_iface_h 0)
+set(found_int_dir 0)
 file(STRINGS "${vcProjectFile}" lines)
 foreach(line IN LISTS lines)
   if(line MATCHES "<([A-Za-z0-9_]+) +Include=.*iface\\.h")
@@ -19,7 +20,15 @@ foreach(line IN LISTS lines)
     endif()
     set(found_iface_h 1)
   endif()
+  if(line MATCHES "^ *<IntDir [^<>]+>[^<>]+</IntDir>")
+    set(found_int_dir 1)
+  endif()
 endforeach()
 if(NOT found_iface_h)
   set(RunCMake_TEST_FAILED "iface.h not referenced in\n  ${vcProjectFile}")
+  return()
+endif()
+if(NOT found_int_dir)
+  set(RunCMake_TEST_FAILED "No references to IntDir in\n  ${vcProjectFile}")
+  return()
 endif()
index e540b9f..f027e94 100644 (file)
@@ -88,3 +88,4 @@ run_cmake(VsDotnetStartupObject)
 run_cmake(VsDotnetTargetFramework)
 run_cmake(VsDotnetTargetFrameworkVersion)
 run_cmake(VsNoCompileBatching)
+run_cmake(DebugInformationFormat)
index 100f482..4d5508d 100644 (file)
@@ -9,7 +9,7 @@ function(check_file target filename)
   if(filename MATCHES "^(.*)(\\.[a-z]+)$")
     set(header_filename "${CMAKE_MATCH_1}")
   endif()
-  set(expected_contents "#include <${header_filename}>\n")
+  set(expected_contents "#include <${header_filename}> // IWYU pragma: associated\n")
   file(READ "${full_filename}" actual_contents)
 
   if(NOT actual_contents STREQUAL expected_contents)
index 9c9074e..22e2bb3 100644 (file)
@@ -4,6 +4,7 @@ include(RunCMake)
 run_cmake(VsDotnetSdkCustomCommandsTarget)
 run_cmake(VsDotnetSdkCustomCommandsSource)
 run_cmake(VsDotnetSdkStartupObject)
+run_cmake(VsDotnetSdkDefines)
 run_cmake(DotnetSdkVariables)
 
 function(run_VsDotnetSdk)
index c585f5e..bd914f8 100644 (file)
@@ -6,16 +6,22 @@ project (DotNetSdk CSharp)
 set(CMAKE_DOTNET_TARGET_FRAMEWORK net472)
 set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk")
 
+if(CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
+    set(VS_RT_IDENTIFIER arm64)
+else()
+    set(VS_RT_IDENTIFIER win10-x64)
+endif()
+
 add_library(dotNetSdkLib1 SHARED lib1.cs)
 set_target_properties(dotNetSdkLib1
     PROPERTIES
-        VS_GLOBAL_RuntimeIdentifier win10-x64)
+        VS_GLOBAL_RuntimeIdentifier ${VS_RT_IDENTIFIER})
 
 add_executable(DotNetSdk csharponly.cs)
 target_link_libraries(DotNetSdk dotNetSdkLib1)
 set_target_properties(DotNetSdk
     PROPERTIES
-        VS_GLOBAL_RuntimeIdentifier win10-x64
+        VS_GLOBAL_RuntimeIdentifier ${VS_RT_IDENTIFIER}
 
         VS_DOTNET_REFERENCE_SomeDll
             ${PROJECT_SOURCE_DIR}/SomeDll.dll)
diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines-check.cmake
new file mode 100644 (file)
index 0000000..eaeba24
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# Check C# VS project for required elements.
+#
+set(csProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj")
+if(NOT EXISTS "${csProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${csProjectFile} does not exist.")
+  return()
+endif()
+
+
+set(inDebug FALSE)
+set(inRelease FALSE)
+set(debugOK FALSE)
+set(releaseOK FALSE)
+
+
+file(STRINGS "${csProjectFile}" lines)
+foreach(line IN LISTS lines)
+  #message(STATUS ${line})
+  if(line MATCHES "^ *<PropertyGroup .*Debug.*")
+    set(inDebug TRUE)
+  elseif(line MATCHES "^ *<PropertyGroup .*Release.*")
+    set(inRelease TRUE)
+  elseif(line MATCHES "^ *</PropertyGroup> *$")
+    set(inRelease FALSE)
+    set(inDebug  FALSE)
+  elseif(inDebug AND
+     (line MATCHES "^ *<DefineConstants>.*MY_FOO_DEFINE.*</DefineConstants> *$") AND
+     (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_DEBUG.*</DefineConstants> *$") AND
+     (NOT (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_RELEASE.*</DefineConstants> *$")) AND
+     (NOT (line MATCHES "^ *<DefineConstants>.*MY_BAR_ASSIGNMENT=bar.*</DefineConstants> *$"))
+    )
+    set(debugOK TRUE)
+  elseif(inRelease AND
+     (line MATCHES "^ *<DefineConstants>.*MY_FOO_DEFINE.*</DefineConstants> *$") AND
+     (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_RELEASE.*</DefineConstants> *$") AND
+     (NOT (line MATCHES "^ *<DefineConstants>.*DEFINE_ONLY_FOR_DEBUG.*</DefineConstants> *$")) AND
+     (NOT (line MATCHES "^ *<DefineConstants>.*MY_BAR_ASSIGNMENT=bar.*</DefineConstants> *$"))
+    )
+    set(releaseOK TRUE)
+  endif()
+endforeach()
+
+function(print_csprojfile)
+  file(STRINGS "${csProjectFile}" lines)
+  foreach(line IN LISTS lines)
+    message(STATUS ${line})
+  endforeach()
+endfunction()
+
+
+if(NOT debugOK)
+  message(STATUS "Failed to set Debug configuration defines correctly.")
+  set(RunCMake_TEST_FAILED "Failed to set Debug configuration defines correctly.")
+  print_csprojfile()
+  return()
+endif()
+
+if(NOT releaseOK)
+  message(STATUS "Failed to set Release configuration defines correctly.")
+  set(RunCMake_TEST_FAILED "Failed to set Release configuration defines correctly.")
+  print_csprojfile()
+  return()
+endif()
diff --git a/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake b/Tests/RunCMake/VsDotnetSdk/VsDotnetSdkDefines.cmake
new file mode 100644 (file)
index 0000000..d89f19b
--- /dev/null
@@ -0,0 +1,19 @@
+enable_language(CSharp)
+if(NOT CMAKE_CSharp_COMPILER)
+    return()
+endif()
+
+set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk")
+set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "net5.0")
+
+add_executable(foo csharponly.cs lib1.cs)
+
+# Issue 23376
+target_compile_definitions(
+  foo
+    PUBLIC
+      MY_FOO_DEFINE
+      "MY_BAR_ASSIGNMENT=bar"
+      $<$<CONFIG:Debug>:DEFINE_ONLY_FOR_DEBUG>
+      $<$<CONFIG:Release>:DEFINE_ONLY_FOR_RELEASE>
+)
diff --git a/Tests/RunCMake/XcodeProject/Clean-build-check.cmake b/Tests/RunCMake/XcodeProject/Clean-build-check.cmake
new file mode 100644 (file)
index 0000000..605881a
--- /dev/null
@@ -0,0 +1,5 @@
+set(pattern "${RunCMake_TEST_BINARY_DIR}/build/empty.build/Debug/Objects-normal/*/empty.o")
+file(GLOB objs "${pattern}")
+if(NOT objs)
+  set(RunCMake_TEST_FAILED "Expected object does not exist:\n ${pattern}")
+endif()
diff --git a/Tests/RunCMake/XcodeProject/Clean-clean-check.cmake b/Tests/RunCMake/XcodeProject/Clean-clean-check.cmake
new file mode 100644 (file)
index 0000000..76ea8a1
--- /dev/null
@@ -0,0 +1,5 @@
+set(pattern "${RunCMake_TEST_BINARY_DIR}/build/empty.build/Debug/Objects-normal/*/empty.o")
+file(GLOB objs "${pattern}")
+if(objs)
+  set(RunCMake_TEST_FAILED "Object file(s) not cleaned:\n ${objs}")
+endif()
diff --git a/Tests/RunCMake/XcodeProject/Clean.cmake b/Tests/RunCMake/XcodeProject/Clean.cmake
new file mode 100644 (file)
index 0000000..1ab7e10
--- /dev/null
@@ -0,0 +1,2 @@
+enable_language(C)
+add_subdirectory(Clean)
diff --git a/Tests/RunCMake/XcodeProject/Clean/CMakeLists.txt b/Tests/RunCMake/XcodeProject/Clean/CMakeLists.txt
new file mode 100644 (file)
index 0000000..59e62cb
--- /dev/null
@@ -0,0 +1 @@
+add_library(empty empty.c)
diff --git a/Tests/RunCMake/XcodeProject/Clean/empty.c b/Tests/RunCMake/XcodeProject/Clean/empty.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/XcodeProject/EffectivePlatformNameOFF.cmake b/Tests/RunCMake/XcodeProject/EffectivePlatformNameOFF.cmake
new file mode 100644 (file)
index 0000000..4a3bf56
--- /dev/null
@@ -0,0 +1,12 @@
+enable_language(CXX)
+
+set_property(GLOBAL PROPERTY XCODE_EMIT_EFFECTIVE_PLATFORM_NAME OFF)
+
+set(CMAKE_MACOSX_BUNDLE true)
+
+add_library(library STATIC foo.cpp)
+
+add_executable(main main.cpp)
+target_link_libraries(main library)
+
+install(TARGETS library ARCHIVE DESTINATION lib)
index 128a9dc..573d5f7 100644 (file)
@@ -1,5 +1,14 @@
 include(RunCMake)
 
+function(RunClean)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Clean-build)
+  run_cmake(Clean -DCMAKE_CONFIGURATION_TYPES=Debug)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(Clean-build xcodebuild)
+  run_cmake_command(Clean-clean xcodebuild clean)
+endfunction()
+RunClean()
+
 run_cmake(ExplicitCMakeLists)
 run_cmake(ImplicitCMakeLists)
 run_cmake(InterfaceLibSources)
@@ -346,6 +355,7 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 6)
 endif()
 
 if(NOT XCODE_VERSION VERSION_LESS 5)
+  # XcodeMultiplatform
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeMultiplatform-build)
   set(RunCMake_TEST_NO_CLEAN 1)
   set(RunCMake_TEST_OPTIONS "${IOS_DEPLOYMENT_TARGET}")
@@ -365,6 +375,23 @@ if(NOT XCODE_VERSION VERSION_LESS 5)
   unset(RunCMake_TEST_BINARY_DIR)
   unset(RunCMake_TEST_NO_CLEAN)
   unset(RunCMake_TEST_OPTIONS)
+
+  # EffectivePlatformNameOFF
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EffectivePlatformNameOFF-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OPTIONS "-DCMAKE_SYSTEM_NAME=iOS" "-DCMAKE_OSX_SYSROOT=iphonesimulator")
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(EffectivePlatformNameOFF)
+
+  run_cmake_command(EffectivePlatformNameOFF-iphonesimulator-build ${CMAKE_COMMAND} --build .)
+  run_cmake_command(EffectivePlatformNameOFF-iphonesimulator-install ${CMAKE_COMMAND} --build . --target install -- DESTDIR=${RunCMake_TEST_BINARY_DIR}/_install_iphonesimulator)
+
+  unset(RunCMake_TEST_BINARY_DIR)
+  unset(RunCMake_TEST_NO_CLEAN)
+  unset(RunCMake_TEST_OPTIONS)
 endif()
 
 if(XCODE_VERSION VERSION_GREATER_EQUAL 8)
index 71b7d8f..bec8790 100644 (file)
@@ -12,6 +12,8 @@ set(found_target_library_FRAMEWORK_SEARCH_PATHS 0)
 set(found_inherited_FRAMEWORK_SEARCH_PATHS 0)
 set(found_project_LIBRARY_SEARCH_PATHS 0)
 set(found_target_library_LIBRARY_SEARCH_PATHS 0)
+set(found_target_cmp0142old_LIBRARY_SEARCH_PATHS 0)
+set(found_target_cmp0142new_LIBRARY_SEARCH_PATHS 0)
 set(found_inherited_LIBRARY_SEARCH_PATHS 0)
 file(STRINGS "${xcProjectFile}" lines)
 foreach(line IN LISTS lines)
@@ -42,6 +44,12 @@ foreach(line IN LISTS lines)
     if(line MATCHES [[LIBRARY_SEARCH_PATHS = \("(\\")?[^"]*/Tests/RunCMake/XcodeProject/SearchPaths-build/TargetSearchPathLib/\$\(CONFIGURATION\)\$\(EFFECTIVE_PLATFORM_NAME\)(\\")?","(\\")?[^"]*/Tests/RunCMake/XcodeProject/SearchPaths-build/TargetSearchPathLib(\\")?","\$\(inherited\)"\);]])
       set(found_target_library_LIBRARY_SEARCH_PATHS 1)
     endif()
+    if(line MATCHES [[LIBRARY_SEARCH_PATHS = \("(\\")?[^"]*/Tests/RunCMake/XcodeProject/SearchPaths-build/TargetSearchPathCMP0142OLD/\$\(CONFIGURATION\)\$\(EFFECTIVE_PLATFORM_NAME\)(\\")?","(\\")?[^"]*/Tests/RunCMake/XcodeProject/SearchPaths-build/TargetSearchPathCMP0142OLD(\\")?","\$\(inherited\)"\);]])
+      set(found_target_cmp0142old_LIBRARY_SEARCH_PATHS 1)
+    endif()
+    if(line MATCHES [[LIBRARY_SEARCH_PATHS = \("(\\")?[^"]*/Tests/RunCMake/XcodeProject/SearchPaths-build/TargetSearchPathCMP0142NEW(\\")?","\$\(inherited\)"\);]])
+      set(found_target_cmp0142new_LIBRARY_SEARCH_PATHS 1)
+    endif()
     if(line MATCHES [[LIBRARY_SEARCH_PATHS = \("\$\(inherited\)"\);]])
       set(found_inherited_LIBRARY_SEARCH_PATHS 1)
     endif()
@@ -68,6 +76,12 @@ endif()
 if(NOT found_target_library_LIBRARY_SEARCH_PATHS)
   string(APPEND RunCMake_TEST_FAILED "Did not find expected LIBRARY_SEARCH_PATHS for target 'library' in\n  ${xcProjectFile}\n")
 endif()
+if(NOT found_target_cmp0142old_LIBRARY_SEARCH_PATHS)
+  string(APPEND RunCMake_TEST_FAILED "Did not find expected LIBRARY_SEARCH_PATHS for target 'cmp0142old' in\n  ${xcProjectFile}\n")
+endif()
+if(NOT found_target_cmp0142new_LIBRARY_SEARCH_PATHS)
+  string(APPEND RunCMake_TEST_FAILED "Did not find expected LIBRARY_SEARCH_PATHS for target 'cmp0142new' in\n  ${xcProjectFile}\n")
+endif()
 if(found_inherited_LIBRARY_SEARCH_PATHS)
   string(APPEND RunCMake_TEST_FAILED "Found unexpected LIBRARY_SEARCH_PATHS inherited-only value in\n  ${xcProjectFile}\n")
 endif()
index ef97709..b469772 100644 (file)
@@ -3,6 +3,8 @@ enable_language(C)
 file(MAKE_DIRECTORY  "${CMAKE_CURRENT_BINARY_DIR}/ProjectSearchPath")
 file(MAKE_DIRECTORY  "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathInc/TargetInc.framework")
 file(MAKE_DIRECTORY  "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathLib/TargetLib.framework")
+file(MAKE_DIRECTORY  "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathCMP0142OLD")
+file(MAKE_DIRECTORY  "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathCMP0142NEW")
 
 set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS "${CMAKE_CURRENT_BINARY_DIR}/ProjectSearchPath")
 set(CMAKE_XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS "${CMAKE_CURRENT_BINARY_DIR}/ProjectSearchPath")
@@ -19,3 +21,11 @@ target_include_directories(include PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/TargetSe
 add_executable(library main.c)
 target_link_libraries(library PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathLib/TargetLib.framework")
 target_link_directories(library PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathLib")
+
+cmake_policy(SET CMP0142 OLD)
+add_executable(cmp0142old main.c)
+target_link_directories(cmp0142old PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathCMP0142OLD")
+
+cmake_policy(SET CMP0142 NEW)
+add_executable(cmp0142new main.c)
+target_link_directories(cmp0142new PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/TargetSearchPathCMP0142NEW")
index be44ecd..6bad527 100644 (file)
@@ -29,6 +29,8 @@ check_property("UNDEFINED_BEHAVIOUR_SANITIZER" "enableUBSanitizer")
 check_property("UNDEFINED_BEHAVIOUR_SANITIZER_STOP" "stopOnEveryUBSanitizerIssue")
 check_property("DISABLE_MAIN_THREAD_CHECKER" "disableMainThreadChecker")
 check_property("MAIN_THREAD_CHECKER_STOP" "stopOnEveryMainThreadCheckerIssue")
+check_property("DISABLE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION" "enableGPUValidationMode")
+check_property("ENABLE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION" "enableGPUShaderValidationMode")
 
 check_property("MALLOC_SCRIBBLE" "MallocScribble")
 check_property("MALLOC_GUARD_EDGES" "MallocGuardEdges")
@@ -43,6 +45,8 @@ check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED" "enableGPUFrameCaptureMo
 check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_METAL" "enableGPUFrameCaptureMode=\"1\"")
 check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED_MIXED_CASE" "enableGPUFrameCaptureMode=\"3\"")
 check_property("ENABLE_GPU_FRAME_CAPTURE_MODE_METAL_MIXED_CASE" "enableGPUFrameCaptureMode=\"1\"")
+check_property("LAUNCH_MODE_AUTO" "launchStyle=\"0\"")
+check_property("LAUNCH_MODE_WAIT" "launchStyle=\"1\"")
 
 check_property("EXECUTABLE" "myExecutable")
 check_property("ARGUMENTS" [=["--foo"]=])
index 126a9fc..267e379 100644 (file)
@@ -32,12 +32,16 @@ function(create_scheme_for_property scheme property value)
   set_target_properties(${scheme} PROPERTIES XCODE_SCHEME_${property} "${value}")
 endfunction()
 
+create_scheme_for_property(DISABLE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION ENABLE_GPU_API_VALIDATION OFF)
+create_scheme_for_property(ENABLE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION ENABLE_GPU_SHADER_VALIDATION ON)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_1 ENABLE_GPU_FRAME_CAPTURE_MODE 1)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_3 ENABLE_GPU_FRAME_CAPTURE_MODE 3)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED ENABLE_GPU_FRAME_CAPTURE_MODE Disabled)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_METAL ENABLE_GPU_FRAME_CAPTURE_MODE Metal)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_DISABLED_MIXED_CASE ENABLE_GPU_FRAME_CAPTURE_MODE DISAbled)
 create_scheme_for_property(ENABLE_GPU_FRAME_CAPTURE_MODE_METAL_MIXED_CASE ENABLE_GPU_FRAME_CAPTURE_MODE METal)
+create_scheme_for_property(LAUNCH_MODE_AUTO LAUNCH_MODE AUTO)
+create_scheme_for_property(LAUNCH_MODE_WAIT LAUNCH_MODE WAIT)
 create_scheme_for_property(EXECUTABLE EXECUTABLE myExecutable)
 create_scheme_for_property(ARGUMENTS ARGUMENTS "--foo;--bar=baz")
 create_scheme_for_property(ENVIRONMENT ENVIRONMENT "FOO=foo;BAR=bar")
index 951e03c..ddf45af 100644 (file)
@@ -3,6 +3,7 @@ include(RunCMake)
 run_cmake(DoesNotExist)
 run_cmake(Missing)
 run_cmake(Function)
+run_cmake(System)
 
 macro(run_cmake_install case)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${case}-build)
diff --git a/Tests/RunCMake/add_subdirectory/System.cmake b/Tests/RunCMake/add_subdirectory/System.cmake
new file mode 100644 (file)
index 0000000..45d7d9a
--- /dev/null
@@ -0,0 +1,22 @@
+enable_language(CXX)
+
+add_subdirectory(System SYSTEM)
+
+function(check_target_system target expected_value)
+  get_target_property(var ${target} SYSTEM)
+  if ((var AND NOT expected_value) OR (NOT var AND expected_value))
+    message(SEND_ERROR "\
+The 'SYSTEM' property of ${target} should be ${expected_value}, \
+but got ${var}")
+  endif()
+endfunction()
+
+check_target_system(foo OFF)
+check_target_system(bar ON)
+check_target_system(zot ON)
+check_target_system(subsub1foo OFF)
+check_target_system(subsub1bar ON)
+check_target_system(subsub1zot ON)
+check_target_system(subsub2foo OFF)
+check_target_system(subsub2bar ON)
+check_target_system(subsub2zot ON)
diff --git a/Tests/RunCMake/add_subdirectory/System/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/System/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ef74e80
--- /dev/null
@@ -0,0 +1,11 @@
+project(SystemSub NONE)
+
+add_subdirectory(SubSub1 SYSTEM)
+add_subdirectory(SubSub2)
+
+add_library(bar STATIC bar.cpp)
+
+add_library(foo STATIC foo.cpp)
+set_target_properties(foo PROPERTIES SYSTEM OFF)
+
+add_executable(zot zot.cpp)
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub1/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/System/SubSub1/CMakeLists.txt
new file mode 100644 (file)
index 0000000..291339b
--- /dev/null
@@ -0,0 +1,6 @@
+add_library(subsub1bar STATIC bar.cpp)
+
+add_library(subsub1foo STATIC foo.cpp)
+set_target_properties(subsub1foo PROPERTIES SYSTEM OFF)
+
+add_executable(subsub1zot zot.cpp)
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub1/bar.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub1/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub1/foo.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub1/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub1/zot.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub1/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub2/CMakeLists.txt b/Tests/RunCMake/add_subdirectory/System/SubSub2/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5755742
--- /dev/null
@@ -0,0 +1,6 @@
+add_library(subsub2bar STATIC bar.cpp)
+
+add_library(subsub2foo STATIC foo.cpp)
+set_target_properties(subsub2foo PROPERTIES SYSTEM OFF)
+
+add_executable(subsub2zot zot.cpp)
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub2/bar.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub2/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub2/foo.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub2/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/SubSub2/zot.cpp b/Tests/RunCMake/add_subdirectory/System/SubSub2/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/bar.cpp b/Tests/RunCMake/add_subdirectory/System/bar.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/foo.cpp b/Tests/RunCMake/add_subdirectory/System/foo.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/add_subdirectory/System/zot.cpp b/Tests/RunCMake/add_subdirectory/System/zot.cpp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/block/CMakeLists.txt b/Tests/RunCMake/block/CMakeLists.txt
new file mode 100644 (file)
index 0000000..45cd10e
--- /dev/null
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.3...3.25)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/block/EndAlone-result.txt b/Tests/RunCMake/block/EndAlone-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/EndAlone-stderr.txt b/Tests/RunCMake/block/EndAlone-stderr.txt
new file mode 100644 (file)
index 0000000..a588dd7
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at EndAlone.cmake:[0-9]+ \(endblock\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/EndAlone.cmake b/Tests/RunCMake/block/EndAlone.cmake
new file mode 100644 (file)
index 0000000..0c428a9
--- /dev/null
@@ -0,0 +1 @@
+endblock()
diff --git a/Tests/RunCMake/block/EndAloneWithArgument-result.txt b/Tests/RunCMake/block/EndAloneWithArgument-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/EndAloneWithArgument-stderr.txt b/Tests/RunCMake/block/EndAloneWithArgument-stderr.txt
new file mode 100644 (file)
index 0000000..c3d25a3
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at EndAloneWithArgument.cmake:[0-9]+ \(endblock\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/EndAloneWithArgument.cmake b/Tests/RunCMake/block/EndAloneWithArgument.cmake
new file mode 100644 (file)
index 0000000..05df5b0
--- /dev/null
@@ -0,0 +1 @@
+endblock(WRONG_ARG)
diff --git a/Tests/RunCMake/block/EndMissing-result.txt b/Tests/RunCMake/block/EndMissing-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/EndMissing-stderr.txt b/Tests/RunCMake/block/EndMissing-stderr.txt
new file mode 100644 (file)
index 0000000..b9739a5
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at EndMissing.cmake:[0-9]+ \(block\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/EndMissing.cmake b/Tests/RunCMake/block/EndMissing.cmake
new file mode 100644 (file)
index 0000000..335b64e
--- /dev/null
@@ -0,0 +1 @@
+block()
diff --git a/Tests/RunCMake/block/EndWithArgument-stderr.txt b/Tests/RunCMake/block/EndWithArgument-stderr.txt
new file mode 100644 (file)
index 0000000..7586453
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Warning \(dev\) in EndWithArgument.cmake:
+  A logical block closing on the line
+
+  .+/Tests/RunCMake/block/EndWithArgument.cmake:[0-9]+ \(endblock\)
+
+  has unexpected arguments.
+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/block/EndWithArgument.cmake b/Tests/RunCMake/block/EndWithArgument.cmake
new file mode 100644 (file)
index 0000000..0641c9a
--- /dev/null
@@ -0,0 +1,2 @@
+block()
+endblock(END_ARG)
diff --git a/Tests/RunCMake/block/InvalidArgument-result.txt b/Tests/RunCMake/block/InvalidArgument-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidArgument-stderr.txt b/Tests/RunCMake/block/InvalidArgument-stderr.txt
new file mode 100644 (file)
index 0000000..bee604b
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidArgument.cmake:[0-9]+ \(block\):
+  block PROPAGATE cannot be specified without a new scope for VARIABLES
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidArgument.cmake b/Tests/RunCMake/block/InvalidArgument.cmake
new file mode 100644 (file)
index 0000000..5269cd0
--- /dev/null
@@ -0,0 +1,2 @@
+block(SCOPE_FOR POLICIES PROPAGATE VAR1)
+endblock()
diff --git a/Tests/RunCMake/block/InvalidNesting1-result.txt b/Tests/RunCMake/block/InvalidNesting1-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting1-stderr.txt b/Tests/RunCMake/block/InvalidNesting1-stderr.txt
new file mode 100644 (file)
index 0000000..6dfe0e1
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting1.cmake:[0-9]+ \(else\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting1.cmake b/Tests/RunCMake/block/InvalidNesting1.cmake
new file mode 100644 (file)
index 0000000..27b7944
--- /dev/null
@@ -0,0 +1,6 @@
+
+if (TRUE)
+  block()
+else()
+  endblock()
+endif()
diff --git a/Tests/RunCMake/block/InvalidNesting2-result.txt b/Tests/RunCMake/block/InvalidNesting2-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting2-stderr.txt b/Tests/RunCMake/block/InvalidNesting2-stderr.txt
new file mode 100644 (file)
index 0000000..71325b6
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting2.cmake:[0-9]+ \(endblock\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting2.cmake b/Tests/RunCMake/block/InvalidNesting2.cmake
new file mode 100644 (file)
index 0000000..ae94cdc
--- /dev/null
@@ -0,0 +1,6 @@
+
+block()
+if (TRUE)
+elseif(FALSE)
+endblock()
+endif()
diff --git a/Tests/RunCMake/block/InvalidNesting3-result.txt b/Tests/RunCMake/block/InvalidNesting3-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting3-stderr.txt b/Tests/RunCMake/block/InvalidNesting3-stderr.txt
new file mode 100644 (file)
index 0000000..344a931
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting3.cmake:[0-9]+ \(endwhile\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting3.cmake b/Tests/RunCMake/block/InvalidNesting3.cmake
new file mode 100644 (file)
index 0000000..f692d24
--- /dev/null
@@ -0,0 +1,5 @@
+
+while(TRUE)
+block()
+endwhile()
+endblock()
diff --git a/Tests/RunCMake/block/InvalidNesting4-result.txt b/Tests/RunCMake/block/InvalidNesting4-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting4-stderr.txt b/Tests/RunCMake/block/InvalidNesting4-stderr.txt
new file mode 100644 (file)
index 0000000..44d6364
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting4.cmake:[0-9]+ \(endblock\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting4.cmake b/Tests/RunCMake/block/InvalidNesting4.cmake
new file mode 100644 (file)
index 0000000..6e8e0ae
--- /dev/null
@@ -0,0 +1,5 @@
+
+block()
+foreach(item IN ITEMS A B)
+endblock()
+endforeach()
diff --git a/Tests/RunCMake/block/InvalidNesting5-result.txt b/Tests/RunCMake/block/InvalidNesting5-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting5-stderr.txt b/Tests/RunCMake/block/InvalidNesting5-stderr.txt
new file mode 100644 (file)
index 0000000..976d2e1
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting5.cmake:[0-9]+ \(endfunction\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting5.cmake b/Tests/RunCMake/block/InvalidNesting5.cmake
new file mode 100644 (file)
index 0000000..0479e8d
--- /dev/null
@@ -0,0 +1,5 @@
+
+function(FUNC)
+  block()
+endfunction()
+endblock()
diff --git a/Tests/RunCMake/block/InvalidNesting6-result.txt b/Tests/RunCMake/block/InvalidNesting6-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/InvalidNesting6-stderr.txt b/Tests/RunCMake/block/InvalidNesting6-stderr.txt
new file mode 100644 (file)
index 0000000..2d67b16
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at InvalidNesting6.cmake:[0-9]+ \(endblock\):
+  Flow control statements are not properly nested.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/InvalidNesting6.cmake b/Tests/RunCMake/block/InvalidNesting6.cmake
new file mode 100644 (file)
index 0000000..a1cb359
--- /dev/null
@@ -0,0 +1,5 @@
+
+  block()
+macro(FUNC)
+endblock()
+endmacro()
diff --git a/Tests/RunCMake/block/MissingArgument-result.txt b/Tests/RunCMake/block/MissingArgument-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/MissingArgument-stderr.txt b/Tests/RunCMake/block/MissingArgument-stderr.txt
new file mode 100644 (file)
index 0000000..d3e63ca
--- /dev/null
@@ -0,0 +1,7 @@
+CMake Error at MissingArgument.cmake:[0-9]+ \(block\):
+  Error after keyword "SCOPE_FOR":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/MissingArgument.cmake b/Tests/RunCMake/block/MissingArgument.cmake
new file mode 100644 (file)
index 0000000..6018887
--- /dev/null
@@ -0,0 +1,2 @@
+block(SCOPE_FOR)
+endblock()
diff --git a/Tests/RunCMake/block/RunCMakeTest.cmake b/Tests/RunCMake/block/RunCMakeTest.cmake
new file mode 100644 (file)
index 0000000..4260e76
--- /dev/null
@@ -0,0 +1,22 @@
+include(RunCMake)
+
+run_cmake(WrongArgument)
+run_cmake(InvalidArgument)
+run_cmake(MissingArgument)
+run_cmake(WrongScope)
+run_cmake(EndMissing)
+run_cmake(EndWithArgument)
+run_cmake(EndAlone)
+run_cmake(EndAloneWithArgument)
+
+run_cmake(InvalidNesting1)
+run_cmake(InvalidNesting2)
+run_cmake(InvalidNesting3)
+run_cmake(InvalidNesting4)
+run_cmake(InvalidNesting5)
+run_cmake(InvalidNesting6)
+
+run_cmake(Scope)
+run_cmake(Scope-VARIABLES)
+run_cmake(Scope-POLICIES)
+run_cmake(Workflows)
diff --git a/Tests/RunCMake/block/Scope-POLICIES.cmake b/Tests/RunCMake/block/Scope-POLICIES.cmake
new file mode 100644 (file)
index 0000000..9536a99
--- /dev/null
@@ -0,0 +1,40 @@
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+
+set(VARSUB1 "OUTERSUB1")
+set(VARSUB2 "OUTERSUB2")
+
+cmake_policy(SET CMP0139 NEW)
+
+# create a block with a new scope for policies
+block(SCOPE_FOR POLICIES)
+  set(VAR1 "INNER1")
+  unset(VAR2)
+  set(VAR3 "INNER3")
+  add_subdirectory(Scope)
+
+  cmake_policy(SET CMP0139 OLD)
+endblock()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "INNER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(DEFINED VAR2)
+  message(SEND_ERROR "block/endblock: VAR2 is unexpectedly defined: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
+if(NOT DEFINED VARSUB1 OR NOT VARSUB1 STREQUAL "SUBDIR1")
+  message(SEND_ERROR "block/endblock: VARSUB1 has unexpected value: ${VARSUB1}")
+endif()
+if(NOT DEFINED VARSUB2 OR NOT VARSUB2 STREQUAL "SUBDIR2")
+  message(SEND_ERROR "block/endblock: VARSUB2 has unexpected value: ${VARSUB2}")
+endif()
+
+cmake_policy(GET CMP0139 CMP0139_STATUS)
+if(NOT CMP0139_STATUS STREQUAL "NEW")
+    message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
+endif()
diff --git a/Tests/RunCMake/block/Scope-VARIABLES.cmake b/Tests/RunCMake/block/Scope-VARIABLES.cmake
new file mode 100644 (file)
index 0000000..ac8da14
--- /dev/null
@@ -0,0 +1,62 @@
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+set(VAR3 "OUTER3")
+set(VAR4 "OUTER4")
+set(VAR5 "OUTER5")
+
+set(VAR6 "CACHE6" CACHE STRING "")
+set(VAR6 "OUTER6")
+
+set(VARSUB1 "OUTERSUB1")
+set(VARSUB2 "OUTERSUB2")
+
+cmake_policy(SET CMP0139 NEW)
+
+# create a block with a new scope for variables
+block(SCOPE_FOR VARIABLES PROPAGATE VAR3 VAR4 VAR5 VAR6 VAR7 VARSUB2)
+  set(VAR1 "INNER1")
+  set(VAR2 "INNER2" PARENT_SCOPE)
+  set(VAR3 "INNER3")
+  unset(VAR4)
+  unset(VAR6)
+  set(VAR7 "INNER7")
+  add_subdirectory(Scope)
+
+  cmake_policy(SET CMP0139 OLD)
+endblock()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
+  message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
+if(DEFINED VAR4)
+  message(SEND_ERROR "block/endblock: VAR4 is unexpectedly defined: ${VAR4}")
+endif()
+if(NOT DEFINED VAR5 OR NOT VAR5 STREQUAL "OUTER5")
+  message(SEND_ERROR "block/endblock: VAR5 has unexpected value: ${VAR5}")
+endif()
+unset(VAR6 CACHE)
+if (DEFINED VAR6)
+  message(SEND_ERROR "block/endblock: VAR6 is unexpectedly defined: ${VAR6}")
+endif()
+if(NOT DEFINED VAR7 OR NOT VAR7 STREQUAL "INNER7")
+  message(SEND_ERROR "block/endblock: VAR7 has unexpected value: ${VAR7}")
+endif()
+if(NOT DEFINED VARSUB1 OR NOT VARSUB1 STREQUAL "OUTERSUB1")
+  message(SEND_ERROR "block/endblock: VARSUB1 has unexpected value: ${VARSUB1}")
+endif()
+if(NOT DEFINED VARSUB2 OR NOT VARSUB2 STREQUAL "SUBDIR2")
+  message(SEND_ERROR "block/endblock: VARSUB2 has unexpected value: ${VARSUB2}")
+endif()
+
+cmake_policy(GET CMP0139 CMP0139_STATUS)
+if(NOT CMP0139_STATUS STREQUAL "OLD")
+    message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
+endif()
diff --git a/Tests/RunCMake/block/Scope.cmake b/Tests/RunCMake/block/Scope.cmake
new file mode 100644 (file)
index 0000000..ef43df6
--- /dev/null
@@ -0,0 +1,62 @@
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+set(VAR3 "OUTER3")
+set(VAR4 "OUTER4")
+set(VAR5 "OUTER5")
+
+set(VAR6 "CACHE6" CACHE STRING "")
+set(VAR6 "OUTER6")
+
+set(VARSUB1 "OUTERSUB1")
+set(VARSUB2 "OUTERSUB2")
+
+cmake_policy(SET CMP0139 NEW)
+
+# create a block with a new scope for variables and policies
+block(PROPAGATE VAR3 VAR4 VAR5 VAR6 VAR7 VARSUB2)
+  set(VAR1 "INNER1")
+  set(VAR2 "INNER2" PARENT_SCOPE)
+  set(VAR3 "INNER3")
+  unset(VAR4)
+  unset(VAR6)
+  set(VAR7 "INNER7")
+  add_subdirectory(Scope)
+
+  cmake_policy(SET CMP0139 OLD)
+endblock()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
+  message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
+if(DEFINED VAR4)
+  message(SEND_ERROR "block/endblock: VAR4 is unexpectedly defined: ${VAR4}")
+endif()
+if(NOT DEFINED VAR5 OR NOT VAR5 STREQUAL "OUTER5")
+  message(SEND_ERROR "block/endblock: VAR5 has unexpected value: ${VAR5}")
+endif()
+unset(VAR6 CACHE)
+if (DEFINED VAR6)
+  message(SEND_ERROR "block/endblock: VAR6 is unexpectedly defined: ${VAR6}")
+endif()
+if(NOT DEFINED VAR7 OR NOT VAR7 STREQUAL "INNER7")
+  message(SEND_ERROR "block/endblock: VAR6 has unexpected value: ${VAR7}")
+endif()
+if(NOT DEFINED VARSUB1 OR NOT VARSUB1 STREQUAL "OUTERSUB1")
+  message(SEND_ERROR "block/endblock: VARSUB1 has unexpected value: ${VARSUB1}")
+endif()
+if(NOT DEFINED VARSUB2 OR NOT VARSUB2 STREQUAL "SUBDIR2")
+  message(SEND_ERROR "block/endblock: VARSUB2 has unexpected value: ${VARSUB2}")
+endif()
+
+cmake_policy(GET CMP0139 CMP0139_STATUS)
+if(NOT CMP0139_STATUS STREQUAL "NEW")
+    message(SEND_ERROR "block/endblock: CMP0139 has unexpected value: ${CMP0139_STATUS}")
+endif()
diff --git a/Tests/RunCMake/block/Scope/CMakeLists.txt b/Tests/RunCMake/block/Scope/CMakeLists.txt
new file mode 100644 (file)
index 0000000..afd79e3
--- /dev/null
@@ -0,0 +1,2 @@
+set(VARSUB1 "SUBDIR1" PARENT_SCOPE)
+set(VARSUB2 "SUBDIR2" PARENT_SCOPE)
diff --git a/Tests/RunCMake/block/Workflows.cmake b/Tests/RunCMake/block/Workflows.cmake
new file mode 100644 (file)
index 0000000..cbf032e
--- /dev/null
@@ -0,0 +1,78 @@
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+set(VAR3 "OUTER3")
+
+while (TRUE)
+  # create a block with a new scope for variables
+  block(SCOPE_FOR VARIABLES PROPAGATE VAR3)
+    set(VAR2 "INNER2" PARENT_SCOPE)
+    set(VAR3 "INNER3")
+    break()
+  endblock()
+endwhile()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
+  message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
+
+
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+set(VAR3 "OUTER3")
+
+function (OUTER)
+  # create a block with a new scope for variables
+  block(SCOPE_FOR VARIABLES PROPAGATE VAR3)
+    set(VAR2 "INNER2" PARENT_SCOPE)
+    set(VAR3 "INNER3")
+    return()
+  endblock()
+  set(VAR1 "INNER1" PARENT_SCOPE)
+endfunction()
+outer()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "OUTER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "OUTER2")
+  message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "OUTER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
+
+
+
+set(VAR1 "OUTER1")
+set(VAR2 "OUTER2")
+set(VAR3 "OUTER3")
+
+foreach (id IN ITEMS 1 2 3)
+  # create a block with a new scope for variables
+  block(SCOPE_FOR VARIABLES PROPAGATE VAR${id})
+    set(VAR${id} "INNER${id}")
+    continue()
+    set(VAR${id} "BAD${id}")
+  endblock()
+endforeach()
+
+# check final values for variables
+if(NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "INNER1")
+  message(SEND_ERROR "block/endblock: VAR1 has unexpected value: ${VAR1}")
+endif()
+if(NOT DEFINED VAR2 OR NOT VAR2 STREQUAL "INNER2")
+  message(SEND_ERROR "block/endblock: VAR2 has unexpected value: ${VAR2}")
+endif()
+if(NOT DEFINED VAR3 OR NOT VAR3 STREQUAL "INNER3")
+  message(SEND_ERROR "block/endblock: VAR3 has unexpected value: ${VAR3}")
+endif()
diff --git a/Tests/RunCMake/block/WrongArgument-result.txt b/Tests/RunCMake/block/WrongArgument-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/WrongArgument-stderr.txt b/Tests/RunCMake/block/WrongArgument-stderr.txt
new file mode 100644 (file)
index 0000000..56faea7
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at WrongArgument.cmake:[0-9]+ \(block\):
+  block called with unsupported argument "WRONG_ARG"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/WrongArgument.cmake b/Tests/RunCMake/block/WrongArgument.cmake
new file mode 100644 (file)
index 0000000..e460866
--- /dev/null
@@ -0,0 +1,2 @@
+block(WRONG_ARG)
+endblock()
diff --git a/Tests/RunCMake/block/WrongScope-result.txt b/Tests/RunCMake/block/WrongScope-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/block/WrongScope-stderr.txt b/Tests/RunCMake/block/WrongScope-stderr.txt
new file mode 100644 (file)
index 0000000..dd2a1ef
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at WrongScope.cmake:[0-9]+ \(block\):
+  block SCOPE_FOR unsupported scope "WRONG_SCOPE"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/block/WrongScope.cmake b/Tests/RunCMake/block/WrongScope.cmake
new file mode 100644 (file)
index 0000000..97a6783
--- /dev/null
@@ -0,0 +1,2 @@
+block(SCOPE_FOR WRONG_SCOPE)
+endblock()
index 6a430f1..ea1566d 100644 (file)
@@ -1,5 +1,7 @@
 CMake Error at Registry_BadQuery2.cmake:[0-9]+ \(cmake_host_system_information\):
-  cmake_host_system_information missing expected value for argument\(s\)
-  "VALUE".
+  Error after keyword "VALUE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
index 5eda4ff..f8c96d8 100644 (file)
@@ -1,5 +1,7 @@
 CMake Error at Registry_BadView1.cmake:[0-9]+ \(cmake_host_system_information\):
-  cmake_host_system_information missing expected value for argument\(s\)
-  "VIEW".
+  Error after keyword "VIEW":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
index 6480b2e..38ce10b 100644 (file)
@@ -8,6 +8,7 @@ foreach(command IN ITEMS
     "if" "elseif" "else" "endif"
     "while" "endwhile"
     "foreach" "endforeach"
+    "block" "endblock"
     )
   message(STATUS "Running call_invalid_command for ${command}...")
   run_cmake_with_options(call_invalid_command -Dcommand=${command})
@@ -42,6 +43,7 @@ foreach(command IN ITEMS
     "if" "elseif" "else" "endif"
     "while" "endwhile"
     "foreach" "endforeach"
+    "block" "endblock"
     "return"
     )
   message(STATUS "Running defer_call_invalid_command for ${command}...")
@@ -82,3 +84,61 @@ run_cmake(defer_get_call_id_var)
 run_cmake(defer_missing_arg)
 run_cmake(defer_missing_call)
 run_cmake(defer_unknown_option)
+
+# Default log level
+run_cmake_command(
+    get_message_log_level_none
+    ${CMAKE_COMMAND}
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from cache
+run_cmake_command(
+    get_message_log_level_cache
+    ${CMAKE_COMMAND}
+    -DCMAKE_MESSAGE_LOG_LEVEL=TRACE
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from regular variable
+run_cmake_command(
+    get_message_log_level_var
+    ${CMAKE_COMMAND}
+    -DNEW_LOG_LEVEL=TRACE
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from command line
+run_cmake_command(
+    get_message_log_level_cli
+    ${CMAKE_COMMAND}
+    --log-level=DEBUG
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from command line, it has higher priority over a cache variable
+run_cmake_command(
+    get_message_log_level_cli_and_cache
+    ${CMAKE_COMMAND}
+    --log-level=DEBUG
+    -DCMAKE_MESSAGE_LOG_LEVEL=TRACE
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from command line, it has higher priority over a regular variable
+run_cmake_command(
+    get_message_log_level_cli_and_var
+    ${CMAKE_COMMAND}
+    --log-level=DEBUG
+    -DNEW_LOG_LEVEL=TRACE
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
+
+# Log level from variable, it has higher priority over a cache variable
+run_cmake_command(
+    get_message_log_level_var_and_cache
+    ${CMAKE_COMMAND}
+    -DNEW_LOG_LEVEL=DEBUG
+    -DCMAKE_MESSAGE_LOG_LEVEL=TRACE
+    -P ${RunCMake_SOURCE_DIR}/get_message_log_level.cmake
+  )
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level.cmake b/Tests/RunCMake/cmake_language/get_message_log_level.cmake
new file mode 100644 (file)
index 0000000..1740c1f
--- /dev/null
@@ -0,0 +1,5 @@
+if(NEW_LOG_LEVEL)
+    set(CMAKE_MESSAGE_LOG_LEVEL "${NEW_LOG_LEVEL}")
+endif()
+cmake_language(GET_MESSAGE_LOG_LEVEL log_level)
+message(STATUS "log level is: ${log_level}")
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cache-stdout.txt
new file mode 100644 (file)
index 0000000..cf1cd7b
--- /dev/null
@@ -0,0 +1 @@
+log level is: TRACE
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli-stdout.txt
new file mode 100644 (file)
index 0000000..4d6e1eb
--- /dev/null
@@ -0,0 +1 @@
+log level is: DEBUG
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_cache-stdout.txt
new file mode 100644 (file)
index 0000000..4d6e1eb
--- /dev/null
@@ -0,0 +1 @@
+log level is: DEBUG
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_cli_and_var-stdout.txt
new file mode 100644 (file)
index 0000000..4d6e1eb
--- /dev/null
@@ -0,0 +1 @@
+log level is: DEBUG
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_none-stdout.txt
new file mode 100644 (file)
index 0000000..92ffd34
--- /dev/null
@@ -0,0 +1 @@
+log level is: STATUS
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_var-stdout.txt
new file mode 100644 (file)
index 0000000..cf1cd7b
--- /dev/null
@@ -0,0 +1 @@
+log level is: TRACE
diff --git a/Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt b/Tests/RunCMake/cmake_language/get_message_log_level_var_and_cache-stdout.txt
new file mode 100644 (file)
index 0000000..4d6e1eb
--- /dev/null
@@ -0,0 +1 @@
+log level is: DEBUG
diff --git a/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt b/Tests/RunCMake/cmake_path/BASE_DIRECTORY-no-arg-stderr.txt
new file mode 100644 (file)
index 0000000..ad7d134
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
+  Error after keyword "BASE_DIRECTORY":
+
+    missing required value
diff --git a/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt b/Tests/RunCMake/cmake_path/OUTPUT_VARIABLE-empty-stderr.txt
new file mode 100644 (file)
index 0000000..f1b52cc
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at .+/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
+  Error after keyword "OUTPUT_VARIABLE":
+
+    empty string not allowed
index e1d6592..63289ef 100644 (file)
@@ -1,2 +1,4 @@
 CMake Error at .+/cmake_path/call-cmake_path.cmake:[0-9]+ \(cmake_path\):
-  cmake_path OUTPUT_VARIABLE requires an argument.
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
index 991f46b..1742b06 100644 (file)
@@ -74,6 +74,14 @@ foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME
 endforeach()
 
 
+## BASE_DIRECTORY without argument
+set (RunCMake-stderr-file "BASE_DIRECTORY-no-arg-stderr.txt")
+
+foreach (command IN ITEMS RELATIVE_PATH ABSOLUTE_PATH)
+  run_cmake_command (${command}-OUTPUT_VARIABLE-no-arg "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path BASE_DIRECTORY" -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake")
+endforeach()
+
+
 ## Invalid output variable
 set (RunCMake-stderr-file "invalid-output-var-stderr.txt")
 
@@ -106,6 +114,9 @@ foreach (command IN ITEMS NATIVE_PATH
   run_cmake_command (${command}-invalid-output "${CMAKE_COMMAND}" "-DCMAKE_PATH_ARGUMENTS=${command} path ${extra_args}" -DCHECK_INVALID_OUTPUT=ON -P "${RunCMake_SOURCE_DIR}/call-cmake_path.cmake")
 endforeach()
 
+# OUTPUT_VARIABLE empty name
+set (RunCMake-stderr-file "OUTPUT_VARIABLE-empty-stderr.txt")
+
 foreach (command IN ITEMS APPEND APPEND_STRING REMOVE_FILENAME REPLACE_FILENAME
                           REMOVE_EXTENSION REPLACE_EXTENSION NORMAL_PATH
                           RELATIVE_PATH ABSOLUTE_PATH)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-result.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-result.txt
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-stdout.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop-stdout.txt
new file mode 100644 (file)
index 0000000..a7eae05
--- /dev/null
@@ -0,0 +1 @@
+CTEST_TEST_VAR=set-via-ENVIRONMENT-property
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop.cmake b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-prop.cmake
new file mode 100644 (file)
index 0000000..51e7f2a
--- /dev/null
@@ -0,0 +1,9 @@
+include(CTest)
+
+add_test(NAME cmake_environment COMMAND "${CMAKE_COMMAND}" -E environment)
+set_tests_properties(
+    cmake_environment
+    PROPERTIES
+    ENVIRONMENT "CTEST_TEST_VAR=set-via-ENVIRONMENT-property"
+    ENVIRONMENT_MODIFICATION "CTEST_TEST_VAR=set:set-via-ENVIRONMENT_MODIFICATION;CTEST_TEST_VAR=reset:"
+)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-result.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-result.txt
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-stdout.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system-stdout.txt
new file mode 100644 (file)
index 0000000..beaf133
--- /dev/null
@@ -0,0 +1 @@
+CTEST_TEST_VAR=set-via-system-environment
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system.cmake b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-reset-to-system.cmake
new file mode 100644 (file)
index 0000000..23268aa
--- /dev/null
@@ -0,0 +1,9 @@
+include(CTest)
+
+add_test(NAME cmake_environment COMMAND "${CMAKE_COMMAND}" -E environment)
+set_tests_properties(
+    cmake_environment
+    PROPERTIES
+    # ENVIRONMENT "CTEST_TEST_VAR=set-via-ENVIRONMENT-property"
+    ENVIRONMENT_MODIFICATION "CTEST_TEST_VAR=set:set-via-ENVIRONMENT_MODIFICATION;CTEST_TEST_VAR=reset:"
+)
index 3447779..365c9e8 100644 (file)
@@ -10,3 +10,7 @@ set(RunCTest_VERBOSE_FLAG "-VV")
 run_ctest(ENVIRONMENT_MODIFICATION-invalid-op)
 run_ctest(ENVIRONMENT_MODIFICATION-no-colon)
 run_ctest(ENVIRONMENT_MODIFICATION-no-equals)
+
+set(ENV{CTEST_TEST_VAR} set-via-system-environment)
+run_ctest(ENVIRONMENT_MODIFICATION-reset-to-prop)
+run_ctest(ENVIRONMENT_MODIFICATION-reset-to-system)
index adc7a1a..850f72c 100644 (file)
@@ -277,3 +277,12 @@ file(APPEND "${LOG_FILE}"
 =========
 ========= RACECHECK SUMMARY: 12 hazards displayed (0 errors, 12 warnings)
 ")
+
+# false-positives
+file(APPEND "${LOG_FILE}"
+"========= COMPUTE-SANITIZER
+========= Error: Target application terminated before first instrumented API call
+========= Tracking kernels launched by child processes requires the --target-processes all option.
+========= Error: No attachable process found. compute-sanitizer timed-out.
+========= Default timeout can be adjusted with --launch-timeout. Awaiting target completion.
+")
index e27f1e6..62cad52 100644 (file)
@@ -1,2 +1,4 @@
 CMake Error at .*EchoCommand.cmake:.*\(execute_process\):
-  execute_process called with no value for COMMAND_ECHO.
+  Error after keyword "COMMAND_ECHO":
+
+    missing required value
index 1a69579..7f85654 100644 (file)
@@ -1,4 +1,7 @@
 ^CMake Error at EncodingMissing.cmake:[0-9]+ \(execute_process\):
-  execute_process called with no value for ENCODING.
+  Error after keyword "ENCODING":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms-stderr.txt
deleted file mode 100644 (file)
index b22387b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-CMake Error at CHMOD-all-perms\.cmake:[0-9]+ \(file\):
-  file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS
-  from the invocation
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-all-perms.cmake
deleted file mode 100644 (file)
index b49583d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
-  FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path-stderr.txt
deleted file mode 100644 (file)
index 8d09e35..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at CHMOD-invalid-path\.cmake:[0-9]+ \(file\):
-  file does not exist:
-
-  .*/chmod-tests/I_dont_exist
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-path.cmake
deleted file mode 100644 (file)
index 36915c1..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/I_dont_exist PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms-stderr.txt
deleted file mode 100644 (file)
index 84ba2a2..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-invalid-perms\.cmake:[0-9]+ \(file\):
-  file INVALID_PERMISSION is an invalid permission specifier
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-invalid-perms.cmake
deleted file mode 100644 (file)
index 22cab0b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS INVALID_PERMISSION)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword-stderr.txt
deleted file mode 100644 (file)
index 2c248f8..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-no-keyword\.cmake:[0-9]+ \(file\):
-  file No permissions given
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-keyword.cmake
deleted file mode 100644 (file)
index 8b62106..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms-stderr.txt
deleted file mode 100644 (file)
index a18609f..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Error at CHMOD-no-perms\.cmake:[0-9]+ \(file\):
-  file No permissions given
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-no-perms.cmake
deleted file mode 100644 (file)
index 9fbd359..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-ok.cmake
deleted file mode 100644 (file)
index 87e3e57..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-override.cmake
deleted file mode 100644 (file)
index d9226b8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a)
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_READ
-    FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/CHMOD-write-only-stderr.txt
deleted file mode 100644 (file)
index 1c87a59..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at CHMOD-write-only\.cmake:[0-9]+ \(file\):
-  file failed to open for reading \(Permission denied\):
-
-    .*/chmod-tests/a
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake b/Tests/RunCMake/file-CHMOD/CHMOD-write-only.cmake
deleted file mode 100644 (file)
index 1289efc..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests)
-
-file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a "CONTENT")
-file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a PERMISSIONS OWNER_WRITE)
-file(READ ${CMAKE_CURRENT_BINARY_DIR}/chmod-tests/a content)
index 18deb89..e6b1169 100644 (file)
@@ -1,12 +1,14 @@
 include(RunCMake)
 
-run_cmake(CHMOD-no-perms)
-run_cmake(CHMOD-no-keyword)
-run_cmake(CHMOD-all-perms)
-run_cmake(CHMOD-invalid-perms)
-run_cmake(CHMOD-invalid-path)
-run_cmake(CHMOD-ok)
-run_cmake(CHMOD-override)
+run_cmake_script(no-perms)
+run_cmake_script(missing-perms)
+run_cmake_script(missing-file-perms)
+run_cmake_script(missing-dir-perms)
+run_cmake_script(all-perms)
+run_cmake_script(invalid-perms)
+run_cmake_script(invalid-path)
+run_cmake_script(ok)
+run_cmake_script(override)
 
 if(UNIX)
   execute_process(COMMAND id -u $ENV{USER}
@@ -15,5 +17,5 @@ if(UNIX)
 endif()
 
 if(NOT WIN32 AND NOT MSYS AND NOT "${uid}" STREQUAL "0")
-  run_cmake(CHMOD-write-only)
+  run_cmake_script(write-only)
 endif()
diff --git a/Tests/RunCMake/file-CHMOD/all-perms-result.txt b/Tests/RunCMake/file-CHMOD/all-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/all-perms-stderr.txt
new file mode 100644 (file)
index 0000000..6932a1f
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at [^
+]*/all-perms\.cmake:[0-9]+ \(file\):
+  file Remove either PERMISSIONS or FILE_PERMISSIONS or DIRECTORY_PERMISSIONS
+  from the invocation$
diff --git a/Tests/RunCMake/file-CHMOD/all-perms.cmake b/Tests/RunCMake/file-CHMOD/all-perms.cmake
new file mode 100644 (file)
index 0000000..5ff81b8
--- /dev/null
@@ -0,0 +1,3 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ
+  FILE_PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/invalid-path-result.txt b/Tests/RunCMake/file-CHMOD/invalid-path-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-path-stderr.txt
new file mode 100644 (file)
index 0000000..eb5fb31
--- /dev/null
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/invalid-path\.cmake:[0-9]+ \(file\):
+  file does not exist:
+
+    [^
+]*/Tests/RunCMake/file-CHMOD/invalid-path-build/I_dont_exist$
diff --git a/Tests/RunCMake/file-CHMOD/invalid-path.cmake b/Tests/RunCMake/file-CHMOD/invalid-path.cmake
new file mode 100644 (file)
index 0000000..e8b0313
--- /dev/null
@@ -0,0 +1 @@
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/I_dont_exist PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/invalid-perms-stderr.txt
new file mode 100644 (file)
index 0000000..daab22e
--- /dev/null
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/invalid-perms\.cmake:[0-9]+ \(file\):
+  file INVALID_PERMISSION is an invalid permission specifier$
diff --git a/Tests/RunCMake/file-CHMOD/invalid-perms.cmake b/Tests/RunCMake/file-CHMOD/invalid-perms.cmake
new file mode 100644 (file)
index 0000000..42129b9
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS INVALID_PERMISSION)
diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-dir-perms-stderr.txt
new file mode 100644 (file)
index 0000000..c05bb2d
--- /dev/null
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-dir-perms.cmake:[0-9]+ \(file\):
+  Error after keyword "DIRECTORY_PERMISSIONS":
+
+    missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-dir-perms.cmake
new file mode 100644 (file)
index 0000000..9d0fdad
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ DIRECTORY_PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-file-perms-stderr.txt
new file mode 100644 (file)
index 0000000..c3f4f0f
--- /dev/null
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-file-perms.cmake:[0-9]+ \(file\):
+  Error after keyword "FILE_PERMISSIONS":
+
+    missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-file-perms.cmake
new file mode 100644 (file)
index 0000000..bcf35aa
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ FILE_PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/missing-perms-result.txt b/Tests/RunCMake/file-CHMOD/missing-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/missing-perms-stderr.txt
new file mode 100644 (file)
index 0000000..1508b13
--- /dev/null
@@ -0,0 +1,5 @@
+^CMake Error at [^
+]*/missing-perms.cmake:[0-9]+ \(file\):
+  Error after keyword "PERMISSIONS":
+
+    missing required value$
diff --git a/Tests/RunCMake/file-CHMOD/missing-perms.cmake b/Tests/RunCMake/file-CHMOD/missing-perms.cmake
new file mode 100644 (file)
index 0000000..da9f182
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a FILE_PERMISSIONS OWNER_READ PERMISSIONS)
diff --git a/Tests/RunCMake/file-CHMOD/no-perms-result.txt b/Tests/RunCMake/file-CHMOD/no-perms-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt b/Tests/RunCMake/file-CHMOD/no-perms-stderr.txt
new file mode 100644 (file)
index 0000000..4c5a139
--- /dev/null
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/no-perms\.cmake:[0-9]+ \(file\):
+  file No permissions given$
diff --git a/Tests/RunCMake/file-CHMOD/no-perms.cmake b/Tests/RunCMake/file-CHMOD/no-perms.cmake
new file mode 100644 (file)
index 0000000..602cfc2
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a)
diff --git a/Tests/RunCMake/file-CHMOD/ok.cmake b/Tests/RunCMake/file-CHMOD/ok.cmake
new file mode 100644 (file)
index 0000000..7c74d27
--- /dev/null
@@ -0,0 +1,2 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ)
diff --git a/Tests/RunCMake/file-CHMOD/override.cmake b/Tests/RunCMake/file-CHMOD/override.cmake
new file mode 100644 (file)
index 0000000..67e5a23
--- /dev/null
@@ -0,0 +1,3 @@
+file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/a)
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_READ
+    FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
diff --git a/Tests/RunCMake/file-CHMOD/write-only-result.txt b/Tests/RunCMake/file-CHMOD/write-only-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-CHMOD/write-only-stderr.txt b/Tests/RunCMake/file-CHMOD/write-only-stderr.txt
new file mode 100644 (file)
index 0000000..169a092
--- /dev/null
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/write-only\.cmake:[0-9]+ \(file\):
+  file failed to open for reading \(Permission denied\):
+
+    [^
+]*/Tests/RunCMake/file-CHMOD/write-only-build/a$
diff --git a/Tests/RunCMake/file-CHMOD/write-only.cmake b/Tests/RunCMake/file-CHMOD/write-only.cmake
new file mode 100644 (file)
index 0000000..aa9d803
--- /dev/null
@@ -0,0 +1,3 @@
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/a "CONTENT")
+file(CHMOD ${CMAKE_CURRENT_BINARY_DIR}/a PERMISSIONS OWNER_WRITE)
+file(READ ${CMAKE_CURRENT_BINARY_DIR}/a content)
index c6ad3d0..39f307d 100644 (file)
@@ -13,11 +13,21 @@ Call Stack \(most recent call first\):
 This warning is for project developers\.  Use -Wno-dev to suppress it\.
 
 CMake Error at badargs2\.cmake:[0-9]+ \(file\):
-  file Keywords missing values:
+  Error after keyword "BUNDLE_EXECUTABLE":
+
+    missing required value
+
+  Error after keyword "CONFLICTING_DEPENDENCIES_PREFIX":
+
+    missing required value
+
+  Error after keyword "RESOLVED_DEPENDENCIES_VAR":
+
+    missing required value
+
+  Error after keyword "UNRESOLVED_DEPENDENCIES_VAR":
+
+    missing required value
 
-    RESOLVED_DEPENDENCIES_VAR
-    UNRESOLVED_DEPENDENCIES_VAR
-    CONFLICTING_DEPENDENCIES_PREFIX
-    BUNDLE_EXECUTABLE
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
index 7c58aeb..54a3e5a 100644 (file)
@@ -1,2 +1,7 @@
-CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\):
-  file BASE_DIRECTORY requires a value
+^CMake Error at REAL_PATH-no-base-dir.cmake:[0-9]+ \(file\):
+  Error after keyword "BASE_DIRECTORY":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
index aff4735..db88956 100644 (file)
@@ -47,6 +47,7 @@ run_cmake(GLOB_RECURSE)
 run_cmake(GLOB_RECURSE-noexp-FOLLOW_SYMLINKS)
 run_cmake(SIZE)
 run_cmake(SIZE-error-does-not-exist)
+run_cmake(TIMESTAMP)
 
 run_cmake(REMOVE-empty)
 
diff --git a/Tests/RunCMake/file/TIMESTAMP-stdout.txt b/Tests/RunCMake/file/TIMESTAMP-stdout.txt
new file mode 100644 (file)
index 0000000..42be6ba
--- /dev/null
@@ -0,0 +1 @@
+-- '[0-9]*-[01][0-9]-[0-3][0-9]T[0-2][0-9]:[0-5][0-9]:[0-6][0-9]Z'
diff --git a/Tests/RunCMake/file/TIMESTAMP.cmake b/Tests/RunCMake/file/TIMESTAMP.cmake
new file mode 100644 (file)
index 0000000..2bd2577
--- /dev/null
@@ -0,0 +1,2 @@
+file(TIMESTAMP "TIMESTAMP.cmake" output UTC)
+message(STATUS "'${output}'")
index 23765d4..296bb71 100644 (file)
@@ -7,6 +7,10 @@ run_cmake(Required)
 run_cmake(NO_CACHE)
 run_cmake(REGISTRY_VIEW-no-view)
 run_cmake(REGISTRY_VIEW-wrong-view)
+run_cmake(VALIDATOR-no-function)
+run_cmake(VALIDATOR-undefined-function)
+run_cmake(VALIDATOR-specify-macro)
+run_cmake(VALIDATOR)
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PrefixInPATH_File)
 
diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_file/VALIDATOR-no-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-no-function-stderr.txt
new file mode 100644 (file)
index 0000000..4d49649
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_file\):
+  find_file missing required argument for "VALIDATOR"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_file/VALIDATOR-no-function.cmake
new file mode 100644 (file)
index 0000000..4800fe9
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_file(result NAMES input.txt VALIDATOR)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
new file mode 100644 (file)
index 0000000..7e0eda0
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_file\):
+  find_file command specified for "VALIDATOR" is not a function: check.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
new file mode 100644 (file)
index 0000000..ec0ce9a
--- /dev/null
@@ -0,0 +1,5 @@
+
+macro(CHECK result path)
+endmacro()
+
+find_file(result NAMES input.txt VALIDATOR check)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-stderr.txt
new file mode 100644 (file)
index 0000000..6c8159b
--- /dev/null
@@ -0,0 +1,3 @@
+CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+CHECK='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/VALIDATOR-stdout.txt b/Tests/RunCMake/find_file/VALIDATOR-stdout.txt
new file mode 100644 (file)
index 0000000..44fa77c
--- /dev/null
@@ -0,0 +1,3 @@
+-- FILE='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- FILE='[^']+/Tests/RunCMake/find_file/include/PrefixInPATH.h'
+-- FILE='FILE-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-undefined-function-stderr.txt
new file mode 100644 (file)
index 0000000..b4125e6
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_file\):
+  find_file command specified for "VALIDATOR" is undefined: undefined.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_file/VALIDATOR-undefined-function.cmake
new file mode 100644 (file)
index 0000000..c465887
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_file(result NAMES input.txt VALIDATOR undefined)
diff --git a/Tests/RunCMake/find_file/VALIDATOR.cmake b/Tests/RunCMake/find_file/VALIDATOR.cmake
new file mode 100644 (file)
index 0000000..72dd8f9
--- /dev/null
@@ -0,0 +1,39 @@
+
+function(CHECK_DEFAULT result filename)
+  message("CHECK='${filename}'")
+endfunction()
+
+function(CHECK_OK result filename)
+  message("CHECK='${filename}'")
+  set(${result} TRUE PARENT_SCOPE)
+endfunction()
+
+function(CHECK_KO result filename)
+  message("CHECK='${filename}'")
+  set(${result} FALSE PARENT_SCOPE)
+endfunction()
+
+
+find_file(FILE
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_default
+  )
+message(STATUS "FILE='${FILE}'")
+unset(FILE CACHE)
+
+find_file(FILE
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_ok
+  )
+message(STATUS "FILE='${FILE}'")
+unset(FILE CACHE)
+
+find_file(FILE
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_ko
+  )
+message(STATUS "FILE='${FILE}'")
+unset(FILE CACHE)
index a912077..8b223b4 100644 (file)
@@ -15,6 +15,10 @@ run_cmake(Required)
 run_cmake(NO_CACHE)
 run_cmake(REGISTRY_VIEW-no-view)
 run_cmake(REGISTRY_VIEW-wrong-view)
+run_cmake(VALIDATOR-no-function)
+run_cmake(VALIDATOR-undefined-function)
+run_cmake(VALIDATOR-specify-macro)
+run_cmake(VALIDATOR)
 
 run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp")
 
diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_library/VALIDATOR-no-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-no-function-stderr.txt
new file mode 100644 (file)
index 0000000..6c04aa4
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_library\):
+  find_library missing required argument for "VALIDATOR"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_library/VALIDATOR-no-function.cmake
new file mode 100644 (file)
index 0000000..516e54e
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_library(result NAMES input.txt VALIDATOR)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
new file mode 100644 (file)
index 0000000..03e7df9
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_library\):
+  find_library command specified for "VALIDATOR" is not a function: check.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
new file mode 100644 (file)
index 0000000..fa90d72
--- /dev/null
@@ -0,0 +1,5 @@
+
+macro(CHECK result path)
+endmacro()
+
+find_library(result NAMES input.txt VALIDATOR check)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-stderr.txt
new file mode 100644 (file)
index 0000000..7211450
--- /dev/null
@@ -0,0 +1,3 @@
+CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+CHECK='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/VALIDATOR-stdout.txt b/Tests/RunCMake/find_library/VALIDATOR-stdout.txt
new file mode 100644 (file)
index 0000000..bd2549b
--- /dev/null
@@ -0,0 +1,3 @@
+-- LIB='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- LIB='[^']+/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
+-- LIB='LIB-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-undefined-function-stderr.txt
new file mode 100644 (file)
index 0000000..dc7ad9e
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_library\):
+  find_library command specified for "VALIDATOR" is undefined: undefined.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_library/VALIDATOR-undefined-function.cmake
new file mode 100644 (file)
index 0000000..9ac7ee0
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_library(result NAMES input.txt VALIDATOR undefined)
diff --git a/Tests/RunCMake/find_library/VALIDATOR.cmake b/Tests/RunCMake/find_library/VALIDATOR.cmake
new file mode 100644 (file)
index 0000000..7593941
--- /dev/null
@@ -0,0 +1,41 @@
+list(APPEND CMAKE_FIND_LIBRARY_PREFIXES lib)
+list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .a)
+
+function(CHECK_DEFAULT result filename)
+  message("CHECK='${filename}'")
+endfunction()
+
+function(CHECK_OK result filename)
+  message("CHECK='${filename}'")
+  set(${result} TRUE PARENT_SCOPE)
+endfunction()
+
+function(CHECK_KO result filename)
+  message("CHECK='${filename}'")
+  set(${result} FALSE PARENT_SCOPE)
+endfunction()
+
+
+find_library(LIB
+  NAMES PrefixInPATH
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib
+  VALIDATOR check_default
+  )
+message(STATUS "LIB='${LIB}'")
+unset(LIB CACHE)
+
+find_library(LIB
+  NAMES PrefixInPATH
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib
+  VALIDATOR check_ok
+  )
+message(STATUS "LIB='${LIB}'")
+unset(LIB CACHE)
+
+find_library(LIB
+  NAMES PrefixInPATH
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/lib
+  VALIDATOR check_ko
+  )
+message(STATUS "LIB='${LIB}'")
+unset(LIB CACHE)
index 32e54d5..fa41fc1 100644 (file)
@@ -55,6 +55,22 @@ run_cmake(REGISTRY_VIEW-no-view)
 run_cmake(REGISTRY_VIEW-wrong-view)
 run_cmake(REGISTRY_VIEW-propagated)
 
+file(
+    GLOB SearchPaths_TEST_CASE_LIST
+    LIST_DIRECTORIES TRUE
+    "${RunCMake_SOURCE_DIR}/SearchPaths/*"
+  )
+foreach(TestCasePrefix IN LISTS SearchPaths_TEST_CASE_LIST)
+  if(IS_DIRECTORY "${TestCasePrefix}")
+    cmake_path(GET TestCasePrefix FILENAME TestSuffix)
+    run_cmake_with_options(
+      SearchPaths_${TestSuffix}
+        "-DSearchPaths_ROOT=${TestCasePrefix}"
+        "--debug-find-pkg=SearchPaths"
+      )
+  endif()
+endforeach()
+
 if(UNIX
     AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS
     )
diff --git a/Tests/RunCMake/find_package/SearchPaths.cmake b/Tests/RunCMake/find_package/SearchPaths.cmake
new file mode 100644 (file)
index 0000000..a5a10fc
--- /dev/null
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0074 NEW)
+find_package(SearchPaths REQUIRED CONFIG)
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1.2.3/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1.2.3/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1.2.3/lib/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1.2.3/lib/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1.2.3/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1.2.3/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1.2.3/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1.2.3/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake b/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1.2.3/cmake/SearchPathsConfig.cmake
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix-stderr.txt
new file mode 100644 (file)
index 0000000..fae13ef
--- /dev/null
@@ -0,0 +1,10 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..eba88c9
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_cmake/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..cebc169
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_cmake_pkg/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_cmake_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..4e5d3f6
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..f290545
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_lib_pkg_cmake/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_lib_pkg_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..32b9fcb
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..7bf5cf8
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..812c607
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1\.2\.3/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_cmake_pkg/SearchPaths-1\.2\.3/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_cmake_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..3592f72
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_cmake_pkg/SearchPaths-1\.2\.3/lib/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_cmake_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..b196b7a
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..17e0399
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_lib_pkg_cmake/SearchPaths-1\.2\.3/lib/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_lib_pkg_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..ee01a50
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_cmake_pkg/SearchPaths-1\.2\.3/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_cmake_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..d6abd2f
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..b578b2b
--- /dev/null
@@ -0,0 +1,14 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_pkg_share_pkg_cmake/SearchPaths-1\.2\.3/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_pkg_share_pkg_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..2f3f18a
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_cmake_pkg/share/cmake/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_cmake_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-stderr.txt
new file mode 100644 (file)
index 0000000..3eef002
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg/share/SearchPaths-1\.2\.3/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-stderr.txt
new file mode 100644 (file)
index 0000000..c962f8b
--- /dev/null
@@ -0,0 +1,12 @@
+  find_package considered the following locations for SearchPaths's Config
+  module:
+
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake-build/CMakeFiles/pkgRedirects/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/SearchPathsConfig\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/searchpaths-config\.cmake
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
+
+  The file was found at
+
+    .*/Tests/RunCMake/find_package/SearchPaths/prefix_share_pkg_cmake/share/SearchPaths-1\.2\.3/cmake/SearchPathsConfig\.cmake
diff --git a/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake b/Tests/RunCMake/find_package/SearchPaths_prefix_share_pkg_cmake.cmake
new file mode 100644 (file)
index 0000000..d831313
--- /dev/null
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_SOURCE_DIR}/SearchPaths.cmake")
index 63cadc2..9c76f2e 100644 (file)
@@ -7,6 +7,10 @@ run_cmake(Required)
 run_cmake(NO_CACHE)
 run_cmake(REGISTRY_VIEW-no-view)
 run_cmake(REGISTRY_VIEW-wrong-view)
+run_cmake(VALIDATOR-no-function)
+run_cmake(VALIDATOR-undefined-function)
+run_cmake(VALIDATOR-specify-macro)
+run_cmake(VALIDATOR)
 
 if(APPLE)
   run_cmake(FrameworksWithSubdirs)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_path/VALIDATOR-no-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-no-function-stderr.txt
new file mode 100644 (file)
index 0000000..1dfd064
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_path\):
+  find_path missing required argument for "VALIDATOR"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_path/VALIDATOR-no-function.cmake
new file mode 100644 (file)
index 0000000..bac2752
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_path(result NAMES input.txt VALIDATOR)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
new file mode 100644 (file)
index 0000000..92ee17e
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_path\):
+  find_path command specified for "VALIDATOR" is not a function: check.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
new file mode 100644 (file)
index 0000000..62b44ed
--- /dev/null
@@ -0,0 +1,5 @@
+
+macro(CHECK result path)
+endmacro()
+
+find_path(result NAMES input.txt VALIDATOR check)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-stderr.txt
new file mode 100644 (file)
index 0000000..851353d
--- /dev/null
@@ -0,0 +1,3 @@
+CHECK='[^']+/Tests/RunCMake/find_path/include/'
+CHECK='[^']+/Tests/RunCMake/find_path/include/'
+CHECK='[^']+/Tests/RunCMake/find_path/include/'
diff --git a/Tests/RunCMake/find_path/VALIDATOR-stdout.txt b/Tests/RunCMake/find_path/VALIDATOR-stdout.txt
new file mode 100644 (file)
index 0000000..3fce030
--- /dev/null
@@ -0,0 +1,3 @@
+-- DIR='[^']+/Tests/RunCMake/find_path/include'
+-- DIR='[^']+/Tests/RunCMake/find_path/include'
+-- DIR='DIR-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-undefined-function-stderr.txt
new file mode 100644 (file)
index 0000000..d3e0517
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_path\):
+  find_path command specified for "VALIDATOR" is undefined: undefined.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_path/VALIDATOR-undefined-function.cmake
new file mode 100644 (file)
index 0000000..3fb55c5
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_path(result NAMES input.txt VALIDATOR undefined)
diff --git a/Tests/RunCMake/find_path/VALIDATOR.cmake b/Tests/RunCMake/find_path/VALIDATOR.cmake
new file mode 100644 (file)
index 0000000..1e01250
--- /dev/null
@@ -0,0 +1,39 @@
+
+function(CHECK_DEFAULT result filename)
+  message("CHECK='${filename}'")
+endfunction()
+
+function(CHECK_OK result filename)
+  message("CHECK='${filename}'")
+  set(${result} TRUE PARENT_SCOPE)
+endfunction()
+
+function(CHECK_KO result filename)
+  message("CHECK='${filename}'")
+  set(${result} FALSE PARENT_SCOPE)
+endfunction()
+
+
+find_path(DIR
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_default
+  )
+message(STATUS "DIR='${DIR}'")
+unset(DIR CACHE)
+
+find_path(DIR
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_ok
+  )
+message(STATUS "DIR='${DIR}'")
+unset(DIR CACHE)
+
+find_path(DIR
+  NAMES PrefixInPATH.h
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/include
+  VALIDATOR check_ko
+  )
+message(STATUS "DIR='${DIR}'")
+unset(DIR CACHE)
index d0ce8fc..f8ecb8f 100644 (file)
@@ -9,6 +9,10 @@ run_cmake(NO_CACHE)
 run_cmake(IgnorePrefixPath)
 run_cmake(REGISTRY_VIEW-no-view)
 run_cmake(REGISTRY_VIEW-wrong-view)
+run_cmake(VALIDATOR-no-function)
+run_cmake(VALIDATOR-undefined-function)
+run_cmake(VALIDATOR-specify-macro)
+run_cmake(VALIDATOR)
 
 if(CMAKE_SYSTEM_NAME MATCHES "^(Windows|CYGWIN|MSYS)$")
   run_cmake(WindowsCom)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt b/Tests/RunCMake/find_program/VALIDATOR-no-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-no-function-stderr.txt
new file mode 100644 (file)
index 0000000..b8868af
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-no-function.cmake:[0-9]+ \(find_program\):
+  find_program missing required argument for "VALIDATOR"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-no-function.cmake b/Tests/RunCMake/find_program/VALIDATOR-no-function.cmake
new file mode 100644 (file)
index 0000000..0d6a74f
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_program(result NAMES input.txt VALIDATOR)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
new file mode 100644 (file)
index 0000000..1ae5148
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-specify-macro.cmake:[0-9]+ \(find_program\):
+  find_program command specified for "VALIDATOR" is not a function: check.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
new file mode 100644 (file)
index 0000000..43bb9ec
--- /dev/null
@@ -0,0 +1,5 @@
+
+macro(CHECK result path)
+endmacro()
+
+find_program(result NAMES input.txt VALIDATOR check)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-stderr.txt
new file mode 100644 (file)
index 0000000..6704ed1
--- /dev/null
@@ -0,0 +1,3 @@
+CHECK='[^']+/Tests/RunCMake/find_program/A/testA'
+CHECK='[^']+/Tests/RunCMake/find_program/A/testA'
+CHECK='[^']+/Tests/RunCMake/find_program/A/testA'
diff --git a/Tests/RunCMake/find_program/VALIDATOR-stdout.txt b/Tests/RunCMake/find_program/VALIDATOR-stdout.txt
new file mode 100644 (file)
index 0000000..d434742
--- /dev/null
@@ -0,0 +1,3 @@
+-- PROG='[^']+/Tests/RunCMake/find_program/A/testA'
+-- PROG='[^']+/Tests/RunCMake/find_program/A/testA'
+-- PROG='PROG-NOTFOUND'
diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-undefined-function-stderr.txt
new file mode 100644 (file)
index 0000000..a89ffa0
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at VALIDATOR-undefined-function.cmake:[0-9]+ \(find_program\):
+  find_program command specified for "VALIDATOR" is undefined: undefined.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake b/Tests/RunCMake/find_program/VALIDATOR-undefined-function.cmake
new file mode 100644 (file)
index 0000000..96be5fd
--- /dev/null
@@ -0,0 +1,2 @@
+
+find_program(result NAMES input.txt VALIDATOR undefined)
diff --git a/Tests/RunCMake/find_program/VALIDATOR.cmake b/Tests/RunCMake/find_program/VALIDATOR.cmake
new file mode 100644 (file)
index 0000000..8be2b39
--- /dev/null
@@ -0,0 +1,39 @@
+
+function(CHECK_DEFAULT result filename)
+  message("CHECK='${filename}'")
+endfunction()
+
+function(CHECK_OK result filename)
+  message("CHECK='${filename}'")
+  set(${result} TRUE PARENT_SCOPE)
+endfunction()
+
+function(CHECK_KO result filename)
+  message("CHECK='${filename}'")
+  set(${result} FALSE PARENT_SCOPE)
+endfunction()
+
+
+find_program(PROG
+  NAMES testA
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A
+  VALIDATOR check_default
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
+
+find_program(PROG
+  NAMES testA
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A
+  VALIDATOR check_ok
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
+
+find_program(PROG
+  NAMES testA
+  HINTS ${CMAKE_CURRENT_SOURCE_DIR}/A
+  VALIDATOR check_ko
+  )
+message(STATUS "PROG='${PROG}'")
+unset(PROG CACHE)
diff --git a/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-result.txt b/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stderr.txt b/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stderr.txt
new file mode 100644 (file)
index 0000000..d082b8a
--- /dev/null
@@ -0,0 +1,12 @@
+^CMake Error at cmake_install.cmake:[0-9]+ \(file\):
+  file INSTALL cannot duplicate symlink
+
+    [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/new/dir
+
+  at
+
+    [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/dir
+
+  because: A directory already exists at that location
diff --git a/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stdout.txt b/Tests/RunCMake/install/DIRECTORY-symlink-clobber-all-stdout.txt
new file mode 100644 (file)
index 0000000..c520de1
--- /dev/null
@@ -0,0 +1,12 @@
+-- Installing: [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/dir
+-- Installing: [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/dir/file
+-- Installing: [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/lnk
+-- Installing: [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/lnk
+-- (Up-to-date|Installing): [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/lnk/file
+-- Installing: [^
+]*/Tests/RunCMake/install/DIRECTORY-symlink-clobber-build/root-all/dest/dir
diff --git a/Tests/RunCMake/install/DIRECTORY-symlink-clobber.cmake b/Tests/RunCMake/install/DIRECTORY-symlink-clobber.cmake
new file mode 100644 (file)
index 0000000..ac7a2cf
--- /dev/null
@@ -0,0 +1,11 @@
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/old/dir)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/old/dir/file "")
+file(CREATE_LINK dir ${CMAKE_CURRENT_BINARY_DIR}/old/lnk SYMBOLIC)
+install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/old/dir DESTINATION dest)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/old/lnk     DESTINATION dest)
+
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/new/lnk)
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/new/lnk/file "")
+file(CREATE_LINK lnk ${CMAKE_CURRENT_BINARY_DIR}/new/dir SYMBOLIC)
+install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/new/lnk DESTINATION dest)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/new/dir     DESTINATION dest)
index 7c12d4a..477ffe0 100644 (file)
@@ -175,6 +175,10 @@ run_install_test(FILES-PERMISSIONS)
 run_install_test(TARGETS-RPATH)
 run_install_test(InstallRequiredSystemLibraries)
 
+if(UNIX)
+  run_install_test(DIRECTORY-symlink-clobber)
+endif()
+
 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
   run_cmake(TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle)
   run_cmake(TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework)
diff --git a/Tests/RunCMake/project/LanguagesDuplicate-check.cmake b/Tests/RunCMake/project/LanguagesDuplicate-check.cmake
new file mode 100644 (file)
index 0000000..31c7da3
--- /dev/null
@@ -0,0 +1,10 @@
+if(NOT actual_stderr MATCHES "The following language has been specified multiple times: C\n")
+  set(RunCMake_TEST_FAILED "'LANGUAGES C C C' should report only 'C' and only once.")
+endif()
+
+if(NOT actual_stderr MATCHES "The following languages have been specified multiple times: C, CXX\n")
+  if(RunCMake_TEST_FAILED)
+    string(APPEND RunCMake_TEST_FAILED "\n")
+  endif()
+  string(APPEND RunCMake_TEST_FAILED "'LANGUAGES C C CXX CXX' should report 'C' and 'CXX'.")
+endif()
diff --git a/Tests/RunCMake/project/LanguagesDuplicate.cmake b/Tests/RunCMake/project/LanguagesDuplicate.cmake
new file mode 100644 (file)
index 0000000..97a79d0
--- /dev/null
@@ -0,0 +1,11 @@
+cmake_policy(SET CMP0057 NEW)
+
+project(ProjectA C C C)
+project(ProjectB C C CXX CXX)
+
+get_property(langs GLOBAL PROPERTY ENABLED_LANGUAGES)
+foreach(lang C CXX)
+  if(NOT lang IN_LIST langs)
+    message(FATAL_ERROR "Expected language '${lang}' to be enabled.")
+  endif()
+endforeach()
index 945d9ed..6d9f52f 100644 (file)
@@ -12,6 +12,11 @@ run_cmake_with_options(CodeInjection
 if(CMake_TEST_RESOURCES)
   run_cmake(ExplicitRC)
 endif()
+
+set(RunCMake_DEFAULT_stderr .)
+run_cmake(LanguagesDuplicate)
+unset(RunCMake_DEFAULT_stderr)
+
 run_cmake(LanguagesImplicit)
 run_cmake(LanguagesEmpty)
 run_cmake(LanguagesNONE)
index 2feeb0f..a43133b 100644 (file)
@@ -5,6 +5,16 @@ int main(int argc, char* argv[])
 {
   int i;
   for (i = 1; i < argc; ++i) {
+    if (strcmp(argv[i], "-p") == 0) {
+      // Ensure compile commands were not appended after the source file
+      for (++i; i < argc; ++i) {
+        if (strcmp(argv[i], "--") == 0) {
+          fprintf(stderr, "Command line arguments unexpectedly appended\n");
+          return 1;
+        }
+      }
+      return 0;
+    }
     if (strcmp(argv[i], "-bad") == 0) {
       fprintf(stdout, "stdout from bad command line arg '-bad'\n");
       fprintf(stderr, "stderr from bad command line arg '-bad'\n");
diff --git a/Tests/RunCMake/return/CMP0140-NEW.cmake b/Tests/RunCMake/return/CMP0140-NEW.cmake
new file mode 100644 (file)
index 0000000..eb6b85c
--- /dev/null
@@ -0,0 +1,13 @@
+
+cmake_policy(SET CMP0140 NEW)
+
+function(FUNC)
+  set(VAR "set")
+  return(PROPAGATE VAR)
+endfunction()
+
+set(VAR "initial")
+func()
+if (NOT DEFINED VAR OR NOT VAR  STREQUAL "set")
+  message(FATAL_ERROR "return(PROPAGATE) not handled correctly.")
+endif()
diff --git a/Tests/RunCMake/return/CMP0140-OLD.cmake b/Tests/RunCMake/return/CMP0140-OLD.cmake
new file mode 100644 (file)
index 0000000..8113a43
--- /dev/null
@@ -0,0 +1,8 @@
+
+cmake_policy(SET CMP0140 OLD)
+
+function(FUNC)
+  return(PROPAGATE VAR)
+endfunction()
+
+func()
diff --git a/Tests/RunCMake/return/CMP0140-WARN-result.txt b/Tests/RunCMake/return/CMP0140-WARN-result.txt
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/return/CMP0140-WARN-stderr.txt b/Tests/RunCMake/return/CMP0140-WARN-stderr.txt
new file mode 100644 (file)
index 0000000..ed4beb6
--- /dev/null
@@ -0,0 +1,12 @@
+CMake Warning \(dev\) at CMP0140-WARN.cmake:[0-9]+ \(return\):
+  Policy CMP0140 is not set: The return\(\) command checks its arguments.  Run
+  "cmake --help-policy CMP0140" for policy details.  Use the cmake_policy
+  command to set the policy and suppress this warning.
+
+  return\(\) checks its arguments when the policy is set to NEW.  Since the
+  policy is not set the OLD behavior will be used so the arguments will be
+  ignored.
+Call Stack \(most recent call first\):
+  CMP0140-WARN.cmake:[0-9]+ \(func\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/return/CMP0140-WARN.cmake b/Tests/RunCMake/return/CMP0140-WARN.cmake
new file mode 100644 (file)
index 0000000..df2fc88
--- /dev/null
@@ -0,0 +1,8 @@
+
+cmake_policy(VERSION 3.1)
+
+function(FUNC)
+  return(PROPAGATE VAR)
+endfunction()
+
+func()
index ef2163c..6cc903f 100644 (file)
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.1...3.25)
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/return/PropagateFromDirectory.cmake b/Tests/RunCMake/return/PropagateFromDirectory.cmake
new file mode 100644 (file)
index 0000000..9c820bb
--- /dev/null
@@ -0,0 +1,10 @@
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+
+add_subdirectory(subdir)
+
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC1")
+endif()
diff --git a/Tests/RunCMake/return/PropagateFromFunction.cmake b/Tests/RunCMake/return/PropagateFromFunction.cmake
new file mode 100644 (file)
index 0000000..1c12bc1
--- /dev/null
@@ -0,0 +1,142 @@
+
+function(FUNC1)
+  set(VAR1 "set")
+  unset(VAR2)
+  return(PROPAGATE VAR1 VAR2)
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func1()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC1")
+endif()
+
+
+function(FUNC2)
+  block()
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endblock()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func2()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC2")
+endif()
+
+
+function(FUNC3)
+  block(SCOPE_FOR POLICIES)
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endblock()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func3()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC3")
+endif()
+
+
+function(FUNC4)
+  while(TRUE)
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endwhile()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func4()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC4")
+endif()
+
+
+function(FUNC5)
+  foreach(item IN ITEMS A B)
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endforeach()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func5()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC5")
+endif()
+
+
+function(FUNC6)
+  if(TRUE)
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endif()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func6()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC6")
+endif()
+
+
+function(FUNC7)
+  if(FALSE)
+  else()
+    set(VAR1 "set")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endif()
+endfunction()
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+func7()
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for FUNC7")
+endif()
+
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+cmake_language(CALL func7)
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for cmake_language(CALL FUNC7)")
+endif()
+
+
+set(VAR1 "initial")
+set(VAR2 "initial")
+cmake_language(EVAL CODE "
+  function(FUNC8)
+    set(VAR1 \"set\")
+    unset(VAR2)
+    return(PROPAGATE VAR1 VAR2)
+  endfunction()
+
+  func8()")
+if((NOT DEFINED VAR1 OR NOT VAR1 STREQUAL "set")
+    OR DEFINED VAR2)
+  message(SEND_ERROR "erroneous propagation for cmake_language(EVAL CODE)")
+endif()
diff --git a/Tests/RunCMake/return/PropagateNothing.cmake b/Tests/RunCMake/return/PropagateNothing.cmake
new file mode 100644 (file)
index 0000000..0ace58e
--- /dev/null
@@ -0,0 +1,2 @@
+
+return(PROPAGATE)
index 2cc6c9d..f9e06a5 100644 (file)
@@ -1,3 +1,12 @@
 include(RunCMake)
 
 run_cmake(ReturnFromForeach)
+
+run_cmake(WrongArgument)
+run_cmake(PropagateNothing)
+run_cmake(PropagateFromFunction)
+run_cmake(PropagateFromDirectory)
+
+run_cmake(CMP0140-NEW)
+run_cmake(CMP0140-OLD)
+run_cmake(CMP0140-WARN)
diff --git a/Tests/RunCMake/return/WrongArgument-result.txt b/Tests/RunCMake/return/WrongArgument-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/return/WrongArgument-stderr.txt b/Tests/RunCMake/return/WrongArgument-stderr.txt
new file mode 100644 (file)
index 0000000..4d1c5ad
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at WrongArgument.cmake:[0-9]+ \(return\):
+  return called with unsupported argument "WRONG_ARG"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/return/WrongArgument.cmake b/Tests/RunCMake/return/WrongArgument.cmake
new file mode 100644 (file)
index 0000000..d03b19f
--- /dev/null
@@ -0,0 +1,2 @@
+
+return(WRONG_ARG)
diff --git a/Tests/RunCMake/return/subdir/CMakeLists.txt b/Tests/RunCMake/return/subdir/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e575d47
--- /dev/null
@@ -0,0 +1,5 @@
+
+set(VAR1 "set")
+unset(VAR2)
+
+return(PROPAGATE VAR1 VAR2)
diff --git a/Tests/RunCMake/showIncludes.c b/Tests/RunCMake/showIncludes.c
new file mode 100644 (file)
index 0000000..23b3845
--- /dev/null
@@ -0,0 +1,33 @@
+#if defined(_MSC_VER) && _MSC_VER >= 1928
+#  pragma warning(disable : 5105) /* macro expansion warning in windows.h */
+#endif
+#include <windows.h>
+
+#include <stdio.h>
+
+int main()
+{
+  /* 'cl /showIncludes' encodes output in the console output code page.  */
+  unsigned int cp = GetConsoleOutputCP();
+  printf("Console output code page: %u\n", cp);
+  printf("Console input code page: %u\n", GetConsoleCP());
+  printf("ANSI code page: %u\n", GetACP());
+  printf("OEM code page: %u\n", GetOEMCP());
+
+  if (cp == 54936 || cp == 936) {
+    /* VSLANG=2052 */
+    printf("\xd7\xa2\xd2\xe2: "
+           "\xb0\xfc\xba\xac\xce\xc4\xbc\xfe:\n");
+    return 0;
+  }
+
+  if (cp == 65001) {
+    /* VSLANG=2052  */
+    printf("\xe6\xb3\xa8\xe6\x84\x8f: "
+           "\xe5\x8c\x85\xe5\x90\xab\xe6\x96\x87\xe4\xbb\xb6:\n");
+    return 0;
+  }
+
+  fprintf(stderr, "No example showIncludes for console's output code page.\n");
+  return 1;
+}
index ab4194d..255c16a 100644 (file)
@@ -129,7 +129,7 @@ assert_strequal("${error}" "member '0' not found")
 
 string(JSON result ERROR_VARIABLE error GET "${json1}" array 10)
 assert_strequal("${result}" "array-10-NOTFOUND")
-assert_strequal("${error}" "expected an index less then 4 got '10'")
+assert_strequal("${error}" "expected an index less than 4 got '10'")
 
 string(JSON result ERROR_VARIABLE error GET "${json1}" array 2 some notThere)
 assert_strequal("${result}" "array-2-some-notThere-NOTFOUND")
@@ -240,7 +240,7 @@ endif()
 
 string(JSON result ERROR_VARIABLE error MEMBER "${json1}" values 100)
 assert_strequal("${result}" "values-100-NOTFOUND")
-assert_strequal("${error}" "expected an index less then 5 got '100'")
+assert_strequal("${error}" "expected an index less than 5 got '100'")
 
 # Test length loops
 string(JSON arrayLength ERROR_VARIABLE error LENGTH "${json1}" types array)
@@ -301,7 +301,7 @@ assert_json_equal("${error}" "${result}"
 
 string(JSON result ERROR_VARIABLE error REMOVE ${json2} array 100)
 assert_strequal("${result}" "array-100-NOTFOUND")
-assert_strequal("${error}" "expected an index less then 4 got '100'")
+assert_strequal("${error}" "expected an index less than 4 got '100'")
 
 # Test SET
 string(JSON result ERROR_VARIABLE error SET ${json2} new 5)
diff --git a/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt b/Tests/RunCMake/target_compile_options/CMP0101-BEFORE_keyword-stderr.txt
new file mode 100644 (file)
index 0000000..f04e43f
--- /dev/null
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0101-BEFORE_keyword.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0101 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\)$
index 021de41..9b6581c 100644 (file)
@@ -90,12 +90,22 @@ if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES
   run_cmake_target(apple_framework target-framework main-target-framework)
   run_cmake_target(apple_framework target-reexport_framework main-target-reexport_framework)
   run_cmake_target(apple_framework target-weak_framework main-target-weak_framework)
+
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND (NOT XCODE OR XCODE_VERSION GREATER_EQUAL 13))
+    run_cmake_target(apple_framework target-framework-postfix main-target-framework-postfix)
+    run_cmake_target(apple_framework target-reexport_framework-postfix main-target-reexport_framework-postfix)
+    run_cmake_target(apple_framework target-weak_framework-postfix main-target-weak_framework-postfix)
+  endif()
 endif()
 
 if (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION GREATER_EQUAL "12")
   run_cmake_target(apple_framework needed_framework main-needed_framework)
 
   run_cmake_target(apple_framework target-needed_framework main-target-needed_framework)
+
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG AND (NOT XCODE OR XCODE_VERSION GREATER_EQUAL 13))
+    run_cmake_target(apple_framework target-needed_framework-postfix main-target-needed_framework-postfix)
+  endif()
 endif()
 
 # Apple library features
index e9a93e9..ca0e72d 100644 (file)
@@ -59,3 +59,33 @@ target_link_libraries(main-target-reexport_framework PRIVATE "$<LINK_LIBRARY:FRA
 # feature WEAK_FRAMEWORK
 add_executable(main-target-weak_framework main.mm)
 target_link_libraries(main-target-weak_framework PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework>")
+
+
+
+get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTI_CONFIG)
+  add_library(target-framework-postfix SHARED foo.mm)
+  set_target_properties(target-framework-postfix PROPERTIES FRAMEWORK TRUE
+                                                            FRAMEWORK_MULTI_CONFIG_POSTFIX_RELEASE "_release")
+  target_link_libraries(target-framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>")
+
+
+  # feature FRAMEWORK
+  add_executable(main-target-framework-postfix main.mm)
+  target_link_libraries(main-target-framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:FRAMEWORK,target-framework-postfix>")
+
+
+  # feature NEEDED_FRAMEWORK
+  add_executable(main-target-needed_framework-postfix main.mm)
+  target_link_libraries(main-target-needed_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:NEEDED_FRAMEWORK,target-framework-postfix>")
+
+
+  # feature REEXPORT_FRAMEWORK
+  add_executable(main-target-reexport_framework-postfix main.mm)
+  target_link_libraries(main-target-reexport_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework-postfix>")
+
+
+  # feature WEAK_FRAMEWORK
+  add_executable(main-target-weak_framework-postfix main.mm)
+  target_link_libraries(main-target-weak_framework-postfix PRIVATE "$<LINK_LIBRARY:FRAMEWORK,Foundation>" "$<LINK_LIBRARY:REEXPORT_FRAMEWORK,target-framework-postfix>")
+endif()
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt
new file mode 100644 (file)
index 0000000..042d67d
--- /dev/null
@@ -0,0 +1,12 @@
+^CMake Warning \(dev\) at FileSetDefaultWrongTypeExperimental.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetDefaultWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
+  target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or
+  "CXX_MODULE_HEADER_UNITS"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake
new file mode 100644 (file)
index 0000000..5ade637
--- /dev/null
@@ -0,0 +1,6 @@
+enable_language(C)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+add_library(lib1 STATIC empty.c)
+target_sources(lib1 PRIVATE FILE_SET UNKNOWN)
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt
new file mode 100644 (file)
index 0000000..a1b784f
--- /dev/null
@@ -0,0 +1,12 @@
+^CMake Warning \(dev\) at FileSetWrongTypeExperimental.cmake:6 \(target_sources\):
+  CMake's C\+\+ module support is experimental.  It is meant only for
+  experimentation and feedback to CMake developers.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Error at FileSetWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
+  target_sources File set TYPE may only be "HEADERS", "CXX_MODULES", or
+  "CXX_MODULE_HEADER_UNITS"
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake
new file mode 100644 (file)
index 0000000..332441c
--- /dev/null
@@ -0,0 +1,6 @@
+enable_language(C)
+
+set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
+
+add_library(lib1 STATIC empty.c)
+target_sources(lib1 PRIVATE FILE_SET a TYPE UNKNOWN)
index 6a3c7b9..7c67c3f 100644 (file)
@@ -26,6 +26,8 @@ run_cmake(FileSetProperties)
 run_cmake(FileSetNoType)
 run_cmake(FileSetWrongType)
 run_cmake(FileSetDefaultWrongType)
+run_cmake(FileSetWrongTypeExperimental)
+run_cmake(FileSetDefaultWrongTypeExperimental)
 run_cmake(FileSetChangeScope)
 run_cmake(FileSetChangeType)
 run_cmake(FileSetWrongBaseDirs)
index 652bcfc..4e41a19 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at BadLinkLibraries.cmake:2 \(try_compile\):
+CMake Error at BadLinkLibraries.cmake:[0-9]+ \(try_compile\):
   Only libraries may be used as try_compile or try_run IMPORTED
   LINK_LIBRARIES.  Got not_a_library of type UTILITY.
 Call Stack \(most recent call first\):
index e8b5add..b758e23 100644 (file)
@@ -1,3 +1,7 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 add_custom_target(not_a_library)
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   LINK_LIBRARIES not_a_library)
index 864a294..ddcba4f 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at BadSources1.cmake:1 \(try_compile\):
+CMake Error at BadSources1.cmake:[0-9]+ \(try_compile\):
   Unknown extension ".c" for file
 
     .*/Tests/RunCMake/try_compile/src.c
index aa4dc5e..c95935d 100644 (file)
@@ -1 +1,3 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
index 3313f99..953dd9c 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at BadSources2.cmake:2 \(try_compile\):
+CMake Error at BadSources2.cmake:[0-9]+ \(try_compile\):
   Unknown extension ".cxx" for file
 
     .*/Tests/RunCMake/try_compile/src.cxx
index ed2b036..3f22bb6 100644 (file)
@@ -1,5 +1,6 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
 enable_language(C)
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c
-          ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+                                   ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
   )
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-result.txt b/Tests/RunCMake/try_compile/BinDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/BinDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..b1f5ae3
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_compile\):
+  No <bindir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/BinDirEmpty.cmake b/Tests/RunCMake/try_compile/BinDirEmpty.cmake
new file mode 100644 (file)
index 0000000..7bea43d
--- /dev/null
@@ -0,0 +1 @@
+try_compile(resultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_compile/BinDirRelative-result.txt b/Tests/RunCMake/try_compile/BinDirRelative-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt b/Tests/RunCMake/try_compile/BinDirRelative-stderr.txt
new file mode 100644 (file)
index 0000000..a7b6302
--- /dev/null
@@ -0,0 +1,6 @@
+^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_compile\):
+  <bindir> is not an absolute path:
+
+   'bin_dir_relative'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/BinDirRelative.cmake b/Tests/RunCMake/try_compile/BinDirRelative.cmake
new file mode 100644 (file)
index 0000000..8deda11
--- /dev/null
@@ -0,0 +1 @@
+try_compile(resultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_compile/BuildType.cmake b/Tests/RunCMake/try_compile/BuildType.cmake
new file mode 100644 (file)
index 0000000..8d7e3be
--- /dev/null
@@ -0,0 +1,8 @@
+enable_language(C)
+set(CMAKE_BUILD_TYPE RelWithDebInfo)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT
+  ${try_compile_bindir_or_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/out.bin"
+  )
diff --git a/Tests/RunCMake/try_compile/BuildTypeAsFlag.cmake b/Tests/RunCMake/try_compile/BuildTypeAsFlag.cmake
new file mode 100644 (file)
index 0000000..a822156
--- /dev/null
@@ -0,0 +1,9 @@
+enable_language(C)
+set(CMAKE_BUILD_TYPE RelWithDebInfo)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT
+  ${try_compile_bindir_or_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/out.bin"
+  CMAKE_FLAGS "-DCMAKE_BUILD_TYPE=Release"
+  )
index 209afcc..c3c48a1 100644 (file)
@@ -1,4 +1,4 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/CStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^CMake Error at .*/Tests/RunCMake/try_compile/CStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:[0-9]+ \(add_executable\):
   C_STANDARD is set to invalid value '3'
 +
 CMake Error at CStandard.cmake:[0-9]+ \(try_compile\):
index 2849ed4..6ac04db 100644 (file)
@@ -1,7 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   C_STANDARD 3
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index 79ae874..10a4384 100644 (file)
@@ -1,23 +1,30 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c
   C_STANDARD 99
   C_STANDARD_REQUIRED 1
   C_EXTENSIONS 0
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
 
 cmake_policy(SET CMP0067 NEW)
+
 set(CMAKE_C_STANDARD 99)
 set(CMAKE_C_STANDARD_REQUIRED 1)
 set(CMAKE_C_EXTENSIONS 0)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/CStandardGNU.c
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
index 97e72ea..f4dd5ae 100644 (file)
@@ -1,9 +1,13 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   C_STANDARD 3 # bogus, but not used
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
index 5d09c0c..8c49302 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at CopyFileErrorNoCopyFile.cmake:1 \(try_compile\):
+CMake Error at CopyFileErrorNoCopyFile.cmake:[0-9]+ \(try_compile\):
   COPY_FILE_ERROR may be used only with COPY_FILE
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index 8d7cb0e..8d15b3d 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   COPY_FILE_ERROR _copied)
index bcf95d5..52dff8f 100644 (file)
@@ -1,4 +1,4 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/CudaStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^CMake Error at .*/Tests/RunCMake/try_compile/CudaStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:[0-9]+ \(add_executable\):
   CUDA_STANDARD is set to invalid value '4'
 +
 CMake Error at CudaStandard.cmake:[0-9]+ \(try_compile\):
index a230424..0be89be 100644 (file)
@@ -1,7 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(CUDA)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cu
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.cu
   CUDA_STANDARD 4
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index ec7245f..55a06e2 100644 (file)
@@ -1,6 +1,17 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^(CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:
+  The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid
+  value: "3".
+
+
+)?CMake Error at .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:[0-9]+ \(add_executable\):
   CXX_STANDARD is set to invalid value '3'
-+
+
+(
+CMake Error in .*/Tests/RunCMake/try_compile/CxxStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:
+  The CXX_STANDARD property on target "cmTC_[0-9a-f]*" contained an invalid
+  value: "3".
+
+)?
 CMake Error at CxxStandard.cmake:[0-9]+ \(try_compile\):
   Failed to generate test project build system.
 Call Stack \(most recent call first\):
index bcb49b9..e5b4e78 100644 (file)
@@ -1,7 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(CXX)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
   CXX_STANDARD 3
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index e714fe4..552d99d 100644 (file)
@@ -1,23 +1,30 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(CXX)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx
   CXX_STANDARD 11
   CXX_STANDARD_REQUIRED 1
   CXX_EXTENSIONS 0
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
 
 cmake_policy(SET CMP0067 NEW)
+
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED 1)
 set(CMAKE_CXX_EXTENSIONS 0)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/CxxStandardGNU.cxx
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
index 35caa9d..e0ebfee 100644 (file)
@@ -1,9 +1,13 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(CXX)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
   CXX_STANDARD 3 # bogus, but not used
   OUTPUT_VARIABLE out
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
diff --git a/Tests/RunCMake/try_compile/EmptyListArgs.cmake b/Tests/RunCMake/try_compile/EmptyListArgs.cmake
new file mode 100644 (file)
index 0000000..5cd7465
--- /dev/null
@@ -0,0 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+enable_language(C)
+
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  CMAKE_FLAGS           # no values
+  COMPILE_DEFINITIONS   # no values
+  LINK_LIBRARIES        # no values
+  LINK_OPTIONS          # no values
+  )
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt b/Tests/RunCMake/try_compile/EmptyValueArgs-stderr.txt
new file mode 100644 (file)
index 0000000..b1344bd
--- /dev/null
@@ -0,0 +1,9 @@
+CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\):
+  COPY_FILE must be followed by a file path
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at EmptyValueArgs.cmake:[0-9]+ \(try_compile\):
+  COPY_FILE_ERROR must be followed by a variable name
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/EmptyValueArgs.cmake b/Tests/RunCMake/try_compile/EmptyValueArgs.cmake
new file mode 100644 (file)
index 0000000..fda4f10
--- /dev/null
@@ -0,0 +1,7 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "")
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "x" COPY_FILE_ERROR "")
index 4040c59..6e66825 100644 (file)
@@ -1,3 +1,5 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
 
 set(ENV{CMAKE_BUILD_TYPE} "Bad")
@@ -6,8 +8,8 @@ set(ENV{CMAKE_CONFIGURATION_TYPES} "Bad;Debug")
 add_library(tc_defs INTERFACE IMPORTED)
 target_compile_definitions(tc_defs INTERFACE "TC_CONFIG_$<UPPER_CASE:$<CONFIG>>")
 
-try_compile(ENV_CONFIG_RESULT "${CMAKE_BINARY_DIR}"
-  SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/EnvConfig.c"
+try_compile(ENV_CONFIG_RESULT "${try_compile_bindir_or_SOURCES}"
+  ${try_compile_redundant_SOURCES} "${CMAKE_CURRENT_SOURCE_DIR}/EnvConfig.c"
   COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/EnvConfig.bin"
   OUTPUT_VARIABLE tc_output
   LINK_LIBRARIES tc_defs
index 6d29069..1ead4c2 100644 (file)
@@ -1,8 +1,12 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(ISPC)
-set(CMAKE_ISPC_INSTRUCTION_SETS avx512skx-i32x16
-                                avx512skx-i32x16)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
+
+set(CMAKE_ISPC_INSTRUCTION_SETS avx512skx-i32x16 avx512skx-i32x16)
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index 7f59c14..e08e25f 100644 (file)
@@ -1,11 +1,16 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(ISPC)
-set(CMAKE_ISPC_INSTRUCTION_SETS avx512skx-i32x16
-                                avx512skx-i32x16)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
+
+set(CMAKE_ISPC_INSTRUCTION_SETS avx512skx-i32x16 avx512skx-i32x16)
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
+
 if(NOT result)
   message(FATAL_ERROR "making Ninja and Ninja Multi-Config behave the same")
 endif()
index c1ab6f2..2276bd3 100644 (file)
@@ -1,7 +1,12 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(ISPC)
+
 set(CMAKE_ISPC_INSTRUCTION_SETS "avxknl-i32x16")
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index 0d3bd43..bd45569 100644 (file)
@@ -1,7 +1,12 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(ISPC)
+
 set(CMAKE_ISPC_INSTRUCTION_SETS avx512knl-i32x16 avx512skx-i32x16)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.ispc
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index 8808fd1..4228580 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at NoArgs.cmake:1 \(try_compile\):
-  try_compile unknown error.
+CMake Error at NoArgs.cmake:[0-9]+ \(try_compile\):
+  The try_compile\(\) command requires at least 3 arguments.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/NoCStandard-result.txt b/Tests/RunCMake/try_compile/NoCStandard-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/NoCStandard-stderr.txt b/Tests/RunCMake/try_compile/NoCStandard-stderr.txt
new file mode 100644 (file)
index 0000000..e0bb9ed
--- /dev/null
@@ -0,0 +1,7 @@
+CMake Error at NoCStandard.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "C_STANDARD":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/NoCStandard.cmake b/Tests/RunCMake/try_compile/NoCStandard.cmake
new file mode 100644 (file)
index 0000000..e0d0478
--- /dev/null
@@ -0,0 +1,4 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  C_STANDARD)
index d65d948..55ba687 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoCopyFile.cmake:1 \(try_compile\):
-  COPY_FILE must be followed by a file path
+CMake Error at NoCopyFile.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "COPY_FILE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index 8c648ff..270dff9 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   COPY_FILE)
index e889524..008d4e9 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoCopyFile2.cmake:1 \(try_compile\):
-  COPY_FILE must be followed by a file path
+CMake Error at NoCopyFile2.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "COPY_FILE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index 04b7f68..2ea8182 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   COPY_FILE CMAKE_FLAGS -DA=B)
index ed552fd..7ca185d 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoCopyFileError.cmake:1 \(try_compile\):
-  COPY_FILE_ERROR must be followed by a variable name
+CMake Error at NoCopyFileError.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "COPY_FILE_ERROR":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index d4d69ee..1a56e86 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/copied.bin COPY_FILE_ERROR)
index 18ad751..ec29f0b 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoOutputVariable.cmake:1 \(try_compile\):
-  OUTPUT_VARIABLE must be followed by a variable name
+CMake Error at NoOutputVariable.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index 3b9cb34..13dc4b4 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   OUTPUT_VARIABLE)
index 8b2cc25..dd34559 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoOutputVariable2.cmake:1 \(try_compile\):
-  OUTPUT_VARIABLE must be followed by a variable name
+CMake Error at NoOutputVariable2.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index ad9ac9a..4d5dda3 100644 (file)
@@ -1,2 +1,4 @@
-try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   OUTPUT_VARIABLE CMAKE_FLAGS -DA=B)
index 023032b..55603dc 100644 (file)
@@ -1,4 +1,7 @@
-CMake Error at NoSources.cmake:1 \(try_compile\):
-  SOURCES must be followed by at least one source file
+CMake Error at NoSources.cmake:[0-9]+ \(try_compile\):
+  Error after keyword "SOURCES":
+
+    missing required value
+
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index 025e658..dcd5c7a 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at NonSourceCompileDefinitions.cmake:1 \(try_compile\):
-  COMPILE_DEFINITIONS specified on a srcdir type TRY_COMPILE
+CMake Error at NonSourceCompileDefinitions.cmake:[0-9]+ \(try_compile\):
+  COMPILE_DEFINITIONS allowed only in source file signature
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index f5893e1..1b7dfeb 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at NonSourceCopyFile.cmake:1 \(try_compile\):
-  COPY_FILE specified on a srcdir type TRY_COMPILE
+CMake Error at NonSourceCopyFile.cmake:[0-9]+ \(try_compile\):
+  COPY_FILE allowed only in source file signature
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
index f1b4df9..59f1d73 100644 (file)
@@ -1,4 +1,4 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/ObjCStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^CMake Error at .*/Tests/RunCMake/try_compile/ObjCStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:[0-9]+ \(add_executable\):
   OBJC_STANDARD is set to invalid value '3'
 +
 CMake Error at ObjCStandard.cmake:[0-9]+ \(try_compile\):
index b2066f9..a691ddd 100644 (file)
@@ -1,7 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(OBJC)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.m
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.m
   OBJC_STANDARD 3
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
index a2f91b4..21fa20f 100644 (file)
@@ -1,4 +1,4 @@
-^CMake Error at .*/Tests/RunCMake/try_compile/ObjCxxStandard-build/CMakeFiles/CMakeTmp/CMakeLists.txt:[0-9]+ \(add_executable\):
+^CMake Error at .*/Tests/RunCMake/try_compile/ObjCxxStandard-build/CMakeFiles/CMake(Tmp|Scratch/TryCompile-[^/]+)/CMakeLists.txt:[0-9]+ \(add_executable\):
   OBJCXX_STANDARD is set to invalid value '3'
 +
 CMake Error at ObjCxxStandard.cmake:[0-9]+ \(try_compile\):
index 1221805..b03f560 100644 (file)
@@ -1,7 +1,11 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(OBJCXX)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.mm
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.mm
   OBJCXX_STANDARD 3
   OUTPUT_VARIABLE out
   )
+
 message("try_compile output:\n${out}")
diff --git a/Tests/RunCMake/try_compile/OldProjectBinDirEmpty-result.txt b/Tests/RunCMake/try_compile/OldProjectBinDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/OldProjectBinDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/OldProjectBinDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..e9ec450
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at OldProjectBinDirEmpty.cmake:[0-9]+ \(try_compile\):
+  No <bindir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/OldProjectBinDirEmpty.cmake b/Tests/RunCMake/try_compile/OldProjectBinDirEmpty.cmake
new file mode 100644 (file)
index 0000000..fa922d9
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT "" ${CMAKE_CURRENT_SOURCE_DIR}/proj Foo)
diff --git a/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-result.txt b/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..47dd60f
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at OldProjectSrcDirEmpty.cmake:[0-9]+ \(try_compile\):
+  No <srcdir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty.cmake b/Tests/RunCMake/try_compile/OldProjectSrcDirEmpty.cmake
new file mode 100644 (file)
index 0000000..dfbfba6
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} "" Foo)
index 12835be..a2e983e 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at OneArg.cmake:1 \(try_compile\):
-  try_compile unknown error.
+CMake Error at OneArg.cmake:[0-9]+ \(try_compile\):
+  The try_compile\(\) command requires at least 3 arguments.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/OutputDirAsFlag.cmake b/Tests/RunCMake/try_compile/OutputDirAsFlag.cmake
new file mode 100644 (file)
index 0000000..1214bbb
--- /dev/null
@@ -0,0 +1,9 @@
+enable_language(C)
+set(CMAKE_BUILD_TYPE RelWithDebInfo)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+try_compile(RESULT
+  ${try_compile_bindir_or_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/out.bin"
+  CMAKE_FLAGS "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=bin"
+  )
diff --git a/Tests/RunCMake/try_compile/PlatformVariables-stderr.txt b/Tests/RunCMake/try_compile/PlatformVariables-stderr.txt
new file mode 100644 (file)
index 0000000..4a42b2d
--- /dev/null
@@ -0,0 +1,11 @@
+^CMake Debug Log at [^
+]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\):
+  Executing try_compile \(CMAKE_C_ABI_COMPILED\) in:
+
+    [^
+]*/Tests/RunCMake/try_compile/PlatformVariables-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+
+Call Stack \(most recent call first\):
+  [^
+]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)
+  PlatformVariables.cmake:[0-9]+ \(enable_language\)
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/ProjectBinDirEmpty-result.txt b/Tests/RunCMake/try_compile/ProjectBinDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/ProjectBinDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/ProjectBinDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..57a2bd0
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at ProjectBinDirEmpty.cmake:[0-9]+ \(try_compile\):
+  No <bindir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_compile/ProjectBinDirEmpty.cmake b/Tests/RunCMake/try_compile/ProjectBinDirEmpty.cmake
new file mode 100644 (file)
index 0000000..e867cc6
--- /dev/null
@@ -0,0 +1,4 @@
+try_compile(RESULT PROJECT Foo
+  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/proj
+  BINARY_DIR ""
+  )
diff --git a/Tests/RunCMake/try_compile/ProjectCopyFile-result.txt b/Tests/RunCMake/try_compile/ProjectCopyFile-result.txt
new file mode 100644 (file)
index 0000000..573541a
--- /dev/null
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt b/Tests/RunCMake/try_compile/ProjectCopyFile-stderr.txt
new file mode 100644 (file)
index 0000000..45241c9
--- /dev/null
@@ -0,0 +1,7 @@
+CMake Warning \(dev\) at ProjectCopyFile.cmake:[0-9]+ \(try_compile\):
+  Unknown arguments:
+
+    "COPY_FILE"
+    "result"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/ProjectCopyFile.cmake b/Tests/RunCMake/try_compile/ProjectCopyFile.cmake
new file mode 100644 (file)
index 0000000..6bfec99
--- /dev/null
@@ -0,0 +1,4 @@
+try_compile(RESULT
+  PROJECT TestProject
+  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/proj
+  COPY_FILE result)
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-result.txt b/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt b/Tests/RunCMake/try_compile/ProjectSrcDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..dc6f354
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at ProjectSrcDirEmpty.cmake:[0-9]+ \(try_compile\):
+  No <srcdir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirEmpty.cmake b/Tests/RunCMake/try_compile/ProjectSrcDirEmpty.cmake
new file mode 100644 (file)
index 0000000..ac33ed0
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT PROJECT Foo SOURCE_DIR "")
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirMissing-result.txt b/Tests/RunCMake/try_compile/ProjectSrcDirMissing-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt b/Tests/RunCMake/try_compile/ProjectSrcDirMissing-stderr.txt
new file mode 100644 (file)
index 0000000..af6edd5
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at ProjectSrcDirMissing.cmake:[0-9]+ \(try_compile\):
+  No <srcdir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/ProjectSrcDirMissing.cmake b/Tests/RunCMake/try_compile/ProjectSrcDirMissing.cmake
new file mode 100644 (file)
index 0000000..e32cdf4
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT PROJECT Foo)
index 3cfbfbf..7245471 100644 (file)
@@ -1,32 +1,39 @@
 include(RunCMake)
 
-run_cmake(CopyFileErrorNoCopyFile)
 run_cmake(NoArgs)
 run_cmake(OneArg)
 run_cmake(TwoArgs)
-run_cmake(NoCopyFile)
-run_cmake(NoCopyFile2)
-run_cmake(NoCopyFileError)
-run_cmake(NoOutputVariable)
-run_cmake(NoOutputVariable2)
 run_cmake(NoSources)
-run_cmake(BadLinkLibraries)
-run_cmake(BadSources1)
-run_cmake(BadSources2)
+run_cmake(BinDirEmpty)
+run_cmake(BinDirRelative)
+run_cmake(ProjectSrcDirMissing)
+run_cmake(ProjectSrcDirEmpty)
+run_cmake(ProjectBinDirEmpty)
+run_cmake(OldProjectSrcDirEmpty)
+run_cmake(OldProjectBinDirEmpty)
+
+set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=old_signature.cmake)
+include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake)
+unset(RunCMake_TEST_OPTIONS)
+
+set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=new_signature.cmake)
+include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake)
+unset(RunCMake_TEST_OPTIONS)
+
+run_cmake(SourceFromOneArg)
+run_cmake(SourceFromThreeArgs)
+run_cmake(SourceFromBadName)
+run_cmake(SourceFromBadFile)
+
+run_cmake(ProjectCopyFile)
 run_cmake(NonSourceCopyFile)
 run_cmake(NonSourceCompileDefinitions)
 
-run_cmake(EnvConfig)
-
 set(RunCMake_TEST_OPTIONS --debug-trycompile)
 run_cmake(PlatformVariables)
 run_cmake(WarnDeprecated)
 unset(RunCMake_TEST_OPTIONS)
 
-run_cmake(TargetTypeExe)
-run_cmake(TargetTypeInvalid)
-run_cmake(TargetTypeStatic)
-
 if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
     CMAKE_C_COMPILER_ID MATCHES "^(MSVC|GNU|LCC|Clang|AppleClang)$")
   set (RunCMake_TEST_OPTIONS -DRunCMake_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
@@ -34,41 +41,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
   unset (RunCMake_TEST_OPTIONS)
 endif()
 
-if(CMAKE_C_STANDARD_DEFAULT)
-  run_cmake(CStandard)
-elseif(DEFINED CMAKE_C_STANDARD_DEFAULT)
-  run_cmake(CStandardNoDefault)
-endif()
-if(CMAKE_OBJC_STANDARD_DEFAULT)
-  run_cmake(ObjCStandard)
-endif()
-if(CMAKE_CXX_STANDARD_DEFAULT)
-  run_cmake(CxxStandard)
-elseif(DEFINED CMAKE_CXX_STANDARD_DEFAULT)
-  run_cmake(CxxStandardNoDefault)
-endif()
-if(CMAKE_OBJCXX_STANDARD_DEFAULT)
-  run_cmake(ObjCxxStandard)
-endif()
-if(CMake_TEST_CUDA)
-  run_cmake(CudaStandard)
-endif()
-if(CMake_TEST_ISPC)
-  run_cmake(ISPCTargets)
-  run_cmake(ISPCInvalidTarget)
-  set(ninja "")
-  if(RunCMake_GENERATOR MATCHES "Ninja")
-    set(ninja "Ninja")
-  endif()
-  run_cmake(ISPCDuplicateTarget${ninja})
-endif()
-if((CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.4) OR CMAKE_C_COMPILER_ID MATCHES "LCC")
-  run_cmake(CStandardGNU)
-endif()
-if((CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) OR CMAKE_C_COMPILER_ID MATCHES "LCC")
-  run_cmake(CxxStandardGNU)
-endif()
-
 run_cmake(CMP0056)
 run_cmake(CMP0066)
 run_cmake(CMP0067)
diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile-result.txt b/Tests/RunCMake/try_compile/SourceFromBadFile-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadFile-stderr.txt
new file mode 100644 (file)
index 0000000..53a6d8d
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at SourceFromBadFile.cmake:[0-9]+ \(try_compile\):
+  SOURCE_FROM_FILE failed to copy "bad#source.c": No such file or directory
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/SourceFromBadFile.cmake b/Tests/RunCMake/try_compile/SourceFromBadFile.cmake
new file mode 100644 (file)
index 0000000..0a37f11
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT SOURCE_FROM_FILE bad.c "bad#source.c")
diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-result.txt b/Tests/RunCMake/try_compile/SourceFromBadName-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt b/Tests/RunCMake/try_compile/SourceFromBadName-stderr.txt
new file mode 100644 (file)
index 0000000..041f3f1
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at SourceFromBadName.cmake:[0-9]+ \(try_compile\):
+  SOURCE_FROM_CONTENT given invalid filename "bad/name.c"
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/SourceFromBadName.cmake b/Tests/RunCMake/try_compile/SourceFromBadName.cmake
new file mode 100644 (file)
index 0000000..44d92fd
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT SOURCE_FROM_CONTENT bad/name.c "int main();")
diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg-result.txt b/Tests/RunCMake/try_compile/SourceFromOneArg-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt b/Tests/RunCMake/try_compile/SourceFromOneArg-stderr.txt
new file mode 100644 (file)
index 0000000..8b2248a
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at SourceFromOneArg.cmake:[0-9]+ \(try_compile\):
+  SOURCE_FROM_CONTENT requires exactly two arguments
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/SourceFromOneArg.cmake b/Tests/RunCMake/try_compile/SourceFromOneArg.cmake
new file mode 100644 (file)
index 0000000..5a50499
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT SOURCE_FROM_CONTENT test.c)
diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt b/Tests/RunCMake/try_compile/SourceFromThreeArgs-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt b/Tests/RunCMake/try_compile/SourceFromThreeArgs-stderr.txt
new file mode 100644 (file)
index 0000000..5f2ff61
--- /dev/null
@@ -0,0 +1,4 @@
+CMake Error at SourceFromThreeArgs.cmake:[0-9]+ \(try_compile\):
+  SOURCE_FROM_CONTENT requires exactly two arguments
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake b/Tests/RunCMake/try_compile/SourceFromThreeArgs.cmake
new file mode 100644 (file)
index 0000000..196851a
--- /dev/null
@@ -0,0 +1 @@
+try_compile(RESULT SOURCE_FROM_CONTENT test.c "int" "main();")
index 9b6e727..330f5f5 100644 (file)
@@ -1,14 +1,20 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
+
 set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   OUTPUT_VARIABLE out
   COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/copy
   COPY_FILE_ERROR copy_err
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
+
 if(copy_err)
   message(FATAL_ERROR "try_compile COPY_FILE failed:\n${copy_err}")
 endif()
index 08b281a..e9123e3 100644 (file)
@@ -1,4 +1,4 @@
-^CMake Error at TargetTypeInvalid.cmake:2 \(try_compile\):
+^CMake Error at TargetTypeInvalid.cmake:[0-9]+ \(try_compile\):
   Invalid value 'INVALID' for CMAKE_TRY_COMPILE_TARGET_TYPE.  Only
   'EXECUTABLE' and 'STATIC_LIBRARY' are allowed.
 Call Stack \(most recent call first\):
index 0bbc4ac..15a20bb 100644 (file)
@@ -1,2 +1,6 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 set(CMAKE_TRY_COMPILE_TARGET_TYPE INVALID)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
index 006b8b8..98dea41 100644 (file)
@@ -1,14 +1,20 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 enable_language(C)
+
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
-try_compile(result ${CMAKE_CURRENT_BINARY_DIR}
-  SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/other.c
+
+try_compile(result ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/other.c
   OUTPUT_VARIABLE out
   COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/copy
   COPY_FILE_ERROR copy_err
   )
+
 if(NOT result)
   message(FATAL_ERROR "try_compile failed:\n${out}")
 endif()
+
 if(copy_err)
   message(FATAL_ERROR "try_compile COPY_FILE failed:\n${copy_err}")
 endif()
diff --git a/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt b/Tests/RunCMake/try_compile/TryRunArgs-stderr.txt
new file mode 100644 (file)
index 0000000..2a58e71
--- /dev/null
@@ -0,0 +1,18 @@
+^CMake Warning \(dev\) at TryRunArgs.cmake:[0-9]+ \(try_compile\):
+  Unknown arguments:
+
+    "COMPILE_OUTPUT_VARIABLE"
+    "compOutputVar"
+    "RUN_OUTPUT_VARIABLE"
+    "runOutputVar"
+    "RUN_OUTPUT_STDOUT_VARIABLE"
+    "runOutputStdOutVar"
+    "RUN_OUTPUT_STDERR_VARIABLE"
+    "runOutputStdErrVar"
+    "WORKING_DIRECTORY"
+    "runWorkDir"
+    "ARGS"
+    "runArgs"
+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/try_compile/TryRunArgs.cmake b/Tests/RunCMake/try_compile/TryRunArgs.cmake
new file mode 100644 (file)
index 0000000..e4cb1fe
--- /dev/null
@@ -0,0 +1,14 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+enable_language(C)
+
+try_compile(RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/out.bin"
+  COMPILE_OUTPUT_VARIABLE compOutputVar
+  RUN_OUTPUT_VARIABLE runOutputVar
+  RUN_OUTPUT_STDOUT_VARIABLE runOutputStdOutVar
+  RUN_OUTPUT_STDERR_VARIABLE runOutputStdErrVar
+  WORKING_DIRECTORY runWorkDir
+  ARGS runArgs
+  )
index b9c08fc..b68e78e 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at TwoArgs.cmake:1 \(try_compile\):
-  try_compile unknown error.
+CMake Error at TwoArgs.cmake:[0-9]+ \(try_compile\):
+  The try_compile\(\) command requires at least 3 arguments.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt b/Tests/RunCMake/try_compile/WarnDeprecated-stderr.txt
new file mode 100644 (file)
index 0000000..6474990
--- /dev/null
@@ -0,0 +1,11 @@
+^CMake Debug Log at [^
+]*/Modules/CMakeDetermineCompilerABI.cmake:[0-9]+ \(try_compile\):
+  Executing try_compile \(CMAKE_C_ABI_COMPILED\) in:
+
+    [^
+]*/Tests/RunCMake/try_compile/WarnDeprecated-build/CMakeFiles/CMakeScratch/TryCompile-[^/]+
+Call Stack \(most recent call first\):
+  [^
+]*/Modules/CMakeTestCCompiler.cmake:[0-9]+ \(CMAKE_DETERMINE_COMPILER_ABI\)
+  WarnDeprecated.cmake:[0-9]+ \(enable_language\)
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/try_compile/new_signature.cmake b/Tests/RunCMake/try_compile/new_signature.cmake
new file mode 100644 (file)
index 0000000..cbcb261
--- /dev/null
@@ -0,0 +1,2 @@
+set(try_compile_bindir_or_SOURCES SOURCES)
+set(try_compile_redundant_SOURCES "")
diff --git a/Tests/RunCMake/try_compile/old_and_new_signature_tests.cmake b/Tests/RunCMake/try_compile/old_and_new_signature_tests.cmake
new file mode 100644 (file)
index 0000000..ac07ad3
--- /dev/null
@@ -0,0 +1,66 @@
+# These tests are performed using both the historic and the newer SOURCES
+# signatures of try_compile. It is critical that they behave the same and
+# produce comparable output for both signatures. Tests that cannot do this
+# belong in RunCMakeTests.txt, not here.
+#
+# Tests here MUST include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS}) and
+# use the variables defined therein appropriately. Refer to existing tests for
+# examples.
+
+run_cmake(CopyFileErrorNoCopyFile)
+run_cmake(NoCopyFile)
+run_cmake(NoCopyFile2)
+run_cmake(NoCopyFileError)
+run_cmake(NoCStandard)
+run_cmake(NoOutputVariable)
+run_cmake(NoOutputVariable2)
+run_cmake(BadLinkLibraries)
+run_cmake(BadSources1)
+run_cmake(BadSources2)
+run_cmake(EmptyValueArgs)
+run_cmake(EmptyListArgs)
+run_cmake(TryRunArgs)
+run_cmake(BuildType)
+run_cmake(BuildTypeAsFlag)
+run_cmake(OutputDirAsFlag)
+
+run_cmake(EnvConfig)
+
+run_cmake(TargetTypeExe)
+run_cmake(TargetTypeInvalid)
+run_cmake(TargetTypeStatic)
+
+if(CMAKE_C_STANDARD_DEFAULT)
+  run_cmake(CStandard)
+elseif(DEFINED CMAKE_C_STANDARD_DEFAULT)
+  run_cmake(CStandardNoDefault)
+endif()
+if(CMAKE_OBJC_STANDARD_DEFAULT)
+  run_cmake(ObjCStandard)
+endif()
+if(CMAKE_CXX_STANDARD_DEFAULT)
+  run_cmake(CxxStandard)
+elseif(DEFINED CMAKE_CXX_STANDARD_DEFAULT)
+  run_cmake(CxxStandardNoDefault)
+endif()
+if(CMAKE_OBJCXX_STANDARD_DEFAULT)
+  run_cmake(ObjCxxStandard)
+endif()
+if(CMake_TEST_CUDA)
+  run_cmake(CudaStandard)
+endif()
+if(CMake_TEST_ISPC)
+  run_cmake(ISPCTargets)
+  run_cmake(ISPCInvalidTarget)
+  set(ninja "")
+  if(RunCMake_GENERATOR MATCHES "Ninja")
+    set(ninja "Ninja")
+  endif()
+  run_cmake(ISPCDuplicateTarget${ninja})
+endif()
+if((CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.4) OR CMAKE_C_COMPILER_ID MATCHES "LCC")
+  run_cmake(CStandardGNU)
+endif()
+if((CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) OR CMAKE_C_COMPILER_ID MATCHES "LCC")
+  run_cmake(CxxStandardGNU)
+endif()
diff --git a/Tests/RunCMake/try_compile/old_signature.cmake b/Tests/RunCMake/try_compile/old_signature.cmake
new file mode 100644 (file)
index 0000000..94b12de
--- /dev/null
@@ -0,0 +1,2 @@
+set(try_compile_bindir_or_SOURCES ${CMAKE_CURRENT_BINARY_DIR})
+set(try_compile_redundant_SOURCES SOURCES)
index dcd1bfc..f9cc50e 100644 (file)
@@ -1,4 +1,4 @@
-CMake Error at BadLinkLibraries.cmake:2 \(try_run\):
+CMake Error at BadLinkLibraries.cmake:[0-9+] \(try_run\):
   Only libraries may be used as try_compile or try_run IMPORTED
   LINK_LIBRARIES.  Got not_a_library of type UTILITY.
 Call Stack \(most recent call first\):
index a124bf6..950a942 100644 (file)
@@ -1,4 +1,7 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
 add_custom_target(not_a_library)
-try_run(RUN_RESULT COMPILE_RESULT
-  ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
   LINK_LIBRARIES not_a_library)
diff --git a/Tests/RunCMake/try_run/BinDirEmpty-result.txt b/Tests/RunCMake/try_run/BinDirEmpty-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt b/Tests/RunCMake/try_run/BinDirEmpty-stderr.txt
new file mode 100644 (file)
index 0000000..def1c22
--- /dev/null
@@ -0,0 +1,4 @@
+^CMake Error at BinDirEmpty.cmake:[0-9]+ \(try_run\):
+  No <bindir> specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/BinDirEmpty.cmake b/Tests/RunCMake/try_run/BinDirEmpty.cmake
new file mode 100644 (file)
index 0000000..d4b7ee3
--- /dev/null
@@ -0,0 +1 @@
+try_run(runResultVar compileResultVar "" ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_run/BinDirRelative-result.txt b/Tests/RunCMake/try_run/BinDirRelative-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/BinDirRelative-stderr.txt b/Tests/RunCMake/try_run/BinDirRelative-stderr.txt
new file mode 100644 (file)
index 0000000..54d4e86
--- /dev/null
@@ -0,0 +1,6 @@
+^CMake Error at BinDirRelative.cmake:[0-9]+ \(try_run\):
+  <bindir> is not an absolute path:
+
+   'bin_dir_relative'
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/BinDirRelative.cmake b/Tests/RunCMake/try_run/BinDirRelative.cmake
new file mode 100644 (file)
index 0000000..a277403
--- /dev/null
@@ -0,0 +1 @@
+try_run(runResultVar compileResultVar bin_dir_relative ${CMAKE_CURRENT_SOURCE_DIR}/src.c)
diff --git a/Tests/RunCMake/try_run/NoCompileOutputVariable-result.txt b/Tests/RunCMake/try_run/NoCompileOutputVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoCompileOutputVariable-stderr.txt b/Tests/RunCMake/try_run/NoCompileOutputVariable-stderr.txt
new file mode 100644 (file)
index 0000000..e8baffb
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoCompileOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "COMPILE_OUTPUT_VARIABLE":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/NoCompileOutputVariable.cmake b/Tests/RunCMake/try_run/NoCompileOutputVariable.cmake
new file mode 100644 (file)
index 0000000..9eb7c5e
--- /dev/null
@@ -0,0 +1,6 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  COMPILE_OUTPUT_VARIABLE
+  )
diff --git a/Tests/RunCMake/try_run/NoOutputCompileVariable-result.txt b/Tests/RunCMake/try_run/NoOutputCompileVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoOutputVariable-result.txt b/Tests/RunCMake/try_run/NoOutputVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoOutputVariable-stderr.txt b/Tests/RunCMake/try_run/NoOutputVariable-stderr.txt
new file mode 100644 (file)
index 0000000..46cfca0
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "OUTPUT_VARIABLE":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/NoOutputVariable.cmake b/Tests/RunCMake/try_run/NoOutputVariable.cmake
new file mode 100644 (file)
index 0000000..1901b30
--- /dev/null
@@ -0,0 +1,4 @@
+try_run(RUN_RESULT COMPILE_RESULT
+  ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  OUTPUT_VARIABLE
+  )
diff --git a/Tests/RunCMake/try_run/NoRunOutputVariable-result.txt b/Tests/RunCMake/try_run/NoRunOutputVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoRunOutputVariable-stderr.txt b/Tests/RunCMake/try_run/NoRunOutputVariable-stderr.txt
new file mode 100644 (file)
index 0000000..8ccbdab
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoRunOutputVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "RUN_OUTPUT_VARIABLE":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/NoRunOutputVariable.cmake b/Tests/RunCMake/try_run/NoRunOutputVariable.cmake
new file mode 100644 (file)
index 0000000..f9e5e0b
--- /dev/null
@@ -0,0 +1,6 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  RUN_OUTPUT_VARIABLE
+  )
diff --git a/Tests/RunCMake/try_run/NoRunStdErrVariable-result.txt b/Tests/RunCMake/try_run/NoRunStdErrVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoRunStdErrVariable-stderr.txt b/Tests/RunCMake/try_run/NoRunStdErrVariable-stderr.txt
new file mode 100644 (file)
index 0000000..1443ab2
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoRunStdErrVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "RUN_OUTPUT_STDERR_VARIABLE":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/try_run/NoRunStdErrVariable.cmake b/Tests/RunCMake/try_run/NoRunStdErrVariable.cmake
new file mode 100644 (file)
index 0000000..888bc25
--- /dev/null
@@ -0,0 +1,7 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+  RUN_OUTPUT_STDERR_VARIABLE
+  )
diff --git a/Tests/RunCMake/try_run/NoRunStdOutVariable-result.txt b/Tests/RunCMake/try_run/NoRunStdOutVariable-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoRunStdOutVariable-stderr.txt b/Tests/RunCMake/try_run/NoRunStdOutVariable-stderr.txt
new file mode 100644 (file)
index 0000000..8b90e94
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoRunStdOutVariable.cmake:[0-9]+ \(try_run\):
+  Error after keyword "RUN_OUTPUT_STDOUT_VARIABLE":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)$
diff --git a/Tests/RunCMake/try_run/NoRunStdOutVariable.cmake b/Tests/RunCMake/try_run/NoRunStdOutVariable.cmake
new file mode 100644 (file)
index 0000000..ed6ab88
--- /dev/null
@@ -0,0 +1,7 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
+  RUN_OUTPUT_STDOUT_VARIABLE
+  )
diff --git a/Tests/RunCMake/try_run/NoWorkingDirectory-result.txt b/Tests/RunCMake/try_run/NoWorkingDirectory-result.txt
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/try_run/NoWorkingDirectory-stderr.txt b/Tests/RunCMake/try_run/NoWorkingDirectory-stderr.txt
new file mode 100644 (file)
index 0000000..b6e258f
--- /dev/null
@@ -0,0 +1,7 @@
+^CMake Error at NoWorkingDirectory.cmake:[0-9]+ \(try_run\):
+  Error after keyword "WORKING_DIRECTORY":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/try_run/NoWorkingDirectory.cmake b/Tests/RunCMake/try_run/NoWorkingDirectory.cmake
new file mode 100644 (file)
index 0000000..0d68182
--- /dev/null
@@ -0,0 +1,6 @@
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  WORKING_DIRECTORY
+  )
index d74add0..dbea089 100644 (file)
@@ -1,6 +1,16 @@
 include(RunCMake)
 
-run_cmake(BadLinkLibraries)
+run_cmake(BinDirEmpty)
+run_cmake(BinDirRelative)
+run_cmake(NoOutputVariable)
+
+set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=old_signature.cmake)
+include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake)
+unset(RunCMake_TEST_OPTIONS)
+
+set(RunCMake_TEST_OPTIONS -Dtry_compile_DEFS=new_signature.cmake)
+include(${RunCMake_SOURCE_DIR}/old_and_new_signature_tests.cmake)
+unset(RunCMake_TEST_OPTIONS)
 
 if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
     CMAKE_C_COMPILER_ID MATCHES "^(MSVC|GNU|LCC|Clang|AppleClang)$")
@@ -8,5 +18,3 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$" AND
   run_cmake(LinkOptions)
   unset (RunCMake_TEST_OPTIONS)
 endif()
-
-run_cmake(WorkingDirArg)
index b583823..375a703 100644 (file)
@@ -1,6 +1,8 @@
-try_run(RUN_RESULT COMPILE_RESULT
-  ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp ${CMAKE_CURRENT_SOURCE_DIR}/src.c
-  RUN_OUTPUT_VARIABLE OUTPUT_VARIABLE
+include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS})
+
+try_run(RUN_RESULT COMPILE_RESULT ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_CURRENT_SOURCE_DIR}/src.c
+  RUN_OUTPUT_VARIABLE RUN_OUTPUT
   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/CMakeTmp/workdir
   )
 
diff --git a/Tests/RunCMake/try_run/new_signature.cmake b/Tests/RunCMake/try_run/new_signature.cmake
new file mode 100644 (file)
index 0000000..cbcb261
--- /dev/null
@@ -0,0 +1,2 @@
+set(try_compile_bindir_or_SOURCES SOURCES)
+set(try_compile_redundant_SOURCES "")
diff --git a/Tests/RunCMake/try_run/old_and_new_signature_tests.cmake b/Tests/RunCMake/try_run/old_and_new_signature_tests.cmake
new file mode 100644 (file)
index 0000000..e1c1784
--- /dev/null
@@ -0,0 +1,20 @@
+# These tests are performed using both the historic and the newer SOURCES
+# signatures of try_run. It is critical that they behave the same and produce
+# comparable output for both signatures. Tests that cannot do this belong in
+# RunCMakeTests.txt, not here.
+#
+# Tests here MUST include(${CMAKE_CURRENT_SOURCE_DIR}/${try_compile_DEFS}) and
+# use the variables defined therein appropriately. Refer to existing tests for
+# examples.
+
+run_cmake(BadLinkLibraries)
+run_cmake(BinDirEmpty)
+run_cmake(BinDirRelative)
+
+run_cmake(WorkingDirArg)
+
+run_cmake(NoCompileOutputVariable)
+run_cmake(NoRunOutputVariable)
+run_cmake(NoRunStdOutVariable)
+run_cmake(NoRunStdErrVariable)
+run_cmake(NoWorkingDirectory)
diff --git a/Tests/RunCMake/try_run/old_signature.cmake b/Tests/RunCMake/try_run/old_signature.cmake
new file mode 100644 (file)
index 0000000..94b12de
--- /dev/null
@@ -0,0 +1,2 @@
+set(try_compile_bindir_or_SOURCES ${CMAKE_CURRENT_BINARY_DIR})
+set(try_compile_redundant_SOURCES SOURCES)
index 519058e..b274322 100644 (file)
@@ -1,3 +1,16 @@
+#if !defined(FOO)
+#  error "FOO not defined"
+#endif
+#if BAR != 3
+#  error "FOO not defined to 3"
+#endif
+#if CCOND != 2
+#  error "CCOND not defined to 2"
+#endif
+#if defined(SWIFTCOND)
+#  error "SWIFTCOND defined"
+#endif
+
 extern int ObjCMain(void);
 int main(void)
 {
index 6d8e48b..e8b6521 100644 (file)
@@ -4,3 +4,4 @@ project(SwiftMix C Swift)
 add_executable(SwiftMix CMain.c ObjCMain.m SwiftMain.swift ObjC-Swift.h)
 set_property(TARGET SwiftMix PROPERTY XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "ObjC-Swift.h")
 target_compile_options(SwiftMix PRIVATE "$<$<COMPILE_LANGUAGE:C>:-Werror=objc-method-access>")
+target_compile_definitions(SwiftMix PRIVATE "$<IF:$<COMPILE_LANGUAGE:Swift>,SWIFTCOND,CCOND=2>" FOO BAR=3)
index d9c8cd7..238059d 100644 (file)
@@ -3,6 +3,26 @@ import Foundation
 @objc class SwiftMainClass : NSObject {
   @objc class func SwiftMain() -> Int32 {
     dump("Hello World!");
+#if FOO
+    dump("FOO defined");
+#else
+    fatalError("FOO not defined");
+#endif
+#if BAR
+    dump("BAR defined");
+#else
+    fatalError("BAR not defined");
+#endif
+#if CCOND
+    fatalError("CCOND defined");
+#else
+    dump("CCOND not defined");
+#endif
+#if SWIFTCOND
+    dump("SWIFTCOND defined");
+#else
+    fatalError("SWIFTCOND not defined");
+#endif
     return 0;
   }
 }
diff --git a/Tests/SwiftMixLib/CMakeLists.txt b/Tests/SwiftMixLib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..40d3498
--- /dev/null
@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.24)
+project(SwiftMixLib C CXX Swift)
+
+add_library(SwiftMixedLib lib.c lib.cpp lib.swift)
+add_executable(Swifty main.swift)
+target_link_libraries(Swifty PUBLIC SwiftMixedLib)
diff --git a/Tests/SwiftMixLib/lib.c b/Tests/SwiftMixLib/lib.c
new file mode 100644 (file)
index 0000000..8eca512
--- /dev/null
@@ -0,0 +1,4 @@
+int add_int(int a, int b)
+{
+  return a + b;
+}
diff --git a/Tests/SwiftMixLib/lib.cpp b/Tests/SwiftMixLib/lib.cpp
new file mode 100644 (file)
index 0000000..3e156b8
--- /dev/null
@@ -0,0 +1,4 @@
+int add(int a, int b)
+{
+  return a + b;
+}
diff --git a/Tests/SwiftMixLib/lib.swift b/Tests/SwiftMixLib/lib.swift
new file mode 100644 (file)
index 0000000..61009d8
--- /dev/null
@@ -0,0 +1,3 @@
+public func add(a: Int, b: Int) -> Int {
+    a + b
+}
diff --git a/Tests/SwiftMixLib/main.swift b/Tests/SwiftMixLib/main.swift
new file mode 100644 (file)
index 0000000..d2e9364
--- /dev/null
@@ -0,0 +1,3 @@
+import SwiftMixedLib
+
+print(add(a: 1, b: 2))
index 01c2222..fa8687d 100644 (file)
@@ -22,9 +22,13 @@ elseif(NOT XCODE_VERSION VERSION_LESS 8.0)
   set(CMAKE_Swift_LANGUAGE_VERSION 3.0)
 endif()
 
+add_subdirectory(SubA)
+add_subdirectory(SubB)
+
 set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)
 
 add_executable(SwiftOnly main.swift)
+target_compile_definitions(SwiftOnly PRIVATE SWIFTONLY)
 
 add_library(L L.swift)
 
diff --git a/Tests/SwiftOnly/SubA/CMakeLists.txt b/Tests/SwiftOnly/SubA/CMakeLists.txt
new file mode 100644 (file)
index 0000000..edebc16
--- /dev/null
@@ -0,0 +1,2 @@
+add_library(SubA SubA.swift)
+target_include_directories(SubA INTERFACE "$<TARGET_FILE_DIR:SubA>")
diff --git a/Tests/SwiftOnly/SubA/SubA.swift b/Tests/SwiftOnly/SubA/SubA.swift
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tests/SwiftOnly/SubB/CMakeLists.txt b/Tests/SwiftOnly/SubB/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e92927
--- /dev/null
@@ -0,0 +1,2 @@
+add_library(SubB SubB.swift)
+target_link_libraries(SubB PRIVATE SubA)
diff --git a/Tests/SwiftOnly/SubB/SubB.swift b/Tests/SwiftOnly/SubB/SubB.swift
new file mode 100644 (file)
index 0000000..d593c4c
--- /dev/null
@@ -0,0 +1 @@
+import SubA
index 28560d0..a3f1a2c 100644 (file)
@@ -1 +1,7 @@
 dump("SwiftOnly")
+
+#if SWIFTONLY
+dump("SWIFTONLY defined")
+#else
+fatalError("SWIFTONLY NOT defined")
+#endif
index 000fd2c..3e46ed5 100644 (file)
@@ -4,163 +4,96 @@ if(POLICY CMP0129)
 endif()
 project(TryCompile)
 
-macro(TEST_ASSERT value msg)
-  if (NOT ${value})
-    message (SEND_ERROR "Assertion failure:" ${msg} )
-  endif ()
+macro(EXPECT_PASS var out)
+  if(NOT ${var})
+    message(SEND_ERROR "Should pass failed:\n${out}")
+  endif()
 endmacro()
 
-macro(TEST_FAIL value msg)
-  if (${value})
-    message (SEND_ERROR "Failing test succeeded:" ${msg} )
-  endif ()
+macro(EXPECT_FAIL var out)
+  if(${var})
+    message(SEND_ERROR "Should fail passed:\n${out}")
+  endif()
 endmacro()
 
-macro(TEST_EXPECT_EXACT command expected)
-  if(NOT "x${result}" STREQUAL "x${expected}")
-    message(SEND_ERROR "${CMAKE_CURRENT_LIST_LINE}: TEST \"${command}\" failed: \"${result}\" expected: \"${expected}\"")
+macro(EXPECT_COMPILED name var out)
+  if(NOT ${var})
+    message(SEND_ERROR "${name} failed compiling:\n${out}")
   endif()
 endmacro()
 
-macro(TEST_EXPECT_CONTAINS command expected)
-  if(NOT "${result}" MATCHES "${expected}")
-    message(SEND_ERROR "${CMAKE_CURRENT_LIST_LINE}: TEST \"${command}\" failed: \"${result}\" expected: \"${expected}\"")
+macro(EXPECT_RUN_RESULT name var expected)
+  if(NOT ${var} EQUAL ${expected})
+    message(SEND_ERROR " ${name} gave unexpected run result: ${${var}} expected: ${expected}")
   endif()
 endmacro()
 
+macro(TEST_ASSERT value msg)
+  if (NOT ${value})
+    message (SEND_ERROR "Assertion failure:" ${msg} )
+  endif ()
+endmacro()
 
-# try to compile a file that should compile
-# also check that COPY_FILE works
-try_compile(SHOULD_PASS
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/pass.c
-    OUTPUT_VARIABLE TRY_OUT
-    COPY_FILE ${TryCompile_BINARY_DIR}/CopyOfPass
-    )
-
-if(NOT SHOULD_PASS)
-  message(SEND_ERROR "should pass failed ${TRY_OUT}")
-endif()
-if(NOT EXISTS "${TryCompile_BINARY_DIR}/CopyOfPass")
-   message(SEND_ERROR "COPY_FILE to \"${TryCompile_BINARY_DIR}/CopyOfPass\" failed")
-else()
-   file(REMOVE "${TryCompile_BINARY_DIR}/CopyOfPass")
-endif()
-
-# try to compile a file that should compile
-# also check that COPY_FILE_ERROR works
-file(WRITE ${TryCompile_BINARY_DIR}/invalid "")
-try_compile(SHOULD_PASS
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/pass.c
-    OUTPUT_VARIABLE TRY_OUT
-    COPY_FILE ${TryCompile_BINARY_DIR}/invalid/path
-    COPY_FILE_ERROR _captured
-    )
-if(NOT SHOULD_PASS)
-  message(SEND_ERROR "should pass failed ${TRY_OUT}")
-endif()
-if(NOT _captured MATCHES "Cannot copy output executable.*/invalid/path")
-  message(SEND_ERROR "COPY_FILE_ERROR did not capture expected message")
-endif()
-
-# try to compile a file that should not compile
-try_compile(SHOULD_FAIL
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/fail.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(SHOULD_FAIL)
-   message(SEND_ERROR "Should fail passed ${TRY_OUT}")
-endif()
-
-# try to compile a file that should compile
-try_compile(SHOULD_PASS
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/pass.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(NOT SHOULD_PASS)
-  message(SEND_ERROR "should pass failed ${TRY_OUT}")
-endif()
-
-# try to compile a file that should not compile
-try_compile(SHOULD_FAIL
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/fail.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(SHOULD_FAIL)
-   message(SEND_ERROR "Should fail passed ${TRY_OUT}")
-endif()
-
-# try to compile two files that should compile
-try_compile(SHOULD_PASS
-    ${TryCompile_BINARY_DIR}
-    SOURCES ${TryCompile_SOURCE_DIR}/pass2a.c ${TryCompile_SOURCE_DIR}/pass2b.cxx
-    OUTPUT_VARIABLE TRY_OUT)
-if(NOT SHOULD_PASS)
-  message(SEND_ERROR "should pass failed ${TRY_OUT}")
-endif()
-
-# try to compile two files that should not compile
-try_compile(SHOULD_FAIL
-    ${TryCompile_BINARY_DIR}
-    SOURCES ${TryCompile_SOURCE_DIR}/fail2a.c ${TryCompile_SOURCE_DIR}/fail2b.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(SHOULD_FAIL)
-   message(SEND_ERROR "Should fail passed ${TRY_OUT}")
-endif()
-
-# try to compile a file that should compile
-set(_c_flags "${CMAKE_C_FLAGS}")
-if(WATCOM)
-  string(APPEND CMAKE_C_FLAGS " -dTESTDEF")
-else()
-  string(APPEND CMAKE_C_FLAGS " \"-DTESTDEF\"")
-endif()
+# run old signature tests
+set(try_compile_bindir_or_SOURCES ${TryCompile_BINARY_DIR})
+set(try_compile_redundant_SOURCES SOURCES)
+set(try_compile_output_vars OUTPUT_VARIABLE TRY_OUT)
+set(try_compile_compile_output_var TRY_OUT)
+set(try_compile_run_output_var TRY_OUT)
+include(old_and_new_signature_tests.cmake)
+
+# run new signature tests
+set(try_compile_bindir_or_SOURCES SOURCES)
+set(try_compile_redundant_SOURCES "")
+set(try_compile_output_vars
+  COMPILE_OUTPUT_VARIABLE COMPILE_OUT
+  RUN_OUTPUT_VARIABLE RUN_OUTPUT)
+set(try_compile_compile_output_var COMPILE_OUT)
+set(try_compile_run_output_var RUN_OUTPUT)
+include(old_and_new_signature_tests.cmake)
+
+# try to compile an empty source specified directly
+try_compile(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE
+  SOURCE_FROM_CONTENT empty.c "")
+if(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE)
+  message(SEND_ERROR "Trying to compile an empty source succeeded?")
+endif()
+
+try_compile(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE
+  SOURCE_FROM_VAR empty.c NAME_OF_A_VAR_THAT_IS_NOT_SET)
+if(SHOULD_FAIL_DUE_TO_EMPTY_SOURCE)
+  message(SEND_ERROR "Trying to compile an empty source succeeded?")
+endif()
+
+# try to compile a copied source
 try_compile(SHOULD_PASS
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/testdef.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(NOT SHOULD_PASS)
-  message(SEND_ERROR "should pass failed ${TRY_OUT}")
-endif()
-set(CMAKE_C_FLAGS "${_c_flags}")
-
-if(NOT SHOULD_FAIL)
-  if(SHOULD_PASS)
-    message("All Tests passed, ignore all previous output.")
-  else()
-    message("Test failed")
-  endif()
-else()
-  message("Test failed")
-endif()
-try_compile(CMAKE_ANSI_FOR_SCOPE
-  ${TryCompile_BINARY_DIR}
-    ${CMAKE_ROOT}/Modules/TestForAnsiForScope.cxx OUTPUT_VARIABLE OUT)
-if (CMAKE_ANSI_FOR_SCOPE)
-   message("Compiler supports ansi for")
-else()
-   message("Compiler does not support ansi for scope")
-endif()
+  SOURCE_FROM_FILE pass.c ${TryCompile_SOURCE_DIR}/pass.c
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_COMPILED("SOURCE_FROM_FILE" SHOULD_PASS "${TRY_OUT}")
 
-try_compile(CMAKE_ANSI_FOR_SCOPE
-  ${TryCompile_BINARY_DIR}
-    ${CMAKE_ROOT}/Modules/TestForAnsiForScope.cxx OUTPUT_VARIABLE OUT)
-if (CMAKE_ANSI_FOR_SCOPE)
-   message("Compiler supports ansi for")
-else()
-   message("Compiler does not support ansi for scope")
-endif()
+# try to run a source specified directly
+set(TRY_RUN_MAIN_CODE
+  "extern int answer(); \n"
+  "int main() { return answer(); }\n")
+set(TRY_RUN_EXT_CODE
+  "int answer() { return 42; }\n")
 
-message("use the module now")
-include(${CMAKE_ROOT}/Modules/TestForANSIForScope.cmake)
-if (CMAKE_ANSI_FOR_SCOPE)
-   message("Compiler supports ansi for")
-else()
-   message("Compiler does not support ansi for scope")
-endif()
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+  SOURCE_FROM_CONTENT main.c "${TRY_RUN_MAIN_CODE}"
+  SOURCE_FROM_CONTENT answer.c "${TRY_RUN_EXT_CODE}"
+  COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT)
+EXPECT_COMPILED("SOURCE_FROM_CONTENT" SHOULD_COMPILE "${COMPILE_OUTPUT}")
+EXPECT_RUN_RESULT("SOURCE_FROM_CONTENT" SHOULD_EXIT_WITH_ERROR 42)
 
-message("Testing try_compile project mode")
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+  SOURCE_FROM_VAR main.c TRY_RUN_MAIN_CODE
+  SOURCE_FROM_VAR answer.c TRY_RUN_EXT_CODE
+  COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT)
+EXPECT_COMPILED("SOURCE_FROM_VAR" SHOULD_COMPILE "${COMPILE_OUTPUT}")
+EXPECT_RUN_RESULT("SOURCE_FROM_VAR" SHOULD_EXIT_WITH_ERROR 42)
+
+# try to compile a project (old signature)
+message("Testing try_compile project mode (old signature)")
 try_compile(TEST_INNER
   ${TryCompile_BINARY_DIR}/CMakeFiles/Inner
   ${TryCompile_SOURCE_DIR}/Inner
@@ -168,241 +101,17 @@ try_compile(TEST_INNER
   OUTPUT_VARIABLE output)
 TEST_ASSERT(TEST_INNER "try_compile project mode failed:\n${output}")
 
-try_compile(COMPILE_DEFINITIONS_LIST_EXPANDED
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/check_a_b.c
-    OUTPUT_VARIABLE output
-    COMPILE_DEFINITIONS "-DDEF_A;-DDEF_B"
-    )
-if(COMPILE_DEFINITIONS_LIST_EXPANDED)
-  message(STATUS "COMPILE_DEFINITIONS list expanded correctly")
-else()
-  string(REPLACE "\n" "\n  " output "  ${output}")
-  message(SEND_ERROR "COMPILE_DEFINITIONS list did not expand correctly\n${output}")
-endif()
-
-try_compile(SHOULD_FAIL_DUE_TO_BAD_SOURCE
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/pass.c
-    OUTPUT_VARIABLE output
-    COMPILE_DEFINITIONS "bad#source.c"
-    )
-if(SHOULD_FAIL_DUE_TO_BAD_SOURCE AND NOT CMAKE_GENERATOR MATCHES "Watcom WMake|NMake Makefiles")
-  string(REPLACE "\n" "\n  " output "  ${output}")
-  message(SEND_ERROR "try_compile with bad#source.c did not fail:\n${output}")
-elseif(NOT output MATCHES [[(bad#source\.c|bad\.c|bad')]])
-  string(REPLACE "\n" "\n  " output "  ${output}")
-  message(SEND_ERROR "try_compile with bad#source.c failed without mentioning bad source:\n${output}")
-else()
-  message(STATUS "try_compile with bad#source.c correctly failed")
-endif()
+# try to compile a project (new signature)
+message("Testing try_compile project mode (new signature)")
+try_compile(TEST_INNER
+  PROJECT TryCompileInner
+  SOURCE_DIR ${TryCompile_SOURCE_DIR}/Inner
+  TARGET innerexe
+  OUTPUT_VARIABLE output)
+TEST_ASSERT(TEST_INNER "try_compile project mode failed:\n${output}")
 
 add_executable(TryCompile pass.c)
 
-######################################
-
-# now two tests for try_run()
-
-# try to run a file that should compile and run without error
-# also check that OUTPUT_VARIABLE contains both the compile output
-# and the run output
-try_run(SHOULD_RUN SHOULD_COMPILE
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/exit_success.c
-    OUTPUT_VARIABLE TRY_OUT)
-if(NOT SHOULD_COMPILE)
-  message(SEND_ERROR "exit_success failed compiling: ${TRY_OUT}")
-endif()
-if(NOT "${SHOULD_RUN}" STREQUAL "0")
-  message(SEND_ERROR "exit_success failed running with exit code ${SHOULD_RUN}")
-endif()
-# check the compile output for the filename
-if(NOT "${TRY_OUT}" MATCHES "exit_success")
-  message(SEND_ERROR " TRY_OUT didn't contain \"exit_success\": \"${TRY_OUT}\"")
-endif()
-# check the run output
-if(NOT "${TRY_OUT}" MATCHES "hello world")
-  message(SEND_ERROR " TRY_OUT didn't contain \"hello world\": \"${TRY_OUT}\"")
-endif()
-
-try_run(ARG_TEST_RUN ARG_TEST_COMPILE
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/expect_arg.c
-    OUTPUT_VARIABLE TRY_OUT
-    ARGS arg1 arg2)
-if(NOT ARG_TEST_COMPILE)
-  message(SEND_ERROR "expect_arg failed compiling: ${TRY_OUT}")
-endif()
-if(NOT "${ARG_TEST_RUN}" STREQUAL "0")
-  message(SEND_ERROR "expect_arg failed running with exit code ${ARG_TEST_RUN} ${TRY_OUT}")
-endif()
-
-# try to run a file that should compile and run, but return an error
-try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
-    ${TryCompile_BINARY_DIR}
-    ${TryCompile_SOURCE_DIR}/exit_with_error.c
-    COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
-    RUN_OUTPUT_VARIABLE RUN_OUTPUT)
-
-if(NOT SHOULD_COMPILE)
-  message(STATUS " exit_with_error failed compiling: ${COMPILE_OUTPUT}")
-endif()
-if("${SHOULD_EXIT_WITH_ERROR}" STREQUAL "0")
-  message(SEND_ERROR " exit_with_error passed with exit code ${SHOULD_EXIT_WITH_ERROR}")
-endif()
-
-# check the compile output, it should contain the filename
-if(NOT "${COMPILE_OUTPUT}" MATCHES "exit_with_error")
-  message(SEND_ERROR " COMPILE_OUT didn't contain \"exit_with_error\": \"${COMPILE_OUTPUT}\"")
-endif()
-#... but not the run time output
-if("${COMPILE_OUTPUT}" MATCHES "hello world")
-  message(SEND_ERROR " COMPILE_OUT contains the run output: \"${COMPILE_OUTPUT}\"")
-endif()
-# check the run output, it should stdout
-if(NOT "${RUN_OUTPUT}" MATCHES "hello world")
-  message(SEND_ERROR " RUN_OUTPUT didn't contain \"hello world\": \"${RUN_OUTPUT}\"")
-endif()
-
-#######################################################################
-#
-# also test that the CHECK_C_SOURCE_COMPILES, CHECK_CXX_SOURCE_COMPILES
-# CHECK_C_SOURCE_RUNS and CHECK_CXX_SOURCE_RUNS macros work
-
-include(CheckCSourceCompiles)
-include(CheckCXXSourceCompiles)
-include(CheckCSourceRuns)
-include(CheckCXXSourceRuns)
-
-CHECK_C_SOURCE_COMPILES("I don't build" C_BUILD_SHOULD_FAIL)
-CHECK_C_SOURCE_COMPILES("int main() {return 0;}" C_BUILD_SHOULD_WORK)
-CHECK_C_SOURCE_RUNS("int main() {return 1;}" C_RUN_SHOULD_FAIL)
-CHECK_C_SOURCE_RUNS("int main() {return 0;}" C_RUN_SHOULD_WORK)
-
-TEST_FAIL(C_BUILD_SHOULD_FAIL "CHECK_C_SOURCE_COMPILES() succeeded, but should have failed")
-TEST_ASSERT(C_BUILD_SHOULD_WORK "CHECK_C_SOURCE_COMPILES() failed")
-TEST_FAIL(C_RUN_SHOULD_FAIL "CHECK_C_SOURCE_RUNS() succeeded, but should have failed")
-TEST_ASSERT(C_RUN_SHOULD_WORK "CHECK_C_SOURCE_RUNS() failed")
-
-CHECK_CXX_SOURCE_COMPILES("I don't build" CXX_BUILD_SHOULD_FAIL)
-CHECK_CXX_SOURCE_COMPILES("int main() {return 0;}" CXX_BUILD_SHOULD_WORK)
-CHECK_CXX_SOURCE_COMPILES("void l(char const (&x)[2]){}; int main() { l(\"\\\\n\"); return 0;}"
-  CXX_BUILD_SHOULD_WORK_COMPLEX)
-
-CHECK_CXX_SOURCE_RUNS("int main() {return 2;}" CXX_RUN_SHOULD_FAIL)
-CHECK_CXX_SOURCE_RUNS("int main() {return 0;}" CXX_RUN_SHOULD_WORK)
-
-TEST_FAIL(CXX_BUILD_SHOULD_FAIL "CHECK_CXX_SOURCE_COMPILES() succeeded, but should have failed")
-TEST_ASSERT(CXX_BUILD_SHOULD_WORK "CHECK_CXX_SOURCE_COMPILES() failed")
-TEST_ASSERT(CXX_BUILD_SHOULD_WORK_COMPLEX "CHECK_CXX_SOURCE_COMPILES() failed")
-TEST_FAIL(CXX_RUN_SHOULD_FAIL "CHECK_CXX_SOURCE_RUNS() succeeded, but should have failed")
-TEST_ASSERT(CXX_RUN_SHOULD_WORK "CHECK_CXX_SOURCE_RUNS() failed")
-
-foreach(lang C CXX)
-  if(NOT CMAKE_${lang}_COMPILER_ID STREQUAL "PathScale")
-    set(${lang}_DD --)
-  endif()
-endforeach()
-
-unset(C_BOGUS_FLAG CACHE)
-include(CheckCCompilerFlag)
-CHECK_C_COMPILER_FLAG(${C_DD}-_this_is_not_a_flag_ C_BOGUS_FLAG)
-TEST_FAIL(C_BOGUS_FLAG "CHECK_C_COMPILER_FLAG() succeeded, but should have failed")
-unset(C_BOGUS_FLAG CACHE)
-if(DEFINED C_BOGUS_FLAG)
-  # Verify that CHECK_C_COMPILER_FLAG didn't construct a normal variable
-  message(SEND_ERROR "CHECK_C_COMPILER_FLAG shouldn't construct C_BOGUS_FLAG as a normal variable")
-endif()
-
-unset(CXX_BOGUS_FLAG CACHE)
-include(CheckCXXCompilerFlag)
-CHECK_CXX_COMPILER_FLAG(${CXX_DD}-_this_is_not_a_flag_ CXX_BOGUS_FLAG)
-TEST_FAIL(CXX_BOGUS_FLAG "CHECK_CXX_COMPILER_FLAG() succeeded, but should have failed")
-unset(CXX_BOGUS_FLAG CACHE)
-if(DEFINED CXX_BOGUS_FLAG)
-  # Verify that CHECK_C_COMPILER_FLAG didn't construct a normal variable
-  message(SEND_ERROR "CHECK_CXX_COMPILER_FLAG shouldn't construct CXX_BOGUS_FLAG as a normal variable")
-endif()
-
-if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "LCC")
-  unset(C_STRICT_PROTOTYPES CACHE)
-  CHECK_C_COMPILER_FLAG("-Werror;-Wstrict-prototypes" C_STRICT_PROTOTYPES)
-  TEST_ASSERT(C_STRICT_PROTOTYPES "CHECK_C_COMPILER_FLAG failed -Werror -Wstrict-prototypes")
-endif()
-
-#########################################################################
-#
-# Test that the CHECK_OBJCC_SOURCE_COMPILES, CHECK_OBJCXX_SOURCE_COMPILES
-# CHECK_OBJC_SOURCE_RUNS and CHECK_OBJCXX_SOURCE_RUNS macros work
-
-if (APPLE)
-    enable_language(OBJC)
-    enable_language(OBJCXX)
-
-    include(CheckOBJCSourceCompiles)
-    include(CheckOBJCXXSourceCompiles)
-    include(CheckOBJCSourceRuns)
-    include(CheckOBJCXXSourceRuns)
-
-    CHECK_OBJC_SOURCE_COMPILES("I don't build in Objective-C" OBJC_BUILD_SHOULD_FAIL)
-    CHECK_OBJC_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJC_BUILD_SHOULD_WORK)
-
-    TEST_FAIL(OBJC_BUILD_SHOULD_FAIL "CHECK_OBJC_SOURCE_COMPILES() succeeded, but should have failed")
-    TEST_ASSERT(SIMPLE_OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded")
-
-    set(CMAKE_REQUIRED_LIBRARIES "-framework Foundation")
-
-    CHECK_OBJC_SOURCE_COMPILES("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_BUILD_SHOULD_WORK)
-    CHECK_OBJC_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJC_RUN_SHOULD_FAIL)
-    CHECK_OBJC_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJC_RUN_SHOULD_WORK)
-    CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 2;\n}\n" OBJC_RUN_SHOULD_FAIL)
-    CHECK_OBJC_SOURCE_RUNS("#import <Foundation/Foundation.h>\nint main()\n{\nNSObject *foo;\nreturn 0;\n}\n" OBJC_RUN_SHOULD_WORK)
-
-    TEST_ASSERT(OBJC_BUILD_SHOULD_WORK "CHECK_OBJC_SOURCE_COMPILES() failed, but should have succeeded")
-    TEST_FAIL(SIMPLE_OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURC_RUNS() succeeds, but should have failed")
-    TEST_ASSERT(SIMPLE_OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded")
-    TEST_FAIL(OBJC_RUN_SHOULD_FAIL "CHECK_OBJC_SOURCE_RUNS() succeeds, but should have failed")
-    TEST_ASSERT(OBJC_RUN_SHOULD_WORK "CHECK_OBJC_SOURCE_RUNS() failed, but should have succeeded")
-
-
-    CHECK_OBJCXX_SOURCE_COMPILES("I don't build in Objective-C++" OBJCXX_BUILD_SHOULD_FAIL)
-    CHECK_OBJCXX_SOURCE_COMPILES("int main() { return 0; }" SIMPLE_OBJCXX_BUILD_SHOULD_WORK)
-
-    TEST_FAIL(OBJCXX_BUILD_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_COMPILES() succeeded, but should have failed")
-    TEST_ASSERT(SIMPLE_OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded")
-
-    CHECK_OBJCXX_SOURCE_COMPILES("#import <Foundation/Foundation.h>\n#include <iostream>\nint main()\n{\nNSObject *foo;\nstd::cout << \"Hello\" << std::endl;\nreturn 0;\n}\n" OBJCXX_BUILD_SHOULD_WORK)
-    CHECK_OBJCXX_SOURCE_RUNS("int main() { return 2; }" SIMPLE_OBJCXX_RUN_SHOULD_FAIL)
-    CHECK_OBJCXX_SOURCE_RUNS("int main() { return 0; }" SIMPLE_OBJCXX_RUN_SHOULD_WORK)
-    CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 2;\n}\n" OBJCXX_RUN_SHOULD_FAIL)
-    CHECK_OBJCXX_SOURCE_RUNS("#import <Foundation/Foundation.h>\n#include <vector>\nint main()\n{\nNSObject *foo;\nstd::vector<int> bar;\nreturn 0;\n}\n" OBJCXX_RUN_SHOULD_WORK)
-
-    TEST_ASSERT(OBJCXX_BUILD_SHOULD_WORK "CHECK_OBJCXX_SOURCE_COMPILES() failed, but should have succeeded")
-    TEST_FAIL(SIMPLE_OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURC_RUNS() succeeds, but should have failed")
-    TEST_ASSERT(SIMPLE_OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded")
-    TEST_FAIL(OBJCXX_RUN_SHOULD_FAIL "CHECK_OBJCXX_SOURCE_RUNS() succeeds, but should have failed")
-    TEST_ASSERT(OBJCXX_RUN_SHOULD_WORK "CHECK_OBJCXX_SOURCE_RUNS() failed, but should have succeeded")
-
-    # try to compile a file that should compile
-    try_compile(SHOULD_PASS
-      ${TryCompile_BINARY_DIR}
-      ${TryCompile_SOURCE_DIR}/pass.m
-      OUTPUT_VARIABLE TRY_OUT)
-    if(NOT SHOULD_PASS)
-      message(SEND_ERROR "should pass failed ${TRY_OUT}")
-    endif()
-
-    # try to compile a file that should not compile
-    try_compile(SHOULD_FAIL
-      ${TryCompile_BINARY_DIR}
-      ${TryCompile_SOURCE_DIR}/fail.m
-      OUTPUT_VARIABLE TRY_OUT)
-    if(SHOULD_FAIL)
-      message(SEND_ERROR "Should fail passed ${TRY_OUT}")
-    endif()
-
-endif()
-
 #######################################################################
 #
 # also test that the check_prototype_definition macro works
index f3c523d..dbddcf5 100644 (file)
@@ -3,5 +3,5 @@
 int main()
 {
   printf("hello world\n");
-  return -1;
+  return 1;
 }
diff --git a/Tests/TryCompile/old_and_new_signature_tests.cmake b/Tests/TryCompile/old_and_new_signature_tests.cmake
new file mode 100644 (file)
index 0000000..1373ad7
--- /dev/null
@@ -0,0 +1,276 @@
+# try to compile a file that should compile
+try_compile(SHOULD_PASS
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/pass.c
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+
+# try to compile a file that should compile
+# also check that COPY_FILE works
+try_compile(SHOULD_PASS
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/pass.c
+  OUTPUT_VARIABLE TRY_OUT
+  COPY_FILE ${TryCompile_BINARY_DIR}/CopyOfPass
+  )
+EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+
+if(NOT EXISTS "${TryCompile_BINARY_DIR}/CopyOfPass")
+  message(SEND_ERROR "COPY_FILE to \"${TryCompile_BINARY_DIR}/CopyOfPass\" failed")
+else()
+  file(REMOVE "${TryCompile_BINARY_DIR}/CopyOfPass")
+endif()
+
+# try to compile a file that should compile
+# also check that COPY_FILE_ERROR works
+file(WRITE ${TryCompile_BINARY_DIR}/invalid "")
+try_compile(SHOULD_PASS
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/pass.c
+  OUTPUT_VARIABLE TRY_OUT
+  COPY_FILE ${TryCompile_BINARY_DIR}/invalid/path
+  COPY_FILE_ERROR _captured
+  )
+EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+
+if(NOT _captured MATCHES "Cannot copy output executable.*/invalid/path")
+  message(SEND_ERROR "COPY_FILE_ERROR did not capture expected message")
+endif()
+
+# try to compile a file that should not compile
+try_compile(SHOULD_FAIL
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/fail.c
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_FAIL(SHOULD_FAIL "${TRY_OUT}")
+
+# try to compile two files that should compile
+try_compile(SHOULD_PASS
+  ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES}
+  ${TryCompile_SOURCE_DIR}/pass2a.c
+  ${TryCompile_SOURCE_DIR}/pass2b.cxx
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+
+# try to compile two files that should not compile
+try_compile(SHOULD_FAIL
+  ${try_compile_bindir_or_SOURCES}
+  ${try_compile_redundant_SOURCES}
+  ${TryCompile_SOURCE_DIR}/fail2a.c
+  ${TryCompile_SOURCE_DIR}/fail2b.c
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_FAIL(SHOULD_FAIL "${TRY_OUT}")
+
+# try to compile a file that should compile
+set(_c_flags "${CMAKE_C_FLAGS}")
+if(WATCOM)
+  string(APPEND CMAKE_C_FLAGS " -dTESTDEF")
+else()
+  string(APPEND CMAKE_C_FLAGS " \"-DTESTDEF\"")
+endif()
+try_compile(SHOULD_PASS
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/testdef.c
+  OUTPUT_VARIABLE TRY_OUT)
+EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+set(CMAKE_C_FLAGS "${_c_flags}")
+
+try_compile(CMAKE_ANSI_FOR_SCOPE
+  ${try_compile_bindir_or_SOURCES}
+  ${CMAKE_ROOT}/Modules/TestForAnsiForScope.cxx OUTPUT_VARIABLE OUT)
+if(CMAKE_ANSI_FOR_SCOPE)
+  message("Compiler supports ansi for")
+else()
+  message("Compiler does not support ansi for scope")
+endif()
+
+message("use the module now")
+include(${CMAKE_ROOT}/Modules/TestForANSIForScope.cmake)
+if(CMAKE_ANSI_FOR_SCOPE)
+  message("Compiler supports ansi for")
+else()
+  message("Compiler does not support ansi for scope")
+endif()
+
+# test that COMPILE_DEFINITIONS are correctly expanded
+try_compile(COMPILE_DEFINITIONS_LIST_EXPANDED
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/check_a_b.c
+    OUTPUT_VARIABLE output
+    COMPILE_DEFINITIONS "-DDEF_A;-DDEF_B"
+    )
+if(COMPILE_DEFINITIONS_LIST_EXPANDED)
+  message(STATUS "COMPILE_DEFINITIONS list expanded correctly")
+else()
+  string(REPLACE "\n" "\n  " output "  ${output}")
+  message(SEND_ERROR "COMPILE_DEFINITIONS list did not expand correctly\n${output}")
+endif()
+
+# try to compile a file that doesn't exist
+try_compile(SHOULD_FAIL_DUE_TO_BAD_SOURCE
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/pass.c
+    OUTPUT_VARIABLE output
+    COMPILE_DEFINITIONS "bad#source.c"
+    )
+if(SHOULD_FAIL_DUE_TO_BAD_SOURCE AND NOT CMAKE_GENERATOR MATCHES "Watcom WMake|NMake Makefiles")
+  string(REPLACE "\n" "\n  " output "  ${output}")
+  message(SEND_ERROR "try_compile with bad#source.c did not fail:\n${output}")
+elseif(NOT output MATCHES [[(bad#source\.c|bad\.c|bad[':])]])
+  string(REPLACE "\n" "\n  " output "  ${output}")
+  message(SEND_ERROR "try_compile with bad#source.c failed without mentioning bad source:\n${output}")
+else()
+  message(STATUS "try_compile with bad#source.c correctly failed")
+endif()
+
+if(APPLE)
+  # try to compile a file that should compile
+  try_compile(SHOULD_PASS
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/pass.m
+    OUTPUT_VARIABLE TRY_OUT)
+  EXPECT_PASS(SHOULD_PASS "${TRY_OUT}")
+
+  # try to compile a file that should not compile
+  try_compile(SHOULD_FAIL
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/fail.m
+    OUTPUT_VARIABLE TRY_OUT)
+  EXPECT_FAIL(SHOULD_FAIL "${TRY_OUT}")
+endif()
+
+# check that try_compile honors NO_CACHE
+function(try_compile_scope_test)
+  try_compile(
+    CACHED_RESULT
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/pass.c)
+  try_compile(
+    SHOULD_NOT_ESCAPE_SCOPE_RESULT
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/pass.c
+    NO_CACHE)
+endfunction()
+
+try_compile_scope_test()
+
+if(NOT DEFINED CACHE{CACHED_RESULT})
+  message(SEND_ERROR " Result from try_compile was not cached")
+endif()
+if(DEFINED SHOULD_NOT_ESCAPE_SCOPE_RESULT)
+  message(SEND_ERROR " Result from try_compile(NO_CACHE) leaked")
+endif()
+
+######################################
+
+# now test try_run()
+
+# try to run a file that should compile and run without error
+# also check that OUTPUT_VARIABLE contains both the compile output
+# and the run output
+try_run(SHOULD_RUN SHOULD_COMPILE
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/exit_success.c
+    ${try_compile_output_vars})
+EXPECT_COMPILED("exit_success" SHOULD_COMPILE "${${try_compile_compile_output_var}}")
+EXPECT_RUN_RESULT("exit_success" SHOULD_RUN 0)
+
+# check the compile output for the filename
+if(NOT "${${try_compile_compile_output_var}}" MATCHES "exit_success")
+  message(SEND_ERROR
+    " ${try_compile_compile_output_var} didn't contain \"exit_success\":"
+    " \"${${try_compile_compile_output_var}}\"")
+endif()
+# check the run output
+if(NOT "${${try_compile_run_output_var}}" MATCHES "hello world")
+  message(SEND_ERROR
+    " ${try_compile_run_output_var} didn't contain \"hello world\":"
+    " \"${${try_compile_run_output_var}}\"")
+endif()
+
+try_run(ARG_TEST_RUN ARG_TEST_COMPILE
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/expect_arg.c
+    COMPILE_OUTPUT_VARIABLE TRY_OUT
+    ARGS arg1 arg2)
+EXPECT_COMPILED("expect_arg" ARG_TEST_COMPILE "${TRY_OUT}")
+EXPECT_RUN_RESULT("expect_arg" ARG_TEST_RUN 0)
+
+# try to run a file that should compile and run, but return an error
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/exit_with_error.c
+    COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
+    RUN_OUTPUT_VARIABLE RUN_OUTPUT)
+EXPECT_COMPILED("exit_with_error" SHOULD_COMPILE "${COMPILE_OUTPUT}")
+EXPECT_RUN_RESULT("exit_with_error" SHOULD_EXIT_WITH_ERROR 1)
+
+# check the compile output, it should contain the filename
+if(NOT "${COMPILE_OUTPUT}" MATCHES "exit_with_error")
+  message(SEND_ERROR " COMPILE_OUT didn't contain \"exit_with_error\": \"${COMPILE_OUTPUT}\"")
+endif()
+#... but not the run time output
+if("${COMPILE_OUTPUT}" MATCHES "hello world")
+  message(SEND_ERROR " COMPILE_OUT contains the run output: \"${COMPILE_OUTPUT}\"")
+endif()
+# check the run output, it should contain stdout
+if(NOT "${RUN_OUTPUT}" MATCHES "hello world")
+  message(SEND_ERROR " RUN_OUTPUT didn't contain \"hello world\": \"${RUN_OUTPUT}\"")
+endif()
+
+# try to run a file and parse stdout and stderr separately
+# also check that COPY_FILE works
+try_run(SHOULD_EXIT_WITH_ERROR SHOULD_COMPILE
+  ${try_compile_bindir_or_SOURCES}
+  ${TryCompile_SOURCE_DIR}/stdout_and_stderr.c
+  COPY_FILE ${TryCompile_BINARY_DIR}/CopyOfRun
+  COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
+  RUN_OUTPUT_STDOUT_VARIABLE RUN_OUTPUT_STDOUT
+  RUN_OUTPUT_STDERR_VARIABLE RUN_OUTPUT_STDERR)
+EXPECT_PASS(SHOULD_COMPILE "${COMPILE_OUTPUT}")
+
+if(NOT EXISTS "${TryCompile_BINARY_DIR}/CopyOfRun")
+  message(SEND_ERROR "COPY_FILE to \"${TryCompile_BINARY_DIR}/CopyOfRun\" failed")
+else()
+  file(REMOVE "${TryCompile_BINARY_DIR}/CopyOfRun")
+endif()
+
+# check the run stdout output
+if(NOT "${RUN_OUTPUT_STDOUT}" MATCHES "hello world")
+  message(SEND_ERROR " RUN_OUTPUT_STDOUT didn't contain \"hello world\": \"${RUN_OUTPUT_STDOUT}\"")
+endif()
+# check the run stderr output
+if(NOT "${RUN_OUTPUT_STDERR}" MATCHES "error")
+  message(SEND_ERROR " RUN_OUTPUT_STDERR didn't contain \"error\": \"${RUN_OUTPUT_STDERR}\"")
+endif()
+
+# check that try_run honors NO_CACHE
+function(try_run_scope_test)
+  try_run(
+    CACHED_RUN_RESULT
+    CACHED_COMPILE_RESULT
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/exit_success.c)
+  try_run(
+    SHOULD_NOT_ESCAPE_SCOPE_RUN_RESULT
+    SHOULD_NOT_ESCAPE_SCOPE_COMPILE_RESULT
+    ${try_compile_bindir_or_SOURCES}
+    ${TryCompile_SOURCE_DIR}/exit_success.c
+    NO_CACHE)
+endfunction()
+
+try_run_scope_test()
+
+if(NOT DEFINED CACHE{CACHED_COMPILE_RESULT})
+  message(SEND_ERROR " Compile result from try_run was not cached")
+endif()
+if(NOT DEFINED CACHE{CACHED_RUN_RESULT})
+  message(SEND_ERROR " Run result from try_run was not cached")
+endif()
+if(DEFINED SHOULD_NOT_ESCAPE_SCOPE_COMPILE_RESULT)
+  message(SEND_ERROR " Compile result from try_run(NO_CACHE) leaked")
+endif()
+if(DEFINED SHOULD_NOT_ESCAPE_SCOPE_RUN_RESULT)
+  message(SEND_ERROR " Run result from try_run(NO_CACHE) leaked")
+endif()
diff --git a/Tests/TryCompile/stdout_and_stderr.c b/Tests/TryCompile/stdout_and_stderr.c
new file mode 100644 (file)
index 0000000..84ded1f
--- /dev/null
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main()
+{
+  fputs("error\n", stderr);
+  puts("hello world\n");
+  return 0;
+}
index 993d0d6..2b4f4fa 100644 (file)
@@ -11,7 +11,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
 # to be in the same directory.
 get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
 if(_isMultiConfig)
-  foreach(config ${CMAKE_CONFIGURATION_TYPES})
+  foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
     string(TOUPPER "${config}" config)
     set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
       ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
index f9440d7..52e8a54 100644 (file)
@@ -26,7 +26,7 @@ if(NOT result EQUAL 0)
   message(FATAL_ERROR "Listing app package content failed with: ${error}")
 endif()
 
-foreach(app_pkg_item ${EXPECTED_APP_PKG_CONTENT})
+foreach(app_pkg_item IN LISTS EXPECTED_APP_PKG_CONTENT)
   string(FIND ${APP_PKG_CONTENT_OUTPUT} ${app_pkg_item} _found)
   if(_found EQUAL -1)
     message(FATAL_ERROR "Generated app package is missing an expected item: ${app_pkg_item}")
index b712c27..bc16350 100644 (file)
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.22 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.23 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)
index b80fc22..b0ed911 100644 (file)
@@ -91,7 +91,7 @@
   { symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<cmGlobalNinjaGenerator::TargetAlias &>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__decay_and_strip<__gnu_cxx::__normal_iterator<const cmCTestTestHandler::cmCTestTestProperties *, std::vector<cmCTestTestHandler::cmCTestTestProperties, std::allocator<cmCTestTestHandler::cmCTestTestProperties> > > &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &, void *)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__success_type<std::chrono::duration<double, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::__success_type<std::chrono::duration<long, std::ratio<1, 1000000000> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
index 9d89dd8..dd07f9f 100644 (file)
@@ -7,6 +7,10 @@ if(MSVC)
         "CMAKE_CXX_FLAGS_${CONFIG}"
         "${CMAKE_CXX_FLAGS_${CONFIG}}"
         )
+      string(REPLACE "-MD" "-MT"
+        "CMAKE_CXX_FLAGS_${CONFIG}"
+        "${CMAKE_CXX_FLAGS_${CONFIG}}"
+        )
     endforeach()
   endif()
 endif()
index 9abae2a..e232c01 100644 (file)
@@ -25,7 +25,7 @@ RUN : \
  && nice make -j $(nproc) \
  && if $TEST; then \
         # Run tests that require the full build tree.
-        bin/ctest --output-on-failure -j 8 -R '^(CMake\.|CMakeLib\.|CMakeServerLib\.|RunCMake\.ctest_memcheck)'; \
+        bin/ctest --output-on-failure -j 8 -R '^(CMake\.|CMakeLib\.|RunCMake\.ctest_memcheck)'; \
     fi \
  && bin/cpack -G TGZ \
  && bin/cpack -G STGZ \
index 8c98d3e..736ee26 100644 (file)
@@ -26,7 +26,7 @@ RUN : \
  && nice make -j $(nproc) \
  && if $TEST; then \
         # Run tests that require the full build tree.
-        bin/ctest --output-on-failure -j 8 -R '^(CMake\.|CMakeLib\.|CMakeServerLib\.|RunCMake\.ctest_memcheck)'; \
+        bin/ctest --output-on-failure -j 8 -R '^(CMake\.|CMakeLib\.|RunCMake\.ctest_memcheck)'; \
     fi \
  && bin/cpack -G TGZ \
  && bin/cpack -G STGZ \
index 9715e07..5b66d15 100755 (executable)
@@ -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_83_1"
+readonly tag="curl-7_86_0"
 readonly shortlog=false
 readonly paths="
   CMake/*
index 6d423a7..280c684 100755 (executable)
@@ -8,7 +8,7 @@ readonly name="libuv"
 readonly ownership="libuv upstream <libuv@googlegroups.com>"
 readonly subtree="Utilities/cmlibuv"
 readonly repo="https://github.com/libuv/libuv.git"
-readonly tag="v1.x"
+readonly tag="v1.44.2"
 readonly shortlog=false
 readonly paths="
   LICENSE
index fbce1c8..886f4e0 100644 (file)
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.22 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.23 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)
@@ -118,12 +118,12 @@ if(SPHINX_QTHELP)
     COMMAND ${CMAKE_COMMAND} "-DCSS_DIR=${CMAKE_CURRENT_BINARY_DIR}/qthelp/_static"
       -P "${CMAKE_CURRENT_SOURCE_DIR}/apply_qthelp_css_workaround.cmake"
     # Workaround sphinx configurability:
-    # https://bitbucket.org/birkenfeld/sphinx/issue/1448/make-qthelp-more-configurable
+    # https://github.com/sphinx-doc/sphinx/issues/1448
     COMMAND ${CMAKE_COMMAND} "-DQTHELP_DIR=${CMAKE_CURRENT_BINARY_DIR}/qthelp/"
       -P "${CMAKE_CURRENT_SOURCE_DIR}/fixup_qthelp_names.cmake"
 
     # Create proper identifiers. Workaround for
-    # https://bitbucket.org/birkenfeld/sphinx/issue/1491/qthelp-should-generate-identifiers-for
+    # https://github.com/sphinx-doc/sphinx/issues/1491
     COMMAND "${Python_EXECUTABLE}"
       "${CMAKE_CURRENT_SOURCE_DIR}/create_identifiers.py"
       "${CMAKE_CURRENT_BINARY_DIR}/qthelp/"
@@ -161,7 +161,7 @@ endif()
 
 set(doc_format_outputs "")
 set(doc_format_last "")
-foreach(format ${doc_formats})
+foreach(format IN LISTS doc_formats)
   set(doc_format_output "doc_format_${format}")
   set(doc_format_log "build-${format}.log")
   if(CMake_SPHINX_CMAKE_ORG)
@@ -219,13 +219,7 @@ endforeach()
 add_custom_target(documentation ALL DEPENDS ${doc_format_outputs})
 
 if(CMake_SPHINX_DEPEND_ON_EXECUTABLES)
-  foreach(t
-      cmake
-      ccmake
-      cmake-gui
-      cpack
-      ctest
-      )
+  foreach(t IN ITEMS cmake ccmake cmake-gui cpack ctest)
     if(TARGET ${t})
       # Build documentation after main executables.
       add_dependencies(documentation ${t})
@@ -248,7 +242,7 @@ endif()
 if(SPHINX_MAN)
   file(GLOB man_rst RELATIVE ${CMake_SOURCE_DIR}/Help/manual
     ${CMake_SOURCE_DIR}/Help/manual/*.[1-9].rst)
-  foreach(m ${man_rst})
+  foreach(m IN LISTS man_rst)
     if("x${m}" MATCHES "^x(.+)\\.([1-9])\\.rst$")
       set(name "${CMAKE_MATCH_1}")
       set(sec "${CMAKE_MATCH_2}")
index 9215e14..c7b1233 100644 (file)
@@ -59,12 +59,6 @@ CMakeLexer.tokens["root"] = [
 
 from docutils.parsers.rst import Directive, directives
 from docutils.transforms import Transform
-try:
-    from docutils.utils.error_reporting import SafeString, ErrorString
-except ImportError:
-    # error_reporting was not in utils before version 0.11:
-    from docutils.error_reporting import SafeString, ErrorString
-
 from docutils import io, nodes
 
 from sphinx.directives import ObjectDescription
@@ -130,13 +124,13 @@ class CMakeModule(Directive):
             f = io.FileInput(source_path=path, encoding=encoding,
                              error_handler=e_handler)
         except UnicodeEncodeError as error:
-            raise self.severe('Problems with "%s" directive path:\n'
-                              'Cannot encode input file path "%s" '
-                              '(wrong locale?).' %
-                              (self.name, SafeString(path)))
+            msg = ('Problems with "%s" directive path:\n'
+                   'Cannot encode input file path "%s" '
+                   '(wrong locale?).' % (self.name, path))
+            raise self.severe(msg)
         except IOError as error:
-            raise self.severe('Problems with "%s" directive path:\n%s.' %
-                      (self.name, ErrorString(error)))
+            msg = 'Problems with "%s" directive path:\n%s.' % (self.name, error)
+            raise self.severe(msg)
         raw_lines = f.read().splitlines()
         f.close()
         rst = None
index 4539cf9..324cd92 100644 (file)
@@ -34,3 +34,14 @@ div.outdated {
   text-align: center;
   width: 100%;
 }
+
+/* Revert style to the inherited (normal text) for `:guide:` links */
+code.xref.cmake-guide {
+  font-size: inherit;
+  font-family: inherit;
+  font-weight: inherit;
+  padding: inherit;
+}
+code.xref.cmake-guide span.pre {
+  white-space: inherit;
+}
index c7b3c8a..a38c101 100644 (file)
@@ -10,6 +10,7 @@ set(HAVE_CTYPE_H 1)
 set(HAVE_LOCALE_H 1)
 set(HAVE_MEMMOVE 1)
 set(HAVE_SETLOCALE 1)
+set(HAVE_SNPRINTF 1)
 set(HAVE_STDDEF_H 1)
 set(HAVE_STDIO_H 1)
 set(HAVE_STDLIB_H 1)
@@ -76,6 +77,7 @@ if(WIN32)
   set(HAVE_FUTIMENS 0)
   set(HAVE_FUTIMES 0)
   set(HAVE_FUTIMESAT 0)
+  set(HAVE_GETADDRINFO 1)
   set(HAVE_GETEUID 0)
   set(HAVE_GETGRGID_R 0)
   set(HAVE_GETGRNAM_R 0)
@@ -98,6 +100,7 @@ if(WIN32)
   set(HAVE_IDN2_H 0)
   set(HAVE_IFADDRS_H 0)
   set(HAVE_IF_NAMETOINDEX 0)
+  set(HAVE_INET_NTOP 1)
   set(HAVE_INTTYPES_H 1)
   set(HAVE_IOCTL 0)
   set(HAVE_IOCTL_FIONBIO 0)
@@ -169,6 +172,7 @@ if(WIN32)
   set(HAVE_SIGNAL_H 1)
   set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
   set(HAVE_SIZEOF_SA_FAMILY_T 0)
+  set(HAVE_SOCKETPAIR 0)
   set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 0)
   set(HAVE_SPAWN_H 0)
   set(HAVE_SSL_H 0)
@@ -234,6 +238,10 @@ if(WIN32)
   set(HAVE_WORKING_EXT2_IOC_GETFLAGS 0)
   set(HAVE_WORKING_FS_IOC_GETFLAGS 0)
 
+  if(NOT MINGW)
+    set(HAVE_STRTOK_R 0)
+  endif()
+
   # Some POSIX headers are available on Windows.
   set(HAVE_SYS_TYPES_H 1)
   set(HAVE_SYS_UTIME_H 1)
index 8ccd016..b93e753 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,5 +18,7 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 @CMAKE_CONFIGURABLE_FILE_CONTENT@
index e99ea6f..75215a1 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 include(CheckCSourceCompiles)
 
index 42addd7..6a9fdea 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifdef TIME_WITH_SYS_TIME
 /* Time with sys/time test */
@@ -182,28 +184,6 @@ if (sizeof (bool *) )
 #include <float.h>
 int main() { return 0; }
 #endif
-#ifdef HAVE_GETADDRINFO
-#include <netdb.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
-int main(void) {
-    struct addrinfo hints, *ai;
-    int error;
-
-    memset(&hints, 0, sizeof(hints));
-    hints.ai_family = AF_UNSPEC;
-    hints.ai_socktype = SOCK_STREAM;
-#ifndef getaddrinfo
-    (void)getaddrinfo;
-#endif
-    error = getaddrinfo("127.0.0.1", "8080", &hints, &ai);
-    if (error) {
-        return 1;
-    }
-    return 0;
-}
-#endif
 #ifdef HAVE_FILE_OFFSET_BITS
 #ifdef _FILE_OFFSET_BITS
 #undef _FILE_OFFSET_BITS
@@ -514,3 +494,39 @@ main() {
   return 0;
 }
 #endif
+#ifdef HAVE_ATOMIC
+/* includes start */
+#ifdef HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+#ifdef HAVE_STDATOMIC_H
+#  include <stdatomic.h>
+#endif
+/* includes end */
+
+int
+main() {
+  _Atomic int i = 1;
+  i = 0;  // Force an atomic-write operation.
+  return i;
+}
+#endif
+#ifdef HAVE_WIN32_WINNT
+/* includes start */
+#ifdef WIN32
+#  include "../lib/setup-win32.h"
+#endif
+/* includes end */
+
+#define enquote(x) #x
+#define expand(x) enquote(x)
+#pragma message("_WIN32_WINNT=" expand(_WIN32_WINNT))
+
+int
+main() {
+  return 0;
+}
+#endif
index 9455f4b..88d5e87 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 find_path(BEARSSL_INCLUDE_DIRS bearssl.h)
 
index 0ed0855..833e181 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 include(FindPackageHandleStandardArgs)
 
index 7180682..99cf31d 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 # - Find c-ares
 # Find the c-ares includes and library
index 4e4747d..ec2bd57 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 # - Try to find the GSS Kerberos library
 # Once done this will define
diff --git a/Utilities/cmcurl/CMake/FindLibPSL.cmake b/Utilities/cmcurl/CMake/FindLibPSL.cmake
new file mode 100644 (file)
index 0000000..66abdd7
--- /dev/null
@@ -0,0 +1,45 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  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
+#
+###########################################################################
+# - Try to find the libpsl library
+# Once done this will define
+#
+# LIBPSL_FOUND - system has the libpsl library
+# LIBPSL_INCLUDE_DIR - the libpsl include directory
+# LIBPSL_LIBRARY - the libpsl library name
+
+find_path(LIBPSL_INCLUDE_DIR libpsl.h)
+
+find_library(LIBPSL_LIBRARY NAMES psl libpsl)
+
+if(LIBPSL_INCLUDE_DIR)
+  file(STRINGS "${LIBPSL_INCLUDE_DIR}/libpsl.h" libpsl_version_str REGEX "^#define[\t ]+PSL_VERSION[\t ]+\"(.*)\"")
+  string(REGEX REPLACE "^.*\"([^\"]+)\"" "\\1"  LIBPSL_VERSION "${libpsl_version_str}")
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LibPSL
+    REQUIRED_VARS LIBPSL_LIBRARY LIBPSL_INCLUDE_DIR
+    VERSION_VAR LIBPSL_VERSION)
+
+mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY)
index ce46a40..0ec7f7e 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 # - Try to find the libssh2 library
 # Once done this will define
index 1b8b9d8..96477e2 100644 (file)
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 #[=======================================================================[.rst:
index 7bdb197..fcd6717 100644 (file)
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h)
 
index 8614492..6d70c4a 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 include(FindPackageHandleStandardArgs)
 
index 643b600..8d8ebc1 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 #[=======================================================================[.rst:
index 5757009..61e54c2 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 #[=======================================================================[.rst:
@@ -69,7 +71,7 @@ endif()
 if(NGTCP2_FIND_COMPONENTS)
   set(NGTCP2_CRYPTO_BACKEND "")
   foreach(component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(component MATCHES "^(OpenSSL|GnuTLS)")
+    if(component MATCHES "^(BoringSSL|OpenSSL|GnuTLS)")
       if(NGTCP2_CRYPTO_BACKEND)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()
index 899c6b0..6742dda 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 if(UNIX)
   find_package(PkgConfig QUIET)
index 0247364..fc47027 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 #[=======================================================================[.rst:
index 42256b3..986f01e 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 find_path(WolfSSL_INCLUDE_DIR NAMES wolfssl/ssl.h)
 find_library(WolfSSL_LIBRARY NAMES wolfssl)
index eaba397..2d65404 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 #[=======================================================================[.rst:
index d57dd6a..4d7380e 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 #File defines convenience macros for available feature testing
 
index 0421c1c..b3031f7 100644 (file)
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 include(CheckCSourceCompiles)
 # The begin of the sources (macros and includes)
@@ -46,175 +48,6 @@ endif()
 
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
 
-if(1) # CMake hard-codes these
-  set(RECV_TYPE_ARG1 "curl_socket_t")
-  set(RECV_TYPE_ARG2 "char *")
-  set(RECV_TYPE_ARG3 "size_t")
-  set(RECV_TYPE_ARG4 "int")
-  set(RECV_TYPE_RETV "ssize_t")
-else()
-function(curl_cv_func_recv_run_test recv_retv recv_arg1 recv_arg2 recv_arg3 recv_arg4)
-  unset(curl_cv_func_recv_test CACHE)
-  check_c_source_compiles("
-    ${_source_epilogue}
-    #ifdef WINSOCK_API_LINKAGE
-    WINSOCK_API_LINKAGE
-    #endif
-    extern ${recv_retv} ${signature_call_conv}
-    recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
-    int main(void) {
-      ${recv_arg1} s=0;
-      ${recv_arg2} buf=0;
-      ${recv_arg3} len=0;
-      ${recv_arg4} flags=0;
-      ${recv_retv} res = recv(s, buf, len, flags);
-      (void) res;
-      return 0;
-    }"
-    curl_cv_func_recv_test)
-  message(STATUS
-    "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
-  if(curl_cv_func_recv_test)
-    set(curl_cv_func_recv_args
-      "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}" PARENT_SCOPE)
-    set(RECV_TYPE_ARG1 "${recv_arg1}" PARENT_SCOPE)
-    set(RECV_TYPE_ARG2 "${recv_arg2}" PARENT_SCOPE)
-    set(RECV_TYPE_ARG3 "${recv_arg3}" PARENT_SCOPE)
-    set(RECV_TYPE_ARG4 "${recv_arg4}" PARENT_SCOPE)
-    set(RECV_TYPE_RETV "${recv_retv}" PARENT_SCOPE)
-    set(HAVE_RECV 1 PARENT_SCOPE)
-    set(curl_cv_func_recv_done 1 PARENT_SCOPE)
-  endif()
-endfunction()
-
-check_c_source_compiles("${_source_epilogue}
-int main(void) {
-    recv(0, 0, 0, 0);
-    return 0;
-}" curl_cv_recv)
-if(curl_cv_recv)
-  if(NOT DEFINED curl_cv_func_recv_args OR curl_cv_func_recv_args STREQUAL "unknown")
-    if(APPLE)
-      curl_cv_func_recv_run_test("ssize_t" "int" "void *" "size_t" "int")
-    endif()
-    foreach(recv_retv "int" "ssize_t" )
-      foreach(recv_arg1 "SOCKET" "int" )
-        foreach(recv_arg2 "char *" "void *" )
-          foreach(recv_arg3 "int" "size_t" "socklen_t" "unsigned int")
-            foreach(recv_arg4 "int" "unsigned int")
-              if(NOT curl_cv_func_recv_done)
-                curl_cv_func_recv_run_test(${recv_retv} ${recv_arg1} ${recv_arg2} ${recv_arg3} ${recv_arg4})
-              endif()
-            endforeach()
-          endforeach()
-        endforeach()
-      endforeach()
-    endforeach()
-  else()
-    string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG1 "${curl_cv_func_recv_args}")
-    string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG2 "${curl_cv_func_recv_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" RECV_TYPE_ARG3 "${curl_cv_func_recv_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" RECV_TYPE_ARG4 "${curl_cv_func_recv_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" RECV_TYPE_RETV "${curl_cv_func_recv_args}")
-  endif()
-
-  if(curl_cv_func_recv_args STREQUAL "unknown")
-    message(FATAL_ERROR "Cannot find proper types to use for recv args")
-  endif()
-else()
-  message(FATAL_ERROR "Unable to link function recv")
-endif()
-set(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv")
-endif()
-set(HAVE_RECV 1)
-
-if(1) # CMake hard-codes these
-  set(SEND_QUAL_ARG2 " ")
-  set(SEND_TYPE_ARG1 "curl_socket_t")
-  set(SEND_TYPE_ARG2 "char *")
-  set(SEND_TYPE_ARG3 "size_t")
-  set(SEND_TYPE_ARG4 "int")
-  set(SEND_TYPE_RETV "ssize_t")
-else()
-function(curl_cv_func_send_run_test send_retv send_arg1 send_arg2 send_arg3 send_arg4)
-  unset(curl_cv_func_send_test CACHE)
-  check_c_source_compiles("
-    ${_source_epilogue}
-    #ifdef WINSOCK_API_LINKAGE
-    WINSOCK_API_LINKAGE
-    #endif
-    extern ${send_retv} ${signature_call_conv}
-    send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
-    int main(void) {
-      ${send_arg1} s=0;
-      ${send_arg2} buf=0;
-      ${send_arg3} len=0;
-      ${send_arg4} flags=0;
-      ${send_retv} res = send(s, buf, len, flags);
-      (void) res;
-      return 0;
-    }"
-    curl_cv_func_send_test)
-  message(STATUS
-    "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
-  if(curl_cv_func_send_test)
-    string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
-    string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
-    set(curl_cv_func_send_args
-      "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}" PARENT_SCOPE)
-    set(SEND_TYPE_ARG1 "${send_arg1}" PARENT_SCOPE)
-    set(SEND_TYPE_ARG2 "${send_arg2}" PARENT_SCOPE)
-    set(SEND_TYPE_ARG3 "${send_arg3}" PARENT_SCOPE)
-    set(SEND_TYPE_ARG4 "${send_arg4}" PARENT_SCOPE)
-    set(SEND_TYPE_RETV "${send_retv}" PARENT_SCOPE)
-    set(HAVE_SEND 1 PARENT_SCOPE)
-    set(curl_cv_func_send_done 1 PARENT_SCOPE)
-  endif()
-endfunction()
-
-check_c_source_compiles("${_source_epilogue}
-int main(void) {
-    send(0, 0, 0, 0);
-    return 0;
-}" curl_cv_send)
-if(curl_cv_send)
-  if(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
-    if(APPLE)
-      curl_cv_func_send_run_test("ssize_t" "int" "const void *" "size_t" "int")
-    endif()
-    foreach(send_retv "int" "ssize_t" )
-      foreach(send_arg1 "SOCKET" "int" "ssize_t" )
-        foreach(send_arg2 "const char *" "const void *" "void *" "char *")
-          foreach(send_arg3 "int" "size_t" "socklen_t" "unsigned int")
-            foreach(send_arg4 "int" "unsigned int")
-              if(NOT curl_cv_func_send_done)
-                curl_cv_func_send_run_test("${send_retv}" "${send_arg1}" "${send_arg2}" "${send_arg3}" "${send_arg4}")
-              endif()
-            endforeach()
-          endforeach()
-        endforeach()
-      endforeach()
-    endforeach()
-  else()
-    string(REGEX REPLACE "^([^,]*),[^,]*,[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG1 "${curl_cv_func_send_args}")
-    string(REGEX REPLACE "^[^,]*,([^,]*),[^,]*,[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG2 "${curl_cv_func_send_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,([^,]*),[^,]*,[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG3 "${curl_cv_func_send_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,([^,]*),[^,]*,[^,]*$" "\\1" SEND_TYPE_ARG4 "${curl_cv_func_send_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,([^,]*),[^,]*$" "\\1" SEND_TYPE_RETV "${curl_cv_func_send_args}")
-    string(REGEX REPLACE "^[^,]*,[^,]*,[^,]*,[^,]*,[^,]*,([^,]*)$" "\\1" SEND_QUAL_ARG2 "${curl_cv_func_send_args}")
-  endif()
-
-  if("${curl_cv_func_send_args}" STREQUAL "unknown")
-    message(FATAL_ERROR "Cannot find proper types to use for send args")
-  endif()
-  set(SEND_QUAL_ARG2 "const")
-else()
-  message(FATAL_ERROR "Unable to link function send")
-endif()
-set(curl_cv_func_send_args "${curl_cv_func_send_args}" CACHE INTERNAL "Arguments for send")
-endif()
-set(HAVE_SEND 1)
-
 check_c_source_compiles("${_source_epilogue}
   int main(void) {
     int flag = MSG_NOSIGNAL;
index dd653da..9a513bb 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 if(NOT UNIX)
   if(WIN32)
-    set(HAVE_LIBDL 0)
-    set(HAVE_LIBUCB 0)
     set(HAVE_LIBSOCKET 0)
-    set(NOT_NEED_LIBNSL 0)
-    set(HAVE_LIBNSL 0)
     set(HAVE_GETHOSTNAME 1)
     set(HAVE_LIBZ 0)
 
-    set(HAVE_DLOPEN 0)
-
-    set(HAVE_ALLOCA_H 0)
     set(HAVE_ARPA_INET_H 0)
-    set(HAVE_DLFCN_H 0)
     set(HAVE_FCNTL_H 1)
     set(HAVE_INTTYPES_H 0)
     set(HAVE_IO_H 1)
-    set(HAVE_MALLOC_H 1)
-    set(HAVE_MEMORY_H 1)
     set(HAVE_NETDB_H 0)
-    set(HAVE_NETINET_IF_ETHER_H 0)
     set(HAVE_NETINET_IN_H 0)
     set(HAVE_NET_IF_H 0)
     set(HAVE_PROCESS_H 1)
     set(HAVE_PWD_H 0)
     set(HAVE_SETJMP_H 1)
     set(HAVE_SIGNAL_H 1)
-    set(HAVE_SOCKIO_H 0)
     set(HAVE_STDINT_H 0)
     set(HAVE_STDLIB_H 1)
     set(HAVE_STRINGS_H 0)
@@ -64,40 +54,26 @@ if(NOT UNIX)
     set(HAVE_TERMIOS_H 0)
     set(HAVE_TERMIO_H 0)
     set(HAVE_TIME_H 1)
-    set(HAVE_UNISTD_H 0)
     set(HAVE_UTIME_H 0)
-    set(HAVE_X509_H 0)
-    set(HAVE_ZLIB_H 0)
 
     set(HAVE_SOCKET 1)
-    set(HAVE_POLL 0)
     set(HAVE_SELECT 1)
     set(HAVE_STRDUP 1)
-    set(HAVE_STRSTR 1)
-    set(HAVE_STRTOK_R 0)
-    set(HAVE_STRFTIME 1)
-    set(HAVE_UNAME 0)
-    set(HAVE_STRCASECMP 0)
     set(HAVE_STRICMP 1)
     set(HAVE_STRCMPI 1)
     set(HAVE_GETTIMEOFDAY 0)
-    set(HAVE_INET_ADDR 1)
     set(HAVE_CLOSESOCKET 1)
-    set(HAVE_SETVBUF 0)
     set(HAVE_SIGSETJMP 0)
+    set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
     set(HAVE_GETPASS_R 0)
-    set(HAVE_STRLCAT 0)
     set(HAVE_GETPWUID 0)
     set(HAVE_GETEUID 0)
     set(HAVE_UTIME 1)
     set(HAVE_RAND_EGD 0)
-    set(HAVE_RAND_SCREEN 0)
-    set(HAVE_RAND_STATUS 0)
     set(HAVE_GMTIME_R 0)
-    set(HAVE_LOCALTIME_R 0)
+    set(HAVE_GETADDRINFO_THREADSAFE 1)
     set(HAVE_GETHOSTBYNAME_R 0)
-    set(HAVE_SIGNAL_FUNC 1)
-    set(HAVE_SIGNAL_MACRO 0)
+    set(HAVE_SIGNAL 1)
 
     set(HAVE_GETHOSTBYNAME_R_3 0)
     set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
@@ -109,11 +85,6 @@ if(NOT UNIX)
     set(TIME_WITH_SYS_TIME 0)
     set(HAVE_O_NONBLOCK 0)
     set(HAVE_IN_ADDR_T 0)
-    if(ENABLE_IPV6)
-      set(HAVE_GETADDRINFO 1)
-    else()
-      set(HAVE_GETADDRINFO 0)
-    endif()
     set(STDC_HEADERS 1)
 
     set(HAVE_SIGACTION 0)
index 8f9b861..78bfd6f 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 # File containing various utilities
 
index d9d9c4e..2549d41 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
   message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
index 957148e..496a92d 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 @PACKAGE_INIT@
 
index f842270..de5df3e 100644 (file)
@@ -5,6 +5,7 @@ set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs")
 set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build shared libraries")
 set(CURL_USE_BEARSSL OFF)
 set(CURL_USE_GSSAPI OFF)
+set(CURL_USE_LIBPSL OFF)
 set(CURL_USE_LIBSSH2 OFF)
 set(CURL_USE_LIBSSH OFF)
 set(CURL_USE_MBEDTLS OFF)
@@ -65,6 +66,15 @@ set(ENABLE_MANUAL OFF CACHE INTERNAL "No curl built-in manual")
 set(ENABLE_THREADED_RESOLVER OFF CACHE INTERNAL "No curl POSIX threaded DNS lookup")
 set(ENABLE_UNICODE OFF)
 set(ENABLE_UNIX_SOCKETS OFF CACHE INTERNAL "No curl Unix domain sockets support")
+set(ENABLE_WEBSOCKETS OFF)
+set(HAVE_ATOMIC 0)
+set(HAVE_BORINGSSL 0) # we do not need this info
+set(HAVE_MINGW_ORIGINAL 0) # we do not build on original MinGW anyway
+set(HAVE_RECV 1)
+set(HAVE_SEND 1)
+set(HAVE_STDATOMIC_H 0)
+set(HAVE_STRCASECMP 0) # we do not vendor the code that uses this
+set(HAVE_WIN32_WINNT 0) # we do not need this info
 set(HTTP_ONLY OFF CACHE INTERNAL "Curl is not http-only")
 set(PICKY_COMPILER OFF CACHE INTERNAL "Enable picky compiler options")
 set(USE_LIBIDN2 ON)
@@ -153,6 +163,8 @@ endif()
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 # curl/libcurl CMake script
 # by Tetetest and Sukender (Benoit Neil)
@@ -172,6 +184,27 @@ endif()
 # To check:
 # (From Daniel Stenberg) The cmake build selected to run gcc with -fPIC on my box while the plain configure script did not.
 # (From Daniel Stenberg) The gcc command line use neither -g nor any -O options. As a developer, I also treasure our configure scripts's --enable-debug option that sets a long range of "picky" compiler options.
+
+# Note: By default this CMake build script detects the version of some
+# dependencies using `check_symbol_exists`.  Those checks do not work
+# in the case that both CURL and its dependency are included as
+# sub-projects in a larger build using `FetchContent`.  To support
+# that case, additional variables may be defined by the parent
+# project, ideally in the "extra" find package redirect file:
+# https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package
+#
+# The following variables are available:
+#   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_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
+#   HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd
+#
+# For each of the above variables, if the variable is DEFINED (either
+# to ON or OFF), the symbol detection will be skipped.  If the
+# variable is NOT DEFINED, the symbol detection will be performed.
+
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
 include(Utilities)
 include(Macros)
@@ -200,7 +233,11 @@ endif()
 # SET(PACKAGE_STRING "curl-")
 # SET(PACKAGE_BUGREPORT "a suitable curl mailing list => https://curl.se/mail/")
 set(OPERATING_SYSTEM "${CMAKE_SYSTEM_NAME}")
-set(OS "\"${CMAKE_SYSTEM_NAME}\"")
+if(CMAKE_C_COMPILER_TARGET)
+  set(OS "\"${CMAKE_C_COMPILER_TARGET}\"")
+else()
+  set(OS "\"${CMAKE_SYSTEM_NAME}\"")
+endif()
 
 include_directories(${CURL_SOURCE_DIR}/include)
 
@@ -211,21 +248,13 @@ option(BUILD_SHARED_LIBS "Build shared libraries" ON)
 option(ENABLE_ARES "Set to ON to enable c-ares support" OFF)
 if(WIN32)
   option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF)
-  option(ENABLE_INET_PTON "Set to OFF to prevent usage of inet_pton when building against modern SDKs while still requiring compatibility with older Windows versions, such as Windows XP, Windows Server 2003 etc." ON)
   option(ENABLE_UNICODE "Set to ON to use the Unicode version of the Windows API functions" OFF)
   if(0) # This code not needed for building within CMake.
   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}")
-  elseif(ENABLE_INET_PTON)
-    # _WIN32_WINNT_VISTA (0x0600)
-    add_definitions(-D_WIN32_WINNT=0x0600)
-    set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=0x0600")
-  else()
-    # _WIN32_WINNT_WINXP (0x0501)
-    add_definitions(-D_WIN32_WINNT=0x0501)
-    set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -D_WIN32_WINNT=0x0501")
+    set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
   endif()
   endif()
   if(ENABLE_UNICODE)
@@ -475,19 +504,7 @@ if(ENABLE_THREADED_RESOLVER)
 endif()
 
 # Check for all needed libraries
-if(0) # This code not needed for building within CMake.
-check_library_exists_concat("${CMAKE_DL_LIBS}" dlopen HAVE_LIBDL)
-else()
-  # Use the cmake-defined dl libs as dl is should not be used
-  # on HPUX, but rather dld this avoids a warning
-  list(APPEND CURL_LIBS ${CMAKE_DL_LIBS})
-endif()
 check_library_exists_concat("socket" connect      HAVE_LIBSOCKET)
-check_library_exists("c" gethostbyname "" NOT_NEED_LIBNSL)
-
-if(NOT NOT_NEED_LIBNSL)
-  check_library_exists_concat("nsl"    gethostbyname  HAVE_LIBNSL)
-endif()
 
 check_function_exists(gethostname HAVE_GETHOSTNAME)
 
@@ -598,16 +615,12 @@ if(CURL_USE_OPENSSL)
   include_directories(${OPENSSL_INCLUDE_DIR})
 
   set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
-  check_include_file("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H)
-  check_include_file("openssl/err.h"    HAVE_OPENSSL_ERR_H)
-  check_include_file("openssl/pem.h"    HAVE_OPENSSL_PEM_H)
-  check_include_file("openssl/rsa.h"    HAVE_OPENSSL_RSA_H)
-  check_include_file("openssl/ssl.h"    HAVE_OPENSSL_SSL_H)
-  check_include_file("openssl/x509.h"   HAVE_OPENSSL_X509_H)
-  check_include_file("openssl/rand.h"   HAVE_OPENSSL_RAND_H)
-  check_symbol_exists(RAND_status "${CURL_INCLUDES}" HAVE_RAND_STATUS)
-  check_symbol_exists(RAND_screen "${CURL_INCLUDES}" HAVE_RAND_SCREEN)
-  check_symbol_exists(RAND_egd    "${CURL_INCLUDES}" HAVE_RAND_EGD)
+  if(NOT DEFINED HAVE_RAND_EGD)
+    check_symbol_exists(RAND_egd "${CURL_INCLUDES}" HAVE_RAND_EGD)
+  endif()
+  if(NOT DEFINED HAVE_BORINGSSL)
+    check_symbol_exists(OPENSSL_IS_BORINGSSL "openssl/base.h" HAVE_BORINGSSL)
+  endif()
 
   # Optionally build with a specific CA cert bundle.
   if(CURL_CA_BUNDLE)
@@ -651,11 +664,13 @@ if(CURL_USE_NSS)
   list(APPEND CURL_LIBS ${NSS_LIBRARIES})
   set(SSL_ENABLED ON)
   set(USE_NSS ON)
-  cmake_push_check_state()
-  set(CMAKE_REQUIRED_INCLUDES ${NSS_INCLUDE_DIRS})
-  set(CMAKE_REQUIRED_LIBRARIES ${NSS_LIBRARIES})
-  check_symbol_exists(PK11_CreateManagedGenericObject "pk11pub.h" HAVE_PK11_CREATEMANAGEDGENERICOBJECT)
-  cmake_pop_check_state()
+  if(NOT DEFINED HAVE_PK11_CREATEMANAGEDGENERICOBJECT)
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_INCLUDES ${NSS_INCLUDE_DIRS})
+    set(CMAKE_REQUIRED_LIBRARIES ${NSS_LIBRARIES})
+    check_symbol_exists(PK11_CreateManagedGenericObject "pk11pub.h" HAVE_PK11_CREATEMANAGEDGENERICOBJECT)
+    cmake_pop_check_state()
+  endif()
 endif()
 
 option(USE_NGHTTP2 "Use Nghttp2 library" OFF)
@@ -667,20 +682,26 @@ endif()
 
 function(CheckQuicSupportInOpenSSL)
   # Be sure that the OpenSSL library actually supports QUIC.
-  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(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)
+    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. Try setting -DOPENSSL_ROOT_DIR")
   endif()
-  cmake_pop_check_state()
 endfunction()
 
 option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF)
 if(USE_NGTCP2)
   if(USE_OPENSSL)
-    find_package(NGTCP2 REQUIRED OpenSSL)
+    if(HAVE_BORINGSSL)
+      find_package(NGTCP2 REQUIRED BoringSSL)
+    else()
+      find_package(NGTCP2 REQUIRED OpenSSL)
+    endif()
     CheckQuicSupportInOpenSSL()
   elseif(USE_GNUTLS)
     # TODO add GnuTLS support as vtls library.
@@ -708,11 +729,13 @@ if(USE_QUICHE)
   set(USE_QUICHE ON)
   include_directories(${QUICHE_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${QUICHE_LIBRARIES})
-  cmake_push_check_state()
-  set(CMAKE_REQUIRED_INCLUDES   "${QUICHE_INCLUDE_DIRS}")
-  set(CMAKE_REQUIRED_LIBRARIES  "${QUICHE_LIBRARIES}")
-  check_symbol_exists(quiche_conn_set_qlog_fd "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD)
-  cmake_pop_check_state()
+  if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD)
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_INCLUDES   "${QUICHE_INCLUDE_DIRS}")
+    set(CMAKE_REQUIRED_LIBRARIES  "${QUICHE_LIBRARIES}")
+    check_symbol_exists(quiche_conn_set_qlog_fd "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD)
+    cmake_pop_check_state()
+  endif()
 endif()
 
 option(USE_MSH3 "Use msquic library for HTTP/3 support" OFF)
@@ -748,7 +771,6 @@ if(NOT CURL_DISABLE_LDAP)
   # Now that we know, we're not using windows LDAP...
   if(USE_WIN32_LDAP)
     check_include_file_concat("winldap.h" HAVE_WINLDAP_H)
-    check_include_file_concat("winber.h"  HAVE_WINBER_H)
   else()
     # Check for LDAP
     set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
@@ -831,34 +853,29 @@ endif()
 
 if(NOT CURL_DISABLE_LDAPS)
   check_include_file_concat("ldap_ssl.h" HAVE_LDAP_SSL_H)
-  check_include_file_concat("ldapssl.h"  HAVE_LDAPSSL_H)
 endif()
 
-# Check for idn
+# Check for idn2
 option(USE_LIBIDN2 "Use libidn2 for IDN support" ON)
-set(HAVE_LIBIDN2 OFF)
 if(USE_LIBIDN2)
   check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2)
+else()
+  set(HAVE_LIBIDN2 OFF)
 endif()
 
 if(WIN32)
   option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF)
   if(USE_WIN32_IDN)
-    list(APPEND CURL_LIBS "Normaliz")
+    list(APPEND CURL_LIBS "normaliz")
     set(WANT_IDN_PROTOTYPES ON)
   endif()
 endif()
 
-# Check for symbol dlopen (same as HAVE_LIBDL)
-check_library_exists("${CURL_LIBS}" dlopen "" HAVE_DLOPEN)
-
-if(0) # This code not needed for building within CMake.
 set(HAVE_LIBZ OFF)
-set(HAVE_ZLIB_H OFF)
 set(USE_ZLIB OFF)
-optional_dependency(ZLIB)
+#optional_dependency(ZLIB)
+find_package(ZLIB)
 if(ZLIB_FOUND)
-  set(HAVE_ZLIB_H ON)
   set(HAVE_LIBZ ON)
   set(USE_ZLIB ON)
 
@@ -870,19 +887,8 @@ if(ZLIB_FOUND)
   else()
     list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
     include_directories(${ZLIB_INCLUDE_DIRS})
+    list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
   endif()
-  list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
-endif()
-endif()
-
-#-----------------------------------------------------------------------------
-# CMake-specific curl code.
-
-if(CURL_SPECIAL_LIBZ)
-  set(CURL_LIBS ${CURL_LIBS} "${CURL_SPECIAL_LIBZ}")
-  include_directories(${CURL_SPECIAL_LIBZ_INCLUDES})
-  set(HAVE_LIBZ 0)
-  set(HAVE_ZLIB_H 0)
 endif()
 
 option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF)
@@ -901,11 +907,13 @@ 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)
-  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()
+  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})
@@ -913,42 +921,46 @@ if(CURL_ZSTD)
   endif()
 endif()
 
+#libpsl
+option(CURL_USE_LIBPSL "Use libPSL" ON)
+mark_as_advanced(CURL_USE_LIBPSL)
+set(USE_LIBPSL OFF)
+
+if(CURL_USE_LIBPSL)
+  find_package(LibPSL)
+  if(LIBPSL_FOUND)
+    list(APPEND CURL_LIBS ${LIBPSL_LIBRARY})
+    list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBPSL_INCLUDE_DIR}")
+    include_directories("${LIBPSL_INCLUDE_DIR}")
+    set(USE_LIBPSL ON)
+  endif()
+endif()
+
 #libSSH2
 option(CURL_USE_LIBSSH2 "Use libSSH2" ON)
 mark_as_advanced(CURL_USE_LIBSSH2)
 set(USE_LIBSSH2 OFF)
-set(HAVE_LIBSSH2 OFF)
-set(HAVE_LIBSSH2_H OFF)
 
 if(CURL_USE_LIBSSH2)
   find_package(LibSSH2)
   if(LIBSSH2_FOUND)
     list(APPEND CURL_LIBS ${LIBSSH2_LIBRARY})
-    set(CMAKE_REQUIRED_LIBRARIES ${LIBSSH2_LIBRARY})
     list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIR}")
     include_directories("${LIBSSH2_INCLUDE_DIR}")
-    set(HAVE_LIBSSH2 ON)
     set(USE_LIBSSH2 ON)
-
-    # find_package has already found the headers
-    set(HAVE_LIBSSH2_H ON)
-    set(CURL_INCLUDES ${CURL_INCLUDES} "${LIBSSH2_INCLUDE_DIR}/libssh2.h")
-    set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DHAVE_LIBSSH2_H")
-    unset(CMAKE_REQUIRED_LIBRARIES)
   endif()
 endif()
 
 # libssh
 option(CURL_USE_LIBSSH "Use libSSH" OFF)
 mark_as_advanced(CURL_USE_LIBSSH)
-if(NOT HAVE_LIBSSH2 AND CURL_USE_LIBSSH)
+if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH)
   find_package(libssh CONFIG)
   if(libssh_FOUND)
     message(STATUS "Found libssh ${libssh_VERSION}")
     # Use imported target for include and library paths.
     list(APPEND CURL_LIBS ssh)
     set(USE_LIBSSH ON)
-    set(HAVE_LIBSSH_LIBSSH_H 1)
   endif()
 endif()
 
@@ -990,14 +1002,15 @@ if(CURL_USE_GSSAPI)
         set(_LINKER_FLAGS_STR "${_LINKER_FLAGS_STR} -L\"${_dir}\"")
       endforeach()
 
-      set(CMAKE_REQUIRED_FLAGS "${_COMPILER_FLAGS_STR} ${_LINKER_FLAGS_STR}")
-      set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES})
-      check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_INCLUDE_LIST} HAVE_GSS_C_NT_HOSTBASED_SERVICE)
+      if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE)
+        set(CMAKE_REQUIRED_FLAGS "${_COMPILER_FLAGS_STR} ${_LINKER_FLAGS_STR}")
+        set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES})
+        check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_INCLUDE_LIST} HAVE_GSS_C_NT_HOSTBASED_SERVICE)
+        unset(CMAKE_REQUIRED_LIBRARIES)
+      endif()
       if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE)
         set(HAVE_OLD_GSSMIT ON)
       endif()
-      unset(CMAKE_REQUIRED_LIBRARIES)
-
     endif()
 
     include_directories(${GSS_INCLUDE_DIR})
@@ -1116,7 +1129,6 @@ else()
   set(HAVE_WINSOCK2_H 0)
 endif()
 
-check_include_file_concat("stdio.h"          HAVE_STDIO_H)
 check_include_file_concat("inttypes.h"       HAVE_INTTYPES_H)
 check_include_file_concat("sys/filio.h"      HAVE_SYS_FILIO_H)
 check_include_file_concat("sys/ioctl.h"      HAVE_SYS_IOCTL_H)
@@ -1129,11 +1141,9 @@ check_include_file_concat("sys/sockio.h"     HAVE_SYS_SOCKIO_H)
 check_include_file_concat("sys/stat.h"       HAVE_SYS_STAT_H)
 check_include_file_concat("sys/time.h"       HAVE_SYS_TIME_H)
 check_include_file_concat("sys/types.h"      HAVE_SYS_TYPES_H)
-check_include_file_concat("sys/uio.h"        HAVE_SYS_UIO_H)
 check_include_file_concat("sys/un.h"         HAVE_SYS_UN_H)
 check_include_file_concat("sys/utime.h"      HAVE_SYS_UTIME_H)
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
-check_include_file_concat("alloca.h"         HAVE_ALLOCA_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("arpa/tftp.h"      HAVE_ARPA_TFTP_H)
 check_include_file_concat("assert.h"         HAVE_ASSERT_H)
@@ -1142,7 +1152,6 @@ check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("idn2.h"           HAVE_IDN2_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
-check_include_file_concat("krb.h"            HAVE_KRB_H)
 check_include_file_concat("libgen.h"         HAVE_LIBGEN_H)
 check_include_file_concat("locale.h"         HAVE_LOCALE_H)
 check_include_file_concat("net/if.h"         HAVE_NET_IF_H)
@@ -1151,15 +1160,14 @@ check_include_file_concat("netinet/in.h"     HAVE_NETINET_IN_H)
 check_include_file_concat("netinet/tcp.h"    HAVE_NETINET_TCP_H)
 check_include_file("linux/tcp.h"      HAVE_LINUX_TCP_H)
 
-check_include_file_concat("pem.h"            HAVE_PEM_H)
 check_include_file_concat("poll.h"           HAVE_POLL_H)
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
 check_include_file_concat("setjmp.h"         HAVE_SETJMP_H)
 check_include_file_concat("signal.h"         HAVE_SIGNAL_H)
 check_include_file_concat("ssl.h"            HAVE_SSL_H)
+check_include_file_concat("stdatomic.h"      HAVE_STDATOMIC_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
 check_include_file_concat("stdint.h"         HAVE_STDINT_H)
-check_include_file_concat("stdio.h"          HAVE_STDIO_H)
 check_include_file_concat("stdlib.h"         HAVE_STDLIB_H)
 check_include_file_concat("string.h"         HAVE_STRING_H)
 check_include_file_concat("strings.h"        HAVE_STRINGS_H)
@@ -1169,24 +1177,20 @@ check_include_file_concat("termios.h"        HAVE_TERMIOS_H)
 check_include_file_concat("time.h"           HAVE_TIME_H)
 check_include_file_concat("unistd.h"         HAVE_UNISTD_H)
 check_include_file_concat("utime.h"          HAVE_UTIME_H)
-check_include_file_concat("x509.h"           HAVE_X509_H)
 
 check_include_file_concat("process.h"        HAVE_PROCESS_H)
 check_include_file_concat("stddef.h"         HAVE_STDDEF_H)
-check_include_file_concat("dlfcn.h"          HAVE_DLFCN_H)
-check_include_file_concat("malloc.h"         HAVE_MALLOC_H)
-check_include_file_concat("memory.h"         HAVE_MEMORY_H)
-check_include_file_concat("netinet/if_ether.h" HAVE_NETINET_IF_ETHER_H)
 check_include_file_concat("stdint.h"        HAVE_STDINT_H)
-check_include_file_concat("sockio.h"        HAVE_SOCKIO_H)
 check_include_file_concat("sys/utsname.h"   HAVE_SYS_UTSNAME_H)
 
 check_type_size(size_t  SIZEOF_SIZE_T)
 check_type_size(ssize_t  SIZEOF_SSIZE_T)
 check_type_size("time_t"  SIZEOF_TIME_T)
 
-find_file(RANDOM_FILE urandom /dev)
-mark_as_advanced(RANDOM_FILE)
+if(NOT CMAKE_CROSSCOMPILING)
+  find_file(RANDOM_FILE urandom /dev)
+  mark_as_advanced(RANDOM_FILE)
+endif()
 
 # Check for some functions that are used
 if(HAVE_LIBWS2_32)
@@ -1197,57 +1201,46 @@ elseif(HAVE_LIBNETWORK)
   set(CMAKE_REQUIRED_LIBRARIES network)
 endif()
 
+check_symbol_exists(fchmod        "${CURL_INCLUDES}" HAVE_FCHMOD)
 check_symbol_exists(basename      "${CURL_INCLUDES}" HAVE_BASENAME)
 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(select        "${CURL_INCLUDES}" HAVE_SELECT)
-check_symbol_exists(poll          "${CURL_INCLUDES}" HAVE_POLL)
 check_symbol_exists(strdup        "${CURL_INCLUDES}" HAVE_STRDUP)
-check_symbol_exists(strstr        "${CURL_INCLUDES}" HAVE_STRSTR)
 check_symbol_exists(strtok_r      "${CURL_INCLUDES}" HAVE_STRTOK_R)
-check_symbol_exists(strftime      "${CURL_INCLUDES}" HAVE_STRFTIME)
-check_symbol_exists(uname         "${CURL_INCLUDES}" HAVE_UNAME)
 check_symbol_exists(strcasecmp    "${CURL_INCLUDES}" HAVE_STRCASECMP)
 check_symbol_exists(stricmp       "${CURL_INCLUDES}" HAVE_STRICMP)
 check_symbol_exists(strcmpi       "${CURL_INCLUDES}" HAVE_STRCMPI)
-check_symbol_exists(strncmpi      "${CURL_INCLUDES}" HAVE_STRNCMPI)
 check_symbol_exists(alarm         "${CURL_INCLUDES}" HAVE_ALARM)
-if(NOT HAVE_STRNCMPI)
-  set(HAVE_STRCMPI)
-endif()
 check_symbol_exists(getppid       "${CURL_INCLUDES}" HAVE_GETPPID)
 check_symbol_exists(utimes        "${CURL_INCLUDES}" HAVE_UTIMES)
 
 check_symbol_exists(gettimeofday  "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
-check_symbol_exists(inet_addr     "${CURL_INCLUDES}" HAVE_INET_ADDR)
 check_symbol_exists(closesocket   "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
 check_symbol_exists(sigsetjmp     "${CURL_INCLUDES}" HAVE_SIGSETJMP)
 check_symbol_exists(getpass_r     "${CURL_INCLUDES}" HAVE_GETPASS_R)
 check_symbol_exists(getpwuid      "${CURL_INCLUDES}" HAVE_GETPWUID)
 check_symbol_exists(getpwuid_r    "${CURL_INCLUDES}" HAVE_GETPWUID_R)
 check_symbol_exists(geteuid       "${CURL_INCLUDES}" HAVE_GETEUID)
-check_symbol_exists(usleep        "${CURL_INCLUDES}" HAVE_USLEEP)
 check_symbol_exists(utime         "${CURL_INCLUDES}" HAVE_UTIME)
 check_symbol_exists(gmtime_r      "${CURL_INCLUDES}" HAVE_GMTIME_R)
-check_symbol_exists(localtime_r   "${CURL_INCLUDES}" HAVE_LOCALTIME_R)
 
-check_symbol_exists(gethostbyname   "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME)
 check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
 
-check_symbol_exists(signal        "${CURL_INCLUDES}" HAVE_SIGNAL_FUNC)
-check_symbol_exists(SIGALRM       "${CURL_INCLUDES}" HAVE_SIGNAL_MACRO)
-if(HAVE_SIGNAL_FUNC AND HAVE_SIGNAL_MACRO)
-  set(HAVE_SIGNAL 1)
-endif()
-check_symbol_exists(uname          "${CURL_INCLUDES}" HAVE_UNAME)
+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)
+if(NOT HAVE_GETADDRINFO)
+  set(HAVE_GETADDRINFO_THREADSAFE OFF)
+endif()
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
-check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
 check_symbol_exists(getpeername    "${CURL_INCLUDES}" HAVE_GETPEERNAME)
 check_symbol_exists(getsockname    "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
 check_symbol_exists(if_nametoindex "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX)
@@ -1255,10 +1248,16 @@ check_symbol_exists(getrlimit      "${CURL_INCLUDES}" HAVE_GETRLIMIT)
 check_symbol_exists(setlocale      "${CURL_INCLUDES}" HAVE_SETLOCALE)
 check_symbol_exists(setmode        "${CURL_INCLUDES}" HAVE_SETMODE)
 check_symbol_exists(setrlimit      "${CURL_INCLUDES}" HAVE_SETRLIMIT)
-check_symbol_exists(fcntl          "${CURL_INCLUDES}" HAVE_FCNTL)
-check_symbol_exists(ioctl          "${CURL_INCLUDES}" HAVE_IOCTL)
-check_symbol_exists(setsockopt     "${CURL_INCLUDES}" HAVE_SETSOCKOPT)
+
+if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
+  # earlier MSVC compilers had faulty snprintf implementations
+  check_symbol_exists(snprintf       "${CURL_INCLUDES}" HAVE_SNPRINTF)
+endif()
 check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME)
+check_symbol_exists(inet_ntop      "${CURL_INCLUDES}" HAVE_INET_NTOP)
+if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600))
+  set(HAVE_INET_NTOP OFF)
+endif()
 check_symbol_exists(inet_pton      "${CURL_INCLUDES}" HAVE_INET_PTON)
 
 check_symbol_exists(fsetxattr "${CURL_INCLUDES}" HAVE_FSETXATTR)
@@ -1308,7 +1307,6 @@ foreach(CURL_TEST
     HAVE_IOCTL_FIONBIO
     HAVE_IOCTL_SIOCGIFADDR
     HAVE_SETSOCKOPT_SO_NONBLOCK
-    HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
     TIME_WITH_SYS_TIME
     HAVE_O_NONBLOCK
     HAVE_GETHOSTBYNAME_R_3
@@ -1320,10 +1318,10 @@ foreach(CURL_TEST
     HAVE_IN_ADDR_T
     HAVE_BOOL_T
     STDC_HEADERS
-    HAVE_GETADDRINFO
     HAVE_FILE_OFFSET_BITS
     HAVE_VARIADIC_MACROS_C99
     HAVE_VARIADIC_MACROS_GCC
+    HAVE_ATOMIC
     )
   curl_internal_test(${CURL_TEST})
 endforeach()
@@ -1340,8 +1338,30 @@ set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h")
 check_type_size("curl_off_t"  SIZEOF_CURL_OFF_T)
 set(CMAKE_EXTRA_INCLUDE_FILES "")
 
+if(WIN32)
+  # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT
+  curl_internal_test(HAVE_WIN32_WINNT)
+  if(HAVE_WIN32_WINNT)
+    string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}")
+    string(REGEX REPLACE ".*_WIN32_WINNT=" "" HAVE_WIN32_WINNT "${OUTPUT}")
+    message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}")
+  endif()
+  # avoid storing HAVE_WIN32_WINNT in CMake cache
+  unset(HAVE_WIN32_WINNT CACHE)
+endif()
+
 set(CMAKE_REQUIRED_FLAGS)
 
+option(ENABLE_WEBSOCKETS "Set to ON to enable EXPERIMENTAL websockets" OFF)
+
+if(ENABLE_WEBSOCKETS)
+  if(${SIZEOF_CURL_OFF_T} GREATER "4")
+    set(USE_WEBSOCKETS ON)
+  else()
+    message(WARNING "curl_off_t is too small to enable WebSockets")
+  endif()
+endif()
+
 foreach(CURL_TEST
     HAVE_GLIBC_STRERROR_R
     HAVE_POSIX_STRERROR_R
@@ -1385,18 +1405,6 @@ if(NOT HAVE_IN_ADDR_T)
   set(in_addr_t "unsigned long")
 endif()
 
-# Fix libz / zlib.h
-
-if(NOT CURL_SPECIAL_LIBZ)
-  if(NOT HAVE_LIBZ)
-    set(HAVE_ZLIB_H 0)
-  endif()
-
-  if(NOT HAVE_ZLIB_H)
-    set(HAVE_LIBZ 0)
-  endif()
-endif()
-
 # Check for nonblocking
 set(HAVE_DISABLED_NONBLOCKING 1)
 if(HAVE_FIONBIO OR
@@ -1456,6 +1464,25 @@ if(WIN32)
   if(USE_WIN32_CRYPTO OR USE_SCHANNEL)
     list(APPEND CURL_LIBS "advapi32" "crypt32")
   endif()
+
+  # Matching logic used for Curl_win32_random()
+  if(MINGW)
+    check_c_source_compiles("
+      #include <_mingw.h>
+      #if defined(__MINGW64_VERSION_MAJOR)
+      #error
+      #endif
+      int main(void) {
+        return 0;
+      }"
+      HAVE_MINGW_ORIGINAL)
+  endif()
+
+  if(NOT HAVE_MINGW_ORIGINAL)
+    list(APPEND CURL_LIBS "bcrypt")
+  else()
+    set(HAVE_FTRUNCATE OFF)
+  endif()
 endif()
 
 if(MSVC)
@@ -1465,6 +1492,8 @@ if(MSVC)
   add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
   if(CMAKE_C_FLAGS MATCHES "/W[0-4]")
     string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+  elseif(CMAKE_C_FLAGS MATCHES "-W[0-4]")
+    string(REGEX REPLACE "-W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
   else()
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
   endif()
@@ -1611,6 +1640,9 @@ _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
 _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 _add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS))
 _add_if("unicode"       ENABLE_UNICODE)
+_add_if("threadsafe"    HAVE_ATOMIC OR (WIN32 AND
+                        HAVE_WIN32_WINNT GREATER_EQUAL 0x600))
+_add_if("PSL"           USE_LIBPSL)
 string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
 message(STATUS "Enabled features: ${SUPPORT_FEATURES}")
 
@@ -1647,6 +1679,8 @@ _add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
 _add_if("RTSP"          NOT CURL_DISABLE_RTSP)
 _add_if("RTMP"          USE_LIBRTMP)
 _add_if("MQTT"          NOT CURL_DISABLE_MQTT)
+_add_if("WS"            USE_WEBSOCKETS)
+_add_if("WSS"           USE_WEBSOCKETS)
 if(_items)
   list(SORT _items)
 endif()
@@ -1780,13 +1814,13 @@ if(MSVC_VERSION EQUAL 1600)
   endif()
 endif()
 
-if(NOT TARGET uninstall)
+if(NOT TARGET curl_uninstall)
   configure_file(
       ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in
       ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake
       IMMEDIATE @ONLY)
 
-  add_custom_target(uninstall
+  add_custom_target(curl_uninstall
       COMMAND ${CMAKE_COMMAND} -P
       ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
 endif()
index 8cdfdb6..7a12581 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -73,7 +75,8 @@
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
-   (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000))
+   (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \
+    defined(__sun__)
 #include <sys/select.h>
 #endif
 
@@ -575,7 +578,7 @@ typedef enum {
   CURLE_TFTP_UNKNOWNID,          /* 72 - Unknown transfer ID */
   CURLE_REMOTE_FILE_EXISTS,      /* 73 - File already exists */
   CURLE_TFTP_NOSUCHUSER,         /* 74 - No such user */
-  CURLE_CONV_FAILED,             /* 75 - conversion failed */
+  CURLE_OBSOLETE75,              /* 75 - NOT IN USE since 7.82.0 */
   CURLE_OBSOLETE76,              /* 76 - NOT IN USE since 7.82.0 */
   CURLE_SSL_CACERT_BADFILE,      /* 77 - could not load CACERT file, missing
                                     or wrong format */
@@ -613,6 +616,7 @@ typedef enum {
   CURLE_QUIC_CONNECT_ERROR,      /* 96 - QUIC connection error */
   CURLE_PROXY,                   /* 97 - proxy handshake error */
   CURLE_SSL_CLIENTCERT,          /* 98 - client-side certificate required */
+  CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
   CURL_LAST /* never use! */
 } CURLcode;
 
@@ -677,6 +681,7 @@ typedef enum {
 #define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME
 #define CURLE_LDAP_INVALID_URL CURLE_OBSOLETE62
 #define CURLE_CONV_REQD CURLE_OBSOLETE76
+#define CURLE_CONV_FAILED CURLE_OBSOLETE75
 
 /* This was the error code 50 in 7.7.3 and a few earlier versions, this
    is no longer used by libcurl but is instead #defined here only to not
@@ -835,8 +840,8 @@ enum curl_khstat {
   CURLKHSTAT_FINE_ADD_TO_FILE,
   CURLKHSTAT_FINE,
   CURLKHSTAT_REJECT, /* reject the connection, return an error */
-  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right now so
-                        this causes a CURLE_DEFER error but otherwise the
+  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right now.
+                        Causes a CURLE_PEER_FAILED_VERIFICATION error but the
                         connection will be left intact etc */
   CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key*/
   CURLKHSTAT_LAST    /* not for use, only a marker for last-in-list */
@@ -855,7 +860,18 @@ typedef int
                           const struct curl_khkey *knownkey, /* known */
                           const struct curl_khkey *foundkey, /* found */
                           enum curl_khmatch, /* libcurl's view on the keys */
-                          void *clientp); /* custom pointer passed from app */
+                          void *clientp); /* custom pointer passed with */
+                                          /* CURLOPT_SSH_KEYDATA */
+
+typedef int
+  (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed*/
+                                            /* with CURLOPT_SSH_HOSTKEYDATA */
+                          int keytype, /* CURLKHTYPE */
+                          const char *key, /*hostkey to check*/
+                          size_t keylen); /*length of the key*/
+                          /*return CURLE_OK to accept*/
+                          /*or something else to refuse*/
+
 
 /* parameter for the CURLOPT_USE_SSL option */
 typedef enum {
@@ -995,7 +1011,8 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy,
 #define CURLHSTS_ENABLE       (long)(1<<0)
 #define CURLHSTS_READONLYFILE (long)(1<<1)
 
-/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */
+/* The CURLPROTO_ defines below are for the **deprecated** CURLOPT_*PROTOCOLS
+   options. Do not use. */
 #define CURLPROTO_HTTP   (1<<0)
 #define CURLPROTO_HTTPS  (1<<1)
 #define CURLPROTO_FTP    (1<<2)
@@ -1461,12 +1478,11 @@ typedef enum {
      Note that setting multiple bits may cause extra network round-trips. */
   CURLOPT(CURLOPT_PROXYAUTH, CURLOPTTYPE_VALUES, 111),
 
-  /* FTP option that changes the timeout, in seconds, associated with
-     getting a response.  This is different from transfer timeout time and
-     essentially places a demand on the FTP server to acknowledge commands
-     in a timely manner. */
-  CURLOPT(CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112),
-#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT
+  /* Option that changes the timeout, in seconds, associated with getting a
+     response.  This is different from transfer timeout time and essentially
+     places a demand on the server to acknowledge commands in a timely
+     manner. For FTP, SMTP, IMAP and POP3. */
+  CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112),
 
   /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
      tell libcurl to use those IP versions only. This only has effect on
@@ -2122,6 +2138,25 @@ typedef enum {
   /* Set MIME option flags. */
   CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315),
 
+  /* set the SSH host key callback, must point to a curl_sshkeycallback
+     function */
+  CURLOPT(CURLOPT_SSH_HOSTKEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 316),
+
+  /* set the SSH host key callback custom pointer */
+  CURLOPT(CURLOPT_SSH_HOSTKEYDATA, CURLOPTTYPE_CBPOINT, 317),
+
+  /* specify which protocols that are allowed to be used for the transfer,
+     which thus helps the app which takes URLs from users or other external
+     inputs and want to restrict what protocol(s) to deal with. Defaults to
+     all built-in protocols. */
+  CURLOPT(CURLOPT_PROTOCOLS_STR, CURLOPTTYPE_STRINGPOINT, 318),
+
+  /* specify which protocols that libcurl is allowed to follow directs to */
+  CURLOPT(CURLOPT_REDIR_PROTOCOLS_STR, CURLOPTTYPE_STRINGPOINT, 319),
+
+  /* websockets options */
+  CURLOPT(CURLOPT_WS_OPTIONS, CURLOPTTYPE_LONG, 320),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2147,6 +2182,9 @@ typedef enum {
 #define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD
 #define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL
 
+/* */
+#define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT
+
 #else
 /* This is set if CURL_NO_OLDIES is defined at compile-time */
 #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
@@ -2161,7 +2199,7 @@ typedef enum {
 #define CURL_IPRESOLVE_V4       1 /* uses only IPv4 addresses/connections */
 #define CURL_IPRESOLVE_V6       2 /* uses only IPv6 addresses/connections */
 
-  /* three convenient "aliases" that follow the name scheme better */
+  /* Convenient "aliases" */
 #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
 
   /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
@@ -2589,8 +2627,10 @@ CURL_EXTERN void curl_free(void *p);
  *
  * curl_global_init() should be invoked exactly once for each application that
  * uses libcurl and before any call of other libcurl functions.
- *
- * This function is not thread-safe!
+
+ * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the
+ * curl_version_info_data.features flag (fetch by curl_version_info()).
+
  */
 CURL_EXTERN CURLcode curl_global_init(long flags);
 
@@ -2799,8 +2839,9 @@ typedef enum {
   CURLINFO_EFFECTIVE_METHOD = CURLINFO_STRING + 58,
   CURLINFO_PROXY_ERROR      = CURLINFO_LONG + 59,
   CURLINFO_REFERER          = CURLINFO_STRING + 60,
-
-  CURLINFO_LASTONE          = 60
+  CURLINFO_CAINFO           = CURLINFO_STRING + 61,
+  CURLINFO_CAPATH           = CURLINFO_STRING + 62,
+  CURLINFO_LASTONE          = 62
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -3006,6 +3047,7 @@ typedef struct curl_version_info_data curl_version_info_data;
 #define CURL_VERSION_UNICODE      (1<<27) /* Unicode support on Windows */
 #define CURL_VERSION_HSTS         (1<<28) /* HSTS is supported */
 #define CURL_VERSION_GSASL        (1<<29) /* libgsasl is supported */
+#define CURL_VERSION_THREADSAFE   (1<<30) /* libcurl API is thread-safe */
 
  /*
  * NAME curl_version_info()
@@ -3070,6 +3112,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
 #include "urlapi.h"
 #include "options.h"
 #include "header.h"
+#include "websockets.h"
 
 /* the typechecker doesn't work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
index 718d58c..9eb5067 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* This header file contains nothing but libcurl version info, generated by
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "7.83.1"
+#define LIBCURL_VERSION "7.86.0"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 83
-#define LIBCURL_VERSION_PATCH 1
+#define LIBCURL_VERSION_MINOR 86
+#define LIBCURL_VERSION_PATCH 0
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -57,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 0x075301
+#define LIBCURL_VERSION_NUM 0x075600
 
 /*
  * This is the date and time when the full source package was created. The
index 2dbfb26..9c7e63a 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifdef  __cplusplus
 extern "C" {
index 7715b61..1598c6f 100644 (file)
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
 struct curl_header {
   char *name;    /* this might not use the same case */
   char *value;
@@ -61,4 +67,8 @@ CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy,
                                                      int request,
                                                      struct curl_header *prev);
 
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
 #endif /* CURLINC_HEADER_H */
index 3549552..cb948dc 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <stdarg.h>
index 91cd95d..2f3ec37 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /*
   This is an "external" header file. Don't give away any internals here!
@@ -75,6 +77,7 @@ typedef enum {
   CURLM_WAKEUP_FAILURE,  /* wakeup is unavailable or failed */
   CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */
   CURLM_ABORTED_BY_CALLBACK,
+  CURLM_UNRECOVERABLE_POLL,
   CURLM_LAST
 } CURLMcode;
 
@@ -121,7 +124,7 @@ struct curl_waitfd {
 /*
  * Name:    curl_multi_init()
  *
- * Desc:    inititalize multi-style curl usage
+ * Desc:    initialize multi-style curl usage
  *
  * Returns: a new CURLM handle to use in all 'curl_multi' functions.
  */
index 91360b3..a792687 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifdef  __cplusplus
@@ -31,7 +33,7 @@ typedef enum {
   CURLOT_VALUES,  /*      (a defined set or bitmask) */
   CURLOT_OFF_T,   /* curl_off_t (a range of values) */
   CURLOT_OBJECT,  /* pointer (void *) */
-  CURLOT_STRING,  /*         (char * to zero terminated buffer) */
+  CURLOT_STRING,  /*         (char * to null-terminated buffer) */
   CURLOT_SLIST,   /*         (struct curl_slist *) */
   CURLOT_CBPTR,   /*         (void * passed as-is to a callback) */
   CURLOT_BLOB,    /* blob (struct curl_blob *) */
index 60596c7..82e1b5f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <sys/types.h>
index 000fea6..8d56b8a 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 9e14d8a..2dabcb4 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* wraps curl_easy_setopt() with typechecking */
@@ -270,9 +272,9 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_DNS_SERVERS ||                                         \
    (option) == CURLOPT_DOH_URL ||                                             \
    (option) == CURLOPT_EGDSOCKET ||                                           \
-   (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_FTP_ACCOUNT ||                                         \
    (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
+   (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_HSTS ||                                                \
    (option) == CURLOPT_INTERFACE ||                                           \
    (option) == CURLOPT_ISSUERCERT ||                                          \
@@ -286,10 +288,8 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_PASSWORD ||                                            \
    (option) == CURLOPT_PINNEDPUBLICKEY ||                                     \
    (option) == CURLOPT_PRE_PROXY ||                                           \
+   (option) == CURLOPT_PROTOCOLS_STR ||                                       \
    (option) == CURLOPT_PROXY ||                                               \
-   (option) == CURLOPT_PROXYPASSWORD ||                                       \
-   (option) == CURLOPT_PROXYUSERNAME ||                                       \
-   (option) == CURLOPT_PROXYUSERPWD ||                                        \
    (option) == CURLOPT_PROXY_CAINFO ||                                        \
    (option) == CURLOPT_PROXY_CAPATH ||                                        \
    (option) == CURLOPT_PROXY_CRLFILE ||                                       \
@@ -297,17 +297,21 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_PROXY_KEYPASSWD ||                                     \
    (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                               \
    (option) == CURLOPT_PROXY_SERVICE_NAME ||                                  \
+   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \
    (option) == CURLOPT_PROXY_SSLCERT ||                                       \
    (option) == CURLOPT_PROXY_SSLCERTTYPE ||                                   \
    (option) == CURLOPT_PROXY_SSLKEY ||                                        \
    (option) == CURLOPT_PROXY_SSLKEYTYPE ||                                    \
-   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \
    (option) == CURLOPT_PROXY_TLS13_CIPHERS ||                                 \
    (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                              \
    (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                                  \
    (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                              \
+   (option) == CURLOPT_PROXYPASSWORD ||                                       \
+   (option) == CURLOPT_PROXYUSERNAME ||                                       \
+   (option) == CURLOPT_PROXYUSERPWD ||                                        \
    (option) == CURLOPT_RANDOM_FILE ||                                         \
    (option) == CURLOPT_RANGE ||                                               \
+   (option) == CURLOPT_REDIR_PROTOCOLS_STR ||                                 \
    (option) == CURLOPT_REFERER ||                                             \
    (option) == CURLOPT_REQUEST_TARGET ||                                      \
    (option) == CURLOPT_RTSP_SESSION_ID ||                                     \
@@ -374,6 +378,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_WRITEDATA ||                                           \
    (option) == CURLOPT_RESOLVER_START_DATA ||                                 \
    (option) == CURLOPT_TRAILERDATA ||                                         \
+   (option) == CURLOPT_SSH_HOSTKEYDATA ||                                     \
    0)
 
 /* evaluates to true if option takes a POST data argument (void* or char*) */
index a475f91..e15c213 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "curl.h"
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h
new file mode 100644 (file)
index 0000000..4d57f91
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef CURLINC_WEBSOCKETS_H
+#define CURLINC_WEBSOCKETS_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
+ *
+ ***************************************************************************/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+struct curl_ws_frame {
+  int age;              /* zero */
+  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 */
+};
+
+/* flag bits */
+#define CURLWS_TEXT       (1<<0)
+#define CURLWS_BINARY     (1<<1)
+#define CURLWS_CONT       (1<<2)
+#define CURLWS_CLOSE      (1<<3)
+#define CURLWS_PING       (1<<4)
+#define CURLWS_OFFSET     (1<<5)
+
+/*
+ * NAME curl_ws_recv()
+ *
+ * DESCRIPTION
+ *
+ * Receives data from the websocket connection. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
+                                  size_t *recv,
+                                  struct curl_ws_frame **metap);
+
+/* sendflags for curl_ws_send() */
+#define CURLWS_PONG       (1<<6)
+
+/*
+ * NAME curl_easy_send()
+ *
+ * DESCRIPTION
+ *
+ * Sends data over the websocket connection. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
+                                  size_t buflen, size_t *sent,
+                                  curl_off_t framesize,
+                                  unsigned int sendflags);
+
+/* bits for the CURLOPT_WS_OPTIONS bitmask: */
+#define CURLWS_RAW_MODE (1<<0)
+
+CURL_EXTERN struct curl_ws_frame *curl_ws_meta(CURL *curl);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* CURLINC_WEBSOCKETS_H */
index 6cf45ad..3138473 100644 (file)
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+# 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
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 set(LIB_NAME cmcurl)
 set(LIBCURL_OUTPUT_NAME cmcurl)
+add_definitions(-DBUILDING_LIBCURL)
 
 if(BUILD_SHARED_LIBS)
   set(CURL_STATICLIB NO)
@@ -143,7 +146,6 @@ endif()
 
 if(WIN32)
   if(BUILD_SHARED_LIBS)
-    set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "_USRDLL")
     if(MSVC)
       # Add "_imp" as a suffix before the extension to avoid conflicting with
       # the statically linked "libcurl.lib"
@@ -156,16 +158,18 @@ target_include_directories(${LIB_NAME} INTERFACE
   $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
   $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
 
-install(TARGETS ${LIB_NAME}
-  EXPORT ${TARGETS_EXPORT_NAME}
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-)
-
-export(TARGETS ${LIB_NAME}
-       APPEND FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
-       NAMESPACE ${PROJECT_NAME}::
-)
+if(CURL_ENABLE_EXPORT_TARGET)
+  install(TARGETS ${LIB_NAME}
+    EXPORT ${TARGETS_EXPORT_NAME}
+    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+  )
+
+  export(TARGETS ${LIB_NAME}
+         FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
+         NAMESPACE ${PROJECT_NAME}::
+  )
+endif()
 
 endif()
index 1ab0078..b2d2e9e 100644 (file)
@@ -18,6 +18,8 @@
 # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 # KIND, either express or implied.
 #
+# SPDX-License-Identifier: curl
+#
 ###########################################################################
 
 LIB_VAUTH_CFILES =      \
@@ -108,7 +110,6 @@ LIB_CFILES =         \
   content_encoding.c \
   cookie.c           \
   curl_addrinfo.c    \
-  curl_ctype.c       \
   curl_des.c         \
   curl_endian.c      \
   curl_fnmatch.c     \
@@ -127,7 +128,6 @@ LIB_CFILES =         \
   curl_threads.c     \
   dict.c             \
   doh.c              \
-  dotdot.c           \
   dynbuf.c           \
   easy.c             \
   easygetopt.c       \
@@ -135,6 +135,7 @@ LIB_CFILES =         \
   escape.c           \
   file.c             \
   fileinfo.c         \
+  fopen.c            \
   formdata.c         \
   ftp.c              \
   ftplistparser.c    \
@@ -176,6 +177,7 @@ LIB_CFILES =         \
   multi.c            \
   netrc.c            \
   nonblock.c         \
+  noproxy.c          \
   openldap.c         \
   parsedate.c        \
   pingpong.c         \
@@ -215,7 +217,8 @@ LIB_CFILES =         \
   version.c          \
   version_win32.c    \
   warnless.c         \
-  wildcard.c
+  wildcard.c         \
+  ws.c
 
 LIB_HFILES =         \
   altsvc.h           \
@@ -260,14 +263,16 @@ LIB_HFILES =         \
   curlx.h            \
   dict.h             \
   doh.h              \
-  dotdot.h           \
   dynbuf.h           \
+  easy_lock.h        \
   easyif.h           \
   easyoptions.h      \
   escape.h           \
   file.h             \
   fileinfo.h         \
+  fopen.h            \
   formdata.h         \
+  functypes.h        \
   ftp.h              \
   ftplistparser.h    \
   getinfo.h          \
@@ -297,6 +302,7 @@ LIB_HFILES =         \
   multiif.h          \
   netrc.h            \
   nonblock.h         \
+  noproxy.h          \
   parsedate.h        \
   pingpong.h         \
   pop3.h             \
@@ -336,7 +342,8 @@ LIB_HFILES =         \
   urldata.h          \
   version_win32.h    \
   warnless.h         \
-  wildcard.h
+  wildcard.h         \
+  ws.h
 
 LIB_RCFILES = libcurl.rc
 
index dd2d0eb..7bca840 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /*
  * The Alt-Svc: header is defined in RFC 7838:
@@ -34,7 +36,7 @@
 #include "parsedate.h"
 #include "sendf.h"
 #include "warnless.h"
-#include "rand.h"
+#include "fopen.h"
 #include "rename.h"
 
 /* The last 3 #include files should be in this order */
 #define MAX_ALTSVC_ALPNLENSTR "10"
 #define MAX_ALTSVC_ALPNLEN 10
 
-#if defined(USE_QUICHE) && !defined(UNITTESTS)
-#define H3VERSION "h3-29"
-#elif defined(USE_NGTCP2) && !defined(UNITTESTS)
-#define H3VERSION "h3-29"
-#elif defined(USE_MSH3) && !defined(UNITTESTS)
-#define H3VERSION "h3-29"
-#else
 #define H3VERSION "h3"
-#endif
 
 static enum alpnid alpn2alpnid(char *name)
 {
@@ -184,10 +178,9 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
 
 /*
  * Load alt-svc entries from the given file. The text based line-oriented file
- * format is documented here:
- * https://github.com/curl/curl/wiki/QUIC-implementation
+ * format is documented here: https://curl.se/docs/alt-svc.html
  *
- * This function only returns error on major problems that prevents alt-svc
+ * This function only returns error on major problems that prevent alt-svc
  * handling to work completely. It will ignore individual syntactical errors
  * etc.
  */
@@ -336,8 +329,7 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
   struct Curl_llist_element *n;
   CURLcode result = CURLE_OK;
   FILE *out;
-  char *tempstore;
-  unsigned char randsuffix[9];
+  char *tempstore = NULL;
 
   if(!altsvc)
     /* no cache activated */
@@ -351,17 +343,8 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
     /* marked as read-only, no file or zero length file name */
     return CURLE_OK;
 
-  if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
-    return CURLE_FAILED_INIT;
-
-  tempstore = aprintf("%s.%s.tmp", file, randsuffix);
-  if(!tempstore)
-    return CURLE_OUT_OF_MEMORY;
-
-  out = fopen(tempstore, FOPEN_WRITETEXT);
-  if(!out)
-    result = CURLE_WRITE_ERROR;
-  else {
+  result = Curl_fopen(data, file, &out, &tempstore);
+  if(!result) {
     fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
           "# This file was generated by libcurl! Edit at your own risk.\n",
           out);
@@ -373,10 +356,10 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
         break;
     }
     fclose(out);
-    if(!result && Curl_rename(tempstore, file))
+    if(!result && tempstore && Curl_rename(tempstore, file))
       result = CURLE_WRITE_ERROR;
 
-    if(result)
+    if(result && tempstore)
       unlink(tempstore);
   }
   free(tempstore);
@@ -479,6 +462,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
   struct altsvc *as;
   unsigned short dstport = srcport; /* the same by default */
   CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
+  size_t entries = 0;
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
 #endif
@@ -489,11 +473,10 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
 
   DEBUGASSERT(asi);
 
-  /* Flush all cached alternatives for this source origin, if any */
-  altsvc_flush(asi, srcalpnid, srchost, srcport);
-
   /* "clear" is a magic keyword */
   if(strcasecompare(alpnbuf, "clear")) {
+    /* Flush cached alternatives for this source origin */
+    altsvc_flush(asi, srcalpnid, srchost, srcport);
     return CURLE_OK;
   }
 
@@ -511,6 +494,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
         bool quoted = FALSE;
         time_t maxage = 24 * 3600; /* default is 24 hours */
         bool persist = FALSE;
+        bool valid = TRUE;
         p++;
         if(*p != ':') {
           /* host name starts here */
@@ -520,7 +504,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
           len = p - hostp;
           if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
             infof(data, "Excessive alt-svc host name, ignoring.");
-            dstalpnid = ALPN_none;
+            valid = FALSE;
           }
           else {
             memcpy(namebuf, hostp, len);
@@ -537,10 +521,11 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
           unsigned long port = strtoul(++p, &end_ptr, 10);
           if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
             infof(data, "Unknown alt-svc port number, ignoring.");
-            dstalpnid = ALPN_none;
+            valid = FALSE;
           }
+          else
+            dstport = curlx_ultous(port);
           p = end_ptr;
-          dstport = curlx_ultous(port);
         }
         if(*p++ != '\"')
           break;
@@ -592,7 +577,12 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
               persist = TRUE;
           }
         }
-        if(dstalpnid) {
+        if(dstalpnid && valid) {
+          if(!entries++)
+            /* Flush cached alternatives for this source origin, if any - when
+               this is the first entry of the line. */
+            altsvc_flush(asi, srcalpnid, srchost, srcport);
+
           as = altsvc_createid(srchost, dsthost,
                                srcalpnid, dstalpnid,
                                srcport, dstport);
@@ -606,10 +596,6 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                   Curl_alpnid2str(dstalpnid));
           }
         }
-        else {
-          infof(data, "Unknown alt-svc protocol \"%s\", skipping.",
-                alpnbuf);
-        }
       }
       else
         break;
index 2ab89e7..2751d27 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 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
@@ -20,6 +20,8 @@
  * 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"
 
index 78bb22c..e8c2fc0 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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 __AMIGA__
-#  include "amigaos.h"
-#  if defined(HAVE_PROTO_BSDSOCKET_H) && !defined(USE_AMISSL)
+
+#include <curl/curl.h>
+
+#include "hostip.h"
+#include "amigaos.h"
+
+#ifdef HAVE_PROTO_BSDSOCKET_H
+#  if defined(__amigaos4__)
+#    include <bsdsocket/socketbasetags.h>
+#  elif !defined(USE_AMISSL)
 #    include <amitcp/socketbasetags.h>
 #  endif
 #  ifdef __libnix__
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef __AMIGA__
-#if defined(HAVE_PROTO_BSDSOCKET_H) && !defined(USE_AMISSL)
+#ifdef HAVE_PROTO_BSDSOCKET_H
+
+#ifdef __amigaos4__
+/*
+ * AmigaOS 4.x specific code
+ */
+
+/*
+ * hostip4.c - Curl_ipv4_resolve_r() replacement code
+ *
+ * Logic that needs to be considered are the following build cases:
+ * - newlib networking
+ * - clib2 networking
+ * - direct bsdsocket.library networking (usually AmiSSL builds)
+ * Each with the threaded resolver enabled or not.
+ *
+ * With the threaded resolver enabled, try to use gethostbyname_r() where
+ * available, otherwise (re)open bsdsocket.library and fallback to
+ * gethostbyname().
+ */
+
+#include <proto/bsdsocket.h>
+
+static struct SocketIFace *__CurlISocket = NULL;
+static uint32 SocketFeatures = 0;
+
+#define HAVE_BSDSOCKET_GETHOSTBYNAME_R 0x01
+#define HAVE_BSDSOCKET_GETADDRINFO     0x02
+
+CURLcode Curl_amiga_init(void)
+{
+  struct SocketIFace *ISocket;
+  struct Library *base = OpenLibrary("bsdsocket.library", 4);
+
+  if(base) {
+    ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL);
+    if(ISocket) {
+      ULONG enabled = 0;
+
+      SocketBaseTags(SBTM_SETVAL(SBTC_CAN_SHARE_LIBRARY_BASES), TRUE,
+                     SBTM_GETREF(SBTC_HAVE_GETHOSTADDR_R_API), (ULONG)&enabled,
+                     TAG_DONE);
+
+      if(enabled) {
+        SocketFeatures |= HAVE_BSDSOCKET_GETHOSTBYNAME_R;
+      }
+
+      __CurlISocket = ISocket;
+
+      atexit(Curl_amiga_cleanup);
+
+      return CURLE_OK;
+    }
+    CloseLibrary(base);
+  }
+
+  return CURLE_FAILED_INIT;
+}
+
+void Curl_amiga_cleanup(void)
+{
+  if(__CurlISocket) {
+    struct Library *base = __CurlISocket->Data.LibBase;
+    DropInterface((struct Interface *)__CurlISocket);
+    CloseLibrary(base);
+    __CurlISocket = NULL;
+  }
+}
+
+#ifdef CURLRES_AMIGA
+/*
+ * Because we need to handle the different cases in hostip4.c at run-time,
+ * not at compile-time, based on what was detected in Curl_amiga_init(),
+ * we replace it completely with our own as to not complicate the baseline
+ * code. Assumes malloc/calloc/free are thread safe because Curl_he2ai()
+ * allocates memory also.
+ */
+
+struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
+                                          int port)
+{
+  struct Curl_addrinfo *ai = NULL;
+  struct hostent *h;
+  struct SocketIFace *ISocket = __CurlISocket;
+
+  if(SocketFeatures & HAVE_BSDSOCKET_GETHOSTBYNAME_R) {
+    LONG h_errnop = 0;
+    struct hostent *buf;
+
+    buf = calloc(1, CURL_HOSTENT_SIZE);
+    if(buf) {
+      h = gethostbyname_r((STRPTR)hostname, buf,
+                          (char *)buf + sizeof(struct hostent),
+                          CURL_HOSTENT_SIZE - sizeof(struct hostent),
+                          &h_errnop);
+      if(h) {
+        ai = Curl_he2ai(h, port);
+      }
+      free(buf);
+    }
+  }
+  else {
+    #ifdef CURLRES_THREADED
+    /* gethostbyname() is not thread safe, so we need to reopen bsdsocket
+     * on the thread's context
+     */
+    struct Library *base = OpenLibrary("bsdsocket.library", 4);
+    if(base) {
+      ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL);
+      if(ISocket) {
+        h = gethostbyname((STRPTR)hostname);
+        if(h) {
+          ai = Curl_he2ai(h, port);
+        }
+        DropInterface((struct Interface *)ISocket);
+      }
+      CloseLibrary(base);
+    }
+    #else
+    /* not using threaded resolver - safe to use this as-is */
+    h = gethostbyname(hostname);
+    if(h) {
+      ai = Curl_he2ai(h, port);
+    }
+    #endif
+  }
+
+  return ai;
+}
+#endif /* CURLRES_AMIGA */
+
+#ifdef USE_AMISSL
+int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
+                      fd_set *errorfds, struct timeval *timeout)
+{
+  int r = WaitSelect(nfds, readfds, writefds, errorfds, timeout, 0);
+  /* Ensure Ctrl-C signal is actioned */
+  if((r == -1) && (SOCKERRNO == EINTR))
+    raise(SIGINT);
+  return r;
+}
+#endif /* USE_AMISSL */
+
+#elif !defined(USE_AMISSL) /* __amigaos4__ */
+/*
+ * Amiga OS3 specific code
+ */
+
 struct Library *SocketBase = NULL;
 extern int errno, h_errno;
 
@@ -47,7 +203,7 @@ void __request(const char *msg);
 # define __request(msg)       Printf(msg "\n\a")
 #endif
 
-void Curl_amiga_cleanup()
+void Curl_amiga_cleanup(void)
 {
   if(SocketBase) {
     CloseLibrary(SocketBase);
@@ -55,68 +211,36 @@ void Curl_amiga_cleanup()
   }
 }
 
-bool Curl_amiga_init()
+CURLcode Curl_amiga_init(void)
 {
   if(!SocketBase)
     SocketBase = OpenLibrary("bsdsocket.library", 4);
 
   if(!SocketBase) {
     __request("No TCP/IP Stack running!");
-    return FALSE;
+    return CURLE_FAILED_INIT;
   }
 
   if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno,
                     SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "curl",
                     TAG_DONE)) {
     __request("SocketBaseTags ERROR");
-    return FALSE;
+    return CURLE_FAILED_INIT;
   }
 
 #ifndef __libnix__
   atexit(Curl_amiga_cleanup);
 #endif
 
-  return TRUE;
+  return CURLE_OK;
 }
 
 #ifdef __libnix__
 ADD2EXIT(Curl_amiga_cleanup, -50);
 #endif
 
-#endif /* HAVE_PROTO_BSDSOCKET_H */
+#endif /* !USE_AMISSL */
 
-#ifdef USE_AMISSL
-void Curl_amiga_X509_free(X509 *a)
-{
-  X509_free(a);
-}
-
-/* AmiSSL replaces many functions with macros. Curl requires pointer
- * to some of these functions. Thus, we have to encapsulate these macros.
- */
-
-#include "warnless.h"
-
-int (SHA256_Init)(SHA256_CTX *c)
-{
-  return SHA256_Init(c);
-};
-
-int (SHA256_Update)(SHA256_CTX *c, const void *data, size_t len)
-{
-  return SHA256_Update(c, data, curlx_uztoui(len));
-};
-
-int (SHA256_Final)(unsigned char *md, SHA256_CTX *c)
-{
-  return SHA256_Final(md, c);
-};
-
-void (X509_INFO_free)(X509_INFO *a)
-{
-  X509_INFO_free(a);
-};
+#endif /* HAVE_PROTO_BSDSOCKET_H */
 
-#endif /* USE_AMISSL */
 #endif /* __AMIGA__ */
-
index 02e5bb5..9abfb59 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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(__AMIGA__) && defined(HAVE_BSDSOCKET_H) && !defined(USE_AMISSL)
+#if defined(__AMIGA__) && defined(HAVE_PROTO_BSDSOCKET_H) && \
+  (!defined(USE_AMISSL) || defined(__amigaos4__))
 
-bool Curl_amiga_init();
-void Curl_amiga_cleanup();
+CURLcode Curl_amiga_init(void);
+void Curl_amiga_cleanup(void);
 
 #else
 
-#define Curl_amiga_init() 1
+#define Curl_amiga_init() CURLE_OK
 #define Curl_amiga_cleanup() Curl_nop_stmt
 
 #endif
 
-#ifdef USE_AMISSL
-#include <openssl/x509v3.h>
-void Curl_amiga_X509_free(X509 *a);
-#endif /* USE_AMISSL */
-
 #endif /* HEADER_CURL_AMIGAOS_H */
 
index cbe31de..523f7f5 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifndef CURL_DISABLE_TELNET
 /*
index c885ade..33edba1 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -113,6 +115,7 @@ struct thread_data {
 #ifndef HAVE_CARES_GETADDRINFO
   struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
 #endif
+  char hostname[1];
 };
 
 /* How long we are willing to wait for additional parallel responses after
@@ -250,8 +253,6 @@ void Curl_resolver_kill(struct Curl_easy *data)
  */
 static void destroy_async_data(struct Curl_async *async)
 {
-  free(async->hostname);
-
   if(async->tdata) {
     struct thread_data *res = async->tdata;
     if(res) {
@@ -263,8 +264,6 @@ static void destroy_async_data(struct Curl_async *async)
     }
     async->tdata = NULL;
   }
-
-  async->hostname = NULL;
 }
 
 /*
@@ -306,7 +305,7 @@ int Curl_resolver_getsock(struct Curl_easy *data,
  * 2) wait for the timeout period to check for action on ares' sockets.
  * 3) tell ares to act on all the sockets marked as "with action"
  *
- * return number of sockets it worked on
+ * return number of sockets it worked on, or -1 on error
  */
 
 static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
@@ -338,8 +337,11 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
       break;
   }
 
-  if(num)
+  if(num) {
     nfds = Curl_poll(pfd, num, timeout_ms);
+    if(nfds < 0)
+      return -1;
+  }
   else
     nfds = 0;
 
@@ -376,7 +378,8 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
   DEBUGASSERT(dns);
   *dns = NULL;
 
-  waitperform(data, 0);
+  if(waitperform(data, 0) < 0)
+    return CURLE_UNRECOVERABLE_POLL;
 
 #ifndef HAVE_CARES_GETADDRINFO
   /* Now that we've checked for any last minute results above, see if there are
@@ -475,7 +478,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
     else
       timeout_ms = 1000;
 
-    waitperform(data, timeout_ms);
+    if(waitperform(data, timeout_ms) < 0)
+      return CURLE_UNRECOVERABLE_POLL;
     result = Curl_resolver_is_resolved(data, entry);
 
     if(result || data->state.async.done)
@@ -742,7 +746,7 @@ static void addrinfo_cb(void *arg, int status, int timeouts,
  * Curl_resolver_getaddrinfo() - when using ares
  *
  * Returns name information about the given hostname and port number. If
- * successful, the 'hostent' is returned and the forth argument will point to
+ * successful, the 'hostent' is returned and the fourth argument will point to
  * memory we need to free after use. That memory *MUST* be freed with
  * Curl_freeaddrinfo(), nothing else.
  */
@@ -751,25 +755,18 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                                                 int port,
                                                 int *waitp)
 {
-  char *bufp;
-
+  struct thread_data *res = NULL;
+  size_t namelen = strlen(hostname);
   *waitp = 0; /* default to synchronous response */
 
-  bufp = strdup(hostname);
-  if(bufp) {
-    struct thread_data *res = NULL;
-    free(data->state.async.hostname);
-    data->state.async.hostname = bufp;
+  res = calloc(sizeof(struct thread_data) + namelen, 1);
+  if(res) {
+    strcpy(res->hostname, hostname);
+    data->state.async.hostname = res->hostname;
     data->state.async.port = port;
     data->state.async.done = FALSE;   /* not done */
     data->state.async.status = 0;     /* clear */
     data->state.async.dns = NULL;     /* clear */
-    res = calloc(sizeof(struct thread_data), 1);
-    if(!res) {
-      free(data->state.async.hostname);
-      data->state.async.hostname = NULL;
-      return NULL;
-    }
     data->state.async.tdata = res;
 
     /* initial status - failed */
@@ -782,13 +779,17 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
       int pf = PF_INET;
       memset(&hints, 0, sizeof(hints));
 #ifdef CURLRES_IPV6
-      if(Curl_ipv6works(data))
+      if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
         /* The stack seems to be IPv6-enabled */
         pf = PF_UNSPEC;
 #endif /* CURLRES_IPV6 */
       hints.ai_family = pf;
       hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
         SOCK_STREAM : SOCK_DGRAM;
+      /* Since the service is a numerical one, set the hint flags
+       * accordingly to save a call to getservbyname in inside C-Ares
+       */
+      hints.ai_flags = ARES_AI_NUMERICSERV;
       msnprintf(service, sizeof(service), "%d", port);
       res->num_pending = 1;
       ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
@@ -797,7 +798,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
 #else
 
 #ifdef HAVE_CARES_IPV6
-    if(Curl_ipv6works(data)) {
+    if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
       /* The stack seems to be IPv6-enabled */
       res->num_pending = 2;
 
index 149172a..8b375eb 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -262,23 +264,28 @@ int init_thread_sync_data(struct thread_data *td,
   return 1;
 
  err_exit:
-  /* Memory allocation failed */
+#ifndef CURL_DISABLE_SOCKETPAIR
+  if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
+    sclose(tsd->sock_pair[0]);
+    tsd->sock_pair[0] = CURL_SOCKET_BAD;
+  }
+#endif
   destroy_thread_sync_data(tsd);
   return 0;
 }
 
-static int getaddrinfo_complete(struct Curl_easy *data)
+static CURLcode getaddrinfo_complete(struct Curl_easy *data)
 {
   struct thread_sync_data *tsd = conn_thread_sync_data(data);
-  int rc;
+  CURLcode result;
 
-  rc = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
+  result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
   /* The tsd->res structure has been copied to async.dns and perhaps the DNS
      cache.  Set our copy to NULL so destroy_thread_sync_data doesn't free it.
   */
   tsd->res = NULL;
 
-  return rc;
+  return result;
 }
 
 
@@ -700,7 +707,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
   *waitp = 0; /* default to synchronous response */
 
 #ifdef CURLRES_IPV6
-  if(Curl_ipv6works(data))
+  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
     /* The stack seems to be IPv6-enabled */
     pf = PF_UNSPEC;
 #endif /* CURLRES_IPV6 */
index 3130395..1aab21a 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
@@ -146,7 +148,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
  * Curl_resolver_getaddrinfo() - when using this resolver
  *
  * Returns name information about the given hostname and port number. If
- * successful, the 'hostent' is returned and the forth argument will point to
+ * successful, the 'hostent' is returned and the fourth argument will point to
  * memory we need to free after use. That memory *MUST* be freed with
  * Curl_freeaddrinfo(), nothing else.
  *
index 960a1ca..52654c2 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* Base64 encoding/decoding */
 #include "memdebug.h"
 
 /* ---- Base64 Encoding/Decoding Table --- */
+/* Padding character string starts at offset 64. */
 static const char base64[]=
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
-/* The Base 64 encoding with an URL and filename safe alphabet, RFC 4648
+/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
    section 5 */
 static const char base64url[]=
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
@@ -52,25 +55,18 @@ static const char base64url[]=
 static size_t decodeQuantum(unsigned char *dest, const char *src)
 {
   size_t padding = 0;
-  const char *s, *p;
+  const char *s;
   unsigned long i, x = 0;
 
   for(i = 0, s = src; i < 4; i++, s++) {
     if(*s == '=') {
-      x = (x << 6);
+      x <<= 6;
       padding++;
     }
     else {
-      unsigned long v = 0;
-      p = base64;
-
-      while(*p && (*p != *s)) {
-        v++;
-        p++;
-      }
-
-      if(*p == *s)
-        x = (x << 6) + v;
+      const char *p = strchr(base64, *s);
+      if(p)
+        x = (x << 6) + curlx_uztoul(p - base64);
       else
         return 0;
     }
@@ -107,11 +103,11 @@ CURLcode Curl_base64_decode(const char *src,
                             unsigned char **outptr, size_t *outlen)
 {
   size_t srclen = 0;
-  size_t length = 0;
   size_t padding = 0;
   size_t i;
   size_t numQuantums;
   size_t rawlen = 0;
+  const char *padptr;
   unsigned char *pos;
   unsigned char *newstr;
 
@@ -124,19 +120,17 @@ CURLcode Curl_base64_decode(const char *src,
     return CURLE_BAD_CONTENT_ENCODING;
 
   /* Find the position of any = padding characters */
-  while((src[length] != '=') && src[length])
-    length++;
-
-  /* A maximum of two = padding characters is allowed */
-  if(src[length] == '=') {
+  padptr = strchr(src, '=');
+  if(padptr) {
     padding++;
-    if(src[length + 1] == '=')
+    /* A maximum of two = padding characters is allowed */
+    if(padptr[1] == '=')
       padding++;
-  }
 
-  /* Check the = padding characters weren't part way through the input */
-  if(length + padding != srclen)
-    return CURLE_BAD_CONTENT_ENCODING;
+    /* Check the = padding characters weren't part way through the input */
+    if(padptr + padding != src + srclen)
+      return CURLE_BAD_CONTENT_ENCODING;
+  }
 
   /* Calculate the number of quantums */
   numQuantums = srclen / 4;
@@ -144,7 +138,7 @@ CURLcode Curl_base64_decode(const char *src,
   /* Calculate the size of the decoded string */
   rawlen = (numQuantums * 3) - padding;
 
-  /* Allocate our buffer including room for a zero terminator */
+  /* Allocate our buffer including room for a null-terminator */
   newstr = malloc(rawlen + 1);
   if(!newstr)
     return CURLE_OUT_OF_MEMORY;
@@ -185,6 +179,7 @@ static CURLcode base64_encode(const char *table64,
   char *output;
   char *base64data;
   const char *indata = inputbuff;
+  const char *padstr = &table64[64];    /* Point to padding string. */
 
   *outptr = NULL;
   *outlen = 0;
@@ -222,27 +217,30 @@ static CURLcode base64_encode(const char *table64,
 
     switch(inputparts) {
     case 1: /* only one byte read */
-      msnprintf(output, 5, "%c%c==",
-                table64[obuf[0]],
-                table64[obuf[1]]);
+      i = msnprintf(output, 5, "%c%c%s%s",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    padstr,
+                    padstr);
       break;
 
     case 2: /* two bytes read */
-      msnprintf(output, 5, "%c%c%c=",
-                table64[obuf[0]],
-                table64[obuf[1]],
-                table64[obuf[2]]);
+      i = msnprintf(output, 5, "%c%c%c%s",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    table64[obuf[2]],
+                    padstr);
       break;
 
     default:
-      msnprintf(output, 5, "%c%c%c%c",
-                table64[obuf[0]],
-                table64[obuf[1]],
-                table64[obuf[2]],
-                table64[obuf[3]]);
+      i = msnprintf(output, 5, "%c%c%c%c",
+                    table64[obuf[0]],
+                    table64[obuf[1]],
+                    table64[obuf[2]],
+                    table64[obuf[3]]);
       break;
     }
-    output += 4;
+    output += i;
   }
 
   /* Zero terminate */
@@ -270,8 +268,6 @@ static CURLcode base64_encode(const char *table64,
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
- * When encoded data length is 0, returns NULL in *outptr.
- *
  * @unittest: 1302
  */
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
@@ -293,8 +289,6 @@ CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
- * When encoded data length is 0, returns NULL in *outptr.
- *
  * @unittest: 1302
  */
 CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
index b84511e..91b0374 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2021 - 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
@@ -18,6 +18,8 @@
  * 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"
index 25f65d8..96b818b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2021 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 6908298..86abcdb 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -52,6 +54,7 @@
 #include "multiif.h"
 #include "progress.h"
 #include "content_encoding.h"
+#include "ws.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -288,7 +291,7 @@ static CURLcode status_line(struct Curl_easy *data,
              len);
 
   if(!data->state.hconnect || !data->set.suppress_connect_headers) {
-    writetype = CLIENTWRITE_HEADER;
+    writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
     if(data->set.include_header)
       writetype |= CLIENTWRITE_BODY;
     result = Curl_client_write(data, writetype,
@@ -469,6 +472,24 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
     if(result)
       break;
 
+    k->deductheadercount =
+      (100 <= http_status && 199 >= http_status)?k->headerbytecount:0;
+#ifdef USE_WEBSOCKETS
+    if(k->upgr101 == UPGR101_WS) {
+      if(http_status == 101) {
+        /* verify the response */
+        result = Curl_ws_accept(data);
+        if(result)
+          return result;
+      }
+      else {
+        failf(data, "Expected 101, got %u", k->httpcode);
+        result = CURLE_HTTP_RETURNED_ERROR;
+        break;
+      }
+    }
+#endif
+
     /* Curl_http_auth_act() checks what authentication methods that are
      * available and decides which one (if any) to use. It will set 'newurl'
      * if an auth method was picked. */
@@ -690,9 +711,18 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
     data->state.hresult = result;
     return HYPER_POLL_ERROR;
   }
-  if(!fillcount)
-    /* done! */
-    *chunk = NULL;
+  if(!fillcount) {
+    if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE)
+      /* done! */
+      *chunk = NULL;
+    else {
+      /* paused, save a waker */
+      if(data->hyp.send_body_waker)
+        hyper_waker_free(data->hyp.send_body_waker);
+      data->hyp.send_body_waker = hyper_context_waker(ctx);
+      return HYPER_POLL_PENDING;
+    }
+  }
   else {
     hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
     if(copy)
@@ -907,12 +937,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
-  if(conn->negnpn == CURL_HTTP_VERSION_2) {
+  if(conn->alpn == CURL_HTTP_VERSION_2) {
     hyper_clientconn_options_http2(options, 1);
     h2 = TRUE;
   }
   hyper_clientconn_options_set_preserve_header_case(options, 1);
   hyper_clientconn_options_set_preserve_header_order(options, 1);
+  hyper_clientconn_options_http1_allow_multiline_headers(options, 1);
 
   hyper_clientconn_options_exec(options, h->exec);
 
@@ -1002,10 +1033,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     /* For HTTP/2, we show the Host: header as if we sent it, to make it look
        like for HTTP/1 but it isn't actually sent since :authority is then
        used. */
-    result = Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
-                        strlen(data->state.aptr.host));
-    if(result)
-      goto error;
+    Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
+               strlen(data->state.aptr.host));
   }
 
   if(data->state.aptr.proxyuserpwd) {
@@ -1047,6 +1076,21 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       goto error;
   }
 
+#ifndef CURL_DISABLE_ALTSVC
+  if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
+    char *altused = aprintf("Alt-Used: %s:%d\r\n",
+                            conn->conn_to_host.name, conn->conn_to_port);
+    if(!altused) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
+    }
+    result = Curl_hyper_header(data, headers, altused);
+    if(result)
+      goto error;
+    free(altused);
+  }
+#endif
+
 #ifndef CURL_DISABLE_PROXY
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
      !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
@@ -1098,6 +1142,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto error;
 
+  if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+    result = Curl_ws_request(data, headers);
+
   result = Curl_add_timecondition(data, headers);
   if(result)
     goto error;
@@ -1110,9 +1157,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto error;
 
-  result = Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
-  if(result)
-    goto error;
+  Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
 
   data->req.upload_chunky = FALSE;
   sendtask = hyper_clientconn_send(client, req);
index d63defe..70507ad 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
@@ -34,6 +36,7 @@ struct hyptransfer {
   const hyper_executor *exec;
   hyper_task *endtask;
   hyper_waker *exp100_waker;
+  hyper_waker *send_body_waker;
 };
 
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
index aa29620..a557ac6 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
@@ -496,7 +498,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
       conn = curr->ptr;
 
       if(!CONN_INUSE(conn) && !conn->bits.close &&
-         !conn->bits.connect_only) {
+         !conn->connect_only) {
         /* Set higher score for the age passed since the connection was used */
         score = Curl_timediff(now, conn->lastused);
 
index ef11dcf..94664bc 100644 (file)
@@ -21,6 +21,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -29,6 +31,7 @@
  * be shared.
  */
 
+#include <curl/curl.h>
 #include "timeval.h"
 
 struct connectdata;
index 9bcf525..ac007c6 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -470,8 +472,10 @@ static CURLcode bindlocal(struct Curl_easy *data,
     }
 
     if(--portnum > 0) {
-      infof(data, "Bind to local port %hu failed, trying next", port);
       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);
@@ -621,7 +625,8 @@ void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
   else
     data->info.conn_local_ip[0] = 0;
   data->info.conn_scheme = conn->handler->scheme;
-  data->info.conn_protocol = conn->handler->protocol;
+  /* conn_protocol can only provide "old" protocols */
+  data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
   data->info.conn_primary_port = conn->port;
   data->info.conn_remote_port = conn->remote_port;
   data->info.conn_local_port = local_port;
@@ -757,11 +762,10 @@ void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
   char local_ip[MAX_IPADR_LEN] = "";
   int local_port = -1;
 
-  if(conn->transport == TRNSPRT_TCP) {
-    if(!conn->bits.reuse && !conn->bits.tcp_fastopen)
-      Curl_conninfo_remote(data, conn, sockfd);
-    Curl_conninfo_local(data, sockfd, local_ip, &local_port);
-  } /* end of TCP-only section */
+  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);
@@ -899,6 +903,8 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
         conn->tempsock[i] = CURL_SOCKET_BAD;
         post_SOCKS(data, conn, sockindex, connected);
         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
@@ -1033,7 +1039,6 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
      (conn->tempsock[1] == CURL_SOCKET_BAD)) {
     /* no more addresses to try */
     const char *hostname;
-    char buffer[STRERROR_LEN];
     CURLcode failreason = result;
 
     /* if the first address family runs out of addresses to try before the
@@ -1060,11 +1065,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
           "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
           hostname, conn->port,
           Curl_timediff(now, data->progress.t_startsingle),
-#ifdef ENABLE_QUIC
-          (conn->transport == TRNSPRT_QUIC) ?
-          curl_easy_strerror(result) :
-#endif
-          Curl_strerror(error, buffer, sizeof(buffer)));
+          curl_easy_strerror(result));
 
     Curl_quic_disconnect(data, conn, 0);
     Curl_quic_disconnect(data, conn, 1);
@@ -1196,6 +1197,7 @@ static CURLcode singleipconnect(struct Curl_easy *data,
 #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;
@@ -1213,7 +1215,13 @@ static CURLcode singleipconnect(struct Curl_easy *data,
     Curl_closesocket(data, conn, sockfd);
     return CURLE_OK;
   }
-  infof(data, "  Trying %s:%d...", ipaddress, port);
+#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) &&
@@ -1600,9 +1608,20 @@ CURLcode Curl_socket(struct Curl_easy *data,
    */
 
   addr->family = ai->ai_family;
-  addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
-  addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
-    ai->ai_protocol;
+  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))
@@ -1636,6 +1655,24 @@ CURLcode Curl_socket(struct Curl_easy *data,
   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;
+    }
+#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;
+    }
+#endif
+    }
   }
 
 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
@@ -1645,20 +1682,6 @@ CURLcode Curl_socket(struct Curl_easy *data,
   }
 #endif
 
-#if defined(__linux__) && defined(IP_RECVERR)
-  if(addr->socktype == SOCK_DGRAM) {
-    int one = 1;
-    switch(addr->family) {
-    case AF_INET:
-      (void)setsockopt(*sockfd, SOL_IP, IP_RECVERR, &one, sizeof(one));
-      break;
-    case AF_INET6:
-      (void)setsockopt(*sockfd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one));
-      break;
-    }
-  }
-#endif
-
   return CURLE_OK;
 }
 
index 1a055f5..582ff08 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index c03637a..9e345b1 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -26,8 +28,8 @@
 #include <curl/curl.h>
 #include <stddef.h>
 
-#ifdef HAVE_ZLIB_H
-#include <zlib.h>
+#ifdef HAVE_LIBZ
+#include <cm3p/zlib.h>
 #endif
 
 #ifdef HAVE_BROTLI
@@ -80,8 +82,9 @@ typedef enum {
   ZLIB_INIT_GZIP             /* initialized in transparent gzip mode */
 } zlibInitState;
 
-/* Writer parameters. */
-struct zlib_params {
+/* Deflate and gzip writer. */
+struct zlib_writer {
+  struct contenc_writer super;
   zlibInitState zlib_init;   /* zlib init state */
   uInt trailerlen;           /* Remaining trailer byte count. */
   z_stream z;                /* State structure for zlib. */
@@ -133,7 +136,7 @@ exit_zlib(struct Curl_easy *data,
 }
 
 static CURLcode process_trailer(struct Curl_easy *data,
-                                struct zlib_params *zp)
+                                struct zlib_writer *zp)
 {
   z_stream *z = &zp->z;
   CURLcode result = CURLE_OK;
@@ -160,7 +163,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
                                struct contenc_writer *writer,
                                zlibInitState started)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;         /* zlib state structure */
   uInt nread = z->avail_in;
   Bytef *orig_in = z->next_in;
@@ -263,7 +266,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
 static CURLcode deflate_init_writer(struct Curl_easy *data,
                                     struct contenc_writer *writer)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   if(!writer->downstream)
@@ -283,7 +286,7 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data,
                                        struct contenc_writer *writer,
                                        const char *buf, size_t nbytes)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   /* Set the compressed input when this function is called */
@@ -300,7 +303,7 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data,
 static void deflate_close_writer(struct Curl_easy *data,
                                  struct contenc_writer *writer)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
@@ -312,7 +315,7 @@ static const struct content_encoding deflate_encoding = {
   deflate_init_writer,
   deflate_unencode_write,
   deflate_close_writer,
-  sizeof(struct zlib_params)
+  sizeof(struct zlib_writer)
 };
 
 
@@ -320,7 +323,7 @@ static const struct content_encoding deflate_encoding = {
 static CURLcode gzip_init_writer(struct Curl_easy *data,
                                  struct contenc_writer *writer)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   if(!writer->downstream)
@@ -437,7 +440,7 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data,
                                     struct contenc_writer *writer,
                                     const char *buf, size_t nbytes)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
@@ -564,7 +567,7 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data,
 static void gzip_close_writer(struct Curl_easy *data,
                               struct contenc_writer *writer)
 {
-  struct zlib_params *zp = (struct zlib_params *) &writer->params;
+  struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
@@ -576,15 +579,16 @@ static const struct content_encoding gzip_encoding = {
   gzip_init_writer,
   gzip_unencode_write,
   gzip_close_writer,
-  sizeof(struct zlib_params)
+  sizeof(struct zlib_writer)
 };
 
 #endif /* HAVE_LIBZ */
 
 
 #ifdef HAVE_BROTLI
-/* Writer parameters. */
-struct brotli_params {
+/* Brotli writer. */
+struct brotli_writer {
+  struct contenc_writer super;
   BrotliDecoderState *br;    /* State structure for brotli. */
 };
 
@@ -629,7 +633,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
 static CURLcode brotli_init_writer(struct Curl_easy *data,
                                    struct contenc_writer *writer)
 {
-  struct brotli_params *bp = (struct brotli_params *) &writer->params;
+  struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
 
   if(!writer->downstream)
@@ -643,7 +647,7 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
                                       struct contenc_writer *writer,
                                       const char *buf, size_t nbytes)
 {
-  struct brotli_params *bp = (struct brotli_params *) &writer->params;
+  struct brotli_writer *bp = (struct brotli_writer *) writer;
   const uint8_t *src = (const uint8_t *) buf;
   char *decomp;
   uint8_t *dst;
@@ -690,7 +694,8 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
 static void brotli_close_writer(struct Curl_easy *data,
                                 struct contenc_writer *writer)
 {
-  struct brotli_params *bp = (struct brotli_params *) &writer->params;
+  struct brotli_writer *bp = (struct brotli_writer *) writer;
+
   (void) data;
 
   if(bp->br) {
@@ -705,14 +710,15 @@ static const struct content_encoding brotli_encoding = {
   brotli_init_writer,
   brotli_unencode_write,
   brotli_close_writer,
-  sizeof(struct brotli_params)
+  sizeof(struct brotli_writer)
 };
 #endif
 
 
 #ifdef HAVE_ZSTD
-/* Writer parameters. */
-struct zstd_params {
+/* Zstd writer. */
+struct zstd_writer {
+  struct contenc_writer super;
   ZSTD_DStream *zds;    /* State structure for zstd. */
   void *decomp;
 };
@@ -720,7 +726,8 @@ struct zstd_params {
 static CURLcode zstd_init_writer(struct Curl_easy *data,
                                  struct contenc_writer *writer)
 {
-  struct zstd_params *zp = (struct zstd_params *)&writer->params;
+  struct zstd_writer *zp = (struct zstd_writer *) writer;
+
   (void)data;
 
   if(!writer->downstream)
@@ -736,7 +743,7 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
                                     const char *buf, size_t nbytes)
 {
   CURLcode result = CURLE_OK;
-  struct zstd_params *zp = (struct zstd_params *)&writer->params;
+  struct zstd_writer *zp = (struct zstd_writer *) writer;
   ZSTD_inBuffer in;
   ZSTD_outBuffer out;
   size_t errorCode;
@@ -775,7 +782,8 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
 static void zstd_close_writer(struct Curl_easy *data,
                               struct contenc_writer *writer)
 {
-  struct zstd_params *zp = (struct zstd_params *)&writer->params;
+  struct zstd_writer *zp = (struct zstd_writer *) writer;
+
   (void)data;
 
   if(zp->decomp) {
@@ -794,7 +802,7 @@ static const struct content_encoding zstd_encoding = {
   zstd_init_writer,
   zstd_unencode_write,
   zstd_close_writer,
-  sizeof(struct zstd_params)
+  sizeof(struct zstd_writer)
 };
 #endif
 
@@ -827,7 +835,7 @@ static const struct content_encoding identity_encoding = {
   identity_init_writer,
   identity_unencode_write,
   identity_close_writer,
-  0
+  sizeof(struct contenc_writer)
 };
 
 
@@ -919,7 +927,7 @@ static const struct content_encoding client_encoding = {
   client_init_writer,
   client_unencode_write,
   client_close_writer,
-  0
+  sizeof(struct contenc_writer)
 };
 
 
@@ -962,7 +970,7 @@ static const struct content_encoding error_encoding = {
   error_init_writer,
   error_unencode_write,
   error_close_writer,
-  0
+  sizeof(struct contenc_writer)
 };
 
 /* Create an unencoding writer stage using the given handler. */
@@ -971,8 +979,10 @@ new_unencoding_writer(struct Curl_easy *data,
                       const struct content_encoding *handler,
                       struct contenc_writer *downstream)
 {
-  size_t sz = offsetof(struct contenc_writer, params) + handler->paramsize;
-  struct contenc_writer *writer = (struct contenc_writer *)calloc(1, sz);
+  struct contenc_writer *writer;
+
+  DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer));
+  writer = (struct contenc_writer *) calloc(1, handler->writersize);
 
   if(writer) {
     writer->handler = handler;
@@ -1026,19 +1036,23 @@ static const struct content_encoding *find_encoding(const char *name,
   return NULL;
 }
 
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
 /* 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)
 {
   struct SingleRequest *k = &data->req;
+  int counter = 0;
 
   do {
     const char *name;
     size_t namelen;
 
     /* Parse a single encoding name. */
-    while(ISSPACE(*enclist) || *enclist == ',')
+    while(ISBLANK(*enclist) || *enclist == ',')
       enclist++;
 
     name = enclist;
@@ -1066,6 +1080,11 @@ 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);
+        return CURLE_BAD_CONTENT_ENCODING;
+      }
       /* Stack the unencoding stage. */
       writer = new_unencoding_writer(data, encoding, k->writer_stack);
       if(!writer)
index acfd0c2..3c278cf 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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"
 
 struct contenc_writer {
   const struct content_encoding *handler;  /* Encoding handler. */
   struct contenc_writer *downstream;  /* Downstream writer. */
-  void *params;  /* Encoding-specific storage (variable length). */
 };
 
 /* Content encoding writer. */
@@ -40,7 +41,7 @@ struct content_encoding {
                              const char *buf, size_t nbytes);
   void (*close_writer)(struct Curl_easy *data,
                        struct contenc_writer *writer);
-  size_t paramsize;
+  size_t writersize;
 };
 
 
index 0c2d49b..8eaedee 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /***
@@ -33,8 +35,9 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
         called before any cookies are set.
 
 struct Cookie *Curl_cookie_add(struct Curl_easy *data,
-                 struct CookieInfo *c, bool httpheader, char *lineptr,
-                 const char *domain, const char *path);
+                 struct CookieInfo *c, bool httpheader, bool noexpire,
+                 char *lineptr, const char *domain, const char *path,
+                 bool secure);
 
         The 'lineptr' parameter is a full "Set-cookie:" line as
         received from a server.
@@ -96,8 +99,8 @@ Example set of cookies:
 #include "curl_get_line.h"
 #include "curl_memrchr.h"
 #include "parsedate.h"
-#include "rand.h"
 #include "rename.h"
+#include "fopen.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -439,6 +442,29 @@ static bool bad_domain(const char *domain)
 }
 
 /*
+  RFC 6265 section 4.1.1 says a server should accept this range:
+
+  cookie-octet    = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+
+  But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
+  fine. The prime reason for filtering out control bytes is that some HTTP
+  servers return 400 for requests that contain such.
+*/
+static int invalid_octets(const char *p)
+{
+  /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
+  static const char badoctets[] = {
+    "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
+    "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+    "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
+  };
+  size_t len;
+  /* scan for all the octets that are *not* in cookie-octet */
+  len = strcspn(p, badoctets);
+  return (p[len] != '\0');
+}
+
+/*
  * Curl_cookie_add
  *
  * Add a single cookie line to the cookie keeping object. Be aware that
@@ -468,6 +494,8 @@ Curl_cookie_add(struct Curl_easy *data,
   struct Cookie *clist;
   struct Cookie *co;
   struct Cookie *lastc = NULL;
+  struct Cookie *replace_co = NULL;
+  struct Cookie *replace_clist = NULL;
   time_t now = time(NULL);
   bool replace_old = FALSE;
   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
@@ -477,6 +505,10 @@ Curl_cookie_add(struct Curl_easy *data,
   (void)data;
 #endif
 
+  DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
+  if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
+    return NULL;
+
   /* First, alloc and init a new struct for it */
   co = calloc(1, sizeof(struct Cookie));
   if(!co)
@@ -505,7 +537,7 @@ Curl_cookie_add(struct Curl_easy *data,
     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 "[^;\r\n=] =%"
+      if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\t\r\n=] =%"
                      MAX_NAME_TXT "[^;\r\n]",
                      name, what)) {
         /*
@@ -559,6 +591,13 @@ Curl_cookie_add(struct Curl_easy *data,
         while(*whatptr && ISBLANK(*whatptr))
           whatptr++;
 
+        /* Reject cookies with a TAB inside the content */
+        if(strchr(whatptr, '\t')) {
+          freecookie(co);
+          infof(data, "cookie contains TAB, dropping");
+          return NULL;
+        }
+
         /*
          * Check if we have a reserved prefix set before anything else, as we
          * otherwise have to test for the prefix in both the cookie name and
@@ -586,6 +625,11 @@ Curl_cookie_add(struct Curl_easy *data,
             badcookie = TRUE;
             break;
           }
+          if(invalid_octets(whatptr) || invalid_octets(name)) {
+            infof(data, "invalid octets in name/value, cookie dropped");
+            badcookie = TRUE;
+            break;
+          }
         }
         else if(!len) {
           /*
@@ -628,7 +672,7 @@ Curl_cookie_add(struct Curl_easy *data,
             break;
           }
         }
-        else if(strcasecompare("domain", name)) {
+        else if(strcasecompare("domain", name) && whatptr[0]) {
           bool is_ip;
 
           /*
@@ -816,7 +860,7 @@ Curl_cookie_add(struct Curl_easy *data,
       freecookie(co);
       return NULL;
     }
-
+    data->req.setcookies++;
   }
   else {
     /*
@@ -1020,12 +1064,53 @@ Curl_cookie_add(struct Curl_easy *data,
   }
 #endif
 
+  /* A non-secure cookie may not overlay an existing secure cookie. */
   myhash = cookiehash(co->domain);
   clist = c->cookies[myhash];
-  replace_old = FALSE;
   while(clist) {
     if(strcasecompare(clist->name, co->name)) {
       /* the names are identical */
+      bool matching_domains = FALSE;
+
+      if(clist->domain && co->domain) {
+        if(strcasecompare(clist->domain, co->domain))
+          /* The domains are identical */
+          matching_domains = TRUE;
+      }
+      else if(!clist->domain && !co->domain)
+        matching_domains = TRUE;
+
+      if(matching_domains && /* the domains were identical */
+         clist->spath && co->spath && /* both have paths */
+         clist->secure && !co->secure && !secure) {
+        size_t cllen;
+        const char *sep;
+
+        /*
+         * A non-secure cookie may not overlay an existing secure cookie.
+         * For an existing cookie "a" with path "/login", refuse a new
+         * cookie "a" with for example path "/login/en", while the path
+         * "/loginhelper" is ok.
+         */
+
+        sep = strchr(clist->spath + 1, '/');
+
+        if(sep)
+          cllen = sep - clist->spath;
+        else
+          cllen = strlen(clist->spath);
+
+        if(strncasecompare(clist->spath, co->spath, cllen)) {
+          infof(data, "cookie '%s' for domain '%s' dropped, would "
+                "overlay an existing cookie", co->name, co->domain);
+          freecookie(co);
+          return NULL;
+        }
+      }
+    }
+
+    if(!replace_co && strcasecompare(clist->name, co->name)) {
+      /* the names are identical */
 
       if(clist->domain && co->domain) {
         if(strcasecompare(clist->domain, co->domain) &&
@@ -1040,30 +1125,7 @@ Curl_cookie_add(struct Curl_easy *data,
         /* the domains were identical */
 
         if(clist->spath && co->spath) {
-          if(clist->secure && !co->secure && !secure) {
-            size_t cllen;
-            const char *sep;
-
-            /*
-             * A non-secure cookie may not overlay an existing secure cookie.
-             * For an existing cookie "a" with path "/login", refuse a new
-             * cookie "a" with for example path "/login/en", while the path
-             * "/loginhelper" is ok.
-             */
-
-            sep = strchr(clist->spath + 1, '/');
-
-            if(sep)
-              cllen = sep - clist->spath;
-            else
-              cllen = strlen(clist->spath);
-
-            if(strncasecompare(clist->spath, co->spath, cllen)) {
-              freecookie(co);
-              return NULL;
-            }
-          }
-          else if(strcasecompare(clist->spath, co->spath))
+          if(strcasecompare(clist->spath, co->spath))
             replace_old = TRUE;
           else
             replace_old = FALSE;
@@ -1085,42 +1147,37 @@ Curl_cookie_add(struct Curl_easy *data,
         freecookie(co);
         return NULL;
       }
-
       if(replace_old) {
-        co->next = clist->next; /* get the next-pointer first */
-
-        /* when replacing, creationtime is kept from old */
-        co->creationtime = clist->creationtime;
-
-        /* then free all the old pointers */
-        free(clist->name);
-        free(clist->value);
-        free(clist->domain);
-        free(clist->path);
-        free(clist->spath);
-        free(clist->expirestr);
-        free(clist->version);
-        free(clist->maxage);
-
-        *clist = *co;  /* then store all the new data */
-
-        free(co);   /* free the newly allocated memory */
-        co = clist; /* point to the previous struct instead */
-
-        /*
-         * We have replaced a cookie, now skip the rest of the list but make
-         * sure the 'lastc' pointer is properly set
-         */
-        do {
-          lastc = clist;
-          clist = clist->next;
-        } while(clist);
-        break;
+        replace_co = co;
+        replace_clist = clist;
       }
     }
     lastc = clist;
     clist = clist->next;
   }
+  if(replace_co) {
+    co = replace_co;
+    clist = replace_clist;
+    co->next = clist->next; /* get the next-pointer first */
+
+    /* when replacing, creationtime is kept from old */
+    co->creationtime = clist->creationtime;
+
+    /* then free all the old pointers */
+    free(clist->name);
+    free(clist->value);
+    free(clist->domain);
+    free(clist->path);
+    free(clist->spath);
+    free(clist->expirestr);
+    free(clist->version);
+    free(clist->maxage);
+
+    *clist = *co;  /* then store all the new data */
+
+    free(co);   /* free the newly allocated memory */
+    co = clist;
+  }
 
   if(c->running)
     /* Only show this when NOT reading the cookies from a file */
@@ -1357,7 +1414,8 @@ static struct Cookie *dup_cookie(struct Cookie *src)
  *
  * It shall only return cookies that haven't expired.
  */
-struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+                                   struct CookieInfo *c,
                                    const char *host, const char *path,
                                    bool secure)
 {
@@ -1412,6 +1470,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
             mainco = newco;
 
             matches++;
+            if(matches >= MAX_COOKIE_SEND_AMOUNT) {
+              infof(data, "Included max number of cookies (%zu) in request!",
+                    matches);
+              break;
+            }
           }
           else
             goto fail;
@@ -1613,20 +1676,9 @@ static CURLcode cookie_output(struct Curl_easy *data,
     use_stdout = TRUE;
   }
   else {
-    unsigned char randsuffix[9];
-
-    if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
-      return 2;
-
-    tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
-    if(!tempstore)
-      return CURLE_OUT_OF_MEMORY;
-
-    out = fopen(tempstore, FOPEN_WRITETEXT);
-    if(!out) {
-      error = CURLE_WRITE_ERROR;
+    error = Curl_fopen(data, filename, &out, &tempstore);
+    if(error)
       goto error;
-    }
   }
 
   fputs("# Netscape HTTP Cookie File\n"
@@ -1673,7 +1725,7 @@ static CURLcode cookie_output(struct Curl_easy *data,
   if(!use_stdout) {
     fclose(out);
     out = NULL;
-    if(Curl_rename(tempstore, filename)) {
+    if(tempstore && Curl_rename(tempstore, filename)) {
       unlink(tempstore);
       error = CURLE_WRITE_ERROR;
       goto error;
index 0ffe08e..abc0a2e 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
@@ -81,10 +83,26 @@ struct CookieInfo {
 */
 #define MAX_COOKIE_LINE 5000
 
-/* This is the maximum length of a cookie name or content we deal with: */
+/* Maximum length of an incoming cookie name or content we deal with. Longer
+   cookies are ignored. */
 #define MAX_NAME 4096
 #define MAX_NAME_TXT "4095"
 
+/* Maximum size for an outgoing cookie line libcurl will use in an http
+   request. This is the default maximum length used in some versions of Apache
+   httpd. */
+#define MAX_COOKIE_HEADER_LEN 8190
+
+/* Maximum number of cookies libcurl will send in a single request, even if
+   there might be more cookies that match. One reason to cap the number is to
+   keep the maximum HTTP request within the maximum allowed size. */
+#define MAX_COOKIE_SEND_AMOUNT 150
+
+/* Maximum number of Set-Cookie: lines accepted in a single response. If more
+   such header lines are received, they are ignored. This value must be less
+   than 256 since an unsigned char is used to count. */
+#define MAX_SET_COOKIE_AMOUNT 50
+
 struct Curl_easy;
 /*
  * Add a cookie to the internal list of cookies. The domain and path arguments
@@ -97,7 +115,8 @@ struct Cookie *Curl_cookie_add(struct Curl_easy *data,
                                const char *domain, const char *path,
                                bool secure);
 
-struct Cookie *Curl_cookie_getlist(struct CookieInfo *c, const char *host,
+struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
+                                   struct CookieInfo *c, const char *host,
                                    const char *path, bool secure);
 void Curl_cookie_freelist(struct Cookie *cookies);
 void Curl_cookie_clearall(struct CookieInfo *cookies);
index 842fd7f..72e778b 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -277,7 +279,7 @@ Curl_he2ai(const struct hostent *he, int port)
 
   for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) {
     size_t ss_size;
-    size_t namelen = strlen(he->h_name) + 1; /* include zero termination */
+    size_t namelen = strlen(he->h_name) + 1; /* include null-terminatior */
 #ifdef ENABLE_IPV6
     if(he->h_addrtype == AF_INET6)
       ss_size = sizeof(struct sockaddr_in6);
index 73a8c1b..b778121 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 4cb9d73..85368a1 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
index 4a6f90e..b8a58c8 100644 (file)
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /* lib/curl_config.h.in.  Generated somehow by cmake.  */
 
 #include <cm3p/kwiml/abi.h>
 
-/* when building libcurl itself */
-#cmakedefine BUILDING_LIBCURL 1
-
-/* to disable alt-svc */
+/* disables alt-svc */
 #cmakedefine CURL_DISABLE_ALTSVC 1
 
 /* disables cookies support */
 /* Define to 1 if you have the alarm function. */
 #cmakedefine HAVE_ALARM 1
 
-/* Define to 1 if you have the <alloca.h> header file. */
-#cmakedefine HAVE_ALLOCA_H 1
-
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #cmakedefine HAVE_ARPA_INET_H 1
 
 /* Define to 1 if you have the <assert.h> header file. */
 #cmakedefine HAVE_ASSERT_H 1
 
+/* Define to 1 if you have _Atomic support. */
+#cmakedefine HAVE_ATOMIC 1
+
+/* Define to 1 if you have the `fchmod' function. */
+#cmakedefine HAVE_FCHMOD 1
+
 /* Define to 1 if you have the `basename' function. */
 #cmakedefine HAVE_BASENAME 1
 
 /* Define to 1 if you have the `closesocket' function. */
 #cmakedefine HAVE_CLOSESOCKET 1
 
-/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
-#cmakedefine HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#cmakedefine HAVE_DLFCN_H 1
-
 /* Define to 1 if you have the <errno.h> header file. */
 #cmakedefine HAVE_ERRNO_H 1
 
 /* Define to 1 if you have a working getaddrinfo function. */
 #cmakedefine HAVE_GETADDRINFO 1
 
+/* Define to 1 if the getaddrinfo function is threadsafe. */
+#cmakedefine HAVE_GETADDRINFO_THREADSAFE 1
+
 /* Define to 1 if you have the `geteuid' function. */
 #cmakedefine HAVE_GETEUID 1
 
 /* Define to 1 if you have the `getppid' function. */
 #cmakedefine HAVE_GETPPID 1
 
-/* Define to 1 if you have the gethostbyname function. */
-#cmakedefine HAVE_GETHOSTBYNAME 1
-
 /* Define to 1 if you have the gethostbyname_r function. */
 #cmakedefine HAVE_GETHOSTBYNAME_R 1
 
 /* Define to 1 if you have the `getppid' function. */
 #cmakedefine HAVE_GETPPID 1
 
-/* Define to 1 if you have the `getprotobyname' function. */
-#cmakedefine HAVE_GETPROTOBYNAME 1
-
 /* Define to 1 if you have the `getpeername' function. */
 #cmakedefine HAVE_GETPEERNAME 1
 
 /* Define to 1 if you have the `idna_strerror' function. */
 #cmakedefine HAVE_IDNA_STRERROR 1
 
-/* Define to 1 if you have the `idn_free' function. */
-#cmakedefine HAVE_IDN_FREE 1
-
-/* Define to 1 if you have the <idn-free.h> header file. */
-#cmakedefine HAVE_IDN_FREE_H 1
-
 /* Define to 1 if you have the <ifaddrs.h> header file. */
 #cmakedefine HAVE_IFADDRS_H 1
 
-/* Define to 1 if you have the `inet_addr' function. */
-#cmakedefine HAVE_INET_ADDR 1
-
 /* Define to 1 if you have a IPv6 capable working inet_ntop function. */
 #cmakedefine HAVE_INET_NTOP 1
 
 /* Define to 1 if you have the <inttypes.h> header file. */
 #cmakedefine HAVE_INTTYPES_H 1
 
-/* Define to 1 if you have the ioctl function. */
-#cmakedefine HAVE_IOCTL 1
-
 /* Define to 1 if you have the ioctlsocket function. */
 #cmakedefine HAVE_IOCTLSOCKET 1
 
 /* Define to 1 if you have the <io.h> header file. */
 #cmakedefine HAVE_IO_H 1
 
-/* if you have the Kerberos4 libraries (including -ldes) */
-#cmakedefine HAVE_KRB4 1
-
-/* Define to 1 if you have the `krb_get_our_ip_for_realm' function. */
-#cmakedefine HAVE_KRB_GET_OUR_IP_FOR_REALM 1
-
-/* Define to 1 if you have the <krb.h> header file. */
-#cmakedefine HAVE_KRB_H 1
-
 /* Define to 1 if you have the lber.h header file. */
 #cmakedefine HAVE_LBER_H 1
 
-/* Define to 1 if you have the ldapssl.h header file. */
-#cmakedefine HAVE_LDAPSSL_H 1
-
 /* Define to 1 if you have the ldap.h header file. */
 #cmakedefine HAVE_LDAP_H 1
 
 /* Define to 1 if you have the idn2.h header file. */
 #cmakedefine HAVE_IDN2_H 1
 
-/* Define to 1 if you have the `resolv' library (-lresolv). */
-#cmakedefine HAVE_LIBRESOLV 1
-
-/* Define to 1 if you have the `resolve' library (-lresolve). */
-#cmakedefine HAVE_LIBRESOLVE 1
-
 /* Define to 1 if you have the `socket' library (-lsocket). */
 #cmakedefine HAVE_LIBSOCKET 1
 
 /* Define to 1 if you have the `ssh2' library (-lssh2). */
 #cmakedefine HAVE_LIBSSH2 1
 
-/* Define to 1 if you have the <libssh2.h> header file. */
-#cmakedefine HAVE_LIBSSH2_H 1
-
-/* Define to 1 if you have the <libssh/libssh.h> header file. */
-#cmakedefine HAVE_LIBSSH_LIBSSH_H 1
-
 /* if zlib is available */
 #cmakedefine HAVE_LIBZ 1
 
 /* Define to 1 if you have the <locale.h> header file. */
 #cmakedefine HAVE_LOCALE_H 1
 
-/* Define to 1 if you have a working localtime_r function. */
-#cmakedefine HAVE_LOCALTIME_R 1
-
 /* Define to 1 if the compiler supports the 'long long' data type. */
 #if KWIML_ABI_SIZEOF_LONG_LONG
 #  define HAVE_LONGLONG 1
 #endif
 
-/* Define to 1 if you have the malloc.h header file. */
-#cmakedefine HAVE_MALLOC_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#cmakedefine HAVE_MEMORY_H 1
-
 /* Define to 1 if you have the MSG_NOSIGNAL flag. */
 #cmakedefine HAVE_MSG_NOSIGNAL 1
 
 /* Define to 1 if you have the <net/if.h> header file. */
 #cmakedefine HAVE_NET_IF_H 1
 
-/* Define to 1 if NI_WITHSCOPEID exists and works. */
-#cmakedefine HAVE_NI_WITHSCOPEID 1
-
 /* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */
 #cmakedefine HAVE_OLD_GSSMIT 1
 
-/* Define to 1 if you have the <openssl/crypto.h> header file. */
-#cmakedefine HAVE_OPENSSL_CRYPTO_H 1
-
-/* Define to 1 if you have the <openssl/err.h> header file. */
-#cmakedefine HAVE_OPENSSL_ERR_H 1
-
-/* Define to 1 if you have the <openssl/pem.h> header file. */
-#cmakedefine HAVE_OPENSSL_PEM_H 1
-
-/* Define to 1 if you have the <openssl/pkcs12.h> header file. */
-#cmakedefine HAVE_OPENSSL_PKCS12_H 1
-
-/* Define to 1 if you have the <openssl/rsa.h> header file. */
-#cmakedefine HAVE_OPENSSL_RSA_H 1
-
-/* Define to 1 if you have the <openssl/ssl.h> header file. */
-#cmakedefine HAVE_OPENSSL_SSL_H 1
-
-/* Define to 1 if you have the <openssl/x509.h> header file. */
-#cmakedefine HAVE_OPENSSL_X509_H 1
-
-/* Define to 1 if you have the <pem.h> header file. */
-#cmakedefine HAVE_PEM_H 1
-
 /* Define to 1 if you have the `pipe' function. */
 #cmakedefine HAVE_PIPE 1
 
-/* Define to 1 if you have a working poll function. */
-#cmakedefine HAVE_POLL 1
-
 /* If you have a fine poll */
 #cmakedefine HAVE_POLL_FINE 1
 
 /* Define to 1 if you have the `RAND_egd' function. */
 #cmakedefine HAVE_RAND_EGD 1
 
-/* Define to 1 if you have the `RAND_screen' function. */
-#cmakedefine HAVE_RAND_SCREEN 1
-
-/* Define to 1 if you have the `RAND_status' function. */
-#cmakedefine HAVE_RAND_STATUS 1
-
 /* Define to 1 if you have the recv function. */
 #cmakedefine HAVE_RECV 1
 
-/* Define to 1 if you have the recvfrom function. */
-#cmakedefine HAVE_RECVFROM 1
-
 /* Define to 1 if you have the select function. */
 #cmakedefine HAVE_SELECT 1
 
 /* Define to 1 if you have the `setrlimit' function. */
 #cmakedefine HAVE_SETRLIMIT 1
 
-/* Define to 1 if you have the setsockopt function. */
-#cmakedefine HAVE_SETSOCKOPT 1
-
 /* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */
 #cmakedefine HAVE_SETSOCKOPT_SO_NONBLOCK 1
 
 /* Define to 1 if you have the `socket' function. */
 #cmakedefine HAVE_SOCKET 1
 
+/* Define to 1 if you have the socketpair function. */
+#cmakedefine HAVE_SOCKETPAIR 1
+
 /* Define to 1 if you have the <ssl.h> header file. */
 #cmakedefine HAVE_SSL_H 1
 
+/* Define to 1 if you have the <stdatomic.h> header file. */
+#cmakedefine HAVE_STDATOMIC_H 1
+
 /* Define to 1 if you have the <stdbool.h> header file. */
 #cmakedefine HAVE_STDBOOL_H 1
 
 /* Define to 1 if you have the <stdint.h> header file. */
 #cmakedefine HAVE_STDINT_H 1
 
-/* Define to 1 if you have the <stdio.h> header file. */
-#cmakedefine HAVE_STDIO_H 1
-
 /* Define to 1 if you have the <stdlib.h> header file. */
 #cmakedefine HAVE_STDLIB_H 1
 
 /* Define to 1 if you have the strcasecmp function. */
 #cmakedefine HAVE_STRCASECMP 1
 
-/* Define to 1 if you have the strcasestr function. */
-#cmakedefine HAVE_STRCASESTR 1
-
 /* Define to 1 if you have the strcmpi function. */
 #cmakedefine HAVE_STRCMPI 1
 
 /* Define to 1 if you have the <string.h> header file. */
 #cmakedefine HAVE_STRING_H 1
 
-/* Define to 1 if you have the strncmpi function. */
-#cmakedefine HAVE_STRNCMPI 1
-
-/* Define to 1 if you have the strnicmp function. */
-#cmakedefine HAVE_STRNICMP 1
-
 /* Define to 1 if you have the <stropts.h> header file. */
 #cmakedefine HAVE_STROPTS_H 1
 
-/* Define to 1 if you have the strstr function. */
-#cmakedefine HAVE_STRSTR 1
-
 /* Define to 1 if you have the strtok_r function. */
 #cmakedefine HAVE_STRTOK_R 1
 
 /* Define to 1 if you have the <sys/types.h> header file. */
 #cmakedefine HAVE_SYS_TYPES_H 1
 
-/* Define to 1 if you have the <sys/uio.h> header file. */
-#cmakedefine HAVE_SYS_UIO_H 1
-
 /* Define to 1 if you have the <sys/un.h> header file. */
 #cmakedefine HAVE_SYS_UN_H 1
 
 /* Define to 1 if you have the <time.h> header file. */
 #cmakedefine HAVE_TIME_H 1
 
-/* Define to 1 if you have the <tld.h> header file. */
-#cmakedefine HAVE_TLD_H 1
-
-/* Define to 1 if you have the `tld_strerror' function. */
-#cmakedefine HAVE_TLD_STRERROR 1
-
-/* Define to 1 if you have the `uname' function. */
-#cmakedefine HAVE_UNAME 1
-
 /* Define to 1 if you have the <unistd.h> header file. */
 #cmakedefine HAVE_UNISTD_H 1
 
 /* Define to 1 if compiler supports old gcc variadic macro style. */
 #cmakedefine HAVE_VARIADIC_MACROS_GCC 1
 
-/* Define to 1 if you have the winber.h header file. */
-#cmakedefine HAVE_WINBER_H 1
-
 /* Define to 1 if you have the windows.h header file. */
 #cmakedefine HAVE_WINDOWS_H 1
 
 /* Define this symbol if your OS supports changing the contents of argv */
 #cmakedefine HAVE_WRITABLE_ARGV 1
 
-/* Define to 1 if you have the writev function. */
-#cmakedefine HAVE_WRITEV 1
-
 /* Define to 1 if you have the ws2tcpip.h header file. */
 #cmakedefine HAVE_WS2TCPIP_H 1
 
-/* Define to 1 if you have the <x509.h> header file. */
-#cmakedefine HAVE_X509_H 1
-
 /* Define if you have the <process.h> header file. */
 #cmakedefine HAVE_PROCESS_H 1
 
-/* if you have the zlib.h header file */
-#cmakedefine HAVE_ZLIB_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#cmakedefine LT_OBJDIR ${LT_OBJDIR}
-
-/* If you lack a fine basename() prototype */
-#cmakedefine NEED_BASENAME_PROTO 1
-
 /* Define to 1 if you need the lber.h header file even with ldap.h */
 #cmakedefine NEED_LBER_H 1
 
 /* a suitable file to read random data from */
 #cmakedefine RANDOM_FILE "${RANDOM_FILE}"
 
-/* Define to the type of arg 1 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG1 ${RECVFROM_TYPE_ARG1}
-
-/* Define to the type pointed by arg 2 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG2 ${RECVFROM_TYPE_ARG2}
-
-/* Define to 1 if the type pointed by arg 2 for recvfrom is void. */
-#cmakedefine RECVFROM_TYPE_ARG2_IS_VOID 1
-
-/* Define to the type of arg 3 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG3 ${RECVFROM_TYPE_ARG3}
-
-/* Define to the type of arg 4 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG4 ${RECVFROM_TYPE_ARG4}
-
-/* Define to the type pointed by arg 5 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG5 ${RECVFROM_TYPE_ARG5}
-
-/* Define to 1 if the type pointed by arg 5 for recvfrom is void. */
-#cmakedefine RECVFROM_TYPE_ARG5_IS_VOID 1
-
-/* Define to the type pointed by arg 6 for recvfrom. */
-#cmakedefine RECVFROM_TYPE_ARG6 ${RECVFROM_TYPE_ARG6}
-
-/* Define to 1 if the type pointed by arg 6 for recvfrom is void. */
-#cmakedefine RECVFROM_TYPE_ARG6_IS_VOID 1
-
-/* Define to the function return type for recvfrom. */
-#cmakedefine RECVFROM_TYPE_RETV ${RECVFROM_TYPE_RETV}
-
-/* Define to the type of arg 1 for recv. */
-#cmakedefine RECV_TYPE_ARG1 ${RECV_TYPE_ARG1}
-
-/* Define to the type of arg 2 for recv. */
-#cmakedefine RECV_TYPE_ARG2 ${RECV_TYPE_ARG2}
-
-/* Define to the type of arg 3 for recv. */
-#cmakedefine RECV_TYPE_ARG3 ${RECV_TYPE_ARG3}
-
-/* Define to the type of arg 4 for recv. */
-#cmakedefine RECV_TYPE_ARG4 ${RECV_TYPE_ARG4}
-
-/* Define to the function return type for recv. */
-#cmakedefine RECV_TYPE_RETV ${RECV_TYPE_RETV}
-
-/* Define to the type qualifier of arg 5 for select. */
-#cmakedefine SELECT_QUAL_ARG5 ${SELECT_QUAL_ARG5}
-
-/* Define to the type of arg 1 for select. */
-#cmakedefine SELECT_TYPE_ARG1 ${SELECT_TYPE_ARG1}
-
-/* Define to the type of args 2, 3 and 4 for select. */
-#cmakedefine SELECT_TYPE_ARG234 ${SELECT_TYPE_ARG234}
-
-/* Define to the type of arg 5 for select. */
-#cmakedefine SELECT_TYPE_ARG5 ${SELECT_TYPE_ARG5}
-
-/* Define to the function return type for select. */
-#cmakedefine SELECT_TYPE_RETV ${SELECT_TYPE_RETV}
-
-/* Define to the type qualifier of arg 2 for send. */
-#cmakedefine SEND_QUAL_ARG2 ${SEND_QUAL_ARG2}
-
-/* Define to the type of arg 1 for send. */
-#cmakedefine SEND_TYPE_ARG1 ${SEND_TYPE_ARG1}
-
-/* Define to the type of arg 2 for send. */
-#cmakedefine SEND_TYPE_ARG2 ${SEND_TYPE_ARG2}
-
-/* Define to the type of arg 3 for send. */
-#cmakedefine SEND_TYPE_ARG3 ${SEND_TYPE_ARG3}
-
-/* Define to the type of arg 4 for send. */
-#cmakedefine SEND_TYPE_ARG4 ${SEND_TYPE_ARG4}
-
-/* Define to the function return type for send. */
-#cmakedefine SEND_TYPE_RETV ${SEND_TYPE_RETV}
-
 /*
  Note: SIZEOF_* variables are fetched with CMake through check_type_size().
  As per CMake documentation on CheckTypeSize, C preprocessor code is
@@ -882,9 +670,6 @@ ${SIZEOF_TIME_T_CODE}
 /* Define to 1 if you have the ANSI C header files. */
 #cmakedefine STDC_HEADERS 1
 
-/* Define to the type of arg 3 for strerror_r. */
-#cmakedefine STRERROR_R_TYPE_ARG3 ${STRERROR_R_TYPE_ARG3}
-
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #cmakedefine TIME_WITH_SYS_TIME 1
 
@@ -918,6 +703,9 @@ ${SIZEOF_TIME_T_CODE}
 /* if libSSH2 is in use */
 #cmakedefine USE_LIBSSH2 1
 
+/* if libPSL is in use */
+#cmakedefine USE_LIBPSL 1
+
 /* If you want to build curl with the built-in manual */
 #cmakedefine USE_MANUAL 1
 
@@ -967,9 +755,6 @@ ${SIZEOF_TIME_T_CODE}
 /* enable multiple SSL backends */
 #cmakedefine CURL_WITH_MULTI_SSL 1
 
-/* Define to 1 if using yaSSL in OpenSSL compatibility mode. */
-#cmakedefine USE_YASSLEMUL 1
-
 /* Version number of package */
 #cmakedefine VERSION ${VERSION}
 
@@ -1023,3 +808,6 @@ ${SIZEOF_TIME_T_CODE}
 
 /* to make the compiler know the prototypes of Windows IDN APIs */
 #cmakedefine WANT_IDN_PROTOTYPES 1
+
+/* Define to 1 to enable websocket support. */
+#cmakedefine USE_WEBSOCKETS 1
diff --git a/Utilities/cmcurl/lib/curl_ctype.c b/Utilities/cmcurl/lib/curl_ctype.c
deleted file mode 100644 (file)
index 233a69e..0000000
+++ /dev/null
@@ -1,130 +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.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#undef _U
-#define _U (1<<0) /* upper case */
-#undef _L
-#define _L (1<<1) /* lower case */
-#undef _N
-#define _N (1<<2) /* decimal numerical digit */
-#undef _S
-#define _S (1<<3) /* space */
-#undef _P
-#define _P (1<<4) /* punctuation */
-#undef _C
-#define _C (1<<5) /* control */
-#undef _X
-#define _X (1<<6) /* hexadecimal letter */
-#undef _B
-#define _B (1<<7) /* blank */
-
-static const unsigned char ascii[128] = {
-  _C,   _C,     _C,     _C,     _C,     _C,     _C,     _C,
-  _C,   _C|_S,  _C|_S,  _C|_S,  _C|_S,  _C|_S,  _C,     _C,
-  _C,   _C,     _C,     _C,     _C,     _C,     _C,     _C,
-  _C,   _C,     _C,     _C,     _C,     _C,     _C,     _C,
-  _S|_B, _P,    _P,     _P,     _P,     _P,     _P,     _P,
-  _P,   _P,     _P,     _P,     _P,     _P,     _P,     _P,
-  _N,   _N,     _N,     _N,     _N,     _N,     _N,     _N,
-  _N,   _N,     _P,     _P,     _P,     _P,     _P,     _P,
-  _P,   _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U|_X,  _U,
-  _U,   _U,     _U,     _U,     _U,     _U,     _U,     _U,
-  _U,   _U,     _U,     _U,     _U,     _U,     _U,     _U,
-  _U,   _U,     _U,     _P,     _P,     _P,     _P,     _P,
-  _P,   _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L|_X,  _L,
-  _L,   _L,     _L,     _L,     _L,     _L,     _L,     _L,
-  _L,   _L,     _L,     _L,     _L,     _L,     _L,     _L,
-  _L,   _L,     _L,     _P,     _P,     _P,     _P,     _C
-};
-
-int Curl_isspace(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & _S);
-}
-
-int Curl_isdigit(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & _N);
-}
-
-int Curl_isalnum(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_N|_U|_L));
-}
-
-int Curl_isxdigit(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_N|_X));
-}
-
-int Curl_isgraph(int c)
-{
-  if((c < 0) || (c >= 0x80) || (c == ' '))
-    return FALSE;
-  return (ascii[c] & (_N|_X|_U|_L|_P|_S));
-}
-
-int Curl_isprint(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_N|_X|_U|_L|_P|_S));
-}
-
-int Curl_isalpha(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_U|_L));
-}
-
-int Curl_isupper(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_U));
-}
-
-int Curl_islower(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_L));
-}
-
-int Curl_iscntrl(int c)
-{
-  if((c < 0) || (c >= 0x80))
-    return FALSE;
-  return (ascii[c] & (_C));
-}
-
index 2fa749d..dc6b8ca 100644 (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"
+#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f'))
+#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F'))
 
-int Curl_isspace(int c);
-int Curl_isdigit(int c);
-int Curl_isalnum(int c);
-int Curl_isxdigit(int c);
-int Curl_isgraph(int c);
-int Curl_isprint(int c);
-int Curl_isalpha(int c);
-int Curl_isupper(int c);
-int Curl_islower(int c);
-int Curl_iscntrl(int c);
+#define ISLOWCNTRL(x) ((x) >= 0 && ((x) <= 0x1f))
+#define IS7F(x) ((x) == 0x7f)
 
-#define ISSPACE(x)  (Curl_isspace((int)  ((unsigned char)x)))
-#define ISDIGIT(x)  (Curl_isdigit((int)  ((unsigned char)x)))
-#define ISALNUM(x)  (Curl_isalnum((int)  ((unsigned char)x)))
-#define ISXDIGIT(x) (Curl_isxdigit((int) ((unsigned char)x)))
-#define ISGRAPH(x)  (Curl_isgraph((int)  ((unsigned char)x)))
-#define ISALPHA(x)  (Curl_isalpha((int)  ((unsigned char)x)))
-#define ISPRINT(x)  (Curl_isprint((int)  ((unsigned char)x)))
-#define ISUPPER(x)  (Curl_isupper((int)  ((unsigned char)x)))
-#define ISLOWER(x)  (Curl_islower((int)  ((unsigned char)x)))
-#define ISCNTRL(x)  (Curl_iscntrl((int)  ((unsigned char)x)))
-#define ISASCII(x)  (((x) >= 0) && ((x) <= 0x80))
+#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d))
 
-#define ISBLANK(x)  (int)((((unsigned char)x) == ' ') ||        \
-                          (((unsigned char)x) == '\t'))
+#define ISPRINT(x)  (ISLOWPRINT(x) || (((x) >= ' ') && ((x) <= 0x7e)))
+#define ISGRAPH(x)  (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e)))
+#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x))
+#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
+#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x))
+#define ISALNUM(x)  (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x))
+#define ISUPPER(x)  (((x) >= 'A') && ((x) <= 'Z'))
+#define ISLOWER(x)  (((x) >= 'a') && ((x) <= 'z'))
+#define ISDIGIT(x)  (((x) >= '0') && ((x) <= '9'))
+#define ISBLANK(x)  (((x) == ' ') || ((x) == '\t'))
+#define ISSPACE(x)  (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d)))
 
 #endif /* HEADER_CURL_CTYPE_H */
index 5f28ef4..a2bf648 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -39,7 +41,7 @@
  *
  * The function is a port of the Java based oddParity() function over at:
  *
- * https://davenport.sourceforge.io/ntlm.html
+ * https://davenport.sourceforge.net/ntlm.html
  *
  * Parameters:
  *
index 3d0fd92..c1c1674 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index ecde74b..3cc7734 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 4e12d7d..758d55f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* Converts a 16-bit integer from little endian */
index 4bfa585..0dd1eb5 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 1c80ea7..8324be5 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #define CURL_FNMATCH_MATCH    0
index 8f3b0bd..22e3705 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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_COOKIES) || !defined(CURL_DISABLE_ALTSVC) ||  \
-  !defined(CURL_DISABLE_HSTS)
+  !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC)
 
 #include "curl_get_line.h"
 #include "curl_memory.h"
@@ -31,8 +33,8 @@
 #include "memdebug.h"
 
 /*
- * get_line() makes sure to only return complete whole lines that fit in 'len'
- * bytes and end with a newline.
+ * Curl_get_line() makes sure to only return complete whole lines that fit in
+ * 'len' bytes and end with a newline.
  */
 char *Curl_get_line(char *buf, int len, FILE *input)
 {
index 597aa09..b2a534d 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* get_line() makes sure to only return complete whole lines that fit in 'len'
index 2d5ff61..4747e93 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 2161c40..b736096 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* Hostname buffer size */
index 1543a0f..01ab48e 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -89,7 +91,7 @@ static size_t display_gss_error(OM_uint32 status, int type,
   OM_uint32 maj_stat;
   OM_uint32 min_stat;
   OM_uint32 msg_ctx = 0;
-  gss_buffer_desc status_string;
+  gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
 
   do {
     maj_stat = gss_display_status(&min_stat,
@@ -98,10 +100,12 @@ static size_t display_gss_error(OM_uint32 status, int type,
                                   GSS_C_NO_OID,
                                   &msg_ctx,
                                   &status_string);
-    if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
-      len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
-                       "%.*s. ", (int)status_string.length,
-                       (char *)status_string.value);
+    if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
+      if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
+        len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
+                         "%.*s. ", (int)status_string.length,
+                         (char *)status_string.value);
+      }
     }
     gss_release_buffer(&min_stat, &status_string);
   } while(!GSS_ERROR(maj_stat) && msg_ctx);
index 466d09e..b4ed482 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2011 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2011 - 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
@@ -20,6 +20,8 @@
  * 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"
index 5755655..36c0bd6 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_CRYPTO_AUTH
 
+#include <curl/curl.h>
+
 #define HMAC_MD5_LENGTH 16
 
 typedef CURLcode (* HMAC_hinit_func)(void *context);
index ca06840..ccd6f10 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 struct Curl_sec_client_mech {
index 124e18b..ba3ede4 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifndef CURL_DISABLE_LDAP
 extern const struct Curl_handler Curl_handler_ldap;
index f9dafcb..8049355 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index b7d7c1f..7893296 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_CRYPTO_AUTH
index 5806290..092fc9f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 0bd845f..c329a61 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index c8394bb..e7654e1 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 32c03a5..309dccb 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 491155e..9297148 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index f3b8b13..38e193c 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -27,7 +29,7 @@
 /*
  * NTLM details:
  *
- * https://davenport.sourceforge.io/ntlm.html
+ * https://davenport.sourceforge.net/ntlm.html
  * https://www.innovation.ch/java/ntlm.html
  */
 
 
 #if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
 
-#ifdef USE_WOLFSSL
-#include <wolfssl/options.h>
-#endif
-
+#if defined(USE_OPENSSL)
 #  include <openssl/des.h>
 #  include <openssl/md5.h>
 #  include <openssl/ssl.h>
 #  include <openssl/rand.h>
+#else
+#  include <wolfssl/options.h>
+#  include <wolfssl/openssl/des.h>
+#  include <wolfssl/openssl/md5.h>
+#  include <wolfssl/openssl/ssl.h>
+#  include <wolfssl/openssl/rand.h>
+#endif
+
 #  if (defined(OPENSSL_VERSION_NUMBER) && \
        (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL)
 #    define DES_key_schedule des_key_schedule
index 5e52bb2..60444c9 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
 #define NTLM_NEEDS_NSS_INIT
 #endif
 
-#if defined(USE_OPENSSL) || defined(USE_WOLFSSL)
 #ifdef USE_WOLFSSL
 #  include <wolfssl/options.h>
-#endif
+#  include <wolfssl/openssl/ssl.h>
+#elif defined(USE_OPENSSL)
 #  include <openssl/ssl.h>
 #endif
 
index 5a3bc3c..33dcf0c 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -28,7 +30,7 @@
 /*
  * NTLM details:
  *
- * https://davenport.sourceforge.io/ntlm.html
+ * https://davenport.sourceforge.net/ntlm.html
  * https://www.innovation.ch/java/ntlm.html
  */
 
index 961b568..1f04db8 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index a1669d1..b55e830 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl AND ISC
+ *
  ***************************************************************************/
 
 #include "curl_setup.h"
@@ -120,7 +122,8 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
   bool relativePath = false;
   static const char WHITESPACE[] = " \t\r\n";
 
-  if(!*cp) {
+  DEBUGASSERT(homedir);
+  if(!*cp || !homedir) {
     *cpp = NULL;
     *path = NULL;
     return CURLE_QUOTE_ERROR;
index a376bd1..98e56ea 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 9fa625f..3823828 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 24bdb30..dd92d05 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -45,7 +47,7 @@ CURLcode Curl_range(struct Curl_easy *data)
     from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from);
     if(from_t == CURL_OFFT_FLOW)
       return CURLE_RANGE_ERROR;
-    while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
+    while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
       ptr++;
     to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
     if(to_t == CURL_OFFT_FLOW)
index 0a07baf..33570ab 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 2fa0267..b0c3710 100644 (file)
@@ -5,8 +5,8 @@
  *                | (__| |_| |  _ <| |___
  *                 \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, 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
@@ -19,6 +19,8 @@
  * 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"
index f45fa71..f856085 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2010 - 2020, Howard Chu, <hyc@highlandsun.com>
+ * Copyright (C) 2010 - 2022, 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifdef USE_LIBRTMP
 extern const struct Curl_handler Curl_handler_rtmp;
index 48d6625..9684ee4 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2195 CRAM-MD5 authentication
  * RFC2617 Basic and Digest Access Authentication
  * RFC2831 DIGEST-MD5 authentication
index d377ae7..c709d56 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
index 3dd5950..c3ad25f 100644 (file)
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #if defined(BUILDING_LIBCURL) && !defined(CURL_NO_OLDIES)
 #define CURL_NO_OLDIES
 #endif
 
+/* define mingw version macros, eg __MINGW{32,64}_{MINOR,MAJOR}_VERSION */
+#ifdef __MINGW32__
+#include <_mingw.h>
+#endif
+
 /*
  * Disable Visual Studio warnings:
  * 4127 "conditional expression is constant"
 /*  please, do it beyond the point further indicated in this file.  */
 /* ================================================================ */
 
-#include <curl/curl.h>
-
 /*
  * Disable other protocols when http is the only one desired.
  */
 
 /* ================================================================ */
 /* No system header file shall be included in this file before this */
-/* point. The only allowed ones are those included from curl/system.h */
+/* point.                                                           */
 /* ================================================================ */
 
 /*
 #  include "setup-win32.h"
 #endif
 
+#include <curl/system.h>
+
 /*
  * Use getaddrinfo to resolve the IPv4 address literal. If the current network
  * interface doesn't support IPv4, but supports IPv6, NAT64, and DNS64,
 #endif
 
 #ifdef __AMIGA__
+#  ifdef __amigaos4__
+#    define __USE_INLINE__
+     /* use our own resolver which uses runtime feature detection */
+#    define CURLRES_AMIGA
+     /* getaddrinfo() currently crashes bsdsocket.library, so disable */
+#    undef HAVE_GETADDRINFO
+#    if !(defined(__NEWLIB__) || \
+          (defined(__CLIB2__) && defined(__THREAD_SAFE)))
+       /* disable threaded resolver with clib2 - requires newlib or clib-ts */
+#      undef USE_THREADS_POSIX
+#    endif
+#  endif
 #  include <exec/types.h>
 #  include <exec/execbase.h>
 #  include <proto/exec.h>
 #  include <proto/dos.h>
 #  include <unistd.h>
-#  ifdef HAVE_PROTO_BSDSOCKET_H
-#    include <proto/bsdsocket.h> /* ensure bsdsocket.library use */
-#    define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0)
+#  if defined(HAVE_PROTO_BSDSOCKET_H) && \
+    (!defined(__amigaos4__) || defined(USE_AMISSL))
+     /* use bsdsocket.library directly, instead of libc networking functions */
+#    include <proto/bsdsocket.h>
+#    ifdef __amigaos4__
+       int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
+                             fd_set *errorfds, struct timeval *timeout);
+#      define select(a,b,c,d,e) Curl_amiga_select(a,b,c,d,e)
+#    else
+#      define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0)
+#    endif
+     /* must not use libc's fcntl() on bsdsocket.library sockfds! */
+#    undef HAVE_FCNTL
+#    undef HAVE_FCNTL_O_NONBLOCK
+#  else
+     /* use libc networking and hence close() and fnctl() */
+#    undef HAVE_CLOSESOCKET_CAMEL
+#    undef HAVE_IOCTLSOCKET_CAMEL
 #  endif
 /*
  * In clib2 arpa/inet.h warns that some prototypes may clash
 #include <assert.h>
 #endif
 
-#ifdef __TANDEM /* for nsr-tandem-nsk systems */
-#include <floss.h>
+#ifdef __TANDEM /* for ns*-tandem-nsk systems */
+# if ! defined __LP64
+#  include <floss.h> /* FLOSS is only used for 32-bit builds. */
+# endif
 #endif
 
 #ifndef STDC_HEADERS /* no standard C headers! */
 /* now undef the stock libc functions just to avoid them being used */
 #  undef HAVE_GETADDRINFO
 #  undef HAVE_FREEADDRINFO
-#  undef HAVE_GETHOSTBYNAME
 #elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
 #  define CURLRES_ASYNCH
 #  define CURLRES_THREADED
 #define SHUT_RDWR 0x02
 #endif
 
-/* Define S_ISREG if not defined by system headers, f.e. MSVC */
+/* Define S_ISREG if not defined by system headers, e.g. MSVC */
 #if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
 #endif
 
-/* Define S_ISDIR if not defined by system headers, f.e. MSVC */
+/* Define S_ISDIR if not defined by system headers, e.g. MSVC */
 #if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
 #endif
@@ -811,6 +846,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 
 #if defined(USE_NGTCP2) || defined(USE_QUICHE) || defined(USE_MSH3)
 #define ENABLE_QUIC
+#define USE_HTTP3
 #endif
 
 #if defined(USE_UNIX_SOCKETS) && defined(WIN32)
index 38018d2..f09b00f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 
@@ -31,7 +33,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
-#include <ctype.h>
 #include <time.h>
 
 #ifdef HAVE_ERRNO_H
@@ -86,6 +87,8 @@
 #include <sys/socket.h>
 #endif
 
+#include "functypes.h"
+
 #ifdef __hpux
 #  if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
 #    ifdef OLD_APP32_64BIT_OFF_T
@@ -148,20 +151,10 @@ struct timeval {
  * SEND_TYPE_RETV must also be defined.
  */
 
-#if !defined(RECV_TYPE_ARG1) || \
-    !defined(RECV_TYPE_ARG2) || \
-    !defined(RECV_TYPE_ARG3) || \
-    !defined(RECV_TYPE_ARG4) || \
-    !defined(RECV_TYPE_RETV)
-  /* */
-  Error Missing_definition_of_return_and_arguments_types_of_recv
-  /* */
-#else
 #define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \
                                    (RECV_TYPE_ARG2)(y), \
                                    (RECV_TYPE_ARG3)(z), \
                                    (RECV_TYPE_ARG4)(0))
-#endif
 #else /* HAVE_RECV */
 #ifndef sread
   /* */
@@ -178,21 +171,10 @@ struct timeval {
                                     (SEND_TYPE_ARG3)(z))
 
 #elif defined(HAVE_SEND)
-#if !defined(SEND_TYPE_ARG1) || \
-    !defined(SEND_QUAL_ARG2) || \
-    !defined(SEND_TYPE_ARG2) || \
-    !defined(SEND_TYPE_ARG3) || \
-    !defined(SEND_TYPE_ARG4) || \
-    !defined(SEND_TYPE_RETV)
-  /* */
-  Error Missing_definition_of_return_and_arguments_types_of_send
-  /* */
-#else
 #define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \
                                     (SEND_QUAL_ARG2 SEND_TYPE_ARG2)(y), \
                                     (SEND_TYPE_ARG3)(z), \
                                     (SEND_TYPE_ARG4)(SEND_4TH_ARG))
-#endif
 #else /* HAVE_SEND */
 #ifndef swrite
   /* */
@@ -202,46 +184,6 @@ struct timeval {
 #endif /* HAVE_SEND */
 
 
-#if 0
-#if defined(HAVE_RECVFROM)
-/*
- * Currently recvfrom is only used on udp sockets.
- */
-#if !defined(RECVFROM_TYPE_ARG1) || \
-    !defined(RECVFROM_TYPE_ARG2) || \
-    !defined(RECVFROM_TYPE_ARG3) || \
-    !defined(RECVFROM_TYPE_ARG4) || \
-    !defined(RECVFROM_TYPE_ARG5) || \
-    !defined(RECVFROM_TYPE_ARG6) || \
-    !defined(RECVFROM_TYPE_RETV)
-  /* */
-  Error Missing_definition_of_return_and_arguments_types_of_recvfrom
-  /* */
-#else
-#define sreadfrom(s,b,bl,f,fl) (ssize_t)recvfrom((RECVFROM_TYPE_ARG1)  (s),  \
-                                                 (RECVFROM_TYPE_ARG2 *)(b),  \
-                                                 (RECVFROM_TYPE_ARG3)  (bl), \
-                                                 (RECVFROM_TYPE_ARG4)  (0),  \
-                                                 (RECVFROM_TYPE_ARG5 *)(f),  \
-                                                 (RECVFROM_TYPE_ARG6 *)(fl))
-#endif
-#else /* HAVE_RECVFROM */
-#ifndef sreadfrom
-  /* */
-  Error Missing_definition_of_macro_sreadfrom
-  /* */
-#endif
-#endif /* HAVE_RECVFROM */
-
-
-#ifdef RECVFROM_TYPE_ARG6_IS_VOID
-#  define RECVFROM_ARG6_T int
-#else
-#  define RECVFROM_ARG6_T RECVFROM_TYPE_ARG6
-#endif
-#endif /* if 0 */
-
-
 /*
  * Function-like macro definition used to close a socket.
  */
@@ -267,9 +209,6 @@ struct timeval {
 #  define sfcntl  fcntl
 #endif
 
-#define TOLOWER(x)  (tolower((int)  ((unsigned char)x)))
-
-
 /*
  * 'bool' stuff compatible with HP-UX headers.
  */
@@ -306,6 +245,14 @@ struct timeval {
 #  define HAVE_BOOL_T
 #endif
 
+/* the type we use for storing a single boolean bit */
+#ifdef _MSC_VER
+typedef bool bit;
+#define BIT(x) bool x
+#else
+typedef unsigned int bit;
+#define BIT(x) bit x:1
+#endif
 
 /*
  * Redefine TRUE and FALSE too, to catch current use. With this
index 2b7890a..754c761 100644 (file)
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_CRYPTO_AUTH
+#include <curl/curl.h>
 #include "curl_hmac.h"
 
 extern const struct HMAC_params Curl_HMAC_SHA256[1];
index 339bf54..33108c4 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 881384d..ad11130 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 4146144..eb8e136 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index e10b7a1..63392f6 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 9f21f60..1796afa 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index e23e661..6f7678f 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 6a6c772..b283a0d 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_DICT
index 4aef8b2..3b1d5d6 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -69,12 +71,6 @@ static const char *doh_strerror(DOHcode code)
 }
 #endif
 
-#ifdef DEBUGBUILD
-#define UNITTEST
-#else
-#define UNITTEST static
-#endif
-
 /* @unittest 1655
  */
 UNITTEST DOHcode doh_encode(const char *host,
@@ -247,6 +243,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
+    ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
     ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
@@ -306,14 +303,6 @@ static CURLcode dohprobe(struct Curl_easy *data,
     }
     if(data->set.ssl.certinfo)
       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
-    if(data->set.str[STRING_SSL_RANDOM_FILE]) {
-      ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
-                         data->set.str[STRING_SSL_RANDOM_FILE]);
-    }
-    if(data->set.str[STRING_SSL_EGDSOCKET]) {
-      ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
-                         data->set.str[STRING_SSL_EGDSOCKET]);
-    }
     if(data->set.ssl.fsslctx)
       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
     if(data->set.ssl.fsslctxp)
@@ -407,7 +396,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
     goto error;
   dohp->pending++;
 
-  if(Curl_ipv6works(data)) {
+  if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
     /* create IPv6 DoH request */
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
@@ -803,7 +792,7 @@ doh2ai(const struct dohentry *de, const char *hostname, int port)
 #endif
   CURLcode result = CURLE_OK;
   int i;
-  size_t hostlen = strlen(hostname) + 1; /* include zero terminator */
+  size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
 
   if(!de)
     /* no input == no output! */
index 70e96e0..678e807 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "urldata.h"
 
 #ifndef CURL_DISABLE_DOH
 
-/*
- * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
- * and returns a 'Curl_addrinfo *' with the address information.
- */
-
-struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
-                               const char *hostname,
-                               int port,
-                               int *waitp);
-
-CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
-                              struct Curl_dns_entry **dns);
-
-int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
-
 typedef enum {
   DOH_OK,
   DOH_DNS_BAD_LABEL,    /* 1 */
@@ -67,6 +54,38 @@ typedef enum {
   DNS_TYPE_DNAME = 39           /* RFC6672 */
 } DNStype;
 
+/* one of these for each DoH request */
+struct dnsprobe {
+  CURL *easy;
+  DNStype dnstype;
+  unsigned char dohbuffer[512];
+  size_t dohlen;
+  struct dynbuf serverdoh;
+};
+
+struct dohdata {
+  struct curl_slist *headers;
+  struct dnsprobe probe[DOH_PROBE_SLOTS];
+  unsigned int pending; /* still outstanding requests */
+  int port;
+  const char *host;
+};
+
+/*
+ * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
+ * and returns a 'Curl_addrinfo *' with the address information.
+ */
+
+struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
+                               const char *hostname,
+                               int port,
+                               int *waitp);
+
+CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
+                              struct Curl_dns_entry **dns);
+
+int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
+
 #define DOH_MAX_ADDR 24
 #define DOH_MAX_CNAME 4
 
diff --git a/Utilities/cmcurl/lib/dotdot.c b/Utilities/cmcurl/lib/dotdot.c
deleted file mode 100644 (file)
index 73ef2fa..0000000
+++ /dev/null
@@ -1,182 +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.
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "dotdot.h"
-#include "curl_memory.h"
-
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * "Remove Dot Segments"
- * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
- */
-
-/*
- * Curl_dedotdotify()
- * @unittest: 1395
- *
- * This function gets a null-terminated path with dot and dotdot sequences
- * passed in and strips them off according to the rules in RFC 3986 section
- * 5.2.4.
- *
- * The function handles a query part ('?' + stuff) appended but it expects
- * that fragments ('#' + stuff) have already been cut off.
- *
- * RETURNS
- *
- * an allocated dedotdotified output string
- */
-char *Curl_dedotdotify(const char *input)
-{
-  size_t inlen = strlen(input);
-  char *clone;
-  size_t clen = inlen; /* the length of the cloned input */
-  char *out = malloc(inlen + 1);
-  char *outptr;
-  char *orgclone;
-  char *queryp;
-  if(!out)
-    return NULL; /* out of memory */
-
-  *out = 0; /* null-terminates, for inputs like "./" */
-
-  /* get a cloned copy of the input */
-  clone = strdup(input);
-  if(!clone) {
-    free(out);
-    return NULL;
-  }
-  orgclone = clone;
-  outptr = out;
-
-  if(!*clone) {
-    /* zero length string, return that */
-    free(out);
-    return clone;
-  }
-
-  /*
-   * 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
-   * string.
-   */
-  queryp = strchr(clone, '?');
-  if(queryp)
-    *queryp = 0;
-
-  do {
-
-    /*  A.  If the input buffer begins with a prefix of "../" or "./", then
-        remove that prefix from the input buffer; otherwise, */
-
-    if(!strncmp("./", clone, 2)) {
-      clone += 2;
-      clen -= 2;
-    }
-    else if(!strncmp("../", clone, 3)) {
-      clone += 3;
-      clen -= 3;
-    }
-
-    /*  B.  if the input buffer begins with a prefix of "/./" or "/.", where
-        "."  is a complete path segment, then replace that prefix with "/" in
-        the input buffer; otherwise, */
-    else if(!strncmp("/./", clone, 3)) {
-      clone += 2;
-      clen -= 2;
-    }
-    else if(!strcmp("/.", clone)) {
-      clone[1]='/';
-      clone++;
-      clen -= 1;
-    }
-
-    /*  C.  if the input buffer begins with a prefix of "/../" or "/..", where
-        ".." is a complete path segment, then replace that prefix with "/" in
-        the input buffer and remove the last segment and its preceding "/" (if
-        any) from the output buffer; otherwise, */
-
-    else if(!strncmp("/../", clone, 4)) {
-      clone += 3;
-      clen -= 3;
-      /* remove the last segment from the output buffer */
-      while(outptr > out) {
-        outptr--;
-        if(*outptr == '/')
-          break;
-      }
-      *outptr = 0; /* null-terminate where it stops */
-    }
-    else if(!strcmp("/..", clone)) {
-      clone[2]='/';
-      clone += 2;
-      clen -= 2;
-      /* remove the last segment from the output buffer */
-      while(outptr > out) {
-        outptr--;
-        if(*outptr == '/')
-          break;
-      }
-      *outptr = 0; /* null-terminate where it stops */
-    }
-
-    /*  D.  if the input buffer consists only of "." or "..", then remove
-        that from the input buffer; otherwise, */
-
-    else if(!strcmp(".", clone) || !strcmp("..", clone)) {
-      *clone = 0;
-      *out = 0;
-    }
-
-    else {
-      /*  E.  move the first path segment in the input buffer to the end of
-          the output buffer, including the initial "/" character (if any) and
-          any subsequent characters up to, but not including, the next "/"
-          character or the end of the input buffer. */
-
-      do {
-        *outptr++ = *clone++;
-        clen--;
-      } while(*clone && (*clone != '/'));
-      *outptr = 0;
-    }
-
-  } while(*clone);
-
-  if(queryp) {
-    size_t qlen;
-    /* There was a query part, append that to the output. The 'clone' string
-       may now have been altered so we copy from the original input string
-       from the correct index. */
-    size_t oindex = queryp - orgclone;
-    qlen = strlen(&input[oindex]);
-    memcpy(outptr, &input[oindex], qlen + 1); /* include the end zero byte */
-  }
-
-  free(orgclone);
-  return out;
-}
index ada7e0c..0b1cf9a 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2020 - 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
@@ -18,6 +18,8 @@
  * 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"
@@ -126,7 +128,6 @@ void Curl_dyn_reset(struct dynbuf *s)
   s->leng = 0;
 }
 
-#ifdef USE_NGTCP2
 /*
  * Specify the size of the tail to keep (number of bytes from the end of the
  * buffer). The rest will be dropped.
@@ -151,7 +152,6 @@ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
   return CURLE_OK;
 
 }
-#endif
 
 /*
  * Appends a buffer with length.
@@ -253,3 +253,18 @@ size_t Curl_dyn_len(const struct dynbuf *s)
   DEBUGASSERT(!s->leng || s->bufr);
   return s->leng;
 }
+
+/*
+ * Set a new (smaller) length.
+ */
+CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
+{
+  DEBUGASSERT(s);
+  DEBUGASSERT(s->init == DYNINIT);
+  DEBUGASSERT(!s->leng || s->bufr);
+  if(set > s->leng)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  s->leng = set;
+  s->bufr[s->leng] = 0;
+  return CURLE_OK;
+}
index 252411f..04a728c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020, 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2020 - 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
+#include <curl/curl.h>
+
 #ifndef BUILDING_LIBCURL
 /* this renames the functions so that the tool code can use the same code
    without getting symbol collisions */
 #define Curl_dyn_len(a) curlx_dyn_len(a)
 #define Curl_dyn_reset(a) curlx_dyn_reset(a)
 #define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
+#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
 #define curlx_dynbuf dynbuf /* for the struct name */
 #endif
 
 struct dynbuf {
   char *bufr;    /* point to a null-terminated allocated buffer */
-  size_t leng;   /* number of bytes *EXCLUDING* the zero terminator */
+  size_t leng;   /* number of bytes *EXCLUDING* the null-terminator */
   size_t allc;   /* size of the current allocation */
   size_t toobig; /* size limit for the buffer */
 #ifdef DEBUGBUILD
@@ -61,6 +66,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   WARN_UNUSED_RESULT;
 void Curl_dyn_reset(struct dynbuf *s);
 CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
+CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
 char *Curl_dyn_ptr(const struct dynbuf *s);
 unsigned char *Curl_dyn_uptr(const struct dynbuf *s);
 size_t Curl_dyn_len(const struct dynbuf *s);
index bd9d695..b8ac1ef 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -80,6 +82,8 @@
 #include "altsvc.h"
 #include "hsts.h"
 
+#include "easy_lock.h"
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 static unsigned int  initialized;
 static long          init_flags;
 
+#ifdef GLOBAL_INIT_IS_THREADSAFE
+
+static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
+#define global_init_lock() curl_simple_lock_lock(&s_lock)
+#define global_init_unlock() curl_simple_lock_unlock(&s_lock)
+
+#else
+
+#define global_init_lock()
+#define global_init_unlock()
+
+#endif
+
 /*
  * strdup (and other memory functions) is redefined in complicated
  * ways, but at this point it must be defined as the system-supplied strdup
@@ -161,7 +178,7 @@ static CURLcode global_init(long flags, bool memoryfuncs)
 #endif
 
 #ifdef __AMIGA__
-  if(!Curl_amiga_init()) {
+  if(Curl_amiga_init()) {
     DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
     goto fail;
   }
@@ -207,7 +224,14 @@ static CURLcode global_init(long flags, bool memoryfuncs)
  */
 CURLcode curl_global_init(long flags)
 {
-  return global_init(flags, TRUE);
+  CURLcode result;
+  global_init_lock();
+
+  result = global_init(flags, TRUE);
+
+  global_init_unlock();
+
+  return result;
 }
 
 /*
@@ -218,15 +242,20 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
                               curl_free_callback f, curl_realloc_callback r,
                               curl_strdup_callback s, curl_calloc_callback c)
 {
+  CURLcode result;
+
   /* Invalid input, return immediately */
   if(!m || !f || !r || !s || !c)
     return CURLE_FAILED_INIT;
 
+  global_init_lock();
+
   if(initialized) {
     /* Already initialized, don't do it again, but bump the variable anyway to
        work like curl_global_init() and require the same amount of cleanup
        calls. */
     initialized++;
+    global_init_unlock();
     return CURLE_OK;
   }
 
@@ -239,7 +268,11 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
   Curl_ccalloc = c;
 
   /* Call the actual init function, but without setting */
-  return global_init(flags, FALSE);
+  result = global_init(flags, FALSE);
+
+  global_init_unlock();
+
+  return result;
 }
 
 /**
@@ -248,11 +281,17 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
  */
 void curl_global_cleanup(void)
 {
-  if(!initialized)
+  global_init_lock();
+
+  if(!initialized) {
+    global_init_unlock();
     return;
+  }
 
-  if(--initialized)
+  if(--initialized) {
+    global_init_unlock();
     return;
+  }
 
   Curl_ssl_cleanup();
   Curl_resolver_global_cleanup();
@@ -273,6 +312,25 @@ void curl_global_cleanup(void)
 #endif
 
   init_flags  = 0;
+
+  global_init_unlock();
+}
+
+/*
+ * curl_global_sslset() globally initializes the SSL backend to use.
+ */
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+                              const curl_ssl_backend ***avail)
+{
+  CURLsslset rc;
+
+  global_init_lock();
+
+  rc = Curl_init_sslset_nolock(id, name, avail);
+
+  global_init_unlock();
+
+  return rc;
 }
 
 /*
@@ -285,14 +343,18 @@ struct Curl_easy *curl_easy_init(void)
   struct Curl_easy *data;
 
   /* Make sure we inited the global SSL stuff */
+  global_init_lock();
+
   if(!initialized) {
-    result = curl_global_init(CURL_GLOBAL_DEFAULT);
+    result = global_init(CURL_GLOBAL_DEFAULT, TRUE);
     if(result) {
       /* something in the global init failed, return nothing */
       DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
+      global_init_unlock();
       return NULL;
     }
   }
+  global_init_unlock();
 
   /* We use curl_open() with undefined URL so far */
   result = Curl_open(&data);
@@ -507,19 +569,23 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 
     /* wait for activity or timeout */
     pollrc = Curl_poll(fds, numfds, ev->ms);
+    if(pollrc < 0)
+      return CURLE_UNRECOVERABLE_POLL;
 
     after = Curl_now();
 
     ev->msbump = FALSE; /* reset here */
 
-    if(0 == pollrc) {
+    if(!pollrc) {
       /* timeout! */
       ev->ms = 0;
       /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */
       mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
                                        &ev->running_handles);
     }
-    else if(pollrc > 0) {
+    else {
+      /* here pollrc is > 0 */
+
       /* loop over the monitored sockets to see which ones had activity */
       for(i = 0; i< numfds; i++) {
         if(fds[i].revents) {
@@ -545,8 +611,6 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
         }
       }
     }
-    else
-      return CURLE_RECV_ERROR;
 
     if(mcode)
       return CURLE_URL_MALFORMAT;
@@ -662,7 +726,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
   else {
     /* this multi handle will only ever have a single easy handled attached
        to it, so make it use minimal hashes */
-    multi = Curl_multi_handle(1, 3);
+    multi = Curl_multi_handle(1, 3, 7);
     if(!multi)
       return CURLE_OUT_OF_MEMORY;
     data->multi_easy = multi;
@@ -838,6 +902,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
   outcurl->progress.flags    = data->progress.flags;
   outcurl->progress.callback = data->progress.callback;
 
+#ifndef CURL_DISABLE_COOKIES
   if(data->cookies) {
     /* If cookies are enabled in the parent handle, we enable them
        in the clone as well! */
@@ -856,6 +921,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
     if(!outcurl->state.cookielist)
       goto fail;
   }
+#endif
 
   if(data->state.url) {
     outcurl->state.url = strdup(data->state.url);
@@ -878,7 +944,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
       goto fail;
   }
 
-#ifdef USE_ALTSVC
+#ifndef CURL_DISABLE_ALTSVC
   if(data->asi) {
     outcurl->asi = Curl_altsvc_init();
     if(!outcurl->asi)
@@ -937,8 +1003,10 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
   fail:
 
   if(outcurl) {
+#ifndef CURL_DISABLE_COOKIES
     curl_slist_free_all(outcurl->state.cookielist);
     outcurl->state.cookielist = NULL;
+#endif
     Curl_safefree(outcurl->state.buffer);
     Curl_dyn_free(&outcurl->state.headerb);
     Curl_safefree(outcurl->state.url);
@@ -1065,6 +1133,16 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
     }
   }
 
+#ifdef USE_HYPER
+  if(!(newstate & KEEP_SEND_PAUSE)) {
+    /* need to wake the send body waker */
+    if(data->hyp.send_body_waker) {
+      hyper_waker_wake(data->hyp.send_body_waker);
+      data->hyp.send_body_waker = NULL;
+    }
+  }
+#endif
+
   /* if there's no error and we're not pausing both directions, we want
      to have this handle checked soon */
   if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
@@ -1093,8 +1171,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
 }
 
 
-static CURLcode easy_connection(struct Curl_easy *data,
-                                curl_socket_t *sfd,
+static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd,
                                 struct connectdata **connp)
 {
   if(!data)
@@ -1153,11 +1230,12 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
 }
 
 /*
- * Sends data over the connected socket. Use after successful
- * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ * Sends data over the connected socket.
+ *
+ * This is the private internal version of curl_easy_send()
  */
-CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
-                        size_t buflen, size_t *n)
+CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
+                       size_t buflen, ssize_t *n)
 {
   curl_socket_t sfd;
   CURLcode result;
@@ -1165,9 +1243,6 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
   struct connectdata *c = NULL;
   SIGPIPE_VARIABLE(pipe_st);
 
-  if(Curl_is_in_callback(data))
-    return CURLE_RECURSIVE_API_CALL;
-
   result = easy_connection(data, &sfd, &c);
   if(result)
     return result;
@@ -1189,8 +1264,25 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
   if(!result && !n1)
     return CURLE_AGAIN;
 
-  *n = (size_t)n1;
+  *n = n1;
+
+  return result;
+}
+
+/*
+ * Sends data over the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
+                        size_t buflen, size_t *n)
+{
+  ssize_t written = 0;
+  CURLcode result;
+  if(Curl_is_in_callback(data))
+    return CURLE_RECURSIVE_API_CALL;
 
+  result = Curl_senddata(data, buffer, buflen, &written);
+  *n = (size_t)written;
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
new file mode 100644 (file)
index 0000000..d96e56b
--- /dev/null
@@ -0,0 +1,105 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#define GLOBAL_INIT_IS_THREADSAFE
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
+
+#ifdef __MINGW32__
+#ifndef __MINGW64_VERSION_MAJOR
+#if (__MINGW32_MAJOR_VERSION < 5) || \
+    (__MINGW32_MAJOR_VERSION == 5 && __MINGW32_MINOR_VERSION == 0)
+/* mingw >= 5.0.1 defines SRWLOCK, and slightly different from MS define */
+typedef PVOID SRWLOCK, *PSRWLOCK;
+#endif
+#endif
+#ifndef SRWLOCK_INIT
+#define SRWLOCK_INIT NULL
+#endif
+#endif /* __MINGW32__ */
+
+#define curl_simple_lock SRWLOCK
+#define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT
+
+#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m)
+#define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m)
+
+#elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H)
+#include <stdatomic.h>
+#if defined(HAVE_SCHED_YIELD)
+#include <sched.h>
+#endif
+
+#define curl_simple_lock atomic_int
+#define CURL_SIMPLE_LOCK_INIT 0
+
+/* a clang-thing */
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __INTEL_COMPILER
+/* The Intel compiler tries to look like GCC *and* clang *and* lies in its
+   __has_builtin() function, so override it. */
+
+/* if GCC on i386/x86_64 or if the built-in is present */
+#if ( (defined(__GNUC__) && !defined(__clang__)) &&     \
+      (defined(__i386__) || defined(__x86_64__))) ||    \
+  __has_builtin(__builtin_ia32_pause)
+#define HAVE_BUILTIN_IA32_PAUSE
+#endif
+
+#endif
+
+static inline void curl_simple_lock_lock(curl_simple_lock *lock)
+{
+  for(;;) {
+    if(!atomic_exchange_explicit(lock, true, memory_order_acquire))
+      break;
+    /* Reduce cache coherency traffic */
+    while(atomic_load_explicit(lock, memory_order_relaxed)) {
+      /* Reduce load (not mandatory) */
+#ifdef HAVE_BUILTIN_IA32_PAUSE
+      __builtin_ia32_pause();
+#elif defined(__aarch64__)
+      __asm__ volatile("yield" ::: "memory");
+#elif defined(HAVE_SCHED_YIELD)
+      sched_yield();
+#endif
+    }
+  }
+}
+
+static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
+{
+  atomic_store_explicit(lock, false, memory_order_release);
+}
+
+#else
+
+#undef  GLOBAL_INIT_IS_THREADSAFE
+
+#endif
index 7b2213f..a639bb3 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             ___|___/|_| ______|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) 1998 - 2022, 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
@@ -18,6 +18,8 @@
  * 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"
index 3364418..205382c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
  * Prototypes for library-wide functions provided by easy.c
  */
+CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
+                       size_t buflen, ssize_t *n);
+
 #ifdef CURLDEBUG
 CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy);
 #endif
index 04871ad..e59b63a 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             ___|___/|_| ______|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel.se>, et al.
+ * Copyright (C) 1998 - 2022, 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* This source code is generated by optiontable.pl - DO NOT EDIT BY HAND */
@@ -105,7 +107,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS,
    CURLOT_LONG, 0},
   {"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0},
-  {"FTP_RESPONSE_TIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOT_LONG, 0},
+  {"FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
+   CURLOT_LONG, CURLOT_FLAG_ALIAS},
   {"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0},
   {"FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS},
   {"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0},
@@ -201,6 +204,7 @@ struct curl_easyoption Curl_easyopts[] = {
   {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
   {"PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0},
   {"PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0},
+  {"PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0},
   {"PROXY", CURLOPT_PROXY, CURLOT_STRING, 0},
   {"PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0},
   {"PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0},
@@ -243,6 +247,7 @@ struct curl_easyoption Curl_easyopts[] = {
   {"READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0},
   {"READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0},
   {"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0},
+  {"REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0},
   {"REFERER", CURLOPT_REFERER, CURLOT_STRING, 0},
   {"REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0},
   {"RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0},
@@ -262,8 +267,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0},
   {"SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0},
   {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0},
-  {"SERVER_RESPONSE_TIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT,
-   CURLOT_LONG, CURLOT_FLAG_ALIAS},
+  {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT,
+   CURLOT_LONG, 0},
   {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0},
   {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0},
   {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0},
@@ -273,6 +278,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOT_STRING, 0},
   {"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0},
   {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0},
+  {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0},
+  {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0},
   {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
    CURLOT_STRING, 0},
   {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
@@ -347,10 +354,11 @@ struct curl_easyoption Curl_easyopts[] = {
   {"WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0},
   {"WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0},
   {"WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
+  {"WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0},
   {"XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0},
   {"XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0},
   {"XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0},
-  {NULL, CURLOPT_LASTENTRY, 0, 0} /* end of table */
+  {NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0} /* end of table */
 };
 
 #ifdef DEBUGBUILD
@@ -360,6 +368,6 @@ struct curl_easyoption Curl_easyopts[] = {
  */
 int Curl_easyopts_check(void)
 {
-  return ((CURLOPT_LASTENTRY%10000) != (315 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (320 + 1));
 }
 #endif
index 91e1190..33f816d 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* should probably go into the public header */
index ff58875..da7e552 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* Escape and unescape URL encoding in strings. The functions return a new
@@ -75,6 +77,9 @@ char *curl_unescape(const char *string, int length)
   return curl_easy_unescape(NULL, string, length, NULL);
 }
 
+/* Escapes for URL the given unescaped string of given length.
+ * 'data' is ignored since 7.82.0.
+ */
 char *curl_easy_escape(struct Curl_easy *data, const char *string,
                        int inlength)
 {
@@ -116,8 +121,6 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
  * Returns a pointer to a malloced string in *ostring with length given in
  * *olen. If length == 0, the length is assumed to be strlen(string).
  *
- * 'data' can be set to NULL
- *
  * ctrl options:
  * - REJECT_NADA: accept everything
  * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in
@@ -191,6 +194,7 @@ CURLcode Curl_urldecode(const char *string, size_t length,
  * pointer to a malloced string with length given in *olen.
  * If length == 0, the length is assumed to be strlen(string).
  * If olen == NULL, no output length is stored.
+ * 'data' is ignored since 7.82.0.
  */
 char *curl_easy_unescape(struct Curl_easy *data, const char *string,
                          int length, int *olen)
index 0266883..61d4611 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred.  */
index 3da79a2..d82d57b 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -69,6 +71,8 @@
 
 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
 #define DOS_FILESYSTEM 1
+#elif defined(__amigaos4__)
+#define AMIGA_FILESYSTEM 1
 #endif
 
 #ifdef OPEN_NEEDS_ARG3
@@ -194,8 +198,33 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
     return CURLE_URL_MALFORMAT;
   }
 
+  #ifdef AMIGA_FILESYSTEM
+  /*
+   * A leading slash in an AmigaDOS path denotes the parent
+   * directory, and hence we block this as it is relative.
+   * Absolute paths start with 'volumename:', so we check for
+   * this first. Failing that, we treat the path as a real unix
+   * path, but only if the application was compiled with -lunix.
+   */
+  fd = -1;
+  file->path = real_path;
+
+  if(real_path[0] == '/') {
+    extern int __unix_path_semantics;
+    if(strchr(real_path + 1, ':')) {
+      /* Amiga absolute path */
+      fd = open_readonly(real_path + 1, O_RDONLY);
+      file->path++;
+    }
+    else if(__unix_path_semantics) {
+      /* -lunix fallback */
+      fd = open_readonly(real_path, O_RDONLY);
+    }
+  }
+  #else
   fd = open_readonly(real_path, O_RDONLY);
   file->path = real_path;
+  #endif
 #endif
   file->freepath = real_path; /* free this when done */
 
@@ -234,7 +263,7 @@ static CURLcode file_disconnect(struct Curl_easy *data,
 {
   (void)dead_connection; /* not used */
   (void)conn;
-  return file_done(data, 0, 0);
+  return file_done(data, CURLE_OK, FALSE);
 }
 
 #ifdef DOS_FILESYSTEM
index 338f92e..826d453 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 
index b7e9f0f..7bbf24b 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2010 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 5ae23ad..5bad718 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2010 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c
new file mode 100644 (file)
index 0000000..ad3691b
--- /dev/null
@@ -0,0 +1,113 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) ||  \
+  !defined(CURL_DISABLE_HSTS)
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "urldata.h"
+#include "rand.h"
+#include "fopen.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/*
+ * Curl_fopen() opens a file for writing with a temp name, to be renamed
+ * to the final name when completed. If there is an existing file using this
+ * name at the time of the open, this function will clone the mode from that
+ * file.  if 'tempname' is non-NULL, it needs a rename after the file is
+ * written.
+ */
+CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
+                    FILE **fh, char **tempname)
+{
+  CURLcode result = CURLE_WRITE_ERROR;
+  unsigned char randsuffix[9];
+  char *tempstore = NULL;
+  struct_stat sb;
+  int fd = -1;
+  *tempname = NULL;
+
+  if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+    /* a non-regular file, fallback to direct fopen() */
+    *fh = fopen(filename, FOPEN_WRITETEXT);
+    if(*fh)
+      return CURLE_OK;
+    goto fail;
+  }
+
+  result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix));
+  if(result)
+    goto fail;
+
+  tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
+  if(!tempstore) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto fail;
+  }
+
+  result = CURLE_WRITE_ERROR;
+  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600);
+  if(fd == -1)
+    goto fail;
+
+#ifdef HAVE_FCHMOD
+  {
+    struct_stat nsb;
+    if((fstat(fd, &nsb) != -1) &&
+       (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
+      /* if the user and group are the same, clone the original mode */
+      if(fchmod(fd, sb.st_mode) == -1)
+        goto fail;
+    }
+  }
+#endif
+
+  *fh = fdopen(fd, FOPEN_WRITETEXT);
+  if(!*fh)
+    goto fail;
+
+  *tempname = tempstore;
+  return CURLE_OK;
+
+fail:
+  if(fd != -1) {
+    close(fd);
+    unlink(tempstore);
+  }
+
+  free(tempstore);
+
+  *tempname = NULL;
+  return result;
+}
+
+#endif /* ! disabled */
similarity index 77%
rename from Utilities/cmcurl/lib/vssh/wolfssh.h
rename to Utilities/cmcurl/lib/fopen.h
index 7b6ac48..289e55f 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_WOLFSSH_H
-#define HEADER_CURL_WOLFSSH_H
+#ifndef HEADER_CURL_FOPEN_H
+#define HEADER_CURL_FOPEN_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
-extern const struct Curl_handler Curl_handler_sftp;
+CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
+                    FILE **fh, char **tempname);
 
-#endif /* HEADER_CURL_WOLFSSH_H */
+#endif
index 5fefd7a..46542b4 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -249,8 +251,10 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
       }
     }
     else {
-      /* This is not array-state, get next option */
-      option = va_arg(params, CURLformoption);
+      /* This is not array-state, get next option. This gets an 'int' with
+         va_arg() because CURLformoption might be a smaller type than int and
+         might cause compiler warnings and wrong behavior. */
+      option = (CURLformoption)va_arg(params, int);
       if(CURLFORM_END == option)
         break;
     }
index 09c6e9c..c6c6397 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 45d9ced..d02ab99 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -514,10 +516,9 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
   }
   else {
     /* Add timeout to multi handle and break out of the loop */
-    if(*connected == FALSE) {
-      Curl_expire(data, data->set.accepttimeout > 0 ?
-                  data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0);
-    }
+    Curl_expire(data, data->set.accepttimeout ?
+                data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
+                EXPIRE_FTP_ACCEPT);
   }
 
   return result;
@@ -783,8 +784,9 @@ static CURLcode ftp_state_user(struct Curl_easy *data,
                                   &conn->proto.ftpc.pp, "USER %s",
                                   conn->user?conn->user:"");
   if(!result) {
+    struct ftp_conn *ftpc = &conn->proto.ftpc;
+    ftpc->ftp_trying_alternative = FALSE;
     state(data, FTP_USER);
-    data->state.ftp_trying_alternative = FALSE;
   }
   return result;
 }
@@ -2127,9 +2129,11 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
   default:
     infof(data, "unsupported MDTM reply format");
     break;
-  case 550: /* "No such file or directory" */
-    failf(data, "Given file does not exist");
-    result = CURLE_REMOTE_FILE_NOT_FOUND;
+  case 550: /* 550 is used for several different problems, e.g.
+               "No such file or directory" or "Permission denied".
+               It does not mean that the file does not exist at all. */
+    infof(data, "MDTM failed: file does not exist or permission problem,"
+          " continuing");
     break;
   }
 
@@ -2622,13 +2626,13 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
     (the server denies to log the specified user) */
 
     if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
-        !data->state.ftp_trying_alternative) {
+       !ftpc->ftp_trying_alternative) {
       /* Ok, USER failed.  Let's try the supplied command. */
       result =
         Curl_pp_sendf(data, &ftpc->pp, "%s",
                       data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
       if(!result) {
-        data->state.ftp_trying_alternative = TRUE;
+        ftpc->ftp_trying_alternative = TRUE;
         state(data, FTP_USER);
       }
     }
@@ -2701,10 +2705,11 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
            set a valid level */
         Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
 
-        if(Curl_sec_login(data, conn))
-          infof(data, "Logging in with password in cleartext");
-        else
-          infof(data, "Authentication successful");
+        if(Curl_sec_login(data, conn)) {
+          failf(data, "secure login failed");
+          return CURLE_WEIRD_SERVER_REPLY;
+        }
+        infof(data, "Authentication successful");
       }
 #endif
 
@@ -3561,8 +3566,10 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
   bool connected = FALSE;
   bool complete = FALSE;
 
-  /* the ftp struct is inited in ftp_connect() */
-  struct FTP *ftp = data->req.p.ftp;
+  /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
+   * proxy then the state will not be valid until after that connection is
+   * complete */
+  struct FTP *ftp = NULL;
 
   /* if the second connection isn't done yet, wait for it */
   if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
@@ -3603,6 +3610,9 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
     return result;
 #endif
 
+  /* Curl_proxy_connect might have moved the protocol state */
+  ftp = data->req.p.ftp;
+
   if(ftpc->state) {
     /* already in a state so skip the initial commands.
        They are only done to kickstart the do_more state */
index 1cfdac0..7f6f432 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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 "pingpong.h"
 
 #ifndef CURL_DISABLE_FTP
@@ -149,6 +153,7 @@ struct ftp_conn {
   curl_off_t known_filesize; /* file size is different from -1, if wildcard
                                 LIST parsing was done and wc_statemach set
                                 it */
+  BIT(ftp_trying_alternative);
 };
 
 #define DEFAULT_ACCEPT_TIMEOUT   60000 /* milliseconds == one minute */
index 716ff38..40f5f3f 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /**
@@ -420,7 +422,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
               char *endptr = finfo->b_data + 6;
               /* here we can deal with directory size, pass the leading
                  whitespace and then the digits */
-              while(ISSPACE(*endptr))
+              while(ISBLANK(*endptr))
                 endptr++;
               while(ISDIGIT(*endptr))
                 endptr++;
@@ -892,7 +894,7 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
         parser->item_length++;
         switch(parser->state.NT.sub.time) {
         case PL_WINNT_TIME_PRESPACE:
-          if(!ISSPACE(c)) {
+          if(!ISBLANK(c)) {
             parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
           }
           break;
index e4cd820..0a80543 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
diff --git a/Utilities/cmcurl/lib/functypes.h b/Utilities/cmcurl/lib/functypes.h
new file mode 100644 (file)
index 0000000..8891b1d
--- /dev/null
@@ -0,0 +1,115 @@
+#ifndef HEADER_CURL_FUNCTYPES_H
+#define HEADER_CURL_FUNCTYPES_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"
+
+/* defaults:
+
+   ssize_t recv(int, void *, size_t, int);
+   ssize_t send(int, const void *, size_t, int);
+
+   If other argument or return types are needed:
+
+   1. For systems that run configure or cmake, the alternatives are provided
+      here.
+   2. For systems with config-*.h files, define them there.
+*/
+
+#ifdef WIN32
+/* int recv(SOCKET, char *, int, int) */
+#define RECV_TYPE_ARG1 SOCKET
+#define RECV_TYPE_ARG2 char *
+#define RECV_TYPE_ARG3 int
+#define RECV_TYPE_RETV int
+
+/* int send(SOCKET, const char *, int, int); */
+#define SEND_TYPE_ARG1 SOCKET
+#define SEND_TYPE_ARG2 char *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_RETV int
+
+#elif defined(__AMIGA__) /* Any AmigaOS flavour */
+
+/* long recv(long, char *, long, long); */
+#define RECV_TYPE_ARG1 long
+#define RECV_TYPE_ARG2 char *
+#define RECV_TYPE_ARG3 long
+#define RECV_TYPE_ARG4 long
+#define RECV_TYPE_RETV long
+
+/* int send(int, const char *, int, int); */
+#define SEND_TYPE_ARG1 int
+#define SEND_TYPE_ARG2 char *
+#define SEND_TYPE_ARG3 int
+#define SEND_TYPE_RETV int
+#endif
+
+
+#ifndef RECV_TYPE_ARG1
+#define RECV_TYPE_ARG1 int
+#endif
+
+#ifndef RECV_TYPE_ARG2
+#define RECV_TYPE_ARG2 void *
+#endif
+
+#ifndef RECV_TYPE_ARG3
+#define RECV_TYPE_ARG3 size_t
+#endif
+
+#ifndef RECV_TYPE_ARG4
+#define RECV_TYPE_ARG4 int
+#endif
+
+#ifndef RECV_TYPE_RETV
+#define RECV_TYPE_RETV ssize_t
+#endif
+
+#ifndef SEND_QUAL_ARG2
+#define SEND_QUAL_ARG2 const
+#endif
+
+#ifndef SEND_TYPE_ARG1
+#define SEND_TYPE_ARG1 int
+#endif
+
+#ifndef SEND_TYPE_ARG2
+#define SEND_TYPE_ARG2 void *
+#endif
+
+#ifndef SEND_TYPE_ARG3
+#define SEND_TYPE_ARG3 size_t
+#endif
+
+#ifndef SEND_TYPE_ARG4
+#define SEND_TYPE_ARG4 int
+#endif
+
+#ifndef SEND_TYPE_RETV
+#define SEND_TYPE_RETV ssize_t
+#endif
+
+#endif /* HEADER_CURL_FUNCTYPES_H */
index 92c5350..5f00fd1 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 9091e61..c3556b3 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -164,6 +166,20 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
   case CURLINFO_SCHEME:
     *param_charp = data->info.conn_scheme;
     break;
+  case CURLINFO_CAPATH:
+#ifdef CURL_CA_PATH
+    *param_charp = CURL_CA_PATH;
+#else
+    *param_charp = NULL;
+#endif
+    break;
+  case CURLINFO_CAINFO:
+#ifdef CURL_CA_BUNDLE
+    *param_charp = CURL_CA_BUNDLE;
+#else
+    *param_charp = NULL;
+#endif
+    break;
 
   default:
     return CURLE_UNKNOWN_OPTION;
@@ -285,6 +301,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
       /* return if the condition prevented the document to get transferred */
       *param_longp = data->info.timecond ? 1L : 0L;
     break;
+#ifndef CURL_DISABLE_RTSP
   case CURLINFO_RTSP_CLIENT_CSEQ:
     *param_longp = data->state.rtsp_next_client_CSeq;
     break;
@@ -294,6 +311,7 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
   case CURLINFO_RTSP_CSEQ_RECV:
     *param_longp = data->state.rtsp_CSeq_recv;
     break;
+#endif
   case CURLINFO_HTTP_VERSION:
     switch(data->info.httpversion) {
     case 10:
@@ -560,7 +578,7 @@ CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...)
   CURLcode result = CURLE_UNKNOWN_OPTION;
 
   if(!data)
-    return result;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 
   va_start(arg, info);
 
index f35d1b4..1b5e8c2 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...);
 CURLcode Curl_initinfo(struct Curl_easy *data);
index 0a3ba8f..01f4bde 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 6b8bd55..4ea269d 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_GOPHER
index c0ed58d..50254ad 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -189,7 +191,7 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data,
   vptr = Curl_checkheaders(data, STRCONST(H2H3_PSEUDO_SCHEME));
   if(vptr) {
     vptr += sizeof(H2H3_PSEUDO_SCHEME);
-    while(*vptr && ISSPACE(*vptr))
+    while(*vptr && ISBLANK(*vptr))
       vptr++;
     nva[2].value = vptr;
     infof(data, "set pseudo header %s to %s", H2H3_PSEUDO_SCHEME, vptr);
@@ -256,9 +258,6 @@ CURLcode Curl_pseudo_headers(struct Curl_easy *data,
       nva[i].valuelen = (end - hdbuf);
     }
 
-    nva[i].value = hdbuf;
-    nva[i].valuelen = (end - hdbuf);
-
     ++i;
   }
 
index 2225684..84caec5 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
 
index 8848906..b6a2a33 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index e166916..5b59bf1 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 226c696..978c918 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -32,7 +34,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_HEADERS_API)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
 
 /* Generate the curl_header struct for the user. This function MUST assign all
    struct fields in the output struct. */
@@ -72,8 +74,8 @@ CURLHcode curl_easy_header(CURL *easy,
   struct Curl_header_store *hs = NULL;
   struct Curl_header_store *pick = NULL;
   if(!name || !hout || !data ||
-     (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX)) ||
-     !type || (request < -1))
+     (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX|
+              CURLH_PSEUDO)) || !type || (request < -1))
     return CURLHE_BAD_ARGUMENT;
   if(!Curl_llist_count(&data->state.httphdrs))
     return CURLHE_NOHEADERS; /* no headers available */
@@ -205,7 +207,7 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
   /* skip all leading space letters */
-  while(*header && ISSPACE(*header))
+  while(*header && ISBLANK(*header))
     header++;
 
   *value = header;
@@ -216,6 +218,57 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
   return CURLE_OK;
 }
 
+static CURLcode unfold_value(struct Curl_easy *data, const char *value,
+                             size_t vlen)  /* length of the incoming header */
+{
+  struct Curl_header_store *hs;
+  struct Curl_header_store *newhs;
+  size_t olen; /* length of the old value */
+  size_t oalloc; /* length of the old name + value + separator */
+  size_t offset;
+  DEBUGASSERT(data->state.prevhead);
+  hs = data->state.prevhead;
+  olen = strlen(hs->value);
+  offset = hs->value - hs->buffer;
+  oalloc = olen + offset + 1;
+
+  /* skip all trailing space letters */
+  while(vlen && ISSPACE(value[vlen - 1]))
+    vlen--;
+
+  /* save only one leading space */
+  while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) {
+    vlen--;
+    value++;
+  }
+
+  /* since this header block might move in the realloc below, it needs to
+     first be unlinked from the list and then re-added again after the
+     realloc */
+  Curl_llist_remove(&data->state.httphdrs, &hs->node, NULL);
+
+  /* new size = struct + new value length + old name+value length */
+  newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
+  if(!newhs)
+    return CURLE_OUT_OF_MEMORY;
+  /* ->name' and ->value point into ->buffer (to keep the header allocation
+     in a single memory block), which now potentially have moved. Adjust
+     them. */
+  newhs->name = newhs->buffer;
+  newhs->value = &newhs->buffer[offset];
+
+  /* put the data at the end of the previous data, not the newline */
+  memcpy(&newhs->value[olen], value, vlen);
+  newhs->value[olen + vlen] = 0; /* null-terminate at newline */
+
+  /* insert this node into the list of headers */
+  Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
+                         newhs, &newhs->node);
+  data->state.prevhead = newhs;
+  return CURLE_OK;
+}
+
+
 /*
  * Curl_headers_push() gets passed a full HTTP header to store. It gets called
  * immediately before the header callback. The header is CRLF terminated.
@@ -242,6 +295,15 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
   }
   hlen = end - header + 1;
 
+  if((header[0] == ' ') || (header[0] == '\t')) {
+    if(data->state.prevhead)
+      /* line folding, append value to the previous header's value */
+      return unfold_value(data, header, hlen);
+    else
+      /* can't unfold without a previous header */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
   hs = calloc(1, sizeof(*hs) + hlen);
   if(!hs)
     return CURLE_OUT_OF_MEMORY;
@@ -260,7 +322,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
   /* insert this node into the list of headers */
   Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
                          hs, &hs->node);
-
+  data->state.prevhead = hs;
   return CURLE_OK;
   fail:
   free(hs);
index 48c013b..96332db 100644 (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_HEADERS_API)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
 
 struct Curl_header_store {
   struct Curl_llist_element node;
index 85b175d..dfb0db5 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2104 Keyed-Hashing for Message Authentication
  *
  ***************************************************************************/
index f7d99ce..0bfbe2e 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 7000b85..941ecac 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -179,7 +181,7 @@ create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
     len = buflen - 7;
   /* store and lower case the name */
   while(len--)
-    *ptr++ = (char)TOLOWER(*name++);
+    *ptr++ = Curl_raw_tolower(*name++);
   msnprintf(ptr, 7, ":%u", port);
 }
 
@@ -295,6 +297,31 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     }
   }
 
+  /* See if the returned entry matches the required resolve mode */
+  if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
+    int pf = PF_INET;
+    bool found = false;
+    struct Curl_addrinfo *addr = dns->addr;
+
+#ifdef PF_INET6
+    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+      pf = PF_INET6;
+#endif
+
+    while(addr) {
+      if(addr->ai_family == pf) {
+        found = true;
+        break;
+      }
+      addr = addr->ai_next;
+    }
+
+    if(!found) {
+      infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
+      dns = NULL; /* the memory deallocation is being handled by the hash */
+      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+    }
+  }
   return dns;
 }
 
@@ -461,12 +488,12 @@ Curl_cache_addr(struct Curl_easy *data,
 }
 
 #ifdef ENABLE_IPV6
-/* return a static IPv6 resolve for 'localhost' */
-static struct Curl_addrinfo *get_localhost6(int port)
+/* return a static IPv6 ::1 for the name */
+static struct Curl_addrinfo *get_localhost6(int port, const char *name)
 {
   struct Curl_addrinfo *ca;
   const size_t ss_size = sizeof(struct sockaddr_in6);
-  const size_t hostlen = strlen("localhost");
+  const size_t hostlen = strlen(name);
   struct sockaddr_in6 sa6;
   unsigned char ipv6[16];
   unsigned short port16 = (unsigned short)(port & 0xffff);
@@ -491,19 +518,19 @@ static struct Curl_addrinfo *get_localhost6(int port)
   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
   memcpy(ca->ai_addr, &sa6, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
-  strcpy(ca->ai_canonname, "localhost");
+  strcpy(ca->ai_canonname, name);
   return ca;
 }
 #else
-#define get_localhost6(x) NULL
+#define get_localhost6(x,y) NULL
 #endif
 
-/* return a static IPv4 resolve for 'localhost' */
-static struct Curl_addrinfo *get_localhost(int port)
+/* return a static IPv4 127.0.0.1 for the given name */
+static struct Curl_addrinfo *get_localhost(int port, const char *name)
 {
   struct Curl_addrinfo *ca;
   const size_t ss_size = sizeof(struct sockaddr_in);
-  const size_t hostlen = strlen("localhost");
+  const size_t hostlen = strlen(name);
   struct sockaddr_in sa;
   unsigned int ipv4;
   unsigned short port16 = (unsigned short)(port & 0xffff);
@@ -527,8 +554,8 @@ static struct Curl_addrinfo *get_localhost(int port)
   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
   memcpy(ca->ai_addr, &sa, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
-  strcpy(ca->ai_canonname, "localhost");
-  ca->ai_next = get_localhost6(port);
+  strcpy(ca->ai_canonname, name);
+  ca->ai_next = get_localhost6(port, name);
   return ca;
 }
 
@@ -544,7 +571,11 @@ bool Curl_ipv6works(struct Curl_easy *data)
        have the info kept for fast re-use */
     DEBUGASSERT(data);
     DEBUGASSERT(data->multi);
-    return data->multi->ipv6_works;
+    if(data->multi->ipv6_up == IPV6_UNKNOWN) {
+      bool works = Curl_ipv6works(NULL);
+      data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
+    }
+    return data->multi->ipv6_up == IPV6_WORKS;
   }
   else {
     int ipv6_works = -1;
@@ -581,6 +612,17 @@ bool Curl_host_is_ipnum(const char *hostname)
   return FALSE;
 }
 
+
+/* return TRUE if 'part' is a case insensitive tail of 'full' */
+static bool tailmatch(const char *full, const char *part)
+{
+  size_t plen = strlen(part);
+  size_t flen = strlen(full);
+  if(plen > flen)
+    return FALSE;
+  return strncasecompare(part, &full[flen - plen], plen);
+}
+
 /*
  * Curl_resolv() is the main name resolve function within libcurl. It resolves
  * a name and returns a pointer to the entry in the 'entry' argument (if one
@@ -716,8 +758,9 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
       if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
         return CURLRESOLV_ERROR;
 
-      if(strcasecompare(hostname, "localhost"))
-        addr = get_localhost(port);
+      if(strcasecompare(hostname, "localhost") ||
+         tailmatch(hostname, ".localhost"))
+        addr = get_localhost(port, hostname);
 #ifndef CURL_DISABLE_DOH
       else if(allowDOH && data->set.doh && !ipnum)
         addr = Curl_doh(data, hostname, port, &respwait);
@@ -991,9 +1034,9 @@ static void freednsentry(void *freethis)
 /*
  * Curl_init_dnscache() inits a new DNS cache.
  */
-void Curl_init_dnscache(struct Curl_hash *hash)
+void Curl_init_dnscache(struct Curl_hash *hash, int size)
 {
-  Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
+  Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
                  freednsentry);
 }
 
index 1db5981..9d31707 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
@@ -130,7 +132,7 @@ void Curl_resolv_unlock(struct Curl_easy *data,
                         struct Curl_dns_entry *dns);
 
 /* init a new dns cache */
-void Curl_init_dnscache(struct Curl_hash *hash);
+void Curl_init_dnscache(struct Curl_hash *hash, int hashsize);
 
 /* prune old entries from the DNS cache */
 void Curl_hostcache_prune(struct Curl_easy *data);
index 1fd7910..1dd54e8 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -110,7 +112,8 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 #endif /* CURLRES_SYNCH */
 #endif /* CURLRES_IPV4 */
 
-#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES)
+#if defined(CURLRES_IPV4) && \
+   !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA)
 
 /*
  * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function.
@@ -295,4 +298,5 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
 
   return ai;
 }
-#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) */
+#endif /* defined(CURLRES_IPV4) && !defined(CURLRES_ARES) &&
+                                   !defined(CURLRES_AMIGA) */
index c2d5f08..c62c254 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -94,8 +96,8 @@ static void dump_addrinfo(struct connectdata *conn,
  * non-ares version).
  *
  * Returns name information about the given hostname and port number. If
- * successful, the 'addrinfo' is returned and the forth argument will point to
- * memory we need to free after use. That memory *MUST* be freed with
+ * successful, the 'addrinfo' is returned and the fourth argument will point
+ * to memory we need to free after use. That memory *MUST* be freed with
  * Curl_freeaddrinfo(), nothing else.
  */
 struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
@@ -115,7 +117,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 
   *waitp = 0; /* synchronous response only */
 
-  if(Curl_ipv6works(data))
+  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
     /* The stack seems to be IPv6-enabled */
     pf = PF_UNSPEC;
 
index c00c274..ee54363 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index b9fa6f7..e3b686e 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /*
  * The Strict-Transport-Security header is defined in RFC 6797:
@@ -35,7 +37,7 @@
 #include "sendf.h"
 #include "strtoofft.h"
 #include "parsedate.h"
-#include "rand.h"
+#include "fopen.h"
 #include "rename.h"
 #include "strtoofft.h"
 
@@ -154,7 +156,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
     return CURLE_OK;
 
   do {
-    while(*p && ISSPACE(*p))
+    while(*p && ISBLANK(*p))
       p++;
     if(Curl_strncasecompare("max-age=", p, 8)) {
       bool quoted = FALSE;
@@ -165,7 +167,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
         return CURLE_BAD_FUNCTION_ARGUMENT;
 
       p += 8;
-      while(*p && ISSPACE(*p))
+      while(*p && ISBLANK(*p))
         p++;
       if(*p == '\"') {
         p++;
@@ -198,7 +200,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
         p++;
     }
 
-    while(*p && ISSPACE(*p))
+    while(*p && ISBLANK(*p))
       p++;
     if(*p == ';')
       p++;
@@ -354,8 +356,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
   struct Curl_llist_element *n;
   CURLcode result = CURLE_OK;
   FILE *out;
-  char *tempstore;
-  unsigned char randsuffix[9];
+  char *tempstore = NULL;
 
   if(!h)
     /* no cache activated */
@@ -369,17 +370,8 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
     /* marked as read-only, no file or zero length file name */
     goto skipsave;
 
-  if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
-    return CURLE_FAILED_INIT;
-
-  tempstore = aprintf("%s.%s.tmp", file, randsuffix);
-  if(!tempstore)
-    return CURLE_OUT_OF_MEMORY;
-
-  out = fopen(tempstore, FOPEN_WRITETEXT);
-  if(!out)
-    result = CURLE_WRITE_ERROR;
-  else {
+  result = Curl_fopen(data, file, &out, &tempstore);
+  if(!result) {
     fputs("# Your HSTS cache. https://curl.se/docs/hsts.html\n"
           "# This file was generated by libcurl! Edit at your own risk.\n",
           out);
@@ -391,10 +383,10 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
         break;
     }
     fclose(out);
-    if(!result && Curl_rename(tempstore, file))
+    if(!result && tempstore && Curl_rename(tempstore, file))
       result = CURLE_WRITE_ERROR;
 
-    if(result)
+    if(result && tempstore)
       unlink(tempstore);
   }
   free(tempstore);
@@ -494,8 +486,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
 
 /*
  * Load the HSTS cache from the given file. The text based line-oriented file
- * format is documented here:
- * https://github.com/curl/curl/wiki/HSTS
+ * format is documented here: https://curl.se/docs/hsts.html
  *
  * This function only returns error on major problems that prevent hsts
  * handling to work completely. It will ignore individual syntactical errors
index 653c053..0e36a77 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2020 - 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
@@ -20,6 +20,8 @@
  * 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"
 
index b215307..f57859e 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -82,6 +84,7 @@
 #include "strdup.h"
 #include "altsvc.h"
 #include "hsts.h"
+#include "ws.h"
 #include "c-hyper.h"
 
 /* The last 3 #include files should be in this order */
@@ -112,6 +115,10 @@ static int https_getsock(struct Curl_easy *data,
 #endif
 static CURLcode http_setup_conn(struct Curl_easy *data,
                                 struct connectdata *conn);
+#ifdef USE_WEBSOCKETS
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+                              struct connectdata *conn);
+#endif
 
 /*
  * HTTP handler interface.
@@ -140,6 +147,32 @@ const struct Curl_handler Curl_handler_http = {
   PROTOPT_USERPWDCTRL
 };
 
+#ifdef USE_WEBSOCKETS
+const struct Curl_handler Curl_handler_ws = {
+  "WS",                                 /* scheme */
+  ws_setup_conn,                        /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  http_getsock_do,                      /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  PORT_HTTP,                            /* defport */
+  CURLPROTO_WS,                         /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_CREDSPERREQUEST |             /* flags */
+  PROTOPT_USERPWDCTRL
+};
+#endif
+
 #ifdef USE_SSL
 /*
  * HTTPS handler interface.
@@ -164,11 +197,38 @@ const struct Curl_handler Curl_handler_https = {
   PORT_HTTPS,                           /* defport */
   CURLPROTO_HTTPS,                      /* protocol */
   CURLPROTO_HTTP,                       /* family */
-  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN_NPN | /* flags */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
+  PROTOPT_USERPWDCTRL
+};
+
+#ifdef USE_WEBSOCKETS
+const struct Curl_handler Curl_handler_wss = {
+  "WSS",                                /* scheme */
+  ws_setup_conn,                        /* setup_connection */
+  Curl_http,                            /* do_it */
+  Curl_http_done,                       /* done */
+  ZERO_NULL,                            /* do_more */
+  Curl_http_connect,                    /* connect_it */
+  https_connecting,                     /* connecting */
+  ZERO_NULL,                            /* doing */
+  https_getsock,                        /* proto_getsock */
+  http_getsock_do,                      /* doing_getsock */
+  ZERO_NULL,                            /* domore_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  ZERO_NULL,                            /* readwrite */
+  ZERO_NULL,                            /* connection_check */
+  ZERO_NULL,                            /* attach connection */
+  PORT_HTTPS,                           /* defport */
+  CURLPROTO_WSS,                        /* protocol */
+  CURLPROTO_HTTP,                       /* family */
+  PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
   PROTOPT_USERPWDCTRL
 };
 #endif
 
+#endif
+
 static CURLcode http_setup_conn(struct Curl_easy *data,
                                 struct connectdata *conn)
 {
@@ -203,6 +263,16 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+#ifdef USE_WEBSOCKETS
+static CURLcode ws_setup_conn(struct Curl_easy *data,
+                              struct connectdata *conn)
+{
+  /* websockets is 1.1 only (for now) */
+  data->state.httpwant = CURL_HTTP_VERSION_1_1;
+  return http_setup_conn(data, conn);
+}
+#endif
+
 #ifndef CURL_DISABLE_PROXY
 /*
  * checkProxyHeaders() checks the linked list of custom proxy headers
@@ -651,21 +721,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
   return result;
 }
 
-/*
- * Curl_allow_auth_to_host() tells if authentication, cookies or other
- * "sensitive data" can (still) be sent to this host.
- */
-bool Curl_allow_auth_to_host(struct Curl_easy *data)
-{
-  struct connectdata *conn = data->conn;
-  return (!data->state.this_is_a_follow ||
-          data->set.allow_auth_to_other_hosts ||
-          (data->state.first_host &&
-           strcasecompare(data->state.first_host, conn->host.name) &&
-           (data->state.first_remote_port == conn->remote_port) &&
-           (data->state.first_remote_protocol == conn->handler->protocol)));
-}
-
 #ifndef CURL_DISABLE_HTTP_AUTH
 /*
  * Output the correct authentication header depending on the auth type
@@ -864,7 +919,7 @@ Curl_http_output_auth(struct Curl_easy *data,
 
   /* To prevent the user+password to get sent to other than the original host
      due to a location-follow */
-  if(Curl_allow_auth_to_host(data)
+  if(Curl_auth_allowed_to_host(data)
 #ifndef CURL_DISABLE_NETRC
      || conn->bits.netrc
 #endif
@@ -1516,7 +1571,7 @@ CURLcode Curl_http_connect(struct Curl_easy *data, bool *done)
   }
 #endif
 
-  if(conn->given->protocol & CURLPROTO_HTTPS) {
+  if(conn->given->flags & PROTOPT_SSL) {
     /* perform SSL initialization */
     result = https_connecting(data, done);
     if(result)
@@ -1641,6 +1696,7 @@ CURLcode Curl_http_done(struct Curl_easy *data,
   Curl_mime_cleanpart(&http->form);
   Curl_dyn_reset(&data->state.headerb);
   Curl_hyper_done(data);
+  Curl_ws_done(data);
 
   if(status)
     return status;
@@ -1917,7 +1973,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
                    checkprefix("Cookie:", compare)) &&
                   /* be careful of sending this potentially sensitive header to
                      other hosts */
-                  !Curl_allow_auth_to_host(data))
+                  !Curl_auth_allowed_to_host(data))
             ;
           else {
 #ifdef USE_HYPER
@@ -2030,7 +2086,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
 void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
                       const char **method, Curl_HttpReq *reqp)
 {
-  Curl_HttpReq httpreq = data->state.httpreq;
+  Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq;
   const char *request;
   if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
      data->set.upload)
@@ -2149,9 +2205,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
        [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
     const char *host = conn->host.name;
 
-    if(((conn->given->protocol&CURLPROTO_HTTPS) &&
+    if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) &&
         (conn->remote_port == PORT_HTTPS)) ||
-       ((conn->given->protocol&CURLPROTO_HTTP) &&
+       ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) &&
         (conn->remote_port == PORT_HTTP)) )
       /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
          the port number in the host string */
@@ -2700,6 +2756,13 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
                               FIRSTSOCKET);
     if(result)
       failf(data, "Failed sending HTTP request");
+#ifdef USE_WEBSOCKETS
+    else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) &&
+            !(data->set.connect_only))
+      /* Set up the transfer for two-way since without CONNECT_ONLY set, this
+         request probably wants to send data too post upgrade */
+      Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
+#endif
     else
       /* HTTP GET/HEAD download: */
       Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
@@ -2709,12 +2772,14 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
 }
 
 #if !defined(CURL_DISABLE_COOKIES)
+
 CURLcode Curl_http_cookies(struct Curl_easy *data,
                            struct connectdata *conn,
                            struct dynbuf *r)
 {
   CURLcode result = CURLE_OK;
   char *addcookies = NULL;
+  bool linecap = FALSE;
   if(data->set.str[STRING_COOKIE] &&
      !Curl_checkheaders(data, STRCONST("Cookie")))
     addcookies = data->set.str[STRING_COOKIE];
@@ -2727,12 +2792,12 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
       const char *host = data->state.aptr.cookiehost ?
         data->state.aptr.cookiehost : conn->host.name;
       const bool secure_context =
-        conn->handler->protocol&CURLPROTO_HTTPS ||
+        conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
         strcasecompare("localhost", host) ||
         !strcmp(host, "127.0.0.1") ||
         !strcmp(host, "[::1]") ? TRUE : FALSE;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-      co = Curl_cookie_getlist(data->cookies, host, data->state.up.path,
+      co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path,
                                secure_context);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
     }
@@ -2746,6 +2811,13 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
             if(result)
               break;
           }
+          if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >=
+             MAX_COOKIE_HEADER_LEN) {
+            infof(data, "Restricted outgoing cookies due to header size, "
+                  "'%s' not sent", co->name);
+            linecap = TRUE;
+            break;
+          }
           result = Curl_dyn_addf(r, "%s%s=%s", count?"; ":"",
                                  co->name, co->value);
           if(result)
@@ -2756,7 +2828,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
       }
       Curl_cookie_freelist(store);
     }
-    if(addcookies && !result) {
+    if(addcookies && !result && !linecap) {
       if(!count)
         result = Curl_dyn_addn(r, STRCONST("Cookie: "));
       if(!result) {
@@ -3033,7 +3105,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(conn->transport != TRNSPRT_QUIC) {
     if(conn->httpversion < 20) { /* unless the connection is re-used and
                                     already http2 */
-      switch(conn->negnpn) {
+      switch(conn->alpn) {
       case CURL_HTTP_VERSION_2:
         conn->httpversion = 20; /* we know we're on HTTP/2 now */
 
@@ -3245,6 +3317,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   }
 
   result = Curl_http_cookies(data, conn, &req);
+  if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
+    result = Curl_ws_request(data, &req);
   if(!result)
     result = Curl_add_timecondition(data, &req);
   if(!result)
@@ -3509,15 +3583,15 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
   else if(checkprefix("Retry-After:", headp)) {
     /* Retry-After = HTTP-date / delay-seconds */
     curl_off_t retry_after = 0; /* zero for unknown or "now" */
-    time_t date = Curl_getdate_capped(headp + strlen("Retry-After:"));
-    if(-1 == date) {
-      /* not a date, try it as a decimal number */
-      (void)curlx_strtoofft(headp + strlen("Retry-After:"),
-                            NULL, 10, &retry_after);
+    /* Try it as a decimal number, if it works it is not a date */
+    (void)curlx_strtoofft(headp + strlen("Retry-After:"),
+                          NULL, 10, &retry_after);
+    if(!retry_after) {
+      time_t date = Curl_getdate_capped(headp + strlen("Retry-After:"));
+      if(-1 != date)
+        /* convert date to number of seconds into the future */
+        retry_after = date - time(NULL);
     }
-    else
-      /* convert date to number of seconds into the future */
-      retry_after = date - time(NULL);
     data->info.retry_after = retry_after; /* store it */
   }
   else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) {
@@ -3529,7 +3603,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
        The second format was added since Sun's webserver
        JavaWebServer/1.1.1 obviously sends the header this way!
        The third added since some servers use that!
-       The forth means the requested range was unsatisfied.
+       The fourth means the requested range was unsatisfied.
     */
 
     char *ptr = headp + strlen("Content-Range:");
@@ -3557,7 +3631,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
     const char *host = data->state.aptr.cookiehost?
       data->state.aptr.cookiehost:conn->host.name;
     const bool secure_context =
-      conn->handler->protocol&CURLPROTO_HTTPS ||
+      conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
       strcasecompare("localhost", host) ||
       !strcmp(host, "127.0.0.1") ||
       !strcmp(host, "[::1]") ? TRUE : FALSE;
@@ -3641,7 +3715,14 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
 #ifndef CURL_DISABLE_HSTS
   /* If enabled, the header is incoming and this is over HTTPS */
   else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) &&
-          (conn->handler->flags & PROTOPT_SSL)) {
+          ((conn->handler->flags & PROTOPT_SSL) ||
+#ifdef CURLDEBUG
+           /* allow debug builds to circumvent the HTTPS restriction */
+           getenv("CURL_HSTS_HTTP")
+#else
+           0
+#endif
+            )) {
     CURLcode check =
       Curl_hsts_parse(data->hsts, data->state.up.hostname,
                       headp + strlen("Strict-Transport-Security:"));
@@ -3723,7 +3804,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
     connclose(conn, "HTTP/1.0 close after body");
   }
   else if(conn->httpversion == 20 ||
-          (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) {
+          (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) {
     DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
     /* HTTP/2 cannot avoid multiplexing since it is a core functionality
        of the protocol */
@@ -3799,11 +3880,16 @@ static CURLcode verify_header(struct Curl_easy *data)
   if(k->headerline < 2)
     /* the first "header" is the status-line and it has no colon */
     return CURLE_OK;
-  ptr = memchr(header, ':', hlen);
-  if(!ptr) {
-    /* this is bad, bail out */
-    failf(data, "Header without colon");
-    return CURLE_WEIRD_SERVER_REPLY;
+  if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2)
+    /* line folding, can't happen on line 2 */
+    ;
+  else {
+    ptr = memchr(header, ':', hlen);
+    if(!ptr) {
+      /* this is bad, bail out */
+      failf(data, "Header without colon");
+      return CURLE_WEIRD_SERVER_REPLY;
+    }
   }
   return CURLE_OK;
 }
@@ -3944,9 +4030,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           break;
         case 101:
           /* Switching Protocols */
-          if(k->upgr101 == UPGR101_REQUESTED) {
+          if(k->upgr101 == UPGR101_H2) {
             /* Switching to HTTP/2 */
-            infof(data, "Received 101");
+            infof(data, "Received 101, Switching to HTTP/2");
             k->upgr101 = UPGR101_RECEIVED;
 
             /* we'll get more headers (HTTP/2 response) */
@@ -3960,8 +4046,21 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
               return result;
             *nread = 0;
           }
+#ifdef USE_WEBSOCKETS
+          else if(k->upgr101 == UPGR101_WS) {
+            /* verify the response */
+            result = Curl_ws_accept(data);
+            if(result)
+              return result;
+            k->header = FALSE; /* no more header to parse! */
+            if(data->set.connect_only) {
+              k->keepon &= ~KEEP_RECV; /* read no more content */
+              *nread = 0;
+            }
+          }
+#endif
           else {
-            /* Switching to another protocol (e.g. WebSocket) */
+            /* Not switching to another protocol */
             k->header = FALSE; /* no more header to parse! */
           }
           break;
@@ -4054,6 +4153,16 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
         return CURLE_HTTP_RETURNED_ERROR;
       }
 
+#ifdef USE_WEBSOCKETS
+      /* All non-101 HTTP status codes are bad when wanting to upgrade to
+         websockets */
+      if(data->req.upgr101 == UPGR101_WS) {
+        failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
+        return CURLE_HTTP_RETURNED_ERROR;
+      }
+#endif
+
+
       data->req.deductheadercount =
         (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
 
index c4ab3c2..f7cbb34 100644 (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 "ws.h"
 
 typedef enum {
   HTTPREQ_GET,
@@ -48,6 +51,15 @@ extern const struct Curl_handler Curl_handler_http;
 extern const struct Curl_handler Curl_handler_https;
 #endif
 
+#ifdef USE_WEBSOCKETS
+extern const struct Curl_handler Curl_handler_ws;
+
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_wss;
+#endif
+#endif /* websockets */
+
+
 /* Header specific functions */
 bool Curl_compareheader(const char *headerline,  /* line to check */
                         const char *header,   /* header keyword _with_ colon */
@@ -216,6 +228,10 @@ struct HTTP {
     HTTPSEND_BODY     /* sending body */
   } sending;
 
+#ifdef USE_WEBSOCKETS
+  struct websocket ws;
+#endif
+
 #ifndef CURL_DISABLE_HTTP
   struct dynbuf send_buffer; /* used if the request couldn't be sent in one
                                 chunk, points to an allocated send_buffer
@@ -225,13 +241,11 @@ struct HTTP {
   /*********** for HTTP/2 we store stream-local data here *************/
   int32_t stream_id; /* stream we are interested in */
 
-  bool bodystarted;
   /* We store non-final and final response headers here, per-stream */
   struct dynbuf header_recvbuf;
   size_t nread_header_recvbuf; /* number of bytes in header_recvbuf fed into
                                   upper layer */
   struct dynbuf trailer_recvbuf;
-  int status_code; /* HTTP status code */
   const uint8_t *pausedata; /* pointer to data received in on_data_chunk */
   size_t pauselen; /* the number of bytes left in data */
   bool close_handled; /* TRUE if stream closure is handled by libcurl */
@@ -242,6 +256,8 @@ struct HTTP {
   uint32_t error; /* HTTP/2 stream error code */
 #endif
 #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 */
@@ -258,6 +274,7 @@ struct HTTP {
 #ifndef USE_MSH3
   /*********** for HTTP/3 we store stream-local data here *************/
   int64_t stream3_id; /* stream we are interested in */
+  uint64_t error3; /* HTTP/3 stream error code */
   bool firstheader;  /* FALSE until headers arrive */
   bool firstbody;  /* FALSE until body arrives */
   bool h3req;    /* FALSE until request is issued */
@@ -364,10 +381,4 @@ Curl_http_output_auth(struct Curl_easy *data,
                       bool proxytunnel); /* TRUE if this is the request setting
                                             up the proxy tunnel */
 
-/*
- * Curl_allow_auth_to_host() tells if authentication, cookies or other
- * "sensitive data" can (still) be sent to this host.
- */
-bool Curl_allow_auth_to_host(struct Curl_easy *data);
-
 #endif /* HEADER_CURL_HTTP_H */
index 0120b86..b7409b0 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -643,7 +645,7 @@ static int push_promise(struct Curl_easy *data,
                                               frame->promised_stream_id,
                                               newhandle);
     if(rv) {
-      infof(data, "failed to set user_data for stream %d",
+      infof(data, "failed to set user_data for stream %u",
             frame->promised_stream_id);
       DEBUGASSERT(0);
       rv = CURL_PUSH_DENY;
@@ -713,19 +715,19 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
   if(!data_s) {
     H2BUGF(infof(data,
-                 "No Curl_easy associated with stream: %x",
+                 "No Curl_easy associated with stream: %u",
                  stream_id));
     return 0;
   }
 
   stream = data_s->req.p.http;
   if(!stream) {
-    H2BUGF(infof(data_s, "No proto pointer for stream: %x",
+    H2BUGF(infof(data_s, "No proto pointer for stream: %u",
                  stream_id));
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
-  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x",
+  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %u",
                frame->hd.type, stream_id));
 
   switch(frame->hd.type) {
@@ -913,7 +915,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
     /* 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 %d",
+      infof(data_s, "http/2: failed to clear user_data for stream %u",
             stream_id);
       DEBUGASSERT(0);
     }
@@ -1050,6 +1052,12 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     else if(stream->push_headers_used ==
             stream->push_headers_alloc) {
       char **headp;
+      if(stream->push_headers_alloc > 1000) {
+        /* this is beyond crazy many headers, bail out */
+        failf(data_s, "Too many PUSH_PROMISE headers");
+        Curl_safefree(stream->push_headers);
+        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+      }
       stream->push_headers_alloc *= 2;
       headp = Curl_saferealloc(stream->push_headers,
                                stream->push_headers_alloc * sizeof(char *));
@@ -1240,13 +1248,13 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
 
   /* 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 (%x)", http->stream_id));
+    H2BUGF(infof(data, "DONE the pause stream (%u)", http->stream_id));
     httpc->pause_stream_id = 0;
   }
   if(premature || (!http->closed && http->stream_id)) {
     /* RST_STREAM */
     set_transfer(httpc, data); /* set the transfer */
-    H2BUGF(infof(data, "RST stream %x", http->stream_id));
+    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);
@@ -1260,7 +1268,7 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
     int rv = nghttp2_session_set_stream_user_data(httpc->h2,
                                                   http->stream_id, 0);
     if(rv) {
-      infof(data, "http/2: failed to clear user_data for stream %d",
+      infof(data, "http/2: failed to clear user_data for stream %u",
             http->stream_id);
       DEBUGASSERT(0);
     }
@@ -1269,6 +1277,27 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
   }
 }
 
+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
  */
@@ -1309,7 +1338,7 @@ static CURLcode http2_init(struct Curl_easy *data, struct connectdata *conn)
     nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
 
     /* The nghttp2 session is not yet setup, do it */
-    rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
+    rc = client_new(conn, callbacks);
 
     nghttp2_session_callbacks_del(callbacks);
 
@@ -1363,7 +1392,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
                          NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
   free(base64);
 
-  k->upgr101 = UPGR101_REQUESTED;
+  k->upgr101 = UPGR101_H2;
 
   return result;
 }
@@ -1519,7 +1548,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
   /* 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 (%d), try again on a new connection",
+    H2BUGF(infof(data, "REFUSED_STREAM (%u), try again on a new connection",
                  stream->stream_id));
     connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
     data->state.refused_stream = TRUE;
@@ -1527,7 +1556,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
     return -1;
   }
   else if(stream->error != NGHTTP2_NO_ERROR) {
-    failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
+    failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
           stream->stream_id, nghttp2_http2_strerror(stream->error),
           stream->error);
     *err = CURLE_HTTP2_STREAM;
@@ -1535,7 +1564,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
   }
 
   if(!stream->bodystarted) {
-    failf(data, "HTTP/2 stream %d was closed cleanly, but before getting "
+    failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
           " all response header fields, treated as error",
           stream->stream_id);
     *err = CURLE_HTTP2_STREAM;
@@ -1740,7 +1769,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
     if(stream->closed)
       /* closed overrides paused */
       return 0;
-    H2BUGF(infof(data, "stream %x is paused, pause id: %x",
+    H2BUGF(infof(data, "stream %u is paused, pause id: %u",
                  stream->stream_id, httpc->pause_stream_id));
     *err = CURLE_AGAIN;
     return -1;
@@ -1771,7 +1800,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
           /* 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 %d was not closed cleanly before"
+          failf(data, "HTTP/2 stream %u was not closed cleanly before"
                 " end of the underlying stream",
                 stream->stream_id);
           *err = CURLE_HTTP2_STREAM;
@@ -1855,7 +1884,7 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
 
   if(stream->stream_id != -1) {
     if(stream->close_handled) {
-      infof(data, "stream %d closed", stream->stream_id);
+      infof(data, "stream %u closed", stream->stream_id);
       *err = CURLE_HTTP2_STREAM;
       return -1;
     }
@@ -1968,13 +1997,13 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
 
   if(stream_id < 0) {
     H2BUGF(infof(data,
-                 "http2_send() nghttp2_submit_request error (%s)%d",
+                 "http2_send() nghttp2_submit_request error (%s)%u",
                  nghttp2_strerror(stream_id), stream_id));
     *err = CURLE_SEND_ERROR;
     return -1;
   }
 
-  infof(data, "Using Stream ID: %x (easy handle %p)",
+  infof(data, "Using Stream ID: %u (easy handle %p)",
         stream_id, (void *)data);
   stream->stream_id = stream_id;
 
@@ -2094,7 +2123,7 @@ CURLcode Curl_http2_switched(struct Curl_easy *data,
                                               stream->stream_id,
                                               data);
     if(rv) {
-      infof(data, "http/2: failed to set user_data for stream %d",
+      infof(data, "http/2: failed to set user_data for stream %u",
             stream->stream_id);
       DEBUGASSERT(0);
     }
index d6986d9..f039059 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 210c3db..440eb38 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -27,8 +29,6 @@
 #include "urldata.h"
 #include "strcase.h"
 #include "strdup.h"
-#include "vauth/vauth.h"
-#include "vauth/digest.h"
 #include "http_aws_sigv4.h"
 #include "curl_sha256.h"
 #include "transfer.h"
@@ -44,6 +44,8 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#include "slist.h"
+
 #define HMAC_SHA256(k, kl, d, dl, o)        \
   do {                                      \
     ret = Curl_hmacit(Curl_HMAC_SHA256,     \
                       (unsigned int)kl,     \
                       (unsigned char *)d,   \
                       (unsigned int)dl, o); \
-    if(ret != CURLE_OK) {                   \
+    if(ret) {                               \
       goto fail;                            \
     }                                       \
   } while(0)
 
+#define TIMESTAMP_SIZE 17
+
 static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
 {
   int i;
 
   DEBUGASSERT(dst_l >= 65);
   for(i = 0; i < 32; ++i) {
-    curl_msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+    msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+  }
+}
+
+static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
+{
+  char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
+
+  if(tmp)
+    return tmp;
+  return Curl_checkheaders(data, STRCONST("Date"));
+}
+
+/* remove whitespace, and lowercase all headers */
+static void trim_headers(struct curl_slist *head)
+{
+  struct curl_slist *l;
+  for(l = head; l; l = l->next) {
+    char *value; /* to read from */
+    char *store;
+    size_t colon = strcspn(l->data, ":");
+    Curl_strntolower(l->data, l->data, colon);
+
+    value = &l->data[colon];
+    if(!*value)
+      continue;
+    ++value;
+    store = value;
+
+    /* skip leading whitespace */
+    while(*value && ISBLANK(*value))
+      value++;
+
+    while(*value) {
+      int space = 0;
+      while(*value && ISBLANK(*value)) {
+        value++;
+        space++;
+      }
+      if(space) {
+        /* replace any number of consecutive whitespace with a single space,
+           unless at the end of the string, then nothing */
+        if(*value)
+          *store++ = ' ';
+      }
+      else
+        *store++ = *value++;
+    }
+    *store = 0; /* null terminate */
+  }
+}
+
+/* maximum lenth for the aws sivg4 parts */
+#define MAX_SIGV4_LEN 64
+#define MAX_SIGV4_LEN_TXT "64"
+
+#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
+
+#define MAX_HOST_LEN 255
+/* FQDN + host: */
+#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:"))
+
+/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
+#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
+
+/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
+static CURLcode make_headers(struct Curl_easy *data,
+                             const char *hostname,
+                             char *timestamp,
+                             char *provider1,
+                             char **date_header,
+                             struct dynbuf *canonical_headers,
+                             struct dynbuf *signed_headers)
+{
+  char date_hdr_key[DATE_HDR_KEY_LEN];
+  char date_full_hdr[DATE_FULL_HDR_LEN];
+  struct curl_slist *head = NULL;
+  struct curl_slist *tmp_head = NULL;
+  CURLcode ret = CURLE_OUT_OF_MEMORY;
+  struct curl_slist *l;
+  int again = 1;
+
+  /* provider1 mid */
+  Curl_strntolower(provider1, provider1, strlen(provider1));
+  provider1[0] = Curl_raw_toupper(provider1[0]);
+
+  msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
+
+  /* provider1 lowercase */
+  Curl_strntolower(provider1, provider1, 1); /* first byte only */
+  msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
+            "x-%s-date:%s", provider1, timestamp);
+
+  if(Curl_checkheaders(data, STRCONST("Host"))) {
+    head = NULL;
+  }
+  else {
+    char full_host[FULL_HOST_LEN + 1];
+
+    if(data->state.aptr.host) {
+      size_t pos;
+
+      if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
+        ret = CURLE_URL_MALFORMAT;
+        goto fail;
+      }
+      strcpy(full_host, data->state.aptr.host);
+      /* remove /r/n as the separator for canonical request must be '\n' */
+      pos = strcspn(full_host, "\n\r");
+      full_host[pos] = 0;
+    }
+    else {
+      if(strlen(hostname) > MAX_HOST_LEN) {
+        ret = CURLE_URL_MALFORMAT;
+        goto fail;
+      }
+      msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
+    }
+
+    head = curl_slist_append(NULL, full_host);
+    if(!head)
+      goto fail;
+  }
+
+
+  for(l = data->set.headers; l; l = l->next) {
+    tmp_head = curl_slist_append(head, l->data);
+    if(!tmp_head)
+      goto fail;
+    head = tmp_head;
+  }
+
+  trim_headers(head);
+
+  *date_header = find_date_hdr(data, date_hdr_key);
+  if(!*date_header) {
+    tmp_head = curl_slist_append(head, date_full_hdr);
+    if(!tmp_head)
+      goto fail;
+    head = tmp_head;
+    *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
+  }
+  else {
+    char *value;
+
+    *date_header = strdup(*date_header);
+    if(!*date_header)
+      goto fail;
+
+    value = strchr(*date_header, ':');
+    if(!value)
+      goto fail;
+    ++value;
+    while(ISBLANK(*value))
+      ++value;
+    strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
+    timestamp[TIMESTAMP_SIZE - 1] = 0;
+  }
+
+  /* alpha-sort in a case sensitive manner */
+  do {
+    again = 0;
+    for(l = head; l; l = l->next) {
+      struct curl_slist *next = l->next;
+
+      if(next && strcmp(l->data, next->data) > 0) {
+        char *tmp = l->data;
+
+        l->data = next->data;
+        next->data = tmp;
+        again = 1;
+      }
+    }
+  } while(again);
+
+  for(l = head; l; l = l->next) {
+    char *tmp;
+
+    if(Curl_dyn_add(canonical_headers, l->data))
+      goto fail;
+    if(Curl_dyn_add(canonical_headers, "\n"))
+      goto fail;
+
+    tmp = strchr(l->data, ':');
+    if(tmp)
+      *tmp = 0;
+
+    if(l != head) {
+      if(Curl_dyn_add(signed_headers, ";"))
+        goto fail;
+    }
+    if(Curl_dyn_add(signed_headers, l->data))
+      goto fail;
   }
+
+  ret = CURLE_OK;
+fail:
+  curl_slist_free_all(head);
+
+  return ret;
 }
 
 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
@@ -71,29 +273,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   CURLcode ret = CURLE_OUT_OF_MEMORY;
   struct connectdata *conn = data->conn;
   size_t len;
-  const char *tmp0;
-  const char *tmp1;
-  char *provider0_low = NULL;
-  char *provider0_up = NULL;
-  char *provider1_low = NULL;
-  char *provider1_mid = NULL;
-  char *region = NULL;
-  char *service = NULL;
+  const char *arg;
+  char provider0[MAX_SIGV4_LEN + 1]="";
+  char provider1[MAX_SIGV4_LEN + 1]="";
+  char region[MAX_SIGV4_LEN + 1]="";
+  char service[MAX_SIGV4_LEN + 1]="";
   const char *hostname = conn->host.name;
-#ifdef DEBUGBUILD
-  char *force_timestamp;
-#endif
   time_t clock;
   struct tm tm;
-  char timestamp[17];
+  char timestamp[TIMESTAMP_SIZE];
   char date[9];
-  const char *content_type = Curl_checkheaders(data, STRCONST("Content-Type"));
-  char *canonical_headers = NULL;
-  char *signed_headers = NULL;
-  Curl_HttpReq httpreq;
-  const char *method;
-  size_t post_data_len;
-  const char *post_data = data->set.postfields ? data->set.postfields : "";
+  struct dynbuf canonical_headers;
+  struct dynbuf signed_headers;
+  char *date_header = NULL;
+  const char *post_data = data->set.postfields;
+  size_t post_data_len = 0;
   unsigned char sha_hash[32];
   char sha_hex[65];
   char *canonical_request = NULL;
@@ -101,10 +295,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   char *credential_scope = NULL;
   char *str_to_sign = NULL;
   const char *user = data->state.aptr.user ? data->state.aptr.user : "";
-  const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : "";
   char *secret = NULL;
-  unsigned char tmp_sign0[32] = {0};
-  unsigned char tmp_sign1[32] = {0};
+  unsigned char sign0[32] = {0};
+  unsigned char sign1[32] = {0};
   char *auth_headers = NULL;
 
   DEBUGASSERT(!proxy);
@@ -115,6 +308,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     return CURLE_OK;
   }
 
+  /* we init thoses buffers here, so goto fail will free initialized dynbuf */
+  Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+  Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+
   /*
    * Parameters parsing
    * Google and Outscale use the same OSC or GOOG,
@@ -122,223 +319,154 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
    * AWS is the default because most of non-amazon providers
    * are still using aws:amz as a prefix.
    */
-  tmp0 = data->set.str[STRING_AWS_SIGV4] ?
+  arg = data->set.str[STRING_AWS_SIGV4] ?
     data->set.str[STRING_AWS_SIGV4] : "aws:amz";
-  tmp1 = strchr(tmp0, ':');
-  len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
-  if(len < 1) {
-    infof(data, "first provider can't be empty");
+
+  /* provider1[:provider2[:region[:service]]]
+
+     No string can be longer than N bytes of non-whitespace
+   */
+  (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
+               ":%" MAX_SIGV4_LEN_TXT "[^:]"
+               ":%" MAX_SIGV4_LEN_TXT "[^:]"
+               ":%" MAX_SIGV4_LEN_TXT "s",
+               provider0, provider1, region, service);
+  if(!provider0[0]) {
+    failf(data, "first provider can't be empty");
     ret = CURLE_BAD_FUNCTION_ARGUMENT;
     goto fail;
   }
-  provider0_low = malloc(len + 1);
-  provider0_up = malloc(len + 1);
-  if(!provider0_low || !provider0_up) {
-    goto fail;
-  }
-  Curl_strntolower(provider0_low, tmp0, len);
-  provider0_low[len] = '\0';
-  Curl_strntoupper(provider0_up, tmp0, len);
-  provider0_up[len] = '\0';
-
-  if(tmp1) {
-    tmp0 = tmp1 + 1;
-    tmp1 = strchr(tmp0, ':');
-    len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
-    if(len < 1) {
-      infof(data, "second provider can't be empty");
-      ret = CURLE_BAD_FUNCTION_ARGUMENT;
-      goto fail;
-    }
-    provider1_low = malloc(len + 1);
-    provider1_mid = malloc(len + 1);
-    if(!provider1_low || !provider1_mid) {
-      goto fail;
-    }
-    Curl_strntolower(provider1_low, tmp0, len);
-    provider1_low[len] = '\0';
-    Curl_strntolower(provider1_mid, tmp0, len);
-    provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
-    provider1_mid[len] = '\0';
-
-    if(tmp1) {
-      tmp0 = tmp1 + 1;
-      tmp1 = strchr(tmp0, ':');
-      len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
-      if(len < 1) {
-        infof(data, "region can't be empty");
-        ret = CURLE_BAD_FUNCTION_ARGUMENT;
-        goto fail;
-      }
-      region = Curl_memdup(tmp0, len + 1);
-      if(!region) {
-        goto fail;
-      }
-      region[len] = '\0';
+  else if(!provider1[0])
+    strcpy(provider1, provider0);
 
-      if(tmp1) {
-        tmp0 = tmp1 + 1;
-        service = strdup(tmp0);
-        if(!service) {
-          goto fail;
-        }
-        if(strlen(service) < 1) {
-          infof(data, "service can't be empty");
-          ret = CURLE_BAD_FUNCTION_ARGUMENT;
-          goto fail;
-        }
-      }
-    }
-  }
-  else {
-    provider1_low = Curl_memdup(provider0_low, len + 1);
-    provider1_mid = Curl_memdup(provider0_low, len + 1);
-    if(!provider1_low || !provider1_mid) {
-      goto fail;
-    }
-    provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
-  }
-
-  if(!service) {
-    tmp0 = hostname;
-    tmp1 = strchr(tmp0, '.');
-    len = tmp1 - tmp0;
-    if(!tmp1 || len < 1) {
-      infof(data, "service missing in parameters or hostname");
+  if(!service[0]) {
+    char *hostdot = strchr(hostname, '.');
+    if(!hostdot) {
+      failf(data, "service missing in parameters and hostname");
       ret = CURLE_URL_MALFORMAT;
       goto fail;
     }
-    service = Curl_memdup(tmp0, len + 1);
-    if(!service) {
+    len = hostdot - hostname;
+    if(len > MAX_SIGV4_LEN) {
+      failf(data, "service too long in hostname");
+      ret = CURLE_URL_MALFORMAT;
       goto fail;
     }
+    strncpy(service, hostname, len);
     service[len] = '\0';
 
-    if(!region) {
-      tmp0 = tmp1 + 1;
-      tmp1 = strchr(tmp0, '.');
-      len = tmp1 - tmp0;
-      if(!tmp1 || len < 1) {
-        infof(data, "region missing in parameters or hostname");
+    if(!region[0]) {
+      const char *reg = hostdot + 1;
+      const char *hostreg = strchr(reg, '.');
+      if(!hostreg) {
+        failf(data, "region missing in parameters and hostname");
         ret = CURLE_URL_MALFORMAT;
         goto fail;
       }
-      region = Curl_memdup(tmp0, len + 1);
-      if(!region) {
+      len = hostreg - reg;
+      if(len > MAX_SIGV4_LEN) {
+        failf(data, "region too long in hostname");
+        ret = CURLE_URL_MALFORMAT;
         goto fail;
       }
+      strncpy(region, reg, len);
       region[len] = '\0';
     }
   }
 
 #ifdef DEBUGBUILD
-  force_timestamp = getenv("CURL_FORCETIME");
-  if(force_timestamp)
-    clock = 0;
-  else
-    time(&clock);
+  {
+    char *force_timestamp = getenv("CURL_FORCETIME");
+    if(force_timestamp)
+      clock = 0;
+    else
+      time(&clock);
+  }
 #else
   time(&clock);
 #endif
   ret = Curl_gmtime(clock, &tm);
-  if(ret != CURLE_OK) {
+  if(ret) {
     goto fail;
   }
   if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
+    ret = CURLE_OUT_OF_MEMORY;
     goto fail;
   }
+
+  ret = make_headers(data, hostname, timestamp, provider1,
+                     &date_header, &canonical_headers, &signed_headers);
+  if(ret)
+    goto fail;
+  ret = CURLE_OUT_OF_MEMORY;
+
   memcpy(date, timestamp, sizeof(date));
   date[sizeof(date) - 1] = 0;
 
-  if(content_type) {
-    content_type = strchr(content_type, ':');
-    if(!content_type) {
-      ret = CURLE_FAILED_INIT;
-      goto fail;
-    }
-    content_type++;
-    /* Skip whitespace now */
-    while(*content_type == ' ' || *content_type == '\t')
-      ++content_type;
-
-    canonical_headers = curl_maprintf("content-type:%s\n"
-                                      "host:%s\n"
-                                      "x-%s-date:%s\n",
-                                      content_type,
-                                      hostname,
-                                      provider1_low, timestamp);
-    signed_headers = curl_maprintf("content-type;host;x-%s-date",
-                                   provider1_low);
-  }
-  else {
-    canonical_headers = curl_maprintf("host:%s\n"
-                                      "x-%s-date:%s\n",
-                                      hostname,
-                                      provider1_low, timestamp);
-    signed_headers = curl_maprintf("host;x-%s-date", provider1_low);
-  }
-
-  if(!canonical_headers || !signed_headers) {
-    goto fail;
+  if(post_data) {
+    if(data->set.postfieldsize < 0)
+      post_data_len = strlen(post_data);
+    else
+      post_data_len = (size_t)data->set.postfieldsize;
   }
-
-  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)) {
+                   post_data_len))
     goto fail;
-  }
 
   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
 
-  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 : "",
-                  canonical_headers,
-                  signed_headers,
-                  sha_hex);
-  if(!canonical_request) {
-    goto fail;
+  {
+    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),
+                    sha_hex);
+    if(!canonical_request)
+      goto fail;
   }
 
-  request_type = curl_maprintf("%s4_request", provider0_low);
-  if(!request_type) {
+  /* provider 0 lowercase */
+  Curl_strntolower(provider0, provider0, strlen(provider0));
+  request_type = curl_maprintf("%s4_request", provider0);
+  if(!request_type)
     goto fail;
-  }
 
   credential_scope = curl_maprintf("%s/%s/%s/%s",
                                    date, region, service, request_type);
-  if(!credential_scope) {
+  if(!credential_scope)
     goto fail;
-  }
 
   if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
-                   strlen(canonical_request))) {
+                   strlen(canonical_request)))
     goto fail;
-  }
 
   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
 
+  /* provider 0 uppercase */
+  Curl_strntoupper(provider0, provider0, strlen(provider0));
+
   /*
-   * Google allow to use rsa key instead of HMAC, so this code might change
-   * In the future, but for now we support only HMAC version
+   * Google allows using RSA key instead of HMAC, so this code might change
+   * in the future. For now we ony support HMAC.
    */
   str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
                               "%s\n" /* RequestDateTime */
                               "%s\n" /* CredentialScope */
                               "%s",  /* HashedCanonicalRequest in hex */
-                              provider0_up,
+                              provider0,
                               timestamp,
                               credential_scope,
                               sha_hex);
@@ -346,36 +474,33 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     goto fail;
   }
 
-  secret = curl_maprintf("%s4%s", provider0_up, passwd);
-  if(!secret) {
+  /* provider 0 uppercase */
+  secret = curl_maprintf("%s4%s", provider0,
+                         data->state.aptr.passwd ?
+                         data->state.aptr.passwd : "");
+  if(!secret)
     goto fail;
-  }
 
-  HMAC_SHA256(secret, strlen(secret),
-              date, strlen(date), tmp_sign0);
-  HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
-              region, strlen(region), tmp_sign1);
-  HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
-              service, strlen(service), tmp_sign0);
-  HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
-              request_type, strlen(request_type), tmp_sign1);
-  HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
-              str_to_sign, strlen(str_to_sign), tmp_sign0);
+  HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
+  HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
+  HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
+  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, tmp_sign0, sizeof(sha_hex));
+  sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
 
+  /* provider 0 uppercase */
   auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
                                "Credential=%s/%s, "
                                "SignedHeaders=%s, "
                                "Signature=%s\r\n"
-                               "X-%s-Date: %s\r\n",
-                               provider0_up,
+                               "%s\r\n",
+                               provider0,
                                user,
                                credential_scope,
-                               signed_headers,
+                               Curl_dyn_ptr(&signed_headers),
                                sha_hex,
-                               provider1_mid,
-                               timestamp);
+                               date_header);
   if(!auth_headers) {
     goto fail;
   }
@@ -386,19 +511,14 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   ret = CURLE_OK;
 
 fail:
-  free(provider0_low);
-  free(provider0_up);
-  free(provider1_low);
-  free(provider1_mid);
-  free(region);
-  free(service);
-  free(canonical_headers);
-  free(signed_headers);
+  Curl_dyn_free(&canonical_headers);
+  Curl_dyn_free(&signed_headers);
   free(canonical_request);
   free(request_type);
   free(credential_scope);
   free(str_to_sign);
   free(secret);
+  free(date_header);
   return ret;
 }
 
index 886b314..85755e9 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 6bafcd9..0b83685 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -98,7 +100,7 @@ void Curl_httpchunk_init(struct Curl_easy *data)
 CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
                               char *datap,
                               ssize_t datalen,
-                              ssize_t *wrotep,
+                              ssize_t *wrote,
                               CURLcode *extrap)
 {
   CURLcode result = CURLE_OK;
@@ -107,7 +109,6 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
   struct SingleRequest *k = &data->req;
   size_t piece;
   curl_off_t length = (curl_off_t)datalen;
-  size_t *wrote = (size_t *)wrotep;
 
   *wrote = 0; /* nothing's written yet */
 
@@ -124,7 +125,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
   while(length) {
     switch(ch->state) {
     case CHUNK_HEX:
-      if(isxdigit_ascii(*datap)) {
+      if(ISXDIGIT(*datap)) {
         if(ch->hexindex < CHUNK_MAXNUM_LEN) {
           ch->hexbuffer[ch->hexindex] = *datap;
           datap++;
index 741a9a3..2cf5507 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 struct connectdata;
index 34bb5a8..a71c6b7 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -56,11 +58,11 @@ CURLcode Curl_input_digest(struct Curl_easy *data,
     digest = &data->state.digest;
   }
 
-  if(!checkprefix("Digest", header) || !ISSPACE(header[6]))
+  if(!checkprefix("Digest", header) || !ISBLANK(header[6]))
     return CURLE_BAD_CONTENT_ENCODING;
 
   header += strlen("Digest");
-  while(*header && ISSPACE(*header))
+  while(*header && ISBLANK(*header))
     header++;
 
   return Curl_auth_decode_digest_http_message(header, digest);
index 89438d1..eea90b7 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 888d3b2..5909f85 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -82,7 +84,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 
   /* Obtain the input token, if any */
   header += strlen("Negotiate");
-  while(*header && ISSPACE(*header))
+  while(*header && ISBLANK(*header))
     header++;
 
   len = strlen(header);
index 2640a3e..6e2096c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
index bb7e536..5a6a977 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -27,7 +29,7 @@
 /*
  * NTLM details:
  *
- * https://davenport.sourceforge.io/ntlm.html
+ * https://davenport.sourceforge.net/ntlm.html
  * https://www.innovation.ch/java/ntlm.html
  */
 
index 5b4fa00..cec63b8 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index ed08193..cc20b3a 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -210,10 +212,8 @@ void Curl_connect_done(struct Curl_easy *data)
     Curl_dyn_free(&s->rcvbuf);
     Curl_dyn_free(&s->req);
 
-    /* restore the protocol pointer, if not already done */
-    if(s->prot_save)
-      data->req.p.http = s->prot_save;
-    s->prot_save = NULL;
+    /* restore the protocol pointer */
+    data->req.p.http = s->prot_save;
     data->info.httpcode = 0; /* clear it as it might've been used for the
                                 proxy */
     data->req.ignorebody = FALSE;
@@ -391,8 +391,8 @@ static CURLcode CONNECT(struct Curl_easy *data,
 
         if(!result)
           /* send to debug callback! */
-          result = Curl_debug(data, CURLINFO_HEADER_OUT,
-                              k->upload_fromhere, bytes_written);
+          Curl_debug(data, CURLINFO_HEADER_OUT,
+                     k->upload_fromhere, bytes_written);
 
         s->nsend -= bytes_written;
         k->upload_fromhere += bytes_written;
index 67543b5..1e650ee 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index 0914e1f..2433d92 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
  /*
@@ -65,10 +67,10 @@ WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags,
 
 #define IDN_MAX_LENGTH 255
 
-bool curl_win32_idn_to_ascii(const char *in, char **out);
-bool curl_win32_ascii_to_idn(const char *in, char **out);
+bool Curl_win32_idn_to_ascii(const char *in, char **out);
+bool Curl_win32_ascii_to_idn(const char *in, char **out);
 
-bool curl_win32_idn_to_ascii(const char *in, char **out)
+bool Curl_win32_idn_to_ascii(const char *in, char **out)
 {
   bool success = FALSE;
 
@@ -91,7 +93,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out)
   return success;
 }
 
-bool curl_win32_ascii_to_idn(const char *in, char **out)
+bool Curl_win32_ascii_to_idn(const char *in, char **out)
 {
   bool success = FALSE;
 
index 1d34531..c291948 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index a360d4a..5d15459 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
 
index 817513b..ffa08bf 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2195 CRAM-MD5 authentication
  * RFC2595 Using TLS with IMAP, POP3 and ACAP
  * RFC2831 DIGEST-MD5 authentication
@@ -81,6 +83,7 @@
 #include "bufref.h"
 #include "curl_sasl.h"
 #include "warnless.h"
+#include "curl_ctype.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -1883,22 +1886,17 @@ static char *imap_atom(const char *str, bool escape_only)
  */
 static bool imap_is_bchar(char ch)
 {
+  /* Performing the alnum check with this macro is faster because of ASCII
+     arithmetic */
+  if(ISALNUM(ch))
+    return true;
+
   switch(ch) {
     /* bchar */
     case ':': case '@': case '/':
     /* bchar -> achar */
     case '&': case '=':
-    /* bchar -> achar -> uchar -> unreserved */
-    case '0': case '1': case '2': case '3': case '4': case '5': case '6':
-    case '7': case '8': case '9':
-    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
-    case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-    case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
-    case 'V': case 'W': case 'X': case 'Y': case 'Z':
-    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
-    case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
-    case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
-    case 'v': case 'w': case 'x': case 'y': case 'z':
+    /* bchar -> achar -> uchar -> unreserved (without alphanumeric) */
     case '-': case '.': case '_': case '~':
     /* bchar -> achar -> uchar -> sub-delims-sh */
     case '!': case '$': case '\'': case '(': case ')': case '*':
index ef6515d..43cc1e9 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2009 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "pingpong.h"
index b5f9b80..024f8da 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2021  Internet Software Consortium.
+ * Copyright (C) 1996-2022  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
@@ -13,6 +13,8 @@
  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * SPDX-License-Identifier: ISC
  */
 /*
  * Original code by Paul Vixie. "curlified" by Gisle Vanem.
index 067632a..18fbd8b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index ada57af..47fb778 100644 (file)
@@ -1,6 +1,6 @@
 /* This is from the BIND 4.9.4 release, modified to compile by itself */
 
-/* Copyright (c) 1996 - 2021 by Internet Software Consortium.
+/* Copyright (c) 2003 - 2022 by 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
@@ -14,6 +14,8 @@
  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  * SOFTWARE.
+ *
+ * SPDX-License-Identifier: ISC
  */
 
 #include "curl_setup.h"
index ec12373..92ae93e 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index dee94c9..1c993c1 100644 (file)
@@ -5,6 +5,8 @@
  * Copyright (c) 2004 - 2022 Daniel Stenberg
  * All rights reserved.
  *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -140,11 +142,8 @@ krb5_decode(void *app_data, void *buf, int len,
   enc.value = buf;
   enc.length = len;
   maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL);
-  if(maj != GSS_S_COMPLETE) {
-    if(len >= 4)
-      strcpy(buf, "599 ");
+  if(maj != GSS_S_COMPLETE)
     return -1;
-  }
 
   memcpy(buf, dec.value, dec.length);
   len = curlx_uztosi(dec.length);
@@ -506,6 +505,7 @@ static CURLcode read_data(struct connectdata *conn,
 {
   int len;
   CURLcode result;
+  int nread;
 
   result = socket_read(fd, &len, sizeof(len));
   if(result)
@@ -514,7 +514,10 @@ static CURLcode read_data(struct connectdata *conn,
   if(len) {
     /* only realloc if there was a length */
     len = ntohl(len);
-    buf->data = Curl_saferealloc(buf->data, len);
+    if(len > CURL_MAX_INPUT_LENGTH)
+      len = 0;
+    else
+      buf->data = Curl_saferealloc(buf->data, len);
   }
   if(!len || !buf->data)
     return CURLE_OUT_OF_MEMORY;
@@ -522,8 +525,11 @@ static CURLcode read_data(struct connectdata *conn,
   result = socket_read(fd, buf->data, len);
   if(result)
     return result;
-  buf->size = conn->mech->decode(conn->app_data, buf->data, len,
-                                 conn->data_prot, conn);
+  nread = conn->mech->decode(conn->app_data, buf->data, len,
+                             conn->data_prot, conn);
+  if(nread < 0)
+    return CURLE_RECV_ERROR;
+  buf->size = (size_t)nread;
   buf->index = 0;
   return CURLE_OK;
 }
index 03ea14e..b073349 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
  * OpenLDAP library versions, USE_OPENLDAP shall not be defined.
  */
 
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
 #ifdef USE_WIN32_LDAP           /* Use Windows LDAP implementation. */
 # include <winldap.h>
 # ifndef LDAP_VENDOR_NAME
@@ -342,7 +356,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 #ifdef HAVE_LDAP_SSL
 #ifdef USE_WIN32_LDAP
     /* Win32 LDAP SDK doesn't support insecure mode without CA! */
-    server = ldap_sslinit(host, (int)conn->port, 1);
+    server = ldap_sslinit(host, conn->port, 1);
     ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON);
 #else
     int ldap_option;
@@ -388,9 +402,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       result = CURLE_SSL_CERTPROBLEM;
       goto quit;
     }
-    server = ldapssl_init(host, (int)conn->port, 1);
+    server = ldapssl_init(host, conn->port, 1);
     if(!server) {
-      failf(data, "LDAP local: Cannot connect to %s:%ld",
+      failf(data, "LDAP local: Cannot connect to %s:%u",
             conn->host.dispname, conn->port);
       result = CURLE_COULDNT_CONNECT;
       goto quit;
@@ -429,9 +443,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       result = CURLE_SSL_CERTPROBLEM;
       goto quit;
     }
-    server = ldap_init(host, (int)conn->port);
+    server = ldap_init(host, conn->port);
     if(!server) {
-      failf(data, "LDAP local: Cannot connect to %s:%ld",
+      failf(data, "LDAP local: Cannot connect to %s:%u",
             conn->host.dispname, conn->port);
       result = CURLE_COULDNT_CONNECT;
       goto quit;
@@ -470,9 +484,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     goto quit;
   }
   else {
-    server = ldap_init(host, (int)conn->port);
+    server = ldap_init(host, conn->port);
     if(!server) {
-      failf(data, "LDAP local: Cannot connect to %s:%ld",
+      failf(data, "LDAP local: Cannot connect to %s:%u",
             conn->host.dispname, conn->port);
       result = CURLE_COULDNT_CONNECT;
       goto quit;
index fde6c8c..23134a7 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #include <winver.h>
 #include "../include/curl/curlver.h"
index e78da7d..fa2d366 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index ceae2dd..2fcb91c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 117cce4..e976fe7 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -30,7 +32,8 @@
 
 #ifdef USE_OPENSSL
 #include <openssl/opensslconf.h>
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \
+   !defined(USE_AMISSL)
 /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
 #define OPENSSL_NO_MD4
 #endif
@@ -39,7 +42,7 @@
 #ifdef USE_WOLFSSL
 #include <wolfssl/options.h>
 #ifdef NO_MD4
-#define OPENSSL_NO_MD4
+#define WOLFSSL_NO_MD4
 #endif
 #endif
 
@@ -82,9 +85,11 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
   md4_digest(ctx, MD4_DIGEST_SIZE, result);
 }
 
-#elif (defined(USE_OPENSSL) || defined(USE_WOLFSSL)) && \
-      !defined(OPENSSL_NO_MD4)
 /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */
+#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+#include <wolfssl/openssl/md4.h>
+
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
 #include <openssl/md4.h>
 
 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
index d2ca240..5be6399 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -39,7 +41,7 @@
 #endif
 #endif /* USE_MBEDTLS */
 
-#if defined(USE_OPENSSL) && !defined(USE_AMISSL)
+#ifdef USE_OPENSSL
   #include <openssl/opensslconf.h>
   #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)
     #define USE_OPENSSL_MD5
index 050c5d4..15fb491 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -127,7 +129,8 @@ static bool countcheck(const char *func, int line, const char *source)
   return FALSE; /* allow this */
 }
 
-void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
+ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
+                                 int line, const char *source)
 {
   struct memdebug *mem;
   size_t size;
@@ -153,8 +156,8 @@ void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
   return (mem ? mem->mem : NULL);
 }
 
-void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
-                      int line, const char *source)
+ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
+                                 int line, const char *source)
 {
   struct memdebug *mem;
   size_t size, user_size;
@@ -181,7 +184,8 @@ void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
   return (mem ? mem->mem : NULL);
 }
 
-char *curl_dbg_strdup(const char *str, int line, const char *source)
+ALLOC_FUNC char *curl_dbg_strdup(const char *str,
+                                 int line, const char *source)
 {
   char *mem;
   size_t len;
@@ -205,7 +209,8 @@ char *curl_dbg_strdup(const char *str, int line, const char *source)
 }
 
 #if defined(WIN32) && defined(UNICODE)
-wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
+ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
+                                    int line, const char *source)
 {
   wchar_t *mem;
   size_t wsiz, bsiz;
@@ -408,8 +413,8 @@ int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
   return res;
 }
 
-FILE *curl_dbg_fopen(const char *file, const char *mode,
-                    int line, const char *source)
+ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
+                                int line, const char *source)
 {
   FILE *res = fopen(file, mode);
 
@@ -420,8 +425,8 @@ FILE *curl_dbg_fopen(const char *file, const char *mode,
   return res;
 }
 
-FILE *curl_dbg_fdopen(int filedes, const char *mode,
-                      int line, const char *source)
+ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
+                                 int line, const char *source)
 {
   FILE *res = fdopen(filedes, mode);
   if(source)
index 8e88cea..7fc90e8 100644 (file)
@@ -8,7 +8,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -21,6 +21,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
  * as well as the library. Do not mix with library internals!
  */
 
+#include <curl/curl.h>
+#include "functypes.h"
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#  define ALLOC_FUNC __attribute__((malloc))
+#  define ALLOC_SIZE(s) __attribute__((alloc_size(s)))
+#  define ALLOC_SIZE2(n, s) __attribute__((alloc_size(n, s)))
+#elif defined(_MSC_VER)
+#  define ALLOC_FUNC __declspec(restrict)
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#else
+#  define ALLOC_FUNC
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#endif
+
 #define CURL_MT_LOGFNAME_BUFSIZE 512
 
 extern FILE *curl_dbg_logfile;
 
 /* memory functions */
-CURL_EXTERN void *curl_dbg_malloc(size_t size, int line, const char *source);
-CURL_EXTERN void *curl_dbg_calloc(size_t elements, size_t size, int line,
-                                  const char *source);
-CURL_EXTERN void *curl_dbg_realloc(void *ptr, size_t size, int line,
-                                   const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size,
+                                                           int line,
+                                                           const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements,
+                                   size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr,
+                                                 size_t size,
+                                                 int line,
+                                                 const char *source);
 CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
-CURL_EXTERN char *curl_dbg_strdup(const char *str, int line, const char *src);
+CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
+                                             const char *src);
 #if defined(WIN32) && defined(UNICODE)
-CURL_EXTERN wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line,
-                                     const char *source);
+CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
+                                                int line,
+                                                const char *source);
 #endif
 
 CURL_EXTERN void curl_dbg_memdebug(const char *logname);
@@ -77,10 +102,10 @@ CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
                                          const char *source);
 
 /* FILE functions */
-CURL_EXTERN FILE *curl_dbg_fopen(const char *file, const char *mode, int line,
-                                 const char *source);
-CURL_EXTERN FILE *curl_dbg_fdopen(int filedes, const char *mode,
+CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
                                   int line, const char *source);
+CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
+                                             int line, const char *source);
 
 CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
 
index d6985d3..042141f 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -29,8 +31,9 @@
 #include "urldata.h"
 #include "sendf.h"
 
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
-  !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
+#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
+                                    !defined(CURL_DISABLE_SMTP) ||      \
+                                    !defined(CURL_DISABLE_IMAP))
 
 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
 #include <libgen.h>
@@ -1922,8 +1925,8 @@ void Curl_mime_unpause(curl_mimepart *part)
 }
 
 
-#else /* !CURL_DISABLE_HTTP && !CURL_DISABLE_MIME ||
-         !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
+#else /* !CURL_DISABLE_MIME && (!CURL_DISABLE_HTTP ||
+                                !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP) */
 
 /* Mime not compiled in: define stubs for externally-referenced functions. */
 curl_mime *curl_mime_init(CURL *easy)
index f2fc434..bafde29 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -132,8 +134,9 @@ struct curl_mimepart {
 
 CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
 
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) ||     \
-  !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
+#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
+                                    !defined(CURL_DISABLE_SMTP) ||      \
+                                    !defined(CURL_DISABLE_IMAP))
 
 /* Prototypes. */
 void Curl_mime_initpart(struct curl_mimepart *part, struct Curl_easy *easy);
index 1381201..8a7c17a 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  *
  * Purpose:
  *  A merge of Bjorn Reese's format() function and Daniel's dsprintf()
@@ -316,6 +318,11 @@ static int dprintf_Pass1(const char *format, struct va_stack *vto,
             flags |= FLAGS_PREC;
             precision = strtol(fmt, &fmt, 10);
           }
+          if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) ==
+             (FLAGS_PREC | FLAGS_PRECPARAM))
+            /* it is not permitted to use both kinds of precision for the same
+               argument */
+            return 1;
           break;
         case 'h':
           flags |= FLAGS_SHORT;
@@ -592,7 +599,7 @@ static int dprintf_formatf(
 
   /* Do the actual %-code parsing */
   if(dprintf_Pass1(format, vto, endpos, ap_save))
-    return -1;
+    return 0;
 
   end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
                        created for us */
@@ -954,11 +961,22 @@ static int dprintf_formatf(
         else
           *fptr++ = 'f';
 
-        *fptr = 0; /* and a final zero termination */
+        *fptr = 0; /* and a final null-termination */
 
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
         /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
            output characters */
+#ifdef HAVE_SNPRINTF
+        (snprintf)(work, sizeof(work), formatbuf, p->data.dnum);
+#else
         (sprintf)(work, formatbuf, p->data.dnum);
+#endif
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
         DEBUGASSERT(strlen(work) <= sizeof(work));
         for(fptr = work; *fptr; fptr++)
           OUTCHAR(*fptr);
@@ -1016,11 +1034,12 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
   info.max = maxlength;
 
   retcode = dprintf_formatf(&info, addbyter, format, ap_save);
-  if((retcode != -1) && info.max) {
+  if(info.max) {
     /* we terminate this with a zero byte */
     if(info.max == info.length) {
       /* we're at maximum, scrap the last letter */
       info.buffer[-1] = 0;
+      DEBUGASSERT(retcode);
       retcode--; /* don't count the nul byte */
     }
     else
@@ -1058,13 +1077,12 @@ extern int Curl_dyn_vprintf(struct dynbuf *dyn,
 /* appends the formatted string, returns 0 on success, 1 on error */
 int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 {
-  int retcode;
   struct asprintf info;
   info.b = dyn;
   info.fail = 0;
 
-  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  if((-1 == retcode) || info.fail) {
+  (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  if(info.fail) {
     Curl_dyn_free(info.b);
     return 1;
   }
@@ -1073,15 +1091,14 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 
 char *curl_mvaprintf(const char *format, va_list ap_save)
 {
-  int retcode;
   struct asprintf info;
   struct dynbuf dyn;
   info.b = &dyn;
   Curl_dyn_init(info.b, DYN_APRINTF);
   info.fail = 0;
 
-  retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
-  if((-1 == retcode) || info.fail) {
+  (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save);
+  if(info.fail) {
     Curl_dyn_free(info.b);
     return NULL;
   }
index 9bcbaa1..4f3d143 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
@@ -184,7 +186,7 @@ static int add_passwd(const char *passwd, const size_t plen,
   return 0;
 }
 
-/* add user to the CONN packet */
+/* add user to the CONNECT packet */
 static int add_user(const char *username, const size_t ulen,
                     unsigned char *pkt, const size_t start, int remain_pos)
 {
@@ -202,7 +204,7 @@ static int add_user(const char *username, const size_t ulen,
   return 0;
 }
 
-/* add client ID to the CONN packet */
+/* add client ID to the CONNECT packet */
 static int add_client_id(const char *client_id, const size_t client_id_len,
                          char *pkt, const size_t start)
 {
@@ -214,7 +216,7 @@ static int add_client_id(const char *client_id, const size_t client_id_len,
   return 0;
 }
 
-/* Set initial values of CONN packet */
+/* Set initial values of CONNECT packet */
 static int init_connpack(char *packet, char *remain, int remain_pos)
 {
   /* Fixed header starts */
@@ -291,7 +293,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
     return CURLE_OUT_OF_MEMORY;
   memset(packet, 0, packetlen);
 
-  /* set initial values for CONN pack */
+  /* set initial values for the CONNECT packet */
   pos = init_connpack(packet, remain, remain_pos);
 
   result = Curl_rand_hex(data, (unsigned char *)&client_id[clen],
@@ -387,10 +389,18 @@ static CURLcode mqtt_get_topic(struct Curl_easy *data,
                                char **topic, size_t *topiclen)
 {
   char *path = data->state.up.path;
-  if(strlen(path) > 1)
-    return Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA);
-  failf(data, "No MQTT topic found. Forgot to URL encode it?");
-  return CURLE_URL_MALFORMAT;
+  CURLcode result = CURLE_URL_MALFORMAT;
+  if(strlen(path) > 1) {
+    result = Curl_urldecode(path + 1, 0, topic, topiclen, REJECT_NADA);
+    if(!result && (*topiclen > 0xffff)) {
+      failf(data, "Too long MQTT topic");
+      result = CURLE_URL_MALFORMAT;
+    }
+  }
+  else
+    failf(data, "No MQTT topic found. Forgot to URL encode it?");
+
+  return result;
 }
 
 static CURLcode mqtt_subscribe(struct Curl_easy *data)
@@ -688,7 +698,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
 
   result = mqtt_connect(data);
   if(result) {
-    failf(data, "Error %d sending MQTT CONN request", result);
+    failf(data, "Error %d sending MQTT CONNECT request", result);
     return result;
   }
   mqstate(data, MQTT_FIRST, MQTT_CONNACK);
index fb52c72..c400d9b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Björn Stenberg, <bjorn@haxx.se>
+ * Copyright (C) 2019 - 2022, 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifndef CURL_DISABLE_MQTT
index 8e58d78..51acba7 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -43,7 +45,6 @@
 #include "multihandle.h"
 #include "sigpipe.h"
 #include "vtls/vtls.h"
-#include "connect.h"
 #include "http_proxy.h"
 #include "http2.h"
 #include "socketpair.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifdef __APPLE__
+
+#define wakeup_write  write
+#define wakeup_read   read
+#define wakeup_close  close
+#define wakeup_create pipe
+
+#else /* __APPLE__ */
+
+#define wakeup_write     swrite
+#define wakeup_read      sread
+#define wakeup_close     sclose
+#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
+
+#endif /* __APPLE__ */
+
 /*
   CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
   to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes.  Still, every
 #define CURL_CONNECTION_HASH_SIZE 97
 #endif
 
+#ifndef CURL_DNS_HASH_SIZE
+#define CURL_DNS_HASH_SIZE 71
+#endif
+
 #define CURL_MULTI_HANDLE 0x000bab1e
 
 #define GOOD_MULTI_HANDLE(x) \
@@ -370,7 +391,8 @@ static CURLMcode multi_addmsg(struct Curl_multi *multi,
 }
 
 struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
-                                     int chashsize) /* connection hash */
+                                     int chashsize, /* connection hash */
+                                     int dnssize) /* dns hash */
 {
   struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
 
@@ -379,7 +401,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
 
   multi->magic = CURL_MULTI_HANDLE;
 
-  Curl_init_dnscache(&multi->hostcache);
+  Curl_init_dnscache(&multi->hostcache, dnssize);
 
   sh_init(&multi->sockhash, hashsize);
 
@@ -394,7 +416,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   /* -1 means it not set by user, use the default value */
   multi->maxconnects = -1;
   multi->max_concurrent_streams = 100;
-  multi->ipv6_works = Curl_ipv6works(NULL);
 
 #ifdef USE_WINSOCK
   multi->wsa_event = WSACreateEvent();
@@ -402,14 +423,14 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
     goto error;
 #else
 #ifdef ENABLE_WAKEUP
-  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, multi->wakeup_pair) < 0) {
+  if(wakeup_create(multi->wakeup_pair) < 0) {
     multi->wakeup_pair[0] = CURL_SOCKET_BAD;
     multi->wakeup_pair[1] = CURL_SOCKET_BAD;
   }
   else if(curlx_nonblock(multi->wakeup_pair[0], TRUE) < 0 ||
           curlx_nonblock(multi->wakeup_pair[1], TRUE) < 0) {
-    sclose(multi->wakeup_pair[0]);
-    sclose(multi->wakeup_pair[1]);
+    wakeup_close(multi->wakeup_pair[0]);
+    wakeup_close(multi->wakeup_pair[1]);
     multi->wakeup_pair[0] = CURL_SOCKET_BAD;
     multi->wakeup_pair[1] = CURL_SOCKET_BAD;
   }
@@ -433,7 +454,8 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
 struct Curl_multi *curl_multi_init(void)
 {
   return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
-                           CURL_CONNECTION_HASH_SIZE);
+                           CURL_CONNECTION_HASH_SIZE,
+                           CURL_DNS_HASH_SIZE);
 }
 
 CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
@@ -627,7 +649,7 @@ static CURLcode multi_done(struct Curl_easy *data,
   if(CURLE_ABORTED_BY_CALLBACK != result) {
     /* avoid this if we already aborted by callback to avoid this calling
        another callback */
-    CURLcode rc = Curl_pgrsDone(data);
+    int rc = Curl_pgrsDone(data);
     if(!result && rc)
       result = CURLE_ABORTED_BY_CALLBACK;
   }
@@ -729,7 +751,7 @@ static int close_connect_only(struct Curl_easy *data,
   if(data->state.lastconnect_id != conn->connection_id)
     return 0;
 
-  if(!conn->bits.connect_only)
+  if(!conn->connect_only)
     return 1;
 
   connclose(conn, "Removing connect-only easy handle");
@@ -826,6 +848,24 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
   /* Remove the association between the connection and the handle */
   Curl_detach_connection(data);
 
+  if(data->set.connect_only && !data->multi_easy) {
+    /* This removes a handle that was part the multi interface that used
+       CONNECT_ONLY, that connection is now left alive but since this handle
+       has bits.close set nothing can use that transfer anymore and it is
+       forbidden from reuse. And this easy handle cannot find the connection
+       anymore once removed from the multi handle
+
+       Better close the connection here, at once.
+    */
+    struct connectdata *c;
+    curl_socket_t s;
+    s = Curl_getconnectinfo(data, &c);
+    if((s != CURL_SOCKET_BAD) && c) {
+      Curl_conncache_remove_conn(data, c, TRUE);
+      Curl_disconnect(data, c, TRUE);
+    }
+  }
+
   if(data->state.lastconnect_id != -1) {
     /* Mark any connect-only connection for closure */
     Curl_conncache_foreach(data, data->state.conn_cache,
@@ -1307,16 +1347,19 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
       pollrc = Curl_poll(ufds, nfds, 0); /* just pre-check with WinSock */
     else
       pollrc = 0;
-    if(pollrc <= 0) /* now wait... if not ready during the pre-check above */
-      WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
 #else
     pollrc = Curl_poll(ufds, nfds, timeout_ms); /* wait... */
 #endif
+    if(pollrc < 0)
+      return CURLM_UNRECOVERABLE_POLL;
 
     if(pollrc > 0) {
       retcode = pollrc;
 #ifdef USE_WINSOCK
     }
+    else { /* now wait... if not ready during the pre-check (pollrc == 0) */
+      WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, timeout_ms, FALSE);
+    }
     /* With WinSock, we have to run the following section unconditionally
        to call WSAEventSelect(fd, event, 0) on all the sockets */
     {
@@ -1328,20 +1371,23 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
         unsigned r = ufds[curlfds + i].revents;
         unsigned short mask = 0;
 #ifdef USE_WINSOCK
+        curl_socket_t s = extra_fds[i].fd;
         wsa_events.lNetworkEvents = 0;
-        if(WSAEnumNetworkEvents(extra_fds[i].fd, NULL, &wsa_events) == 0) {
+        if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) {
           if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE))
             mask |= CURL_WAIT_POLLIN;
           if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE))
             mask |= CURL_WAIT_POLLOUT;
           if(wsa_events.lNetworkEvents & FD_OOB)
             mask |= CURL_WAIT_POLLPRI;
-          if(ret && pollrc <= 0 && wsa_events.lNetworkEvents)
+          if(ret && !pollrc && wsa_events.lNetworkEvents)
             retcode++;
         }
-        WSAEventSelect(extra_fds[i].fd, multi->wsa_event, 0);
-        if(pollrc <= 0)
+        WSAEventSelect(s, multi->wsa_event, 0);
+        if(!pollrc) {
+          extra_fds[i].revents = mask;
           continue;
+        }
 #endif
         if(r & POLLIN)
           mask |= CURL_WAIT_POLLIN;
@@ -1364,7 +1410,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
             if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
               wsa_events.lNetworkEvents = 0;
               if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) {
-                if(ret && pollrc <= 0 && wsa_events.lNetworkEvents)
+                if(ret && !pollrc && wsa_events.lNetworkEvents)
                   retcode++;
               }
               WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
@@ -1391,7 +1437,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
                data from it until it receives an error (except EINTR).
                In normal cases it will get EAGAIN or EWOULDBLOCK
                when there is no more data, breaking the loop. */
-            nread = sread(multi->wakeup_pair[0], buf, sizeof(buf));
+            nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf));
             if(nread <= 0) {
               if(nread < 0 && EINTR == SOCKERRNO)
                 continue;
@@ -1484,7 +1530,7 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi)
          that will call curl_multi_wait(). If swrite() returns that it
          would block, it's considered successful because it means that
          previous calls to this function will wake up the poll(). */
-      if(swrite(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
+      if(wakeup_write(multi->wakeup_pair[1], buf, sizeof(buf)) < 0) {
         int err = SOCKERRNO;
         int return_success;
 #ifdef USE_WINSOCK
@@ -2096,7 +2142,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         }
       }
 
-      if(data->set.connect_only) {
+      if(data->set.connect_only == 1) {
         /* keep connection open for application to use the socket */
         connkeep(data->conn, "CONNECT_ONLY");
         multistate(data, MSTATE_DONE);
@@ -2720,8 +2766,8 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
     WSACloseEvent(multi->wsa_event);
 #else
 #ifdef ENABLE_WAKEUP
-    sclose(multi->wakeup_pair[0]);
-    sclose(multi->wakeup_pair[1]);
+    wakeup_close(multi->wakeup_pair[0]);
+    wakeup_close(multi->wakeup_pair[1]);
 #endif
 #endif
     free(multi);
index db7f130..a997784 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "llist.h"
@@ -148,11 +150,13 @@ struct Curl_multi {
                                    0 is used for read, 1 is used for write */
 #endif
 #endif
-  /* multiplexing wanted */
-  bool multiplexing;
-  bool recheckstate; /* see Curl_multi_connchanged */
+#define IPV6_UNKNOWN 0
+#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 */
-  bool ipv6_works;
 #ifdef USE_OPENSSL
   bool ssl_seeded;
 #endif
index 5a8c358..0cb9d4f 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -40,8 +42,9 @@ bool Curl_is_in_callback(struct Curl_easy *easy);
 CURLcode Curl_preconnect(struct Curl_easy *data);
 
 /* Internal version of curl_multi_init() accepts size parameters for the
-   socket and connection hashes */
-struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize);
+   socket, connection and dns hashes */
+struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize,
+                                     int dnssize);
 
 /* the write bits start at bit 16 for the *getsock() bitmap */
 #define GETSOCK_WRITEBITSTART 16
index 0a4ae2c..4461b84 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -31,6 +33,7 @@
 #include "netrc.h"
 #include "strtok.h"
 #include "strcase.h"
+#include "curl_get_line.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -56,8 +59,6 @@ enum host_lookup_state {
 static int parsenetrc(const char *host,
                       char **loginp,
                       char **passwordp,
-                      bool *login_changed,
-                      bool *password_changed,
                       char *netrcfile)
 {
   FILE *file;
@@ -71,31 +72,87 @@ static int parsenetrc(const char *host,
 
   char state_login = 0;      /* Found a login keyword */
   char state_password = 0;   /* Found a password keyword */
-  int state_our_login = FALSE;  /* With specific_login, found *our* login
-                                   name */
+  int state_our_login = TRUE;  /* With specific_login, found *our* login
+                                  name (or login-less line) */
 
   DEBUGASSERT(netrcfile);
 
   file = fopen(netrcfile, FOPEN_READTEXT);
   if(file) {
-    char *tok;
-    char *tok_buf;
     bool done = FALSE;
     char netrcbuffer[4096];
     int  netrcbuffsize = (int)sizeof(netrcbuffer);
 
-    while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
+    while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) {
+      char *tok;
+      char *tok_end;
+      bool quoted;
       if(state == MACDEF) {
         if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
           state = NOTHING;
         else
           continue;
       }
-      tok = strtok_r(netrcbuffer, " \t\n", &tok_buf);
-      if(tok && *tok == '#')
-        /* treat an initial hash as a comment line */
-        continue;
+      tok = netrcbuffer;
       while(tok) {
+        while(ISBLANK(*tok))
+          tok++;
+        /* tok is first non-space letter */
+        if(!*tok || (*tok == '#'))
+          /* end of line or the rest is a comment */
+          break;
+
+        /* leading double-quote means quoted string */
+        quoted = (*tok == '\"');
+
+        tok_end = tok;
+        if(!quoted) {
+          while(!ISSPACE(*tok_end))
+            tok_end++;
+          *tok_end = 0;
+        }
+        else {
+          bool escape = FALSE;
+          bool endquote = FALSE;
+          char *store = tok;
+          tok_end++; /* pass the leading quote */
+          while(*tok_end) {
+            char s = *tok_end;
+            if(escape) {
+              escape = FALSE;
+              switch(s) {
+              case 'n':
+                s = '\n';
+                break;
+              case 'r':
+                s = '\r';
+                break;
+              case 't':
+                s = '\t';
+                break;
+              }
+            }
+            else if(s == '\\') {
+              escape = TRUE;
+              tok_end++;
+              continue;
+            }
+            else if(s == '\"') {
+              tok_end++; /* pass the ending quote */
+              endquote = TRUE;
+              break;
+            }
+            *store++ = s;
+            tok_end++;
+          }
+          *store = 0;
+          if(escape || !endquote) {
+            /* bad syntax, get out */
+            retcode = NETRC_FAILED;
+            goto out;
+          }
+        }
+
         if((login && *login) && (password && *password)) {
           done = TRUE;
           break;
@@ -140,9 +197,9 @@ static int parsenetrc(const char *host,
           /* we are now parsing sub-keywords concerning "our" host */
           if(state_login) {
             if(specific_login) {
-              state_our_login = strcasecompare(login, tok);
+              state_our_login = !Curl_timestrcmp(login, tok);
             }
-            else if(!login || strcmp(login, tok)) {
+            else if(!login || Curl_timestrcmp(login, tok)) {
               if(login_alloc) {
                 free(login);
                 login_alloc = FALSE;
@@ -158,7 +215,7 @@ static int parsenetrc(const char *host,
           }
           else if(state_password) {
             if((state_our_login || !specific_login)
-                && (!password || strcmp(password, tok))) {
+               && (!password || Curl_timestrcmp(password, tok))) {
               if(password_alloc) {
                 free(password);
                 password_alloc = FALSE;
@@ -183,27 +240,22 @@ static int parsenetrc(const char *host,
           }
           break;
         } /* switch (state) */
-
-        tok = strtok_r(NULL, " \t\n", &tok_buf);
-      } /* while(tok) */
-    } /* while fgets() */
+        tok = ++tok_end;
+      }
+    } /* while Curl_get_line() */
 
     out:
     if(!retcode) {
       /* success */
-      *login_changed = FALSE;
-      *password_changed = FALSE;
       if(login_alloc) {
         if(*loginp)
           free(*loginp);
         *loginp = login;
-        *login_changed = TRUE;
       }
       if(password_alloc) {
         if(*passwordp)
           free(*passwordp);
         *passwordp = password;
-        *password_changed = TRUE;
       }
     }
     else {
@@ -224,17 +276,16 @@ static int parsenetrc(const char *host,
  * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
  * in.
  */
-int Curl_parsenetrc(const char *host,
-                    char **loginp,
-                    char **passwordp,
-                    bool *login_changed,
-                    bool *password_changed,
+int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
                     char *netrcfile)
 {
   int retcode = 1;
   char *filealloc = NULL;
 
   if(!netrcfile) {
+#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
+    char pwbuf[1024];
+#endif
     char *home = NULL;
     char *homea = curl_getenv("HOME"); /* portable environment reader */
     if(homea) {
@@ -243,7 +294,6 @@ int Curl_parsenetrc(const char *host,
     }
     else {
       struct passwd pw, *pw_res;
-      char pwbuf[1024];
       if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
          && pw_res) {
         home = pw.pw_dir;
@@ -256,6 +306,13 @@ int Curl_parsenetrc(const char *host,
       if(pw) {
         home = pw->pw_dir;
       }
+#elif defined(_WIN32)
+    }
+    else {
+      homea = curl_getenv("USERPROFILE");
+      if(homea) {
+        home = homea;
+      }
 #endif
     }
 
@@ -268,8 +325,7 @@ int Curl_parsenetrc(const char *host,
       free(homea);
       return -1;
     }
-    retcode = parsenetrc(host, loginp, passwordp, login_changed,
-                         password_changed, filealloc);
+    retcode = parsenetrc(host, loginp, passwordp, filealloc);
     free(filealloc);
 #ifdef WIN32
     if(retcode == NETRC_FILE_MISSING) {
@@ -279,16 +335,14 @@ int Curl_parsenetrc(const char *host,
         free(homea);
         return -1;
       }
-      retcode = parsenetrc(host, loginp, passwordp, login_changed,
-                           password_changed, filealloc);
+      retcode = parsenetrc(host, loginp, passwordp, filealloc);
       free(filealloc);
     }
 #endif
     free(homea);
   }
   else
-    retcode = parsenetrc(host, loginp, passwordp, login_changed,
-                         password_changed, netrcfile);
+    retcode = parsenetrc(host, loginp, passwordp, netrcfile);
   return retcode;
 }
 
index 4938a59..53d0056 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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_NETRC
 
 /* returns -1 on failure, 0 if the host is found, 1 is the host isn't found */
-int Curl_parsenetrc(const char *host,
-                    char **loginp,
-                    char **passwordp,
-                    bool *login_changed,
-                    bool *password_changed,
-                    char *filename);
+int Curl_parsenetrc(const char *host, char **loginp,
+                    char **passwordp, char *filename);
   /* Assume: (*passwordp)[0]=0, host[0] != 0.
    * If (*loginp)[0] = 0, search for login and password within a machine
    * section in the netrc.
index 28f6e75..ce73af3 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 761dab4..a42f443 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h> /* for curl_socket_t */
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c
new file mode 100644 (file)
index 0000000..81f1e09
--- /dev/null
@@ -0,0 +1,222 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#ifndef CURL_DISABLE_PROXY
+
+#include "inet_pton.h"
+#include "strcase.h"
+#include "noproxy.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+/*
+ * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the
+ * specified CIDR address range.
+ */
+UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                               const char *network, /* 1.2.3.4 address */
+                               unsigned int bits)
+{
+  unsigned int address = 0;
+  unsigned int check = 0;
+
+  if(bits > 32)
+    /* strange input */
+    return FALSE;
+
+  if(1 != Curl_inet_pton(AF_INET, ipv4, &address))
+    return FALSE;
+  if(1 != Curl_inet_pton(AF_INET, network, &check))
+    return FALSE;
+
+  if(bits && (bits != 32)) {
+    unsigned int mask = 0xffffffff << (32 - bits);
+    unsigned int haddr = htonl(address);
+    unsigned int hcheck = htonl(check);
+#if 0
+    fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n",
+            ipv4, haddr, network, hcheck, bits, mask,
+            (haddr ^ hcheck) & mask);
+#endif
+    if((haddr ^ hcheck) & mask)
+      return FALSE;
+    return TRUE;
+  }
+  return (address == check);
+}
+
+UNITTEST bool Curl_cidr6_match(const char *ipv6,
+                               const char *network,
+                               unsigned int bits)
+{
+#ifdef ENABLE_IPV6
+  int bytes;
+  int rest;
+  unsigned char address[16];
+  unsigned char check[16];
+
+  if(!bits)
+    bits = 128;
+
+  bytes = bits/8;
+  rest = bits & 0x07;
+  if(1 != Curl_inet_pton(AF_INET6, ipv6, address))
+    return FALSE;
+  if(1 != Curl_inet_pton(AF_INET6, network, check))
+    return FALSE;
+  if((bytes > 16) || ((bytes == 16) && rest))
+    return FALSE;
+  if(bytes && memcmp(address, check, bytes))
+    return FALSE;
+  if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
+    return FALSE;
+
+  return TRUE;
+#else
+  (void)ipv6;
+  (void)network;
+  (void)bits;
+  return FALSE;
+#endif
+}
+
+enum nametype {
+  TYPE_HOST,
+  TYPE_IPV4,
+  TYPE_IPV6
+};
+
+/****************************************************************
+* 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)
+{
+  /* no_proxy=domain1.dom,host.domain2.dom
+   *   (a comma-separated list of hosts which should
+   *   not be proxied, or an asterisk to override
+   *   all proxy variables)
+   */
+  if(no_proxy && no_proxy[0]) {
+    const char *p = no_proxy;
+    size_t namelen;
+    enum nametype type = TYPE_HOST;
+    char hostip[128];
+    if(!strcmp("*", no_proxy))
+      return TRUE;
+
+    /* NO_PROXY was specified and it wasn't just an asterisk */
+
+    if(name[0] == '[') {
+      char *endptr;
+      /* IPv6 numerical address */
+      endptr = strchr(name, ']');
+      if(!endptr)
+        return FALSE;
+      name++;
+      namelen = endptr - name;
+      if(namelen >= sizeof(hostip))
+        return FALSE;
+      memcpy(hostip, name, namelen);
+      hostip[namelen] = 0;
+      name = hostip;
+      type = TYPE_IPV6;
+    }
+    else {
+      unsigned int address;
+      if(1 == Curl_inet_pton(AF_INET, name, &address))
+        type = TYPE_IPV4;
+      namelen = strlen(name);
+    }
+
+    while(*p) {
+      const char *token;
+      size_t tokenlen = 0;
+      bool match = FALSE;
+
+      /* pass blanks */
+      while(*p && ISBLANK(*p))
+        p++;
+
+      token = p;
+      /* pass over the pattern */
+      while(*p && !ISBLANK(*p) && (*p != ',')) {
+        p++;
+        tokenlen++;
+      }
+
+      if(tokenlen) {
+        switch(type) {
+        case TYPE_HOST:
+          if(*token == '.') {
+            ++token;
+            --tokenlen;
+            /* tailmatch */
+            match = (tokenlen <= namelen) &&
+              strncasecompare(token, name + (namelen - tokenlen), namelen);
+          }
+          else
+            match = (tokenlen == namelen) &&
+              strncasecompare(token, name, namelen);
+          break;
+        case TYPE_IPV4:
+          /* FALLTHROUGH */
+        case TYPE_IPV6: {
+          const char *check = token;
+          char *slash = strchr(check, '/');
+          unsigned int bits = 0;
+          char checkip[128];
+          /* if the slash is part of this token, use it */
+          if(slash && (slash < &check[tokenlen])) {
+            bits = atoi(slash + 1);
+            /* copy the check name to a temp buffer */
+            if(tokenlen >= sizeof(checkip))
+              break;
+            memcpy(checkip, check, tokenlen);
+            checkip[ slash - check ] = 0;
+            check = checkip;
+          }
+          if(type == TYPE_IPV6)
+            match = Curl_cidr6_match(name, check, bits);
+          else
+            match = Curl_cidr4_match(name, check, bits);
+          break;
+        }
+        }
+        if(match)
+          return TRUE;
+      } /* if(tokenlen) */
+      while(*p == ',')
+        p++;
+    } /* while(*p) */
+  } /* NO_PROXY was specified and it wasn't just an asterisk */
+
+  return FALSE;
+}
+
+#endif /* CURL_DISABLE_PROXY */
similarity index 57%
rename from Utilities/cmcurl/lib/dotdot.h
rename to Utilities/cmcurl/lib/noproxy.h
index ac1ea36..8800a21 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_DOTDOT_H
-#define HEADER_CURL_DOTDOT_H
+#ifndef HEADER_CURL_NOPROXY_H
+#define HEADER_CURL_NOPROXY_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
-char *Curl_dedotdotify(const char *input);
-#endif /* HEADER_CURL_DOTDOT_H */
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_PROXY
+
+#ifdef DEBUGBUILD
+
+UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
+                               const char *network, /* 1.2.3.4 address */
+                               unsigned int bits);
+UNITTEST bool Curl_cidr6_match(const char *ipv6,
+                               const char *network,
+                               unsigned int bits);
+#endif
+
+bool Curl_check_noproxy(const char *name, const char *no_proxy);
+
+#endif
+
+#endif /* HEADER_CURL_NOPROXY_H */
index 4e92567..3a93b67 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
@@ -1066,8 +1068,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
 
         if(!binary) {
           /* check for leading or trailing whitespace */
-          if(ISSPACE(bvals[i].bv_val[0]) ||
-             ISSPACE(bvals[i].bv_val[bvals[i].bv_len - 1]))
+          if(ISBLANK(bvals[i].bv_val[0]) ||
+             ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))
             binval = 1;
           else {
             /* check for unprintable characters */
index 3c38f2c..5ed8819 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 /*
   A brief summary of the date string formats this parser groks:
index a99faf9..4e43477 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 extern const char * const Curl_wkday[7];
index e08c1d8..d4e6be9 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  *   'pingpong' is for generic back-and-forth support functions used by FTP,
  *   IMAP, POP3, SMTP and whatever more that likes them.
  *
@@ -328,7 +330,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
     else if(gotbytes <= 0) {
       keepon = FALSE;
       result = CURLE_RECV_ERROR;
-      failf(data, "response reading failed");
+      failf(data, "response reading failed (errno: %d)", SOCKERRNO);
     }
     else {
       /* we got a whole chunk of data, which can be anything from one
@@ -396,7 +398,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
       }
       else if(keepon) {
 
-        if((perline == gotbytes) && (gotbytes > data->set.buffer_size/2)) {
+        if((perline == gotbytes) &&
+           (gotbytes > (ssize_t)data->set.buffer_size/2)) {
           /* We got an excessive line without newlines and we need to deal
              with it. We keep the first bytes of the line then we throw
              away the rest. */
index 8f56f3f..cefae07 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 2c1b06c..3151a3f 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC1734 POP3 Authentication
  * RFC1939 POP3 protocol
  * RFC2195 CRAM-MD5 authentication
index 17629ee..bb0645f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2009 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "pingpong.h"
index f5ef6bd..4a1e1da 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index ac4ebc0..a129315 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "timeval.h"
index e460918..60c98a4 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index c103674..34f0a5c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #ifdef USE_LIBPSL
index f92720f..b357747 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -54,6 +56,7 @@ 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)
index 8da1e8d..2e7e7e8 100644 (file)
@@ -18,6 +18,8 @@
  * 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_memory.h"
 #include "memdebug.h"
 
+#ifdef WIN32
+
+#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+#  define HAVE_MINGW_ORIGINAL
+#endif
+
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \
+  !defined(HAVE_MINGW_ORIGINAL)
+#  define HAVE_WIN_BCRYPTGENRANDOM
+#  include <bcrypt.h>
+#  ifdef _MSC_VER
+#    pragma comment(lib, "bcrypt.lib")
+#  endif
+#  ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
+#  define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
+#  endif
+#  ifndef STATUS_SUCCESS
+#  define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#  endif
+#elif defined(USE_WIN32_CRYPTO)
+#  include <wincrypt.h>
+#  ifdef _MSC_VER
+#    pragma comment(lib, "advapi32.lib")
+#  endif
+#endif
+
+CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
+{
+  memset(entropy, 0, length);
+
+#if defined(HAVE_WIN_BCRYPTGENRANDOM)
+  if(BCryptGenRandom(NULL, entropy, (ULONG)length,
+                     BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
+    return CURLE_FAILED_INIT;
+
+  return CURLE_OK;
+#elif defined(USE_WIN32_CRYPTO)
+  {
+    HCRYPTPROV hCryptProv = 0;
+
+    if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
+                            CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+      return CURLE_FAILED_INIT;
+
+    if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
+      CryptReleaseContext(hCryptProv, 0UL);
+      return CURLE_FAILED_INIT;
+    }
+
+    CryptReleaseContext(hCryptProv, 0UL);
+  }
+  return CURLE_OK;
+#else
+  return CURLE_NOT_BUILT_IN;
+#endif
+}
+#endif
+
 static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 {
   unsigned int r;
@@ -71,7 +131,15 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 
   /* ---- non-cryptographic version following ---- */
 
-#ifdef RANDOM_FILE
+#ifdef WIN32
+  if(!seeded) {
+    result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
+    if(result != CURLE_NOT_BUILT_IN)
+      return result;
+  }
+#endif
+
+#if defined(RANDOM_FILE) && !defined(WIN32)
   if(!seeded) {
     /* if there's a random file to read a seed from, use it */
     int fd = open(RANDOM_FILE, O_RDONLY);
@@ -106,7 +174,8 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
  * 'rndptr' points to.
  *
  * If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (Gskit or mbedTLS), this function will use "weak" random.
+ * proper random API (rustls, Gskit or mbedTLS), this function will use "weak"
+ * random.
  *
  * When built *with* TLS support and a backend that offers strong random, it
  * will return error if it cannot provide strong random values.
@@ -143,7 +212,7 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
 
 /*
  * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
- * hexadecimal digits PLUS a zero terminating byte. It must be an odd number
+ * hexadecimal digits PLUS a null-terminating byte. It must be an odd number
  * size.
  */
 
@@ -166,7 +235,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
     /* make sure it fits in the local buffer and that it is an odd number! */
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  num--; /* save one for zero termination */
+  num--; /* save one for null-termination */
 
   result = Curl_rand(data, buffer, num/2);
   if(result)
index 02d95d8..30fc296 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -40,10 +42,16 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
 
 /*
  * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
- * hexadecimal digits PLUS a zero terminating byte. It must be an odd number
+ * hexadecimal digits PLUS a null-terminating byte. It must be an odd number
  * size.
  */
 CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
                        size_t num);
 
+#ifdef WIN32
+/* Random generator shared between the Schannel vtls and Curl_rand*()
+   functions */
+CURLcode Curl_win32_random(unsigned char *entropy, size_t length);
+#endif
+
 #endif /* HEADER_CURL_RAND_H */
index f858d43..cfb3699 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2020 - 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "rename.h"
index 534f747..9958e2c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2020 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 int Curl_rename(const char *oldpath, const char *newpath);
index 726bfb9..6d3bf97 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -792,7 +794,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
 
     /* Find the first non-space letter */
     start = header + 8;
-    while(*start && ISSPACE(*start))
+    while(*start && ISBLANK(*start))
       start++;
 
     if(!*start) {
index da11ade..377c828 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifdef USE_HYPER
 #define CURL_DISABLE_RTSP 1
index a48da82..2ac0746 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -308,8 +310,12 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
   else
     pending_ms = 0;
   r = poll(ufds, nfds, pending_ms);
-  if(r <= 0)
+  if(r <= 0) {
+    if((r == -1) && (SOCKERRNO == EINTR))
+      /* make EINTR from select or poll not a "lethal" error */
+      r = 0;
     return r;
+  }
 
   for(i = 0; i < nfds; i++) {
     if(ufds[i].fd == CURL_SOCKET_BAD)
@@ -352,8 +358,12 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
      value).
   */
   r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
-  if(r <= 0)
+  if(r <= 0) {
+    if((r == -1) && (SOCKERRNO == EINTR))
+      /* make EINTR from select or poll not a "lethal" error */
+      r = 0;
     return r;
+  }
 
   r = 0;
   for(i = 0; i < nfds; i++) {
index f4bcba3..f2cf8bb 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -34,8 +36,7 @@
  * Definition of pollfd struct and constants for platforms lacking them.
  */
 
-#if !defined(HAVE_STRUCT_POLLFD) && \
-    !defined(HAVE_SYS_POLL_H) && \
+#if !defined(HAVE_SYS_POLL_H) && \
     !defined(HAVE_POLL_H) && \
     !defined(POLLIN)
 
index d7d4d8a..d26b7e7 100644 (file)
@@ -18,6 +18,8 @@
  * 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 "strdup.h"
 #include "http2.h"
 #include "headers.h"
+#include "ws.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef CURL_DO_LINEEND_CONV
+#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
 /*
  * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
  * (\n), with special processing for CRLF sequences that are split between two
@@ -132,7 +135,7 @@ static size_t convert_lineends(struct Curl_easy *data,
   }
   return size;
 }
-#endif /* CURL_DO_LINEEND_CONV */
+#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)
@@ -243,7 +246,7 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
   DEBUGASSERT(!strchr(fmt, '\n'));
   if(data && data->set.verbose) {
     va_list ap;
-    size_t len;
+    int len;
     char buffer[MAXINFO + 2];
     va_start(ap, fmt);
     len = mvsnprintf(buffer, MAXINFO, fmt, ap);
@@ -263,7 +266,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
   DEBUGASSERT(!strchr(fmt, '\n'));
   if(data->set.verbose || data->set.errorbuffer) {
     va_list ap;
-    size_t len;
+    int len;
     char error[CURL_ERROR_SIZE + 2];
     va_start(ap, fmt);
     len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
@@ -494,6 +497,9 @@ static CURLcode pausewrite(struct Curl_easy *data,
       }
     }
     DEBUGASSERT(i < 3);
+    if(i >= 3)
+      /* There are more types to store than what fits: very bad */
+      return CURLE_OUT_OF_MEMORY;
   }
   else
     i = 0;
@@ -529,6 +535,7 @@ static CURLcode chop_write(struct Curl_easy *data,
   curl_write_callback writebody = NULL;
   char *ptr = optr;
   size_t len = olen;
+  void *writebody_ptr = data->set.out;
 
   if(!len)
     return CURLE_OK;
@@ -539,8 +546,18 @@ static CURLcode chop_write(struct Curl_easy *data,
     return pausewrite(data, type, ptr, len);
 
   /* Determine the callback(s) to use. */
-  if(type & CLIENTWRITE_BODY)
+  if(type & CLIENTWRITE_BODY) {
+#ifdef USE_WEBSOCKETS
+    if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
+      struct HTTP *ws = data->req.p.http;
+      writebody = Curl_ws_writecb;
+      ws->ws.data = data;
+      writebody_ptr = ws;
+    }
+    else
+#endif
     writebody = data->set.fwrite_func;
+  }
   if((type & CLIENTWRITE_HEADER) &&
      (data->set.fwrite_header || data->set.writeheader)) {
     /*
@@ -558,7 +575,7 @@ static CURLcode chop_write(struct Curl_easy *data,
     if(writebody) {
       size_t wrote;
       Curl_set_in_callback(data, true);
-      wrote = writebody(ptr, 1, chunklen, data->set.out);
+      wrote = writebody(ptr, 1, chunklen, writebody_ptr);
       Curl_set_in_callback(data, false);
 
       if(CURL_WRITEFUNC_PAUSE == wrote) {
@@ -581,18 +598,20 @@ static CURLcode chop_write(struct Curl_easy *data,
     len -= chunklen;
   }
 
+#ifndef CURL_DISABLE_HTTP
   /* HTTP header, but not status-line */
   if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
      (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
-    CURLcode result =
-      Curl_headers_push(data, optr,
-                        type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
-                        (type & CLIENTWRITE_1XX ? CURLH_1XX :
-                         (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
-                          CURLH_HEADER)));
+    unsigned char htype = (unsigned char)
+      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
+       (type & CLIENTWRITE_1XX ? CURLH_1XX :
+        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
+         CURLH_HEADER)));
+    CURLcode result = Curl_headers_push(data, optr, htype);
     if(result)
       return result;
   }
+#endif
 
   if(writeheader) {
     size_t wrote;
@@ -605,8 +624,10 @@ static CURLcode chop_write(struct Curl_easy *data,
       /* here we pass in the HEADER bit only since if this was body as well
          then it was passed already and clearly that didn't trigger the
          pause, so this is saved for later with the HEADER bit only */
-      return pausewrite(data, CLIENTWRITE_HEADER, optr, olen);
-
+      return pausewrite(data, CLIENTWRITE_HEADER |
+                        (type & (CLIENTWRITE_STATUS|CLIENTWRITE_CONNECT|
+                                 CLIENTWRITE_1XX|CLIENTWRITE_TRAILER)),
+                        optr, olen);
     if(wrote != olen) {
       failf(data, "Failed writing header");
       return CURLE_WRITE_ERROR;
@@ -631,22 +652,15 @@ CURLcode Curl_client_write(struct Curl_easy *data,
                            char *ptr,
                            size_t len)
 {
-  struct connectdata *conn = data->conn;
-
-  if(!len)
-    return CURLE_OK;
-
+#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
   /* FTP data may need conversion. */
   if((type & CLIENTWRITE_BODY) &&
-     (conn->handler->protocol & PROTO_FAMILY_FTP) &&
-     conn->proto.ftpc.transfertype == 'A') {
-
-#ifdef CURL_DO_LINEEND_CONV
+     (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
+     data->conn->proto.ftpc.transfertype == 'A') {
     /* convert end-of-line markers */
     len = convert_lineends(data, ptr, len);
-#endif /* CURL_DO_LINEEND_CONV */
   }
-
+#endif
   return chop_write(data, type, ptr, len);
 }
 
@@ -714,17 +728,17 @@ CURLcode Curl_read(struct Curl_easy *data,   /* transfer */
 }
 
 /* return 0 on success */
-int Curl_debug(struct Curl_easy *data, curl_infotype type,
-               char *ptr, size_t size)
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+                char *ptr, size_t size)
 {
-  int rc = 0;
   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);
-      rc = (*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
-      Curl_set_in_callback(data, false);
+      (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
+      Curl_set_in_callback(data, inCallback);
     }
     else {
       switch(type) {
@@ -739,5 +753,4 @@ int Curl_debug(struct Curl_easy *data, curl_infotype type,
       }
     }
   }
-  return rc;
 }
index 6676003..7c4c128 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -87,8 +89,8 @@ CURLcode Curl_write_plain(struct Curl_easy *data,
                           ssize_t *written);
 
 /* the function used to output verbose information */
-int Curl_debug(struct Curl_easy *data, curl_infotype type,
-               char *ptr, size_t size);
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+                char *ptr, size_t size);
 
 
 #endif /* HEADER_CURL_SENDF_H */
index 05e1a54..5b59754 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -146,6 +148,40 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp)
 #define C_SSLVERSION_VALUE(x) (x & 0xffff)
 #define C_SSLVERSION_MAX_VALUE(x) (x & 0xffff0000)
 
+static CURLcode protocol2num(const char *str, curl_prot_t *val)
+{
+  if(!str)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  if(curl_strequal(str, "all")) {
+    *val = ~(curl_prot_t) 0;
+    return CURLE_OK;
+  }
+
+  *val = 0;
+
+  do {
+    const char *token = str;
+    size_t tlen;
+
+    str = strchr(str, ',');
+    tlen = str? (size_t) (str - token): strlen(token);
+    if(tlen) {
+      const struct Curl_handler *h = Curl_builtin_scheme(token, tlen);
+
+      if(!h)
+        return CURLE_UNSUPPORTED_PROTOCOL;
+
+      *val |= h->protocol;
+    }
+  } while(str++);
+
+  if(!*val)
+    /* no protocol listed */
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  return CURLE_OK;
+}
+
 /*
  * Do not make Curl_vsetopt() static: it is called from
  * packages/OS400/ccsidcurl.c.
@@ -155,9 +191,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
   char *argptr;
   CURLcode result = CURLE_OK;
   long arg;
-#ifdef ENABLE_IPV6
   unsigned long uarg;
-#endif
   curl_off_t bigsize;
 
   switch(option) {
@@ -165,7 +199,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if(arg < -1)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.dns_cache_timeout = arg;
+    else if(arg > INT_MAX)
+      arg = INT_MAX;
+
+    data->set.dns_cache_timeout = (int)arg;
     break;
   case CURLOPT_DNS_USE_GLOBAL_CACHE:
     /* deprecated */
@@ -203,19 +240,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #endif
   case CURLOPT_RANDOM_FILE:
-    /*
-     * This is the path name to a file that contains random data to seed
-     * the random SSL stuff with. The file is only used for reading.
-     */
-    result = Curl_setstropt(&data->set.str[STRING_SSL_RANDOM_FILE],
-                            va_arg(param, char *));
     break;
   case CURLOPT_EGDSOCKET:
-    /*
-     * The Entropy Gathering Daemon socket pathname
-     */
-    result = Curl_setstropt(&data->set.str[STRING_SSL_EGDSOCKET],
-                            va_arg(param, char *));
     break;
   case CURLOPT_MAXCONNECTS:
     /*
@@ -318,12 +344,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_SERVER_RESPONSE_TIMEOUT:
     /*
-     * Option that specifies how quickly an server response must be obtained
+     * Option that specifies how quickly a server response must be obtained
      * before it is considered failure. For pingpong protocols.
      */
     arg = va_arg(param, long);
     if((arg >= 0) && (arg <= (INT_MAX/1000)))
-      data->set.server_response_timeout = arg * 1000;
+      data->set.server_response_timeout = (unsigned int)arg * 1000;
     else
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
@@ -353,7 +379,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.use_netrc = (enum CURL_NETRC_OPTION)arg;
+    data->set.use_netrc = (unsigned char)arg;
     break;
   case CURLOPT_NETRC_FILE:
     /*
@@ -625,8 +651,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     }
     else
       data->set.method = HTTPREQ_GET;
+    data->set.upload = FALSE;
     break;
 
+#ifndef CURL_DISABLE_MIME
   case CURLOPT_HTTPPOST:
     /*
      * Set to make us do HTTP POST
@@ -635,6 +663,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     data->set.method = HTTPREQ_POST_FORM;
     data->set.opt_no_body = FALSE; /* this is implied */
     break;
+#endif
 
   case CURLOPT_AWS_SIGV4:
     /*
@@ -650,18 +679,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       data->set.httpauth = CURLAUTH_AWS_SIGV4;
     break;
 
-  case CURLOPT_MIMEPOST:
-    /*
-     * Set to make us do MIME/form POST
-     */
-    result = Curl_mime_set_subparts(&data->set.mimepost,
-                                    va_arg(param, curl_mime *), FALSE);
-    if(!result) {
-      data->set.method = HTTPREQ_POST_MIME;
-      data->set.opt_no_body = FALSE; /* this is implied */
-    }
-    break;
-
   case CURLOPT_REFERER:
     /*
      * String to set in the HTTP Referer: field.
@@ -683,13 +700,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
                             va_arg(param, char *));
     break;
 
-  case CURLOPT_HTTPHEADER:
-    /*
-     * Set a list with HTTP headers to use (or replace internals with)
-     */
-    data->set.headers = va_arg(param, struct curl_slist *);
-    break;
-
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXYHEADER:
     /*
@@ -927,6 +937,36 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #endif   /* CURL_DISABLE_HTTP */
 
+#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) ||       \
+    !defined(CURL_DISABLE_IMAP)
+# if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME)
+  case CURLOPT_HTTPHEADER:
+    /*
+     * Set a list with HTTP headers to use (or replace internals with)
+     */
+    data->set.headers = va_arg(param, struct curl_slist *);
+    break;
+# endif
+
+# ifndef CURL_DISABLE_MIME
+  case CURLOPT_MIMEPOST:
+    /*
+     * Set to make us do MIME POST
+     */
+    result = Curl_mime_set_subparts(&data->set.mimepost,
+                                    va_arg(param, curl_mime *), FALSE);
+    if(!result) {
+      data->set.method = HTTPREQ_POST_MIME;
+      data->set.opt_no_body = FALSE; /* this is implied */
+    }
+    break;
+
+  case CURLOPT_MIME_OPTIONS:
+    data->set.mime_options = (unsigned int)va_arg(param, long);
+    break;
+# endif
+#endif
+
   case CURLOPT_HTTPAUTH:
     /*
      * Set HTTP Authentication type BITMASK.
@@ -1115,13 +1155,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       break;
     }
     break;
-#endif   /* CURL_DISABLE_PROXY */
 
   case CURLOPT_SOCKS5_AUTH:
     data->set.socks5auth = va_arg(param, unsigned long);
     if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
       result = CURLE_NOT_BUILT_IN;
     break;
+#endif   /* CURL_DISABLE_PROXY */
+
 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
   case CURLOPT_SOCKS5_GSSAPI_NEC:
     /*
@@ -1270,7 +1311,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
        (arg > CURLFTP_CREATE_DIR_RETRY))
       result = CURLE_BAD_FUNCTION_ARGUMENT;
     else
-      data->set.ftp_create_missing_dirs = (int)arg;
+      data->set.ftp_create_missing_dirs = (unsigned char)arg;
     break;
   case CURLOPT_READDATA:
     /*
@@ -1360,12 +1401,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_PORT:
     /*
-     * The port number to use when getting the URL
+     * The port number to use when getting the URL. 0 disables it.
      */
     arg = va_arg(param, long);
     if((arg < 0) || (arg > 65535))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.use_port = arg;
+    data->set.use_port = (unsigned short)arg;
     break;
   case CURLOPT_TIMEOUT:
     /*
@@ -1374,16 +1415,16 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     arg = va_arg(param, long);
     if((arg >= 0) && (arg <= (INT_MAX/1000)))
-      data->set.timeout = arg * 1000;
+      data->set.timeout = (unsigned int)arg * 1000;
     else
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
 
   case CURLOPT_TIMEOUT_MS:
-    arg = va_arg(param, long);
-    if(arg < 0)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.timeout = arg;
+    uarg = va_arg(param, unsigned long);
+    if(uarg >= UINT_MAX)
+      uarg = UINT_MAX;
+    data->set.timeout = (unsigned int)uarg;
     break;
 
   case CURLOPT_CONNECTTIMEOUT:
@@ -1392,27 +1433,29 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     arg = va_arg(param, long);
     if((arg >= 0) && (arg <= (INT_MAX/1000)))
-      data->set.connecttimeout = arg * 1000;
+      data->set.connecttimeout = (unsigned int)arg * 1000;
     else
       return CURLE_BAD_FUNCTION_ARGUMENT;
     break;
 
   case CURLOPT_CONNECTTIMEOUT_MS:
-    arg = va_arg(param, long);
-    if(arg < 0)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.connecttimeout = arg;
+    uarg = va_arg(param, unsigned long);
+    if(uarg >= UINT_MAX)
+      uarg = UINT_MAX;
+    data->set.connecttimeout = (unsigned int)uarg;
     break;
 
+#ifndef CURL_DISABLE_FTP
   case CURLOPT_ACCEPTTIMEOUT_MS:
     /*
-     * The maximum time you allow curl to wait for server connect
+     * The maximum time for curl to wait for FTP server connect
      */
-    arg = va_arg(param, long);
-    if(arg < 0)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.accepttimeout = arg;
+    uarg = va_arg(param, unsigned long);
+    if(uarg >= UINT_MAX)
+      uarg = UINT_MAX;
+    data->set.accepttimeout = (unsigned int)uarg;
     break;
+#endif
 
   case CURLOPT_USERPWD:
     /*
@@ -1612,13 +1655,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Set data write callback
      */
     data->set.fwrite_func = va_arg(param, curl_write_callback);
-    if(!data->set.fwrite_func) {
-      data->set.is_fwrite_set = 0;
+    if(!data->set.fwrite_func)
       /* When set to NULL, reset to our internal default function */
       data->set.fwrite_func = (curl_write_callback)fwrite;
-    }
-    else
-      data->set.is_fwrite_set = 1;
     break;
   case CURLOPT_READFUNCTION:
     /*
@@ -2166,7 +2205,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     else if(arg < READBUFFER_MIN)
       arg = READBUFFER_MIN;
 
-    data->set.buffer_size = arg;
+    data->set.buffer_size = (int)arg;
     break;
 
   case CURLOPT_UPLOAD_BUFFERSIZE:
@@ -2360,9 +2399,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 
   case CURLOPT_CONNECT_ONLY:
     /*
-     * No data transfer, set up connection and let application use the socket
+     * No data transfer.
+     * (1) - only do connection
+     * (2) - do first get request but get no content
      */
-    data->set.connect_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    arg = va_arg(param, long);
+    if(arg > 2)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    data->set.connect_only = (unsigned char)arg;
     break;
 
   case CURLOPT_SOCKOPTFUNCTION:
@@ -2435,7 +2479,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 #ifdef USE_SSH
     /* we only include SSH options if explicitly built to support SSH */
   case CURLOPT_SSH_AUTH_TYPES:
-    data->set.ssh_auth_types = va_arg(param, long);
+    data->set.ssh_auth_types = (unsigned int)va_arg(param, long);
     break;
 
   case CURLOPT_SSH_PUBLIC_KEYFILE:
@@ -2478,7 +2522,19 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     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);
+    break;
 
+  case CURLOPT_SSH_HOSTKEYDATA:
+    /*
+     * Custom client data to pass to the SSH keyfunc callback
+     */
+    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 */
@@ -2523,7 +2579,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if((arg < 0) || (arg > 0777))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.new_file_perms = arg;
+    data->set.new_file_perms = (unsigned int)arg;
     break;
 
   case CURLOPT_NEW_DIRECTORY_PERMS:
@@ -2533,7 +2589,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if((arg < 0) || (arg > 0777))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.new_directory_perms = arg;
+    data->set.new_directory_perms = (unsigned int)arg;
     break;
 #endif
 
@@ -2558,16 +2614,36 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
        transfer, which thus helps the app which takes URLs from users or other
        external inputs and want to restrict what protocol(s) to deal
        with. Defaults to CURLPROTO_ALL. */
-    data->set.allowed_protocols = va_arg(param, long);
+    data->set.allowed_protocols = (curl_prot_t)va_arg(param, long);
     break;
 
   case CURLOPT_REDIR_PROTOCOLS:
     /* set the bitmask for the protocols that libcurl is allowed to follow to,
        as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
        to be set in both bitmasks to be allowed to get redirected to. */
-    data->set.redir_protocols = va_arg(param, long);
+    data->set.redir_protocols = (curl_prot_t)va_arg(param, long);
     break;
 
+  case CURLOPT_PROTOCOLS_STR: {
+    curl_prot_t prot;
+    argptr = va_arg(param, char *);
+    result = protocol2num(argptr, &prot);
+    if(result)
+      return result;
+    data->set.allowed_protocols = prot;
+    break;
+  }
+
+  case CURLOPT_REDIR_PROTOCOLS_STR: {
+    curl_prot_t prot;
+    argptr = va_arg(param, char *);
+    result = protocol2num(argptr, &prot);
+    if(result)
+      return result;
+    data->set.redir_protocols = prot;
+    break;
+  }
+
   case CURLOPT_DEFAULT_PROTOCOL:
     /* Set the protocol to use when the URL doesn't include any protocol */
     result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL],
@@ -2596,13 +2672,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #endif
 
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
-  !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
-  case CURLOPT_MIME_OPTIONS:
-    data->set.mime_options = va_arg(param, long);
-    break;
-#endif
-
   case CURLOPT_SASL_AUTHZID:
     /* Authorization identity (identity to act as) */
     result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID],
@@ -2833,13 +2902,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if(arg < 0)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.tcp_keepidle = arg;
+    else if(arg > INT_MAX)
+      arg = INT_MAX;
+    data->set.tcp_keepidle = (int)arg;
     break;
   case CURLOPT_TCP_KEEPINTVL:
     arg = va_arg(param, long);
     if(arg < 0)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.tcp_keepintvl = arg;
+    else if(arg > INT_MAX)
+      arg = INT_MAX;
+    data->set.tcp_keepintvl = (int)arg;
     break;
   case CURLOPT_TCP_FASTOPEN:
 #if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \
@@ -2850,7 +2923,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 #endif
     break;
   case CURLOPT_SSL_ENABLE_NPN:
-    data->set.ssl_enable_npn = (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
   case CURLOPT_SSL_ENABLE_ALPN:
     data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE;
@@ -2906,10 +2978,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     data->set.suppress_connect_headers = (0 != va_arg(param, long))?TRUE:FALSE;
     break;
   case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS:
-    arg = va_arg(param, long);
-    if(arg < 0)
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.happy_eyeballs_timeout = arg;
+    uarg = va_arg(param, unsigned long);
+    if(uarg >= UINT_MAX)
+      uarg = UINT_MAX;
+    data->set.happy_eyeballs_timeout = (unsigned int)uarg;
     break;
 #ifndef CURL_DISABLE_SHUFFLE_DNS
   case CURLOPT_DNS_SHUFFLE_ADDRESSES:
@@ -3026,6 +3098,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
   case CURLOPT_PREREQDATA:
     data->set.prereq_userp = va_arg(param, void *);
     break;
+#ifdef USE_WEBSOCKETS
+  case CURLOPT_WS_OPTIONS: {
+    bool raw;
+    arg = va_arg(param, long);
+    raw = (arg & CURLWS_RAW_MODE);
+    data->set.ws_raw_mode = raw;
+    break;
+  }
+#endif
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
index affbfd9..ffc77a7 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 CURLcode Curl_setstropt(char **charp, const char *s);
index 8c97371..6023ca2 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 
index a6710d9..b570683 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*                                                                         */
index fa8742f..bc5f8ef 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
 
 #ifdef HAVE_WINDOWS_H
 #  if defined(UNICODE) && !defined(_UNICODE)
-#    define _UNICODE
+#    error "UNICODE is defined but _UNICODE is not defined"
 #  endif
 #  if defined(_UNICODE) && !defined(UNICODE)
-#    define UNICODE
+#    error "_UNICODE is defined but UNICODE is not defined"
+#  endif
+/*
+ * Don't include unneeded stuff in Windows headers to avoid compiler
+ * warnings and macro clashes.
+ * Make sure to define this macro before including any Windows headers.
+ */
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#  ifndef NOGDI
+#    define NOGDI
 #  endif
 #  include <winerror.h>
 #  include <windows.h>
index 1e879f6..60720f5 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
index 403563f..1a083e7 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
@@ -39,7 +41,7 @@ curl_share_init(void)
   if(share) {
     share->magic = CURL_GOOD_SHARE;
     share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
-    Curl_init_dnscache(&share->hostcache);
+    Curl_init_dnscache(&share->hostcache, 23);
   }
 
   return share;
index 222e34b..32be416 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index d6ec5fc..d12b317 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 907c203..6c80722 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 3114259..4e5834c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 8f44704..a62e858 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
@@ -32,7 +34,7 @@
 #include <process.h>
 #ifdef CURL_WINDOWS_APP
 #define getpid GetCurrentProcessId
-#elif !defined(MSDOS)
+#elif defined(WIN32)
 #define getpid _getpid
 #endif
 #endif
index 0e3c2ec..919f3ac 100644 (file)
@@ -7,8 +7,8 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
- * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
+ * Copyright (C) 2018 - 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
@@ -21,6 +21,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 enum smb_conn_state {
index c736cfa..6ebb41a 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC1870 SMTP Service Extension for Message Size
  * RFC2195 CRAM-MD5 authentication
  * RFC2831 DIGEST-MD5 authentication
@@ -1818,7 +1820,9 @@ static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
   return result;
 }
 
-CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, const ssize_t nread)
+CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
+                              const ssize_t nread,
+                              const ssize_t offset)
 {
   /* When sending a SMTP payload we must detect CRLF. sequences making sure
      they are sent as CRLF.. instead, as a . on the beginning of a line will
@@ -1852,7 +1856,9 @@ CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, const ssize_t nread)
 
   /* This loop can be improved by some kind of Boyer-Moore style of
      approach but that is saved for later... */
-  for(i = 0, si = 0; i < nread; i++) {
+  if(offset)
+    memcpy(scratch, data->req.upload_fromhere, offset);
+  for(i = offset, si = offset; i < nread; i++) {
     if(SMTP_EOB[smtp->eob] == data->req.upload_fromhere[i]) {
       smtp->eob++;
 
index 1fe4534..24c5589 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2009 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2009 - 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "pingpong.h"
@@ -91,6 +93,8 @@ extern const struct Curl_handler Curl_handler_smtps;
 #define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
 #define SMTP_EOB_REPL_LEN 4
 
-CURLcode Curl_smtp_escape_eob(struct Curl_easy *data, const ssize_t nread);
+CURLcode Curl_smtp_escape_eob(struct Curl_easy *data,
+                              const ssize_t nread,
+                              const ssize_t offset);
 
 #endif /* HEADER_CURL_SMTP_H */
index 84c08d9..77ec833 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 409d2ad..0f8798f 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 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
@@ -18,6 +18,8 @@
  * 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"
index cdcc0b9..de70df6 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 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
  * 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 HAVE_SOCKETPAIR
+#include <curl/curl.h>
+
 int Curl_socketpair(int domain, int type, int protocol,
                     curl_socket_t socks[2]);
 #else
index d614ae5..52c2988 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index f30c610..ff83aa5 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index 8ef2f8f..f14099f 100644 (file)
@@ -5,8 +5,8 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, 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
@@ -19,6 +19,8 @@
  * 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"
@@ -51,7 +53,7 @@ static int check_gss_err(struct Curl_easy *data,
   if(GSS_ERROR(major_status)) {
     OM_uint32 maj_stat, min_stat;
     OM_uint32 msg_ctx = 0;
-    gss_buffer_desc status_string;
+    gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
     char buf[1024];
     size_t len;
 
index ffc8703..210a0df 100644 (file)
@@ -5,8 +5,8 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
- * Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012, 2011, 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
@@ -19,6 +19,8 @@
  * 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"
index 841d256..3ddc43d 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 1d4c7bf..cb44eb0 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index bcc0795..33b44aa 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1997 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1997 - 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
@@ -18,6 +18,8 @@
  * 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"
@@ -101,7 +103,7 @@ struct Curl_tree *Curl_splayinsert(struct curltime i,
                                    struct Curl_tree *node)
 {
   static const struct curltime KEY_NOTUSED = {
-    (time_t)-1, (unsigned int)-1
+    ~0, -1
   }; /* will *NEVER* appear */
 
   if(!node)
@@ -211,7 +213,7 @@ int Curl_splayremove(struct Curl_tree *t,
                      struct Curl_tree **newroot)
 {
   static const struct curltime KEY_NOTUSED = {
-    (time_t)-1, (unsigned int)-1
+    ~0, -1
   }; /* will *NEVER* appear */
   struct Curl_tree *x;
 
index eb9f65f..015e2ca 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1997 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1997 - 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
@@ -20,6 +20,8 @@
  * 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 "timeval.h"
index 692a3f1..09d2a8a 100644 (file)
@@ -18,6 +18,8 @@
  * 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 "strcase.h"
 
-static char raw_tolower(char in);
+/* Mapping table to go from lowercase to uppercase for plain ASCII.*/
+static const unsigned char touppermap[256] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65,
+66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
+182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
+214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
+230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245,
+246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+/* Mapping table to go from uppercase to lowercase for plain ASCII.*/
+static const unsigned char tolowermap[256] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
+96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
+128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
+};
+
 
 /* Portable, consistent toupper. Do not use toupper() because its behavior is
    altered by the current locale. */
 char Curl_raw_toupper(char in)
 {
-  if(in >= 'a' && in <= 'z')
-    return (char)('A' + in - 'a');
-  return in;
+  return touppermap[(unsigned char) in];
 }
 
 
 /* Portable, consistent tolower. Do not use tolower() because its behavior is
    altered by the current locale. */
-static char raw_tolower(char in)
+char Curl_raw_tolower(char in)
 {
-  if(in >= 'A' && in <= 'Z')
-    return (char)('a' + in - 'A');
-  return in;
+  return tolowermap[(unsigned char) in];
 }
 
 /*
@@ -62,14 +97,15 @@ int Curl_strcasecompare(const char *first, const char *second)
   while(*first && *second) {
     if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
       /* get out of the loop as soon as they don't match */
-      break;
+      return 0;
     first++;
     second++;
   }
-  /* we do the comparison here (possibly again), just to make sure that if the
-     loop above is skipped because one of the strings reached zero, we must not
-     return this as a successful match */
-  return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
+  /* If we're here either the strings are the same or the length is different.
+     We can just test if the "current" character is non-zero for one and zero
+     for the other. Note that the characters may not be exactly the same even
+     if they match, we only want to compare zero-ness. */
+  return !*first == !*second;
 }
 
 int Curl_safe_strcasecompare(const char *first, const char *second)
@@ -127,7 +163,7 @@ void Curl_strntolower(char *dest, const char *src, size_t n)
     return;
 
   do {
-    *dest++ = raw_tolower(*src);
+    *dest++ = Curl_raw_tolower(*src);
   } while(*src++ && --n);
 }
 
@@ -141,6 +177,28 @@ bool Curl_safecmp(char *a, char *b)
   return !a && !b;
 }
 
+/*
+ * Curl_timestrcmp() returns 0 if the two strings are identical. The time this
+ * function spends is a function of the shortest string, not of the contents.
+ */
+int Curl_timestrcmp(const char *a, const char *b)
+{
+  int match = 0;
+  int i = 0;
+
+  if(a && b) {
+    while(1) {
+      match |= a[i]^b[i];
+      if(!a[i] || !b[i])
+        break;
+      i++;
+    }
+  }
+  else
+    return a || b;
+  return match;
+}
+
 /* --- public functions --- */
 
 int curl_strequal(const char *first, const char *second)
index 2635f51..65a5753 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
@@ -41,6 +43,7 @@ int Curl_safe_strcasecompare(const char *first, const char *second);
 int Curl_strncasecompare(const char *first, const char *second, size_t max);
 
 char Curl_raw_toupper(char in);
+char Curl_raw_tolower(char in);
 
 /* checkprefix() is a shorter version of the above, used when the first
    argument is the string literal */
@@ -50,5 +53,6 @@ void Curl_strntoupper(char *dest, const char *src, size_t n);
 void Curl_strntolower(char *dest, const char *src, size_t n);
 
 bool Curl_safecmp(char *a, char *b);
+int Curl_timestrcmp(const char *first, const char *second);
 
 #endif /* HEADER_CURL_STRCASE_H */
index 85cf33b..ac22b6d 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 8c8a6f2..fb46808 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 781e26b..b9a51e2 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -263,9 +265,6 @@ curl_easy_strerror(CURLcode error)
   case CURLE_TFTP_NOSUCHUSER:
     return "TFTP: No such user";
 
-  case CURLE_CONV_FAILED:
-    return "Conversion failed";
-
   case CURLE_REMOTE_FILE_NOT_FOUND:
     return "Remote file not found";
 
@@ -317,6 +316,9 @@ curl_easy_strerror(CURLcode error)
   case CURLE_SSL_CLIENTCERT:
     return "SSL Client Certificate required";
 
+  case CURLE_UNRECOVERABLE_POLL:
+    return "Unrecoverable error in select/poll";
+
     /* error codes not used by current libcurl */
   case CURLE_OBSOLETE20:
   case CURLE_OBSOLETE24:
@@ -329,6 +331,7 @@ curl_easy_strerror(CURLcode error)
   case CURLE_OBSOLETE51:
   case CURLE_OBSOLETE57:
   case CURLE_OBSOLETE62:
+  case CURLE_OBSOLETE75:
   case CURLE_OBSOLETE76:
   case CURL_LAST:
     break;
@@ -400,6 +403,9 @@ curl_multi_strerror(CURLMcode error)
   case CURLM_ABORTED_BY_CALLBACK:
     return "Operation was aborted by an application callback";
 
+  case CURLM_UNRECOVERABLE_POLL:
+    return "Unrecoverable error in select/poll";
+
   case CURLM_LAST:
     break;
   }
@@ -470,7 +476,7 @@ curl_url_strerror(CURLUcode error)
     return "Port number was not a decimal number between 0 and 65535";
 
   case CURLUE_UNSUPPORTED_SCHEME:
-    return "This libcurl build doesn't support the given URL scheme";
+    return "Unsupported URL scheme";
 
   case CURLUE_URLDECODE:
     return "URL decode error, most likely because of rubbish in the input";
@@ -524,7 +530,7 @@ curl_url_strerror(CURLUcode error)
     return "Bad file:// URL";
 
   case CURLUE_BAD_SLASHES:
-    return "Unsupported number of slashes";
+    return "Unsupported number of slashes following scheme";
 
   case CURLUE_BAD_SCHEME:
     return "Bad scheme";
index 96a7e27..658f16c 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "urldata.h"
index d53e587..6120bcc 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 831ef0c..641a3da 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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 <stddef.h>
index ac87cfc..30deb8c 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <errno.h>
@@ -85,7 +87,7 @@ static curl_off_t strtooff(const char *nptr, char **endptr, int base)
 
   /* Skip leading whitespace. */
   end = (char *)nptr;
-  while(ISSPACE(end[0])) {
+  while(ISBLANK(end[0])) {
     end++;
   }
 
@@ -220,9 +222,9 @@ CURLofft curlx_strtoofft(const char *str, char **endp, int base,
   errno = 0;
   *num = 0; /* clear by default */
 
-  while(*str && ISSPACE(*str))
+  while(*str && ISBLANK(*str))
     str++;
-  if('-' == *str) {
+  if(('-' == *str) || (ISSPACE(*str))) {
     if(endp)
       *endp = (char *)str; /* didn't actually move */
     return CURL_OFFT_INVAL; /* nothing parsed */
index 4d22ba3..311dae4 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 9a6dd9c..bede9c7 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2016 - 2022, 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
@@ -18,6 +18,8 @@
  * 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"
index 69e0c81..167804e 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2016 - 2020, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2016 - 2022, 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
@@ -20,6 +20,8 @@
  * 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"
index 2abfcd9..923c7f8 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 1427473..6dd99b4 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifndef CURL_DISABLE_TELNET
 extern const struct Curl_handler Curl_handler_telnet;
index 7f2c88b..9e6d949 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 4b5bea2..3f1fda6 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifndef CURL_DISABLE_TFTP
 extern const struct Curl_handler Curl_handler_tftp;
index 003477c..c589318 100644 (file)
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "timediff.h"
 
+#include <limits.h>
+
 /*
  * Converts number of milliseconds into a timeval structure.
  *
index fcd5f05..90e5474 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index ca98fe5..647d7b0 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include "timeval.h"
index dce32f4..8d4fef4 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index 315da87..441da73 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -202,6 +204,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
   }
 #endif
 
+#ifndef CURL_DISABLE_HTTP
   /* if we are transmitting trailing data, we don't need to write
      a chunk size so we skip this */
   if(data->req.upload_chunky &&
@@ -211,7 +214,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
     data->req.upload_fromhere += (8 + 2); /* 32bit hex + CRLF */
   }
 
-#ifndef CURL_DISABLE_HTTP
   if(data->state.trailers_state == TRAILERS_SENDING) {
     /* if we're here then that means that we already sent the last empty chunk
        but we didn't send a final CR LF, so we sent 0 CR LF. We then start
@@ -267,6 +269,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
     return CURLE_READ_ERROR;
   }
 
+#ifndef CURL_DISABLE_HTTP
   if(!data->req.forbidchunk && data->req.upload_chunky) {
     /* if chunked Transfer-Encoding
      *    build chunk:
@@ -317,15 +320,12 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
 
       /* always append ASCII CRLF to the data unless
          we have a valid trailer callback */
-#ifndef CURL_DISABLE_HTTP
       if((nread-hexlen) == 0 &&
           data->set.trailer_callback != NULL &&
           data->state.trailers_state == TRAILERS_NONE) {
         data->state.trailers_state = TRAILERS_INITIALIZED;
       }
-      else
-#endif
-      {
+      else {
         memcpy(data->req.upload_fromhere + nread,
                endofline_network,
                strlen(endofline_network));
@@ -333,7 +333,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
       }
     }
 
-#ifndef CURL_DISABLE_HTTP
     if(data->state.trailers_state == TRAILERS_SENDING &&
        !trailers_left(data)) {
       Curl_dyn_free(&data->state.trailers_buf);
@@ -345,7 +344,6 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
       infof(data, "Signaling end of chunked upload after trailers.");
     }
     else
-#endif
       if((nread - hexlen) == 0 &&
          data->state.trailers_state != TRAILERS_INITIALIZED) {
         /* mark this as done once this chunk is transferred */
@@ -357,6 +355,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
     if(added_crlf)
       nread += strlen(endofline_network); /* for the added end of line */
   }
+#endif
 
   *nreadp = nread;
 
@@ -540,6 +539,13 @@ static CURLcode readwrite_data(struct Curl_easy *data,
     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
@@ -550,6 +556,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
          for a particular stream. */
       !is_http2 &&
 #endif
+      !is_http3 && /* Same reason mentioned above. */
       k->size != -1 && !k->header) {
       /* make sure we don't read too much */
       curl_off_t totalleft = k->size - k->bytecount;
@@ -597,6 +604,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
         DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
       else
 #endif
+      if(is_http3 && !nread)
+        DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
+      else
         DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
       k->keepon &= ~KEEP_RECV;
       break;
@@ -754,7 +764,13 @@ static CURLcode readwrite_data(struct Curl_easy *data,
         if(nread < 0) /* this should be unusual */
           nread = 0;
 
-        k->keepon &= ~KEEP_RECV; /* we're done reading */
+        /* HTTP/3 over QUIC should keep reading until QUIC connection
+           is closed.  In contrast to HTTP/2 which can stop reading
+           from TCP connection, HTTP/3 over QUIC needs ACK from server
+           to ensure stream closure.  It should keep reading. */
+        if(!is_http3) {
+          k->keepon &= ~KEEP_RECV; /* we're done reading */
+        }
       }
 
       k->bytecount += nread;
@@ -900,6 +916,9 @@ static void win_update_buffer_size(curl_socket_t sockfd)
 #define win_update_buffer_size(x)
 #endif
 
+#define curl_upload_refill_watermark(data) \
+        ((ssize_t)((data)->set.upload_buffer_size >> 5))
+
 /*
  * Send data to upload to the server, when the socket is writable.
  */
@@ -921,13 +940,25 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
 
   do {
     curl_off_t nbody;
+    ssize_t offset = 0;
+
+    if(0 != k->upload_present &&
+       k->upload_present < curl_upload_refill_watermark(data) &&
+       !k->upload_chunky &&/*(variable sized chunked header; append not safe)*/
+       !k->upload_done &&  /*!(k->upload_done once k->upload_present sent)*/
+       !(k->writebytecount + k->upload_present - k->pendingheader ==
+         data->state.infilesize)) {
+      offset = k->upload_present;
+    }
 
     /* only read more data if there's no upload data already
-       present in the upload buffer */
-    if(0 == k->upload_present) {
+       present in the upload buffer, or if appending to upload buffer */
+    if(0 == k->upload_present || offset) {
       result = Curl_get_upload_buffer(data);
       if(result)
         return result;
+      if(offset && k->upload_fromhere != data->state.ulbuf)
+        memmove(data->state.ulbuf, k->upload_fromhere, offset);
       /* init the "upload from here" pointer */
       k->upload_fromhere = data->state.ulbuf;
 
@@ -960,12 +991,14 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
             sending_http_headers = FALSE;
         }
 
-        result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
+        k->upload_fromhere += offset;
+        result = Curl_fillreadbuffer(data, data->set.upload_buffer_size-offset,
                                      &fillcount);
+        k->upload_fromhere -= offset;
         if(result)
           return result;
 
-        nread = fillcount;
+        nread = offset + fillcount;
       }
       else
         nread = 0; /* we're done uploading/reading */
@@ -1007,7 +1040,9 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
          * That means the hex values for ASCII CR (0x0d) & LF (0x0a)
          * must be used instead of the escape sequences \r & \n.
          */
-        for(i = 0, si = 0; i < nread; i++, si++) {
+        if(offset)
+          memcpy(data->state.scratch, k->upload_fromhere, offset);
+        for(i = offset, si = offset; i < nread; i++, si++) {
           if(k->upload_fromhere[i] == 0x0a) {
             data->state.scratch[si++] = 0x0d;
             data->state.scratch[si] = 0x0a;
@@ -1037,12 +1072,12 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
 
 #ifndef CURL_DISABLE_SMTP
       if(conn->handler->protocol & PROTO_FAMILY_SMTP) {
-        result = Curl_smtp_escape_eob(data, nread);
+        result = Curl_smtp_escape_eob(data, nread, offset);
         if(result)
           return result;
       }
 #endif /* CURL_DISABLE_SMTP */
-    } /* if 0 == k->upload_present */
+    } /* if 0 == k->upload_present or appended to upload buffer */
     else {
       /* We have a partial buffer left from a previous "round". Use
          that instead of reading more data */
@@ -1153,10 +1188,12 @@ CURLcode Curl_readwrite(struct connectdata *conn,
   else
     fd_write = CURL_SOCKET_BAD;
 
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
   if(data->state.drain) {
     select_res |= CURL_CSELECT_IN;
     DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
   }
+#endif
 
   if(!select_res) /* Call for select()/poll() only, if read/write/error
                      status is not known. */
@@ -1222,6 +1259,14 @@ CURLcode Curl_readwrite(struct connectdata *conn,
         infof(data, "Done waiting for 100-continue");
       }
     }
+
+#ifdef ENABLE_QUIC
+    if(conn->transport == TRNSPRT_QUIC) {
+      result = Curl_quic_idle(data);
+      if(result)
+        return result;
+    }
+#endif
   }
 
   if(Curl_pgrsUpdate(data))
@@ -1394,7 +1439,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
   if(result)
     return result;
 
-  data->state.wildcardmatch = data->set.wildcard_enabled;
+  data->state.requests = 0;
   data->state.followlocation = 0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
   data->state.errorbuf = FALSE; /* no error has occurred */
@@ -1416,10 +1461,11 @@ 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
   /* If there is a list of host pairs to deal with */
   if(data->state.resolve)
     result = Curl_loadhostpairs(data);
@@ -1449,6 +1495,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
     data->state.authproxy.picked &= data->state.authproxy.want;
 
 #ifndef CURL_DISABLE_FTP
+    data->state.wildcardmatch = data->set.wildcard_enabled;
     if(data->state.wildcardmatch) {
       struct WildcardData *wc = &data->wildcard;
       if(wc->state < CURLWC_INIT) {
@@ -1562,7 +1609,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
           data->state.referer_alloc = FALSE;
         }
 
-        /* Make a copy of the URL without crenditals and fragment */
+        /* Make a copy of the URL without credentials and fragment */
         u = curl_url();
         if(!u)
           return CURLE_OUT_OF_MEMORY;
@@ -1590,7 +1637,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
 
   if((type != FOLLOW_RETRY) &&
      (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
-     Curl_is_absolute_url(newurl, NULL, 0))
+     Curl_is_absolute_url(newurl, NULL, 0, FALSE))
     /* If this is not redirect due to a 401 or 407 response and an absolute
        URL: don't allow a custom port number */
     disallowport = TRUE;
@@ -1599,10 +1646,14 @@ CURLcode Curl_follow(struct Curl_easy *data,
   uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
                     (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
                     ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
-                    CURLU_ALLOW_SPACE);
+                    CURLU_ALLOW_SPACE |
+                    (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
   if(uc) {
-    if(type != FOLLOW_FAKE)
+    if(type != FOLLOW_FAKE) {
+      failf(data, "The redirect target URL could not be parsed: %s",
+            curl_url_strerror(uc));
       return Curl_uc_to_curlcode(uc);
+    }
 
     /* the URL could not be parsed for some reason, but since this is FAKE
        mode, just duplicate the field as-is */
@@ -1649,7 +1700,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
           return Curl_uc_to_curlcode(uc);
         }
 
-        p = Curl_builtin_scheme(scheme);
+        p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
         if(p && (p->protocol != data->info.conn_protocol)) {
           infof(data, "Clear auth, redirects scheme from %s to %s",
                 data->info.conn_scheme, scheme);
@@ -1813,10 +1864,12 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
     return CURLE_OK;
 
   if((data->req.bytecount + data->req.headerbytecount == 0) &&
-      conn->bits.reuse &&
-      (!data->set.opt_no_body
-        || (conn->handler->protocol & PROTO_FAMILY_HTTP)) &&
-      (data->set.rtspreq != RTSPREQ_RECEIVE))
+     conn->bits.reuse &&
+     (!data->set.opt_no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP))
+#ifndef CURL_DISABLE_RTSP
+     && (data->set.rtspreq != RTSPREQ_RECEIVE)
+#endif
+    )
     /* We got no data, we attempted to re-use a connection. For HTTP this
        can be a retry so we try again regardless if we expected a body.
        For other protocols we only try again only if we expected a body.
@@ -1888,11 +1941,14 @@ Curl_setup_transfer(
   struct SingleRequest *k = &data->req;
   struct connectdata *conn = data->conn;
   struct HTTP *http = data->req.p.http;
-  bool httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
-                      (http->sending == HTTPSEND_REQUEST));
+  bool httpsending;
+
   DEBUGASSERT(conn != NULL);
   DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
 
+  httpsending = ((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
+                 (http->sending == HTTPSEND_REQUEST));
+
   if(conn->bits.multiplex || conn->httpversion == 20 || httpsending) {
     /* when multiplexing, the read/write sockets need to be the same! */
     conn->sockfd = sockindex == -1 ?
index 56d2fd1..65fe68e 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #define Curl_headersep(x) ((((x)==':') || ((x)==';')))
index 6b31d4b..be5ffca 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
 #endif
 
 #elif defined(USE_WIN32_IDN)
-/* prototype for curl_win32_idn_to_ascii() */
-bool curl_win32_idn_to_ascii(const char *in, char **out);
+/* prototype for Curl_win32_idn_to_ascii() */
+bool Curl_win32_idn_to_ascii(const char *in, char **out);
 #endif  /* USE_LIBIDN2 */
 
+#include "doh.h"
 #include "urldata.h"
 #include "netrc.h"
 
@@ -103,6 +106,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
 #include "urlapi-int.h"
 #include "system_win32.h"
 #include "hsts.h"
+#include "noproxy.h"
 
 /* And now for the protocols */
 #include "ftp.h"
@@ -125,7 +129,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
 #include "http_proxy.h"
 #include "conncache.h"
 #include "multihandle.h"
-#include "dotdot.h"
 #include "strdup.h"
 #include "setopt.h"
 #include "altsvc.h"
@@ -147,6 +150,13 @@ static void conn_free(struct connectdata *conn);
 # error READBUFFER_SIZE is too small
 #endif
 
+#ifdef USE_UNIX_SOCKETS
+#define UNIX_SOCKET_PREFIX "localhost"
+#endif
+
+/* Reject URLs exceeding this length */
+#define MAX_URL_LEN 0xffff
+
 /*
 * get_protocol_family()
 *
@@ -158,7 +168,7 @@ static void conn_free(struct connectdata *conn);
 *
 * Returns the family as a single bit protocol identifier.
 */
-static unsigned int get_protocol_family(const struct Curl_handler *h)
+static curl_prot_t get_protocol_family(const struct Curl_handler *h)
 {
   DEBUGASSERT(h);
   DEBUGASSERT(h->family);
@@ -182,6 +192,16 @@ static const struct Curl_handler * const protocols[] = {
   &Curl_handler_http,
 #endif
 
+#ifdef USE_WEBSOCKETS
+#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
+  &Curl_handler_wss,
+#endif
+
+#ifndef CURL_DISABLE_HTTP
+  &Curl_handler_ws,
+#endif
+#endif
+
 #ifndef CURL_DISABLE_FTP
   &Curl_handler_ftp,
 #endif
@@ -433,6 +453,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
   Curl_safefree(data->info.wouldredirect);
 
   /* this destroys the channel and we cannot use it anymore after this */
+  Curl_resolver_cancel(data);
   Curl_resolver_cleanup(data->state.async.resolver);
 
   Curl_http2_cleanup_dependencies(data);
@@ -495,7 +516,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   /* use fread as default function to read input */
   set->fread_func_set = (curl_read_callback)fread;
   set->is_fread_set = 0;
-  set->is_fwrite_set = 0;
 
   set->seek_func = ZERO_NULL;
   set->seek_client = ZERO_NULL;
@@ -505,7 +525,9 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->maxredirs = -1;       /* allow any amount by default */
 
   set->method = HTTPREQ_GET; /* Default HTTP request */
+#ifndef CURL_DISABLE_RTSP
   set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
+#endif
 #ifndef CURL_DISABLE_FTP
   set->ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
   set->ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
@@ -521,10 +543,12 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->proxyport = 0;
   set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
   set->httpauth = CURLAUTH_BASIC;  /* defaults to basic */
-  set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
 
+#ifndef CURL_DISABLE_PROXY
+  set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
   /* SOCKS5 proxy auth defaults to username/password + GSS-API */
   set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
+#endif
 
   /* make libcurl quiet by default: */
   set->hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
@@ -544,8 +568,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
 #ifdef USE_TLS_SRP
   set->ssl.primary.authtype = CURL_TLSAUTH_NONE;
 #endif
-  set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
-                                                      type */
+   /* defaults to any auth type */
+  set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
   set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by
                                         default */
 #ifndef CURL_DISABLE_PROXY
@@ -554,11 +578,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
 
   set->new_file_perms = 0644;    /* Default permissions */
   set->new_directory_perms = 0755; /* Default permissions */
-
-  /* for the *protocols fields we don't use the CURLPROTO_ALL convenience
-     define since we internally only use the lower 16 bits for the passed
-     in bitmask to not conflict with the private bits */
-  set->allowed_protocols = CURLPROTO_ALL;
+  set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
   set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP |
                          CURLPROTO_FTPS;
 
@@ -598,22 +618,23 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
 #endif
   }
 
+#ifndef CURL_DISABLE_FTP
   set->wildcard_enabled = FALSE;
   set->chunk_bgn      = ZERO_NULL;
   set->chunk_end      = ZERO_NULL;
+  set->fnmatch = ZERO_NULL;
+#endif
   set->tcp_keepalive = FALSE;
   set->tcp_keepintvl = 60;
   set->tcp_keepidle = 60;
   set->tcp_fastopen = FALSE;
   set->tcp_nodelay = TRUE;
-  set->ssl_enable_npn = TRUE;
   set->ssl_enable_alpn = TRUE;
   set->expect_100_timeout = 1000L; /* Wait for a second by default. */
   set->sep_headers = TRUE; /* separated header lists by default */
   set->buffer_size = READBUFFER_SIZE;
   set->upload_buffer_size = UPLOADBUFFER_DEFAULT;
   set->happy_eyeballs_timeout = CURL_HET_DEFAULT;
-  set->fnmatch = ZERO_NULL;
   set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
   set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
   set->maxage_conn = 118;
@@ -730,15 +751,6 @@ static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn)
   DEBUGASSERT(data);
   infof(data, "Closing connection %ld", conn->connection_id);
 
-#ifndef USE_HYPER
-  if(conn->connect_state && conn->connect_state->prot_save) {
-    /* If this was closed with a CONNECT in progress, cleanup this temporary
-       struct arrangement */
-    data->req.p.http = NULL;
-    Curl_safefree(conn->connect_state->prot_save);
-  }
-#endif
-
   /* possible left-overs from the async name resolvers */
   Curl_resolver_cancel(data);
 
@@ -853,7 +865,7 @@ void Curl_disconnect(struct Curl_easy *data,
   /* Cleanup NEGOTIATE connection-related data */
   Curl_http_auth_cleanup_negotiate(conn);
 
-  if(conn->bits.connect_only)
+  if(conn->connect_only)
     /* treat the connection as dead in CONNECT_ONLY situations */
     dead_connection = TRUE;
 
@@ -937,19 +949,11 @@ socks_proxy_info_matches(const struct proxy_info *data,
   /* the user information is case-sensitive
      or at least it is not defined as case-insensitive
      see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */
-  if(!data->user != !needle->user)
-    return FALSE;
-  /* curl_strequal does a case insentive comparison, so do not use it here! */
-  if(data->user &&
-     needle->user &&
-     strcmp(data->user, needle->user) != 0)
-    return FALSE;
-  if(!data->passwd != !needle->passwd)
-    return FALSE;
-  /* curl_strequal does a case insentive comparison, so do not use it here! */
-  if(data->passwd &&
-     needle->passwd &&
-     strcmp(data->passwd, needle->passwd) != 0)
+
+  /* curl_strequal does a case insensitive comparison,
+     so do not use it here! */
+  if(Curl_timestrcmp(data->user, needle->user) ||
+     Curl_timestrcmp(data->passwd, needle->passwd))
     return FALSE;
   return TRUE;
 }
@@ -1100,12 +1104,17 @@ static void prune_dead_connections(struct Curl_easy *data)
   }
 }
 
+#ifdef USE_SSH
 static bool ssh_config_matches(struct connectdata *one,
                                struct connectdata *two)
 {
   return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
           Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub));
 }
+#else
+#define ssh_config_matches(x,y) FALSE
+#endif
+
 /*
  * Given one filled in connection struct (named needle), this function should
  * detect if there already is one that has all the significant details
@@ -1194,7 +1203,7 @@ ConnectionExists(struct Curl_easy *data,
       check = curr->ptr;
       curr = curr->next;
 
-      if(check->bits.connect_only || check->bits.close)
+      if(check->connect_only || check->bits.close)
         /* connect-only or to-be-closed connections will not be reused */
         continue;
 
@@ -1346,10 +1355,10 @@ ConnectionExists(struct Curl_easy *data,
       if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
         /* This protocol requires credentials per connection,
            so verify that we're using the same name and password as well */
-        if(strcmp(needle->user, check->user) ||
-           strcmp(needle->passwd, check->passwd) ||
-           !Curl_safecmp(needle->sasl_authzid, check->sasl_authzid) ||
-           !Curl_safecmp(needle->oauth_bearer, check->oauth_bearer)) {
+        if(Curl_timestrcmp(needle->user, check->user) ||
+           Curl_timestrcmp(needle->passwd, check->passwd) ||
+           Curl_timestrcmp(needle->sasl_authzid, check->sasl_authzid) ||
+           Curl_timestrcmp(needle->oauth_bearer, check->oauth_bearer)) {
           /* one of them was different */
           continue;
         }
@@ -1425,8 +1434,8 @@ ConnectionExists(struct Curl_easy *data,
            possible. (Especially we must not reuse the same connection if
            partway through a handshake!) */
         if(wantNTLMhttp) {
-          if(strcmp(needle->user, check->user) ||
-             strcmp(needle->passwd, check->passwd)) {
+          if(Curl_timestrcmp(needle->user, check->user) ||
+             Curl_timestrcmp(needle->passwd, check->passwd)) {
 
             /* we prefer a credential match, but this is at least a connection
                that can be reused and "upgraded" to NTLM */
@@ -1448,8 +1457,10 @@ ConnectionExists(struct Curl_easy *data,
           if(!check->http_proxy.user || !check->http_proxy.passwd)
             continue;
 
-          if(strcmp(needle->http_proxy.user, check->http_proxy.user) ||
-             strcmp(needle->http_proxy.passwd, check->http_proxy.passwd))
+          if(Curl_timestrcmp(needle->http_proxy.user,
+                             check->http_proxy.user) ||
+             Curl_timestrcmp(needle->http_proxy.passwd,
+                             check->http_proxy.passwd))
             continue;
         }
         else if(check->proxy_ntlm_state != NTLMSTATE_NONE) {
@@ -1621,7 +1632,7 @@ CURLcode Curl_idnconvert_hostname(struct Curl_easy *data,
 #elif defined(USE_WIN32_IDN)
     char *ace_hostname = NULL;
 
-    if(curl_win32_idn_to_ascii(host->name, &ace_hostname)) {
+    if(Curl_win32_idn_to_ascii(host->name, &ace_hostname)) {
       host->encalloc = ace_hostname;
       /* change the name pointer to point to the encoded hostname */
       host->name = host->encalloc;
@@ -1652,7 +1663,7 @@ void Curl_free_idnconverted_hostname(struct hostname *host)
   }
 #elif defined(USE_WIN32_IDN)
   free(host->encalloc); /* must be freed with free() since this was
-                           allocated by curl_win32_idn_to_ascii */
+                           allocated by Curl_win32_idn_to_ascii */
   host->encalloc = NULL;
 #else
   (void)host;
@@ -1770,19 +1781,15 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer;
   conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost;
   conn->ssl_config.ssl_options = data->set.ssl.primary.ssl_options;
-#ifdef USE_TLS_SRP
-#endif
 #ifndef CURL_DISABLE_PROXY
   conn->proxy_ssl_config.verifystatus =
     data->set.proxy_ssl.primary.verifystatus;
   conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer;
   conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost;
   conn->proxy_ssl_config.ssl_options = data->set.proxy_ssl.primary.ssl_options;
-#ifdef USE_TLS_SRP
-#endif
 #endif
   conn->ip_version = data->set.ipver;
-  conn->bits.connect_only = data->set.connect_only;
+  conn->connect_only = data->set.connect_only;
   conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
 
 #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
@@ -1826,15 +1833,18 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
 }
 
 /* returns the handler if the given scheme is built-in */
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme)
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+                                               size_t schemelen)
 {
   const struct Curl_handler * const *pp;
   const struct Curl_handler *p;
   /* Scan protocol handler table and match against 'scheme'. The handler may
      be changed later when the protocol specific setup function is called. */
+  if(schemelen == CURL_ZERO_TERMINATED)
+    schemelen = strlen(scheme);
   for(pp = protocols; (p = *pp) != NULL; pp++)
-    if(strcasecompare(p->scheme, scheme))
-      /* Protocol found in table. Check if allowed */
+    if(strncasecompare(p->scheme, scheme, schemelen) && !p->scheme[schemelen])
+      /* Protocol found in table. */
       return p;
   return NULL; /* not found */
 }
@@ -1844,7 +1854,8 @@ static CURLcode findprotocol(struct Curl_easy *data,
                              struct connectdata *conn,
                              const char *protostr)
 {
-  const struct Curl_handler *p = Curl_builtin_scheme(protostr);
+  const struct Curl_handler *p = Curl_builtin_scheme(protostr,
+                                                     CURL_ZERO_TERMINATED);
 
   if(p && /* Protocol found in table. Check if allowed */
      (data->set.allowed_protocols & p->protocol)) {
@@ -1968,7 +1979,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
 
   if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
-     !Curl_is_absolute_url(data->state.url, NULL, 0)) {
+     !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) {
     char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL],
                         data->state.url);
     if(!url)
@@ -2012,10 +2023,60 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
     if(!strcasecompare("file", data->state.up.scheme))
       return CURLE_OUT_OF_MEMORY;
   }
+  else if(strlen(data->state.up.hostname) > MAX_URL_LEN) {
+    failf(data, "Too long host name (maximum is %d)", MAX_URL_LEN);
+    return CURLE_URL_MALFORMAT;
+  }
+  hostname = data->state.up.hostname;
+
+  if(hostname && hostname[0] == '[') {
+    /* This looks like an IPv6 address literal. See if there is an address
+       scope. */
+    size_t hlen;
+    conn->bits.ipv6_ip = TRUE;
+    /* cut off the brackets! */
+    hostname++;
+    hlen = strlen(hostname);
+    hostname[hlen - 1] = 0;
+
+    zonefrom_url(uh, data, conn);
+  }
+
+  /* make sure the connect struct gets its own copy of the host name */
+  conn->host.rawalloc = strdup(hostname ? hostname : "");
+  if(!conn->host.rawalloc)
+    return CURLE_OUT_OF_MEMORY;
+  conn->host.name = conn->host.rawalloc;
+
+  /*************************************************************
+   * IDN-convert the hostnames
+   *************************************************************/
+  result = Curl_idnconvert_hostname(data, &conn->host);
+  if(result)
+    return result;
+  if(conn->bits.conn_to_host) {
+    result = Curl_idnconvert_hostname(data, &conn->conn_to_host);
+    if(result)
+      return result;
+  }
+#ifndef CURL_DISABLE_PROXY
+  if(conn->bits.httpproxy) {
+    result = Curl_idnconvert_hostname(data, &conn->http_proxy.host);
+    if(result)
+      return result;
+  }
+  if(conn->bits.socksproxy) {
+    result = Curl_idnconvert_hostname(data, &conn->socks_proxy.host);
+    if(result)
+      return result;
+  }
+#endif
 
 #ifndef CURL_DISABLE_HSTS
+  /* HSTS upgrade */
   if(data->hsts && strcasecompare("http", data->state.up.scheme)) {
-    if(Curl_hsts(data->hsts, data->state.up.hostname, TRUE)) {
+    /* This MUST use the IDN decoded name */
+    if(Curl_hsts(data->hsts, conn->host.name, TRUE)) {
       char *url;
       Curl_safefree(data->state.up.scheme);
       uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
@@ -2066,7 +2127,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
       return Curl_uc_to_curlcode(uc);
   }
 
-  if(!data->state.aptr.user) {
+  if(!data->set.str[STRING_USERNAME]) {
     /* we don't use the URL API's URL decoder option here since it rejects
        control codes and we want to allow them for some schemes in the user
        and password fields */
@@ -2101,7 +2162,8 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
   else if(uc != CURLUE_NO_OPTIONS)
     return Curl_uc_to_curlcode(uc);
 
-  uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, 0);
+  uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path,
+                    CURLU_URLENCODE);
   if(uc)
     return Curl_uc_to_curlcode(uc);
 
@@ -2115,31 +2177,11 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
     unsigned long port = strtoul(data->state.up.port, NULL, 10);
     conn->port = conn->remote_port =
       (data->set.use_port && data->state.allow_port) ?
-      (int)data->set.use_port : curlx_ultous(port);
+      data->set.use_port : curlx_ultous(port);
   }
 
   (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
 
-  hostname = data->state.up.hostname;
-  if(hostname && hostname[0] == '[') {
-    /* This looks like an IPv6 address literal. See if there is an address
-       scope. */
-    size_t hlen;
-    conn->bits.ipv6_ip = TRUE;
-    /* cut off the brackets! */
-    hostname++;
-    hlen = strlen(hostname);
-    hostname[hlen - 1] = 0;
-
-    zonefrom_url(uh, data, conn);
-  }
-
-  /* make sure the connect struct gets its own copy of the host name */
-  conn->host.rawalloc = strdup(hostname ? hostname : "");
-  if(!conn->host.rawalloc)
-    return CURLE_OUT_OF_MEMORY;
-  conn->host.name = conn->host.rawalloc;
-
 #ifdef ENABLE_IPV6
   if(data->set.scope_id)
     /* Override any scope that was set above.  */
@@ -2237,83 +2279,6 @@ void Curl_free_request_state(struct Curl_easy *data)
 
 
 #ifndef CURL_DISABLE_PROXY
-/****************************************************************
-* Checks if the host is in the noproxy list. returns true if it matches
-* and therefore the proxy should NOT be used.
-****************************************************************/
-static bool check_noproxy(const char *name, const char *no_proxy)
-{
-  /* no_proxy=domain1.dom,host.domain2.dom
-   *   (a comma-separated list of hosts which should
-   *   not be proxied, or an asterisk to override
-   *   all proxy variables)
-   */
-  if(no_proxy && no_proxy[0]) {
-    size_t tok_start;
-    size_t tok_end;
-    const char *separator = ", ";
-    size_t no_proxy_len;
-    size_t namelen;
-    char *endptr;
-    if(strcasecompare("*", no_proxy)) {
-      return TRUE;
-    }
-
-    /* NO_PROXY was specified and it wasn't just an asterisk */
-
-    no_proxy_len = strlen(no_proxy);
-    if(name[0] == '[') {
-      /* IPv6 numerical address */
-      endptr = strchr(name, ']');
-      if(!endptr)
-        return FALSE;
-      name++;
-      namelen = endptr - name;
-    }
-    else
-      namelen = strlen(name);
-
-    for(tok_start = 0; tok_start < no_proxy_len; tok_start = tok_end + 1) {
-      while(tok_start < no_proxy_len &&
-            strchr(separator, no_proxy[tok_start]) != NULL) {
-        /* Look for the beginning of the token. */
-        ++tok_start;
-      }
-
-      if(tok_start == no_proxy_len)
-        break; /* It was all trailing separator chars, no more tokens. */
-
-      for(tok_end = tok_start; tok_end < no_proxy_len &&
-            strchr(separator, no_proxy[tok_end]) == NULL; ++tok_end)
-        /* Look for the end of the token. */
-        ;
-
-      /* To match previous behavior, where it was necessary to specify
-       * ".local.com" to prevent matching "notlocal.com", we will leave
-       * the '.' off.
-       */
-      if(no_proxy[tok_start] == '.')
-        ++tok_start;
-
-      if((tok_end - tok_start) <= namelen) {
-        /* Match the last part of the name to the domain we are checking. */
-        const char *checkn = name + namelen - (tok_end - tok_start);
-        if(strncasecompare(no_proxy + tok_start, checkn,
-                           tok_end - tok_start)) {
-          if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') {
-            /* We either have an exact match, or the previous character is a .
-             * so it is within the same domain, so no proxy for this host.
-             */
-            return TRUE;
-          }
-        }
-      } /* if((tok_end - tok_start) <= namelen) */
-    } /* for(tok_start = 0; tok_start < no_proxy_len;
-         tok_start = tok_end + 1) */
-  } /* NO_PROXY was specified and it wasn't just an asterisk */
-
-  return FALSE;
-}
 
 #ifndef CURL_DISABLE_HTTP
 /****************************************************************
@@ -2353,7 +2318,7 @@ static char *detect_proxy(struct Curl_easy *data,
 
   /* Now, build <protocol>_proxy and check for such a one to use */
   while(*protop)
-    *envp++ = (char)tolower((int)*protop++);
+    *envp++ = Curl_raw_tolower(*protop++);
 
   /* append _proxy */
   strcpy(envp, "_proxy");
@@ -2411,13 +2376,18 @@ static CURLcode parse_proxy(struct Curl_easy *data,
   int port = -1;
   char *proxyuser = NULL;
   char *proxypasswd = NULL;
-  char *host;
+  char *host = NULL;
   bool sockstype;
   CURLUcode uc;
   struct proxy_info *proxyinfo;
   CURLU *uhp = curl_url();
   CURLcode result = CURLE_OK;
   char *scheme = NULL;
+#ifdef USE_UNIX_SOCKETS
+  char *path = NULL;
+  bool is_unix_proxy = FALSE;
+#endif
+
 
   if(!uhp) {
     result = CURLE_OUT_OF_MEMORY;
@@ -2542,21 +2512,54 @@ static CURLcode parse_proxy(struct Curl_easy *data,
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
-  Curl_safefree(proxyinfo->host.rawalloc);
-  proxyinfo->host.rawalloc = host;
-  if(host[0] == '[') {
-    /* this is a numerical IPv6, strip off the brackets */
-    size_t len = strlen(host);
-    host[len-1] = 0; /* clear the trailing bracket */
-    host++;
-    zonefrom_url(uhp, data, conn);
+#ifdef USE_UNIX_SOCKETS
+  if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) {
+    uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE);
+    if(uc) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
+    }
+    /* path will be "/", if no path was was found */
+    if(strcmp("/", path)) {
+      is_unix_proxy = TRUE;
+      free(host);
+      host = aprintf(UNIX_SOCKET_PREFIX"%s", path);
+      if(!host) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto error;
+      }
+      Curl_safefree(proxyinfo->host.rawalloc);
+      proxyinfo->host.rawalloc = host;
+      proxyinfo->host.name = host;
+      host = NULL;
+    }
+  }
+
+  if(!is_unix_proxy) {
+#endif
+    Curl_safefree(proxyinfo->host.rawalloc);
+    proxyinfo->host.rawalloc = host;
+    if(host[0] == '[') {
+      /* this is a numerical IPv6, strip off the brackets */
+      size_t len = strlen(host);
+      host[len-1] = 0; /* clear the trailing bracket */
+      host++;
+      zonefrom_url(uhp, data, conn);
+    }
+    proxyinfo->host.name = host;
+    host = NULL;
+#ifdef USE_UNIX_SOCKETS
   }
-  proxyinfo->host.name = host;
+#endif
 
   error:
   free(proxyuser);
   free(proxypasswd);
+  free(host);
   free(scheme);
+#ifdef USE_UNIX_SOCKETS
+  free(path);
+#endif
   curl_url_cleanup(uhp);
   return result;
 }
@@ -2644,8 +2647,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
     }
   }
 
-  if(check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
-      data->set.str[STRING_NOPROXY] : no_proxy)) {
+  if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
+                        data->set.str[STRING_NOPROXY] : no_proxy)) {
     Curl_safefree(proxy);
     Curl_safefree(socksproxy);
   }
@@ -2683,16 +2686,16 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
    * connection that may exist registered to the same proxy host.
    ***********************************************************************/
   if(proxy || socksproxy) {
+    curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype;
     if(proxy) {
-      result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype);
+      result = parse_proxy(data, conn, proxy, ptype);
       Curl_safefree(proxy); /* parse_proxy copies the proxy string */
       if(result)
         goto out;
     }
 
     if(socksproxy) {
-      result = parse_proxy(data, conn, socksproxy,
-                           conn->socks_proxy.proxytype);
+      result = parse_proxy(data, conn, socksproxy, ptype);
       /* parse_proxy copies the socks proxy string */
       Curl_safefree(socksproxy);
       if(result)
@@ -2842,15 +2845,15 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len,
           (psep && psep > osep ? (size_t)(psep - osep) :
                                  (size_t)(login + len - osep)) - 1 : 0);
 
-  /* Allocate the user portion buffer */
-  if(userp && ulen) {
+  /* Allocate the user portion buffer, which can be zero length */
+  if(userp) {
     ubuf = malloc(ulen + 1);
     if(!ubuf)
       result = CURLE_OUT_OF_MEMORY;
   }
 
   /* Allocate the password portion buffer */
-  if(!result && passwdp && plen) {
+  if(!result && passwdp && psep) {
     pbuf = malloc(plen + 1);
     if(!pbuf) {
       free(ubuf);
@@ -2913,7 +2916,7 @@ static CURLcode parse_remote_port(struct Curl_easy *data,
     /* if set, we use this instead of the port possibly given in the URL */
     char portbuf[16];
     CURLUcode uc;
-    conn->remote_port = (unsigned short)data->set.use_port;
+    conn->remote_port = data->set.use_port;
     msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port);
     uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0);
     if(uc)
@@ -2935,14 +2938,6 @@ static CURLcode override_login(struct Curl_easy *data,
   char **passwdp = &conn->passwd;
   char **optionsp = &conn->options;
 
-#ifndef CURL_DISABLE_NETRC
-  if(data->set.use_netrc == CURL_NETRC_REQUIRED && data->state.aptr.user) {
-    Curl_safefree(*userp);
-    Curl_safefree(*passwdp);
-    Curl_safefree(data->state.aptr.user); /* disable user+password */
-  }
-#endif
-
   if(data->set.str[STRING_OPTIONS]) {
     free(*optionsp);
     *optionsp = strdup(data->set.str[STRING_OPTIONS]);
@@ -2951,29 +2946,31 @@ static CURLcode override_login(struct Curl_easy *data,
   }
 
 #ifndef CURL_DISABLE_NETRC
+  if(data->set.use_netrc == CURL_NETRC_REQUIRED) {
+    Curl_safefree(*userp);
+    Curl_safefree(*passwdp);
+  }
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
-    bool netrc_user_changed = FALSE;
-    bool netrc_passwd_changed = FALSE;
     int ret;
     bool url_provided = FALSE;
 
-    if(data->state.up.user) {
-      /* there was a user name in the URL */
-      userp = &data->state.up.user;
+    if(data->state.aptr.user) {
+      /* there was a user name in the URL. Use the URL decoded version */
+      userp = &data->state.aptr.user;
       url_provided = TRUE;
     }
 
     ret = Curl_parsenetrc(conn->host.name,
                           userp, passwdp,
-                          &netrc_user_changed, &netrc_passwd_changed,
                           data->set.str[STRING_NETRC_FILE]);
     if(ret > 0) {
       infof(data, "Couldn't find host %s in the %s file; using defaults",
             conn->host.name, data->set.str[STRING_NETRC_FILE]);
     }
     else if(ret < 0) {
-      return CURLE_OUT_OF_MEMORY;
+      failf(data, ".netrc parser error");
+      return CURLE_READ_ERROR;
     }
     else {
       /* set bits.netrc TRUE to remember that we got the name from a .netrc
@@ -2986,29 +2983,35 @@ static CURLcode override_login(struct Curl_easy *data,
       conn->user = strdup(*userp);
       if(!conn->user)
         return CURLE_OUT_OF_MEMORY;
-      /* don't update the user name below */
-      userp = NULL;
+    }
+    /* no user was set but a password, set a blank user */
+    if(userp && !*userp && *passwdp) {
+      *userp = strdup("");
+      if(!*userp)
+        return CURLE_OUT_OF_MEMORY;
     }
   }
 #endif
 
   /* for updated strings, we update them in the URL */
-  if(userp) {
-    if(*userp) {
-      CURLcode result = Curl_setstropt(&data->state.aptr.user, *userp);
+  if(*userp) {
+    CURLcode result;
+    if(data->state.aptr.user != *userp) {
+      /* nothing to do then */
+      result = Curl_setstropt(&data->state.aptr.user, *userp);
       if(result)
         return result;
     }
-    if(data->state.aptr.user) {
-      uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user,
-                        CURLU_URLENCODE);
-      if(uc)
-        return Curl_uc_to_curlcode(uc);
-      if(!*userp) {
-        *userp = strdup(data->state.aptr.user);
-        if(!*userp)
-          return CURLE_OUT_OF_MEMORY;
-      }
+  }
+  if(data->state.aptr.user) {
+    uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user,
+                      CURLU_URLENCODE);
+    if(uc)
+      return Curl_uc_to_curlcode(uc);
+    if(!*userp) {
+      *userp = strdup(data->state.aptr.user);
+      if(!*userp)
+        return CURLE_OUT_OF_MEMORY;
     }
   }
   if(*passwdp) {
@@ -3361,131 +3364,166 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
   return result;
 }
 
-/*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
-static CURLcode resolve_server(struct Curl_easy *data,
-                               struct connectdata *conn,
-                               bool *async)
+#ifdef USE_UNIX_SOCKETS
+static CURLcode resolve_unix(struct Curl_easy *data,
+                             struct connectdata *conn,
+                             char *unix_path)
 {
-  CURLcode result = CURLE_OK;
+  struct Curl_dns_entry *hostaddr = NULL;
+  bool longpath = FALSE;
+
+  DEBUGASSERT(unix_path);
+  DEBUGASSERT(conn->dns_entry == NULL);
+
+  /* Unix domain sockets are local. The host gets ignored, just use the
+   * specified domain socket address. Do not cache "DNS entries". There is
+   * no DNS involved and we already have the filesystem path available. */
+  hostaddr = calloc(1, sizeof(struct Curl_dns_entry));
+  if(!hostaddr)
+    return CURLE_OUT_OF_MEMORY;
+
+  hostaddr->addr = Curl_unix2addr(unix_path, &longpath,
+                                  conn->bits.abstract_unix_socket);
+  if(!hostaddr->addr) {
+    if(longpath)
+      /* Long paths are not supported for now */
+      failf(data, "Unix socket path too long: '%s'", unix_path);
+    free(hostaddr);
+    return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
+  }
+
+  hostaddr->inuse++;
+  conn->dns_entry = hostaddr;
+  return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_PROXY
+static CURLcode resolve_proxy(struct Curl_easy *data,
+                              struct connectdata *conn,
+                              bool *async)
+{
+  struct Curl_dns_entry *hostaddr = NULL;
+  struct hostname *host;
   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+  int rc;
 
-  DEBUGASSERT(conn);
-  DEBUGASSERT(data);
-  /*************************************************************
-   * Resolve the name of the server or proxy
-   *************************************************************/
-  if(conn->bits.reuse)
-    /* We're reusing the connection - no need to resolve anything, and
-       idnconvert_hostname() was called already in create_conn() for the re-use
-       case. */
-    *async = FALSE;
+  DEBUGASSERT(conn->dns_entry == NULL);
 
-  else {
-    /* this is a fresh connect */
-    int rc;
-    struct Curl_dns_entry *hostaddr = NULL;
+  host = conn->bits.socksproxy ? &conn->socks_proxy.host :
+    &conn->http_proxy.host;
 
-#ifdef USE_UNIX_SOCKETS
-    if(conn->unix_domain_socket) {
-      /* Unix domain sockets are local. The host gets ignored, just use the
-       * specified domain socket address. Do not cache "DNS entries". There is
-       * no DNS involved and we already have the filesystem path available */
-      const char *path = conn->unix_domain_socket;
-
-      hostaddr = calloc(1, sizeof(struct Curl_dns_entry));
-      if(!hostaddr)
-        result = CURLE_OUT_OF_MEMORY;
-      else {
-        bool longpath = FALSE;
-        hostaddr->addr = Curl_unix2addr(path, &longpath,
-                                        conn->bits.abstract_unix_socket);
-        if(hostaddr->addr)
-          hostaddr->inuse++;
-        else {
-          /* Long paths are not supported for now */
-          if(longpath) {
-            failf(data, "Unix socket path too long: '%s'", path);
-            result = CURLE_COULDNT_RESOLVE_HOST;
-          }
-          else
-            result = CURLE_OUT_OF_MEMORY;
-          free(hostaddr);
-          hostaddr = NULL;
-        }
-      }
-    }
-    else
+  conn->hostname_resolve = strdup(host->name);
+  if(!conn->hostname_resolve)
+    return CURLE_OUT_OF_MEMORY;
+
+  rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
+                           &hostaddr, timeout_ms);
+  conn->dns_entry = hostaddr;
+  if(rc == CURLRESOLV_PENDING)
+    *async = TRUE;
+  else if(rc == CURLRESOLV_TIMEDOUT)
+    return CURLE_OPERATION_TIMEDOUT;
+  else if(!hostaddr) {
+    failf(data, "Couldn't resolve proxy '%s'", host->dispname);
+    return CURLE_COULDNT_RESOLVE_PROXY;
+  }
+
+  return CURLE_OK;
+}
 #endif
 
-    if(!CONN_IS_PROXIED(conn)) {
-      struct hostname *connhost;
-      if(conn->bits.conn_to_host)
-        connhost = &conn->conn_to_host;
-      else
-        connhost = &conn->host;
+static CURLcode resolve_host(struct Curl_easy *data,
+                             struct connectdata *conn,
+                             bool *async)
+{
+  struct Curl_dns_entry *hostaddr = NULL;
+  struct hostname *connhost;
+  timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+  int rc;
 
-      /* If not connecting via a proxy, extract the port from the URL, if it is
-       * there, thus overriding any defaults that might have been set above. */
-      if(conn->bits.conn_to_port)
-        conn->port = conn->conn_to_port;
-      else
-        conn->port = conn->remote_port;
+  DEBUGASSERT(conn->dns_entry == NULL);
 
-      /* Resolve target host right on */
-      conn->hostname_resolve = strdup(connhost->name);
-      if(!conn->hostname_resolve)
-        return CURLE_OUT_OF_MEMORY;
-      rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
-                               &hostaddr, timeout_ms);
-      if(rc == CURLRESOLV_PENDING)
-        *async = TRUE;
-
-      else if(rc == CURLRESOLV_TIMEDOUT) {
-        failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
-              connhost->dispname,
-              Curl_timediff(Curl_now(), data->progress.t_startsingle));
-        result = CURLE_OPERATION_TIMEDOUT;
-      }
-      else if(!hostaddr) {
-        failf(data, "Could not resolve host: %s", connhost->dispname);
-        result = CURLE_COULDNT_RESOLVE_HOST;
-        /* don't return yet, we need to clean up the timeout first */
-      }
-    }
-#ifndef CURL_DISABLE_PROXY
-    else {
-      /* This is a proxy that hasn't been resolved yet. */
+  connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
 
-      struct hostname * const host = conn->bits.socksproxy ?
-        &conn->socks_proxy.host : &conn->http_proxy.host;
+  /* If not connecting via a proxy, extract the port from the URL, if it is
+   * there, thus overriding any defaults that might have been set above. */
+  conn->port = conn->bits.conn_to_port ? conn->conn_to_port :
+    conn->remote_port;
 
-      /* resolve proxy */
-      conn->hostname_resolve = strdup(host->name);
-      if(!conn->hostname_resolve)
-        return CURLE_OUT_OF_MEMORY;
-      rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
-                               &hostaddr, timeout_ms);
+  /* Resolve target host right on */
+  conn->hostname_resolve = strdup(connhost->name);
+  if(!conn->hostname_resolve)
+    return CURLE_OUT_OF_MEMORY;
 
-      if(rc == CURLRESOLV_PENDING)
-        *async = TRUE;
+  rc = Curl_resolv_timeout(data, conn->hostname_resolve, (int)conn->port,
+                           &hostaddr, timeout_ms);
+  conn->dns_entry = hostaddr;
+  if(rc == CURLRESOLV_PENDING)
+    *async = TRUE;
+  else if(rc == CURLRESOLV_TIMEDOUT) {
+    failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
+          connhost->dispname,
+          Curl_timediff(Curl_now(), data->progress.t_startsingle));
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+  else if(!hostaddr) {
+    failf(data, "Could not resolve host: %s", connhost->dispname);
+    return CURLE_COULDNT_RESOLVE_HOST;
+  }
 
-      else if(rc == CURLRESOLV_TIMEDOUT)
-        result = CURLE_OPERATION_TIMEDOUT;
+  return CURLE_OK;
+}
 
-      else if(!hostaddr) {
-        failf(data, "Couldn't resolve proxy '%s'", host->dispname);
-        result = CURLE_COULDNT_RESOLVE_PROXY;
-        /* don't return yet, we need to clean up the timeout first */
-      }
-    }
+/* Perform a fresh resolve */
+static CURLcode resolve_fresh(struct Curl_easy *data,
+                              struct connectdata *conn,
+                              bool *async)
+{
+#ifdef USE_UNIX_SOCKETS
+  char *unix_path = conn->unix_domain_socket;
+
+#ifndef CURL_DISABLE_PROXY
+  if(!unix_path && conn->socks_proxy.host.name &&
+     !strncmp(UNIX_SOCKET_PREFIX"/",
+              conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+    unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
 #endif
-    DEBUGASSERT(conn->dns_entry == NULL);
-    conn->dns_entry = hostaddr;
+
+  if(unix_path) {
+    conn->transport = TRNSPRT_UNIX;
+    return resolve_unix(data, conn, unix_path);
   }
+#endif
 
-  return result;
+#ifndef CURL_DISABLE_PROXY
+  if(CONN_IS_PROXIED(conn))
+    return resolve_proxy(data, conn, async);
+#endif
+
+  return resolve_host(data, conn, async);
+}
+
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode resolve_server(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               bool *async)
+{
+  DEBUGASSERT(conn);
+  DEBUGASSERT(data);
+
+  /* Resolve the name of the server or proxy */
+  if(conn->bits.reuse) {
+    /* We're reusing the connection - no need to resolve anything, and
+       idnconvert_hostname() was called already in create_conn() for the re-use
+       case. */
+    *async = FALSE;
+    return CURLE_OK;
+  }
+
+  return resolve_fresh(data, conn, async);
 }
 
 /*
@@ -3502,17 +3540,6 @@ static void reuse_conn(struct Curl_easy *data,
      **established** from the primary socket to a remote address. */
   char local_ip[MAX_IPADR_LEN] = "";
   int local_port = -1;
-#ifndef CURL_DISABLE_PROXY
-  Curl_free_idnconverted_hostname(&old_conn->http_proxy.host);
-  Curl_free_idnconverted_hostname(&old_conn->socks_proxy.host);
-
-  free(old_conn->http_proxy.host.rawalloc);
-  free(old_conn->socks_proxy.host.rawalloc);
-  Curl_free_primary_ssl_config(&old_conn->proxy_ssl_config);
-#endif
-  /* free the SSL config struct from this connection struct as this was
-     allocated in vain and is targeted for destruction */
-  Curl_free_primary_ssl_config(&old_conn->ssl_config);
 
   /* get the user+password information from the old_conn struct since it may
    * be new for this request even when we re-use an existing connection */
@@ -3543,20 +3570,17 @@ static void reuse_conn(struct Curl_easy *data,
     old_conn->http_proxy.passwd = NULL;
     old_conn->socks_proxy.passwd = NULL;
   }
-  Curl_safefree(old_conn->http_proxy.user);
-  Curl_safefree(old_conn->socks_proxy.user);
-  Curl_safefree(old_conn->http_proxy.passwd);
-  Curl_safefree(old_conn->socks_proxy.passwd);
 #endif
 
-  /* host can change, when doing keepalive with a proxy or if the case is
-     different this time etc */
   Curl_free_idnconverted_hostname(&conn->host);
   Curl_free_idnconverted_hostname(&conn->conn_to_host);
   Curl_safefree(conn->host.rawalloc);
   Curl_safefree(conn->conn_to_host.rawalloc);
   conn->host = old_conn->host;
+  old_conn->host.rawalloc = NULL;
+  old_conn->host.encalloc = NULL;
   conn->conn_to_host = old_conn->conn_to_host;
+  old_conn->conn_to_host.rawalloc = NULL;
   conn->conn_to_port = old_conn->conn_to_port;
   conn->remote_port = old_conn->remote_port;
   Curl_safefree(conn->hostname_resolve);
@@ -3576,15 +3600,7 @@ static void reuse_conn(struct Curl_easy *data,
   /* re-use init */
   conn->bits.reuse = TRUE; /* yes, we're re-using here */
 
-  Curl_safefree(old_conn->user);
-  Curl_safefree(old_conn->passwd);
-  Curl_safefree(old_conn->options);
-  Curl_safefree(old_conn->localdev);
-  Curl_llist_destroy(&old_conn->easyq, NULL);
-
-#ifdef USE_UNIX_SOCKETS
-  Curl_safefree(old_conn->unix_domain_socket);
-#endif
+  conn_free(old_conn);
 }
 
 /**
@@ -3714,29 +3730,6 @@ static CURLcode create_conn(struct Curl_easy *data,
   if(result)
     goto out;
 
-  /*************************************************************
-   * IDN-convert the hostnames
-   *************************************************************/
-  result = Curl_idnconvert_hostname(data, &conn->host);
-  if(result)
-    goto out;
-  if(conn->bits.conn_to_host) {
-    result = Curl_idnconvert_hostname(data, &conn->conn_to_host);
-    if(result)
-      goto out;
-  }
-#ifndef CURL_DISABLE_PROXY
-  if(conn->bits.httpproxy) {
-    result = Curl_idnconvert_hostname(data, &conn->http_proxy.host);
-    if(result)
-      goto out;
-  }
-  if(conn->bits.socksproxy) {
-    result = Curl_idnconvert_hostname(data, &conn->socks_proxy.host);
-    if(result)
-      goto out;
-  }
-#endif
 
   /*************************************************************
    * Check whether the host and the "connect to host" are equal.
@@ -3834,8 +3827,6 @@ static CURLcode create_conn(struct Curl_easy *data,
   data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
   data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
   data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
-  data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
-  data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
   data->set.ssl.primary.cipher_list =
     data->set.str[STRING_SSL_CIPHER_LIST];
   data->set.ssl.primary.cipher_list13 =
@@ -3849,9 +3840,6 @@ static CURLcode create_conn(struct Curl_easy *data,
 #ifndef CURL_DISABLE_PROXY
   data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY];
   data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY];
-  data->set.proxy_ssl.primary.random_file =
-    data->set.str[STRING_SSL_RANDOM_FILE];
-  data->set.proxy_ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
   data->set.proxy_ssl.primary.cipher_list =
     data->set.str[STRING_SSL_CIPHER_LIST_PROXY];
   data->set.proxy_ssl.primary.cipher_list13 =
@@ -3934,10 +3922,6 @@ static CURLcode create_conn(struct Curl_easy *data,
      * allocated before we can move along and use the previously existing one.
      */
     reuse_conn(data, conn, conn_temp);
-#ifdef USE_SSL
-    free(conn->ssl_extra);
-#endif
-    free(conn);          /* we don't need this anymore */
     conn = conn_temp;
     *in_connect = conn;
 
@@ -3958,13 +3942,11 @@ static CURLcode create_conn(struct Curl_easy *data,
        be able to do that if we have reached the limit of how many
        connections we are allowed to open. */
 
-    if(conn->handler->flags & PROTOPT_ALPN_NPN) {
+    if(conn->handler->flags & PROTOPT_ALPN) {
       /* The protocol wants it, so set the bits if enabled in the easy handle
          (default) */
       if(data->set.ssl_enable_alpn)
         conn->bits.tls_enable_alpn = TRUE;
-      if(data->set.ssl_enable_npn)
-        conn->bits.tls_enable_npn = TRUE;
     }
 
     if(waitpipe)
index 59a1c24..ba4270d 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
 
@@ -44,7 +46,8 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
 
-const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme,
+                                               size_t schemelen);
 
 bool Curl_is_ASCII_name(const char *hostname);
 CURLcode Curl_idnconvert_hostname(struct Curl_easy *data,
index bd6b601..43a83ef 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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"
 
-bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen);
+size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
+                            bool guess_scheme);
 
 #ifdef DEBUGBUILD
-CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname, bool);
+CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
+                          bool has_scheme);
 #endif
 
 #endif /* HEADER_CURL_URLAPI_INT_H */
index 2a36de6..7dac81c 100644 (file)
@@ -18,6 +18,8 @@
  * 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 "urldata.h"
 #include "urlapi-int.h"
 #include "strcase.h"
-#include "dotdot.h"
 #include "url.h"
 #include "escape.h"
 #include "curl_ctype.h"
 #include "inet_pton.h"
 #include "inet_ntop.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -66,9 +68,6 @@ struct Curl_URL {
   char *path;
   char *query;
   char *fragment;
-
-  char *scratch; /* temporary scratch area */
-  char *temppath; /* temporary path pointer */
   long portnum; /* the numerical version */
 };
 
@@ -86,8 +85,6 @@ static void free_urlhandle(struct Curl_URL *u)
   free(u->path);
   free(u->query);
   free(u->fragment);
-  free(u->scratch);
-  free(u->temppath);
 }
 
 /*
@@ -119,91 +116,50 @@ static const char *find_host_sep(const char *url)
 }
 
 /*
- * Decide in an encoding-independent manner whether a character in an
- * URL must be escaped. The same criterion must be used in strlen_url()
- * and strcpy_url().
+ * Decide in an encoding-independent manner whether a character in a URL must
+ * be escaped. This is used in urlencode_str().
  */
 static bool urlchar_needs_escaping(int c)
 {
   return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
 }
 
-/*
- * strlen_url() returns the length of the given URL if the spaces within the
- * URL were properly URL encoded.
- * URL encoding should be skipped for host names, otherwise IDN resolution
- * will fail.
- */
-static size_t strlen_url(const char *url, bool relative)
-{
-  const unsigned char *ptr;
-  size_t newlen = 0;
-  bool left = TRUE; /* left side of the ? */
-  const unsigned char *host_sep = (const unsigned char *) url;
-
-  if(!relative)
-    host_sep = (const unsigned char *) find_host_sep(url);
-
-  for(ptr = (unsigned char *)url; *ptr; ptr++) {
-
-    if(ptr < host_sep) {
-      ++newlen;
-      continue;
-    }
-
-    if(*ptr == ' ') {
-      if(left)
-        newlen += 3;
-      else
-        newlen++;
-      continue;
-    }
-
-    if (*ptr == '?')
-      left = FALSE;
-
-    if(urlchar_needs_escaping(*ptr))
-      newlen += 2;
-
-    newlen++;
-  }
-
-  return newlen;
-}
-
-/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
- * the source URL accordingly.
+/* urlencode_str() writes data into an output dynbuf and URL-encodes the
+ * spaces in the source URL accordingly.
+ *
  * URL encoding should be skipped for host names, otherwise IDN resolution
  * will fail.
  */
-static void strcpy_url(char *output, const char *url, bool relative)
+static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
+                               size_t len, bool relative,
+                               bool query)
 {
   /* we must add this with whitespace-replacing */
-  bool left = TRUE;
+  bool left = !query;
   const unsigned char *iptr;
-  char *optr = output;
   const unsigned char *host_sep = (const unsigned char *) url;
 
   if(!relative)
     host_sep = (const unsigned char *) find_host_sep(url);
 
   for(iptr = (unsigned char *)url;    /* read from here */
-      *iptr;         /* until zero byte */
-      iptr++) {
+      len; iptr++, len--) {
 
     if(iptr < host_sep) {
-      *optr++ = *iptr;
+      if(Curl_dyn_addn(o, iptr, 1))
+        return CURLUE_OUT_OF_MEMORY;
       continue;
     }
 
     if(*iptr == ' ') {
       if(left) {
-        *optr++='%'; /* add a '%' */
-        *optr++='2'; /* add a '2' */
-        *optr++='0'; /* add a '0' */
+        if(Curl_dyn_addn(o, "%20", 3))
+          return CURLUE_OUT_OF_MEMORY;
+      }
+      else {
+        if(Curl_dyn_addn(o, "+", 1))
+          return CURLUE_OUT_OF_MEMORY;
       }
-      else
-        *optr++='+'; /* add a '+' here */
       continue;
     }
 
@@ -211,22 +167,28 @@ static void strcpy_url(char *output, const char *url, bool relative)
       left = FALSE;
 
     if(urlchar_needs_escaping(*iptr)) {
-      msnprintf(optr, 4, "%%%02x", *iptr);
-      optr += 3;
+      if(Curl_dyn_addf(o, "%%%02x", *iptr))
+        return CURLUE_OUT_OF_MEMORY;
+    }
+    else {
+      if(Curl_dyn_addn(o, iptr, 1))
+        return CURLUE_OUT_OF_MEMORY;
     }
-    else
-      *optr++ = *iptr;
   }
-  *optr = 0; /* null-terminate output buffer */
 
+  return CURLUE_OK;
 }
 
 /*
- * Returns true if the given URL is absolute (as opposed to relative). Returns
- * the scheme in the buffer if TRUE and 'buf' is non-NULL. The buflen must
- * be larger than MAX_SCHEME_LEN if buf is set.
+ * Returns the length of the scheme if the given URL is absolute (as opposed
+ * to relative). Stores the scheme in the buffer if TRUE and 'buf' is
+ * non-NULL. The buflen must be larger than MAX_SCHEME_LEN if buf is set.
+ *
+ * If 'guess_scheme' is TRUE, it means the URL might be provided without
+ * scheme.
  */
-bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
+size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
+                            bool guess_scheme)
 {
   int i;
   DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN));
@@ -234,8 +196,8 @@ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
   if(buf)
     buf[0] = 0; /* always leave a defined value in buf */
 #ifdef WIN32
-  if(STARTS_WITH_DRIVE_PREFIX(url))
-    return FALSE;
+  if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url))
+    return 0;
 #endif
   for(i = 0; i < MAX_SCHEME_LEN; ++i) {
     char s = url[i];
@@ -248,16 +210,22 @@ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
       break;
     }
   }
-  if(i && (url[i] == ':') && (url[i + 1] == '/')) {
+  if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) {
+    /* If this does not guess scheme, the scheme always ends with the colon so
+       that this also detects data: URLs etc. In guessing mode, data: could
+       be the host name "data" with a specified port number. */
+
+    /* the length of the scheme is the name part only */
+    size_t len = i;
     if(buf) {
       buf[i] = 0;
       while(i--) {
-        buf[i] = (char)TOLOWER(url[i]);
+        buf[i] = Curl_raw_tolower(url[i]);
       }
     }
-    return TRUE;
+    return len;
   }
-  return FALSE;
+  return 0;
 }
 
 /*
@@ -265,34 +233,26 @@ bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
  * URL-encodes any spaces.
  * The returned pointer must be freed by the caller unless NULL
  * (returns NULL on out of memory).
+ *
+ * Note that this function destroys the 'base' string.
  */
-static char *concat_url(const char *base, const char *relurl)
+static char *concat_url(char *base, const char *relurl)
 {
   /***
    TRY to append this new path to the old URL
    to the right of the host part. Oh crap, this is doomed to cause
    problems in the future...
   */
-  char *newest;
+  struct dynbuf newest;
   char *protsep;
   char *pathsep;
-  size_t newlen;
   bool host_changed = FALSE;
-
   const char *useurl = relurl;
-  size_t urllen;
-
-  /* we must make our own copy of the URL to play with, as it may
-     point to read-only data */
-  char *url_clone = strdup(base);
-
-  if(!url_clone)
-    return NULL; /* skip out of this NOW */
 
   /* protsep points to the start of the host name */
-  protsep = strstr(url_clone, "//");
+  protsep = strstr(base, "//");
   if(!protsep)
-    protsep = url_clone;
+    protsep = base;
   else
     protsep += 2; /* pass the slashes */
 
@@ -385,38 +345,24 @@ static char *concat_url(const char *base, const char *relurl)
     }
   }
 
-  /* If the new part contains a space, this is a mighty stupid redirect
-     but we still make an effort to do "right". To the left of a '?'
-     letter we replace each space with %20 while it is replaced with '+'
-     on the right side of the '?' letter.
-  */
-  newlen = strlen_url(useurl, !host_changed);
-
-  urllen = strlen(url_clone);
-
-  newest = malloc(urllen + 1 + /* possible slash */
-                  newlen + 1 /* zero byte */);
-
-  if(!newest) {
-    free(url_clone); /* don't leak this */
-    return NULL;
-  }
+  Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
 
   /* copy over the root url part */
-  memcpy(newest, url_clone, urllen);
+  if(Curl_dyn_add(&newest, base))
+    return NULL;
 
   /* check if we need to append a slash */
   if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
     ;
-  else
-    newest[urllen++]='/';
+  else {
+    if(Curl_dyn_addn(&newest, "/", 1))
+      return NULL;
+  }
 
   /* then append the new piece on the right side */
-  strcpy_url(&newest[urllen], useurl, !host_changed);
-
-  free(url_clone);
+  urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE);
 
-  return newest;
+  return Curl_dyn_ptr(&newest);
 }
 
 /* scan for byte values < 31 or 127 */
@@ -450,7 +396,7 @@ static bool junkscan(const char *part, unsigned int flags)
  *
  */
 static CURLUcode parse_hostname_login(struct Curl_URL *u,
-                                      char **hostname,
+                                      struct dynbuf *host,
                                       unsigned int flags)
 {
   CURLUcode result = CURLUE_OK;
@@ -460,27 +406,31 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
   char *optionsp = NULL;
   const struct Curl_handler *h = NULL;
 
-  /* At this point, we're hoping all the other special cases have
-   * been taken care of, so conn->host.name is at most
-   *    [user[:password][;options]]@]hostname
+  /* At this point, we assume all the other special cases have been taken
+   * care of, so the host is at most
+   *
+   *   [user[:password][;options]]@]hostname
    *
    * We need somewhere to put the embedded details, so do that first.
    */
 
-  char *ptr = strchr(*hostname, '@');
-  char *login = *hostname;
+  char *login = Curl_dyn_ptr(host);
+  char *ptr;
 
+  DEBUGASSERT(login);
+
+  ptr = strchr(login, '@');
   if(!ptr)
     goto out;
 
   /* We will now try to extract the
    * possible login information in a string like:
    * ftp://user:password@ftp.my.site:8021/README */
-  *hostname = ++ptr;
+  ptr++;
 
   /* if this is a known scheme, get some details */
   if(u->scheme)
-    h = Curl_builtin_scheme(u->scheme);
+    h = Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
 
   /* We could use the login information in the URL so extract it. Only parse
      options if the handler says we should. Note that 'h' might be NULL! */
@@ -522,6 +472,10 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
     u->options = optionsp;
   }
 
+  /* move the name to the start of the host buffer */
+  if(Curl_dyn_tail(host, strlen(ptr)))
+    return CURLUE_OUT_OF_MEMORY;
+
   return CURLUE_OK;
   out:
 
@@ -535,13 +489,13 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
   return result;
 }
 
-UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
+UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
                                    bool has_scheme)
 {
   char *portptr = NULL;
   char endbracket;
   int len;
-
+  char *hostname = Curl_dyn_ptr(host);
   /*
    * Find the end of an IPv6 address, either on the ']' ending bracket or
    * a percent-encoded zone index.
@@ -578,6 +532,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
     char *rest;
     long port;
     char portbuf[7];
+    size_t keep = portptr - hostname;
 
     /* Browser behavior adaptation. If there's a colon with no digits after,
        just cut off the name there which makes us ignore the colon and just
@@ -586,15 +541,15 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
        Don't do it if the URL has no scheme, to make something that looks like
        a scheme not work!
     */
-    if(!portptr[1]) {
-      *portptr = '\0';
+    Curl_dyn_setlen(host, keep);
+    portptr++;
+    if(!*portptr)
       return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER;
-    }
 
-    if(!ISDIGIT(portptr[1]))
+    if(!ISDIGIT(*portptr))
       return CURLUE_BAD_PORT_NUMBER;
 
-    port = strtol(portptr + 1, &rest, 10);  /* Port number must be decimal */
+    port = strtol(portptr, &rest, 10);  /* Port number must be decimal */
 
     if(port > 0xffff)
       return CURLUE_BAD_PORT_NUMBER;
@@ -602,7 +557,6 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
     if(rest[0])
       return CURLUE_BAD_PORT_NUMBER;
 
-    *portptr++ = '\0'; /* cut off the name there */
     *rest = 0;
     /* generate a new port number string to get rid of leading zeroes etc */
     msnprintf(portbuf, sizeof(portbuf), "%ld", port);
@@ -615,12 +569,15 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
   return CURLUE_OK;
 }
 
-static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
+static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
+                                size_t hlen) /* length of hostname */
 {
   size_t len;
-  size_t hlen = strlen(hostname);
+  DEBUGASSERT(hostname);
 
-  if(hostname[0] == '[') {
+  if(!hostname[0])
+    return CURLUE_NO_HOST;
+  else if(hostname[0] == '[') {
     const char *l = "0123456789abcdefABCDEF:.";
     if(hlen < 4) /* '[::]' is the shortest possible valid string */
       return CURLUE_BAD_IPV6;
@@ -679,13 +636,11 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
   }
   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;
   }
-  if(!hostname[0])
-    return CURLUE_NO_HOST;
   return CURLUE_OK;
 }
 
@@ -779,79 +734,230 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
   return TRUE;
 }
 
-/* return strdup'ed version in 'outp', possibly percent decoded */
-static CURLUcode decode_host(char *hostname, char **outp)
+/* if necessary, replace the host content with a URL decoded version */
+static CURLUcode decode_host(struct dynbuf *host)
 {
   char *per = NULL;
-  if(hostname[0] != '[')
+  const char *hostname = Curl_dyn_ptr(host);
+  if(hostname[0] == '[')
     /* only decode if not an ipv6 numerical */
-    per = strchr(hostname, '%');
-  if(!per) {
-    *outp = strdup(hostname);
-    if(!*outp)
-      return CURLUE_OUT_OF_MEMORY;
-  }
+    return CURLUE_OK;
+  per = strchr(hostname, '%');
+  if(!per)
+    /* nothing to decode */
+    return CURLUE_OK;
   else {
-    /* might be encoded */
+    /* encoded */
     size_t dlen;
-    CURLcode result = Curl_urldecode(hostname, 0, outp, &dlen, REJECT_CTRL);
+    char *decoded;
+    CURLcode result = Curl_urldecode(hostname, 0, &decoded, &dlen,
+                                     REJECT_CTRL);
     if(result)
       return CURLUE_BAD_HOSTNAME;
+    Curl_dyn_reset(host);
+    result = Curl_dyn_addn(host, decoded, dlen);
+    free(decoded);
+    if(result)
+      return CURLUE_OUT_OF_MEMORY;
   }
 
   return CURLUE_OK;
 }
 
-static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
+/*
+ * "Remove Dot Segments"
+ * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
+ */
+
+/*
+ * dedotdotify()
+ * @unittest: 1395
+ *
+ * This function gets a null-terminated path with dot and dotdot sequences
+ * passed in and strips them off according to the rules in RFC 3986 section
+ * 5.2.4.
+ *
+ * The function handles a query part ('?' + stuff) appended but it expects
+ * that fragments ('#' + stuff) have already been cut off.
+ *
+ * RETURNS
+ *
+ * an allocated dedotdotified output string
+ */
+UNITTEST char *dedotdotify(const char *input, size_t clen);
+UNITTEST char *dedotdotify(const char *input, size_t clen)
 {
-  char *path;
-  bool path_alloced = FALSE;
+  char *out = malloc(clen + 1);
+  char *outptr;
+  const char *orginput = input;
+  char *queryp;
+  if(!out)
+    return NULL; /* 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
+   * string.
+   */
+  queryp = strchr(input, '?');
+
+  do {
+    bool dotdot = TRUE;
+    if(*input == '.') {
+      /*  A.  If the input buffer begins with a prefix of "../" or "./", then
+          remove that prefix from the input buffer; otherwise, */
+
+      if(!strncmp("./", input, 2)) {
+        input += 2;
+        clen -= 2;
+      }
+      else if(!strncmp("../", input, 3)) {
+        input += 3;
+        clen -= 3;
+      }
+      /*  D.  if the input buffer consists only of "." or "..", then remove
+          that from the input buffer; otherwise, */
+
+      else if(!strcmp(".", input) || !strcmp("..", input) ||
+              !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) {
+        *out = 0;
+        break;
+      }
+      else
+        dotdot = FALSE;
+    }
+    else if(*input == '/') {
+      /*  B.  if the input buffer begins with a prefix of "/./" or "/.", where
+          "."  is a complete path segment, then replace that prefix with "/" in
+          the input buffer; otherwise, */
+      if(!strncmp("/./", input, 3)) {
+        input += 2;
+        clen -= 2;
+      }
+      else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) {
+        *outptr++ = '/';
+        *outptr = 0;
+        break;
+      }
+
+      /*  C.  if the input buffer begins with a prefix of "/../" or "/..",
+          where ".." is a complete path segment, then replace that prefix with
+          "/" in the input buffer and remove the last segment and its
+          preceding "/" (if any) from the output buffer; otherwise, */
+
+      else if(!strncmp("/../", input, 4)) {
+        input += 3;
+        clen -= 3;
+        /* remove the last segment from the output buffer */
+        while(outptr > out) {
+          outptr--;
+          if(*outptr == '/')
+            break;
+        }
+        *outptr = 0; /* null-terminate where it stops */
+      }
+      else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) {
+        /* remove the last segment from the output buffer */
+        while(outptr > out) {
+          outptr--;
+          if(*outptr == '/')
+            break;
+        }
+        *outptr++ = '/';
+        *outptr = 0; /* null-terminate where it stops */
+        break;
+      }
+      else
+        dotdot = FALSE;
+    }
+    else
+      dotdot = FALSE;
+
+    if(!dotdot) {
+      /*  E.  move the first path segment in the input buffer to the end of
+          the output buffer, including the initial "/" character (if any) and
+          any subsequent characters up to, but not including, the next "/"
+          character or the end of the input buffer. */
+
+      do {
+        *outptr++ = *input++;
+        clen--;
+      } while(*input && (*input != '/') && (*input != '?'));
+      *outptr = 0;
+    }
+
+    /* continue until end of input string OR, if there is a terminating
+       query part, stop there */
+  } while(*input && (!queryp || (input < queryp)));
+
+  if(queryp) {
+    size_t qlen;
+    /* There was a query part, append that to the output. */
+    size_t oindex = queryp - orginput;
+    qlen = strlen(&orginput[oindex]);
+    memcpy(outptr, &orginput[oindex], qlen + 1); /* include zero byte */
+  }
+
+  return out;
+}
+
+static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
+{
+  const char *path;
+  size_t pathlen;
   bool uncpath = FALSE;
-  char *hostname;
   char *query = NULL;
   char *fragment = NULL;
-  CURLUcode result;
-  bool url_has_scheme = FALSE;
   char schemebuf[MAX_SCHEME_LEN + 1];
   const char *schemep = NULL;
   size_t schemelen = 0;
   size_t urllen;
+  CURLUcode result = CURLUE_OK;
+  size_t fraglen = 0;
+  struct dynbuf host;
 
   DEBUGASSERT(url);
 
+  Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+
   /*************************************************************
    * Parse the URL.
    ************************************************************/
   /* allocate scratch area */
   urllen = strlen(url);
-  if(urllen > CURL_MAX_INPUT_LENGTH)
+  if(urllen > CURL_MAX_INPUT_LENGTH) {
     /* excessive input length */
-    return CURLUE_MALFORMED_INPUT;
-
-  path = u->scratch = malloc(urllen * 2 + 2);
-  if(!path)
-    return CURLUE_OUT_OF_MEMORY;
-
-  hostname = &path[urllen + 1];
-  hostname[0] = 0;
-
-  if(Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf))) {
-    url_has_scheme = TRUE;
-    schemelen = strlen(schemebuf);
+    result = CURLUE_MALFORMED_INPUT;
+    goto fail;
   }
 
+  schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf),
+                                   flags & (CURLU_GUESS_SCHEME|
+                                            CURLU_DEFAULT_SCHEME));
+
   /* handle the file: scheme */
-  if(url_has_scheme && !strcmp(schemebuf, "file")) {
-    if(urllen <= 6)
+  if(schemelen && !strcmp(schemebuf, "file")) {
+    if(urllen <= 6) {
       /* file:/ is not enough to actually be a complete file: URL */
-      return CURLUE_BAD_FILE_URL;
+      result = CURLUE_BAD_FILE_URL;
+      goto fail;
+    }
 
     /* path has been allocated large enough to hold this */
-    strcpy(path, &url[5]);
+    path = (char *)&url[5];
 
-    u->scheme = strdup("file");
-    if(!u->scheme)
-      return CURLUE_OUT_OF_MEMORY;
+    schemep = u->scheme = strdup("file");
+    if(!u->scheme) {
+      result = CURLUE_OUT_OF_MEMORY;
+      goto fail;
+    }
 
     /* Extra handling URLs with an authority component (i.e. that start with
      * "file://")
@@ -861,7 +967,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
      */
     if(path[0] == '/' && path[1] == '/') {
       /* swallow the two slashes */
-      char *ptr = &path[2];
+      const char *ptr = &path[2];
 
       /*
        * According to RFC 8089, a file: URL can be reliably dereferenced if:
@@ -897,13 +1003,17 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
              chars, and the delimiting slash character must be appended to the
              host name */
           path = strpbrk(ptr, "/\\:*?\"<>|");
-          if(!path || *path != '/')
-            return CURLUE_BAD_FILE_URL;
+          if(!path || *path != '/') {
+            result = CURLUE_BAD_FILE_URL;
+            goto fail;
+          }
 
           len = path - ptr;
           if(len) {
-            memcpy(hostname, ptr, len);
-            hostname[len] = 0;
+            if(Curl_dyn_addn(&host, ptr, len)) {
+              result = CURLUE_OUT_OF_MEMORY;
+              goto fail;
+            }
             uncpath = TRUE;
           }
 
@@ -911,7 +1021,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 #else
           /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
              none */
-          return CURLUE_BAD_FILE_URL;
+          result = CURLUE_BAD_FILE_URL;
+          goto fail;
 #endif
         }
       }
@@ -920,7 +1031,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     }
 
     if(!uncpath)
-        hostname = NULL; /* no host for file: URLs by default */
+      /* no host for file: URLs by default */
+      Curl_dyn_reset(&host);
 
 #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
     /* Don't allow Windows drive letters when not in Windows.
@@ -928,13 +1040,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) ||
        STARTS_WITH_URL_DRIVE_PREFIX(path)) {
       /* File drive letters are only accepted in MSDOS/Windows */
-      return CURLUE_BAD_FILE_URL;
+      result = CURLUE_BAD_FILE_URL;
+      goto fail;
     }
 #else
     /* If the path starts with a slash and a drive letter, ditch the slash */
     if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) {
       /* This cannot be done with strcpy, as the memory chunks overlap! */
-      memmove(path, &path[1], strlen(&path[1]) + 1);
+      path++;
     }
 #endif
 
@@ -944,32 +1057,39 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     const char *p;
     const char *hostp;
     size_t len;
-    path[0] = 0;
 
-    if(url_has_scheme) {
+    if(schemelen) {
       int i = 0;
       p = &url[schemelen + 1];
       while(p && (*p == '/') && (i < 4)) {
         p++;
         i++;
       }
-      if((i < 1) || (i>3))
-        /* less than one or more than three slashes */
-        return CURLUE_BAD_SLASHES;
 
       schemep = schemebuf;
-      if(!Curl_builtin_scheme(schemep) &&
-         !(flags & CURLU_NON_SUPPORT_SCHEME))
-        return CURLUE_UNSUPPORTED_SCHEME;
+      if(!Curl_builtin_scheme(schemep, CURL_ZERO_TERMINATED) &&
+         !(flags & CURLU_NON_SUPPORT_SCHEME)) {
+        result = CURLUE_UNSUPPORTED_SCHEME;
+        goto fail;
+      }
 
-      if(junkscan(schemep, flags))
-        return CURLUE_BAD_SCHEME;
+      if((i < 1) || (i>3)) {
+        /* less than one or more than three slashes */
+        result = CURLUE_BAD_SLASHES;
+        goto fail;
+      }
+      if(junkscan(schemep, flags)) {
+        result = CURLUE_BAD_SCHEME;
+        goto fail;
+      }
     }
     else {
       /* no scheme! */
 
-      if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME)))
-        return CURLUE_BAD_SCHEME;
+      if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME))) {
+        result = CURLUE_BAD_SCHEME;
+        goto fail;
+      }
       if(flags & CURLU_DEFAULT_SCHEME)
         schemep = DEFAULT_SCHEME;
 
@@ -986,122 +1106,169 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 
     len = p - hostp;
     if(len) {
-      memcpy(hostname, hostp, len);
-      hostname[len] = 0;
+      if(Curl_dyn_addn(&host, hostp, len)) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
     }
     else {
-      if(!(flags & CURLU_NO_AUTHORITY))
-        return CURLUE_NO_HOST;
+      if(!(flags & CURLU_NO_AUTHORITY)) {
+        result = CURLUE_NO_HOST;
+        goto fail;
+      }
     }
 
-    strcpy(path, p);
+    path = (char *)p;
 
     if(schemep) {
       u->scheme = strdup(schemep);
-      if(!u->scheme)
-        return CURLUE_OUT_OF_MEMORY;
+      if(!u->scheme) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
     }
   }
 
-  if((flags & CURLU_URLENCODE) && path[0]) {
-    /* worst case output length is 3x the original! */
-    char *newp = malloc(strlen(path) * 3);
-    if(!newp)
-      return CURLUE_OUT_OF_MEMORY;
-    path_alloced = TRUE;
-    strcpy_url(newp, path, TRUE); /* consider it relative */
-    u->temppath = path = newp;
-  }
-
   fragment = strchr(path, '#');
   if(fragment) {
-    *fragment++ = 0;
-    if(junkscan(fragment, flags))
-      return CURLUE_BAD_FRAGMENT;
-    if(fragment[0]) {
-      u->fragment = strdup(fragment);
-      if(!u->fragment)
-        return CURLUE_OUT_OF_MEMORY;
+    fraglen = strlen(fragment);
+    if(fraglen > 1) {
+      /* skip the leading '#' in the copy but include the terminating null */
+      u->fragment = Curl_memdup(fragment + 1, fraglen);
+      if(!u->fragment) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
+
+      if(junkscan(u->fragment, flags)) {
+        result = CURLUE_BAD_FRAGMENT;
+        goto fail;
+      }
     }
   }
 
   query = strchr(path, '?');
-  if(query) {
-    *query++ = 0;
-    if(junkscan(query, flags))
-      return CURLUE_BAD_QUERY;
-    /* done even if the query part is a blank string */
-    u->query = strdup(query);
-    if(!u->query)
-      return CURLUE_OUT_OF_MEMORY;
-  }
+  if(query && (!fragment || (query < fragment))) {
+    size_t qlen = strlen(query) - fraglen; /* includes '?' */
+    pathlen = strlen(path) - qlen - fraglen;
+    if(qlen > 1) {
+      if(qlen && (flags & CURLU_URLENCODE)) {
+        struct dynbuf enc;
+        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+        /* skip the leading question mark */
+        if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) {
+          result = CURLUE_OUT_OF_MEMORY;
+          goto fail;
+        }
+        u->query = Curl_dyn_ptr(&enc);
+      }
+      else {
+        u->query = Curl_memdup(query + 1, qlen);
+        if(!u->query) {
+          result = CURLUE_OUT_OF_MEMORY;
+          goto fail;
+        }
+        u->query[qlen - 1] = 0;
+      }
 
-  if(junkscan(path, flags))
-    return CURLUE_BAD_PATH;
+      if(junkscan(u->query, flags)) {
+        result = CURLUE_BAD_QUERY;
+        goto fail;
+      }
+    }
+    else {
+      /* single byte query */
+      u->query = strdup("");
+      if(!u->query) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
+    }
+  }
+  else
+    pathlen = strlen(path) - fraglen;
+
+  if(pathlen && (flags & CURLU_URLENCODE)) {
+    struct dynbuf enc;
+    Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+    if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) {
+      result = CURLUE_OUT_OF_MEMORY;
+      goto fail;
+    }
+    pathlen = Curl_dyn_len(&enc);
+    path = u->path = Curl_dyn_ptr(&enc);
+  }
 
-  if(!path[0])
-    /* if there's no path left set, unset */
+  if(!pathlen) {
+    /* there is no path left, unset */
     path = NULL;
+  }
   else {
+    if(!u->path) {
+      u->path = Curl_memdup(path, pathlen + 1);
+      if(!u->path) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
+      u->path[pathlen] = 0;
+      path = u->path;
+    }
+    else if(flags & CURLU_URLENCODE)
+      /* it might have encoded more than just the path so cut it */
+      u->path[pathlen] = 0;
+
+    if(junkscan(u->path, flags)) {
+      result = CURLUE_BAD_PATH;
+      goto fail;
+    }
+
     if(!(flags & CURLU_PATH_AS_IS)) {
       /* remove ../ and ./ sequences according to RFC3986 */
-      char *newp = Curl_dedotdotify(path);
-      if(!newp)
-        return CURLUE_OUT_OF_MEMORY;
-
-      if(strcmp(newp, path)) {
-        /* if we got a new version */
-        if(path_alloced)
-          Curl_safefree(u->temppath);
-        u->temppath = path = newp;
-        path_alloced = TRUE;
+      char *newp = dedotdotify((char *)path, pathlen);
+      if(!newp) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
       }
-      else
-        free(newp);
+      free(u->path);
+      u->path = newp;
     }
-
-    u->path = path_alloced?path:strdup(path);
-    if(!u->path)
-      return CURLUE_OUT_OF_MEMORY;
-    u->temppath = NULL; /* used now */
   }
 
-  if(hostname) {
+  if(Curl_dyn_len(&host)) {
     char normalized_ipv4[sizeof("255.255.255.255") + 1];
 
     /*
      * Parse the login details and strip them out of the host name.
      */
-    result = parse_hostname_login(u, &hostname, flags);
+    result = parse_hostname_login(u, &host, flags);
+    if(!result)
+      result = Curl_parse_port(u, &host, schemelen);
     if(result)
-      return result;
+      goto fail;
 
-    result = Curl_parse_port(u, hostname, url_has_scheme);
-    if(result)
-      return result;
-
-    if(junkscan(hostname, flags))
-      return CURLUE_BAD_HOSTNAME;
+    if(junkscan(Curl_dyn_ptr(&host), flags)) {
+      result = CURLUE_BAD_HOSTNAME;
+      goto fail;
+    }
 
-    if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) {
-      /* Skip hostname check, it's allowed to be empty. */
-      u->host = strdup("");
+    if(ipv4_normalize(Curl_dyn_ptr(&host),
+                      normalized_ipv4, sizeof(normalized_ipv4))) {
+      Curl_dyn_reset(&host);
+      if(Curl_dyn_add(&host, normalized_ipv4)) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
     }
     else {
-      if(ipv4_normalize(hostname, normalized_ipv4, sizeof(normalized_ipv4)))
-        u->host = strdup(normalized_ipv4);
-      else {
-        result = decode_host(hostname, &u->host);
-        if(result)
-          return result;
-        result = hostname_check(u, u->host);
-        if(result)
-          return result;
-      }
+      result = decode_host(&host);
+      if(!result)
+        result = hostname_check(u, Curl_dyn_ptr(&host), Curl_dyn_len(&host));
+      if(result)
+        goto fail;
     }
-    if(!u->host)
-      return CURLUE_OUT_OF_MEMORY;
+
     if((flags & CURLU_GUESS_SCHEME) && !schemep) {
+      const char *hostname = Curl_dyn_ptr(&host);
       /* legacy curl-style guess based on host name */
       if(checkprefix("ftp.", hostname))
         schemep = "ftp";
@@ -1119,27 +1286,26 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
         schemep = "http";
 
       u->scheme = strdup(schemep);
-      if(!u->scheme)
-        return CURLUE_OUT_OF_MEMORY;
+      if(!u->scheme) {
+        result = CURLUE_OUT_OF_MEMORY;
+        goto fail;
+      }
+    }
+  }
+  else if(flags & CURLU_NO_AUTHORITY) {
+    /* allowed to be empty. */
+    if(Curl_dyn_add(&host, "")) {
+      result = CURLUE_OUT_OF_MEMORY;
+      goto fail;
     }
   }
 
-  Curl_safefree(u->scratch);
-  Curl_safefree(u->temppath);
-
-  return CURLUE_OK;
-}
+  u->host = Curl_dyn_ptr(&host);
 
-/*
- * Parse the URL and set the relevant members of the Curl_URL struct.
- */
-static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
-{
-  CURLUcode result = seturl(url, u, flags);
-  if(result) {
-    free_urlhandle(u);
-    memset(u, 0, sizeof(struct Curl_URL));
-  }
+  return result;
+  fail:
+  Curl_dyn_free(&host);
+  free_urlhandle(u);
   return result;
 }
 
@@ -1157,8 +1323,6 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
     free_urlhandle(u);
     *u = tmpurl;
   }
-  else
-    free_urlhandle(&tmpurl);
   return result;
 }
 
@@ -1257,7 +1421,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       /* there's no stored port number, but asked to deliver
          a default one for the scheme */
       const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme);
+        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
       if(h) {
         msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
         ptr = portbuf;
@@ -1267,7 +1431,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       /* there is a stored port number, but ask to inhibit if
          it matches the default one for the scheme */
       const struct Curl_handler *h =
-        Curl_builtin_scheme(u->scheme);
+        Curl_builtin_scheme(u->scheme, CURL_ZERO_TERMINATED);
       if(h && (h->defport == u->portnum) &&
          (flags & CURLU_NO_DEFAULT_PORT))
         ptr = NULL;
@@ -1313,7 +1477,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       else
         return CURLUE_NO_SCHEME;
 
-      h = Curl_builtin_scheme(scheme);
+      h = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
       if(!port && (flags & CURLU_DEFAULT_PORT)) {
         /* there's no stored port number, but asked to deliver
            a default one for the scheme */
@@ -1336,14 +1500,13 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       if(u->host[0] == '[') {
         if(u->zoneid) {
           /* make it '[ host %25 zoneid ]' */
+          struct dynbuf enc;
           size_t hostlen = strlen(u->host);
-          size_t alen = hostlen + 3 + strlen(u->zoneid) + 1;
-          allochost = malloc(alen);
-          if(!allochost)
+          Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+          if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
+                           u->zoneid))
             return CURLUE_OUT_OF_MEMORY;
-          memcpy(allochost, u->host, hostlen - 1);
-          msnprintf(&allochost[hostlen - 1], alen - hostlen + 1,
-                    "%%25%s]", u->zoneid);
+          allochost = Curl_dyn_ptr(&enc);
         }
       }
       else if(urlencode) {
@@ -1354,32 +1517,32 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       else {
         /* only encode '%' in output host name */
         char *host = u->host;
-        size_t pcount = 0;
+        bool percent = FALSE;
         /* first, count number of percents present in the name */
         while(*host) {
-          if(*host == '%')
-            pcount++;
+          if(*host == '%') {
+            percent = TRUE;
+            break;
+          }
           host++;
         }
-        /* if there were percents, encode the host name */
-        if(pcount) {
-          size_t hostlen = strlen(u->host);
-          size_t alen = hostlen + 2 * pcount + 1;
-          char *o = allochost = malloc(alen);
-          if(!allochost)
-            return CURLUE_OUT_OF_MEMORY;
-
+        /* if there were percent(s), encode the host name */
+        if(percent) {
+          struct dynbuf enc;
+          CURLcode result;
+          Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
           host = u->host;
           while(*host) {
-            if(*host == '%') {
-              memcpy(o, "%25", 3);
-              o += 3;
-              host++;
-              continue;
-            }
-            *o++ = *host++;
+            if(*host == '%')
+              result = Curl_dyn_addn(&enc, "%25", 3);
+            else
+              result = Curl_dyn_addn(&enc, host, 1);
+            if(result)
+              return CURLUE_OUT_OF_MEMORY;
+            host++;
           }
-          *o = '\0';
+          free(u->host);
+          u->host = Curl_dyn_ptr(&enc);
         }
       }
 
@@ -1412,13 +1575,15 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
     break;
   }
   if(ptr) {
-    *part = strdup(ptr);
+    size_t partlen = strlen(ptr);
+    size_t i = 0;
+    *part = Curl_memdup(ptr, partlen + 1);
     if(!*part)
       return CURLUE_OUT_OF_MEMORY;
     if(plusdecode) {
       /* convert + to space */
-      char *plus;
-      for(plus = *part; *plus; ++plus) {
+      char *plus = *part;
+      for(i = 0; i < partlen; ++plus, i++) {
         if(*plus == '+')
           *plus = ' ';
       }
@@ -1435,7 +1600,18 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
         return CURLUE_URLDECODE;
       }
       *part = decoded;
+      partlen = dlen;
     }
+    if(urlencode) {
+      struct dynbuf enc;
+      Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+      if(urlencode_str(&enc, *part, partlen, TRUE,
+                       what == CURLUPART_QUERY))
+        return CURLUE_OUT_OF_MEMORY;
+      free(*part);
+      *part = Curl_dyn_ptr(&enc);
+    }
+
     return CURLUE_OK;
   }
   else
@@ -1497,6 +1673,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     if(storep && *storep) {
       Curl_safefree(*storep);
     }
+    else if(!storep) {
+      free_urlhandle(u);
+      memset(u, 0, sizeof(struct Curl_URL));
+    }
     return CURLUE_OK;
   }
 
@@ -1507,7 +1687,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       return CURLUE_BAD_SCHEME;
     if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
        /* verify that it is a fine scheme */
-       !Curl_builtin_scheme(part))
+       !Curl_builtin_scheme(part, CURL_ZERO_TERMINATED))
       return CURLUE_UNSUPPORTED_SCHEME;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
@@ -1573,7 +1753,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     /* if the new thing is absolute or the old one is not
      * (we could not get an absolute url in 'oldurl'),
      * then replace the existing with the new. */
-    if(Curl_is_absolute_url(part, NULL, 0)
+    if(Curl_is_absolute_url(part, NULL, 0,
+                            flags & (CURLU_GUESS_SCHEME|
+                                     CURLU_DEFAULT_SCHEME))
        || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
       return parseurl_and_replace(part, u, flags);
     }
@@ -1603,14 +1785,16 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
 
     if(urlencode) {
       const unsigned char *i;
-      char *o;
-      char *enc = malloc(nalloc * 3 + 1); /* for worst case! */
-      if(!enc)
-        return CURLUE_OUT_OF_MEMORY;
-      for(i = (const unsigned char *)part, o = enc; *i; i++) {
+      struct dynbuf enc;
+
+      Curl_dyn_init(&enc, nalloc * 3 + 1);
+
+      for(i = (const unsigned char *)part; *i; i++) {
+        CURLcode result;
         if((*i == ' ') && plusencode) {
-          *o = '+';
-          o++;
+          result = Curl_dyn_addn(&enc, "+", 1);
+          if(result)
+            return CURLUE_OUT_OF_MEMORY;
         }
         else if(Curl_isunreserved(*i) ||
                 ((*i == '/') && urlskipslash) ||
@@ -1618,16 +1802,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
           if((*i == '=') && equalsencode)
             /* only skip the first equals sign */
             equalsencode = FALSE;
-          *o = *i;
-          o++;
+          result = Curl_dyn_addn(&enc, i, 1);
+          if(result)
+            return CURLUE_OUT_OF_MEMORY;
         }
         else {
-          msnprintf(o, 4, "%%%02x", *i);
-          o += 3;
+          result = Curl_dyn_addf(&enc, "%%%02x", *i);
+          if(result)
+            return CURLUE_OUT_OF_MEMORY;
         }
       }
-      *o = 0; /* null-terminate */
-      newp = enc;
+      newp = Curl_dyn_ptr(&enc);
     }
     else {
       char *p;
@@ -1639,8 +1824,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
         /* make sure percent encoded are lower case */
         if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
            (ISUPPER(p[1]) || ISUPPER(p[2]))) {
-          p[1] = (char)TOLOWER(p[1]);
-          p[2] = (char)TOLOWER(p[2]);
+          p[1] = Curl_raw_tolower(p[1]);
+          p[2] = Curl_raw_tolower(p[2]);
           p += 3;
         }
         else
@@ -1649,34 +1834,41 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     }
 
     if(appendquery) {
-      /* Append the string onto the old query. Add a '&' separator if none is
-         present at the end of the exsting query already */
+      /* Append the 'newp' string onto the old query. Add a '&' separator if
+         none is present at the end of the existing query already */
+
       size_t querylen = u->query ? strlen(u->query) : 0;
       bool addamperand = querylen && (u->query[querylen -1] != '&');
       if(querylen) {
-        size_t newplen = strlen(newp);
-        char *p = malloc(querylen + addamperand + newplen + 1);
-        if(!p) {
-          free((char *)newp);
-          return CURLUE_OUT_OF_MEMORY;
+        struct dynbuf enc;
+        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+
+        if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */
+          goto nomem;
+
+        if(addamperand) {
+          if(Curl_dyn_addn(&enc, "&", 1))
+            goto nomem;
         }
-        strcpy(p, u->query); /* original query */
-        if(addamperand)
-          p[querylen] = '&'; /* ampersand */
-        strcpy(&p[querylen + addamperand], newp); /* new suffix */
+        if(Curl_dyn_add(&enc, newp))
+          goto nomem;
         free((char *)newp);
         free(*storep);
-        *storep = p;
+        *storep = Curl_dyn_ptr(&enc);
         return CURLUE_OK;
+        nomem:
+        free((char *)newp);
+        return CURLUE_OUT_OF_MEMORY;
       }
     }
 
     if(what == CURLUPART_HOST) {
-      if(0 == strlen(newp) && (flags & CURLU_NO_AUTHORITY)) {
+      size_t n = strlen(newp);
+      if(!n && (flags & CURLU_NO_AUTHORITY)) {
         /* Skip hostname check, it's allowed to be empty. */
       }
       else {
-        if(hostname_check(u, (char *)newp)) {
+        if(hostname_check(u, (char *)newp, n)) {
           free((char *)newp);
           return CURLUE_BAD_HOSTNAME;
         }
index 584434d..1d430b5 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* This file is for lib internal stuff */
 #define PORT_GOPHER 70
 #define PORT_MQTT 1883
 
+#ifdef USE_WEBSOCKETS
+/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
+ * the rest are internal information. If we use higher bits we only do this on
+ * platforms that have a >= 64 bit type and then we use such a type for the
+ * protocol fields in the protocol handler.
+ */
+#define CURLPROTO_WS     (1<<30)
+#define CURLPROTO_WSS    ((curl_prot_t)1<<31)
+#else
+#define CURLPROTO_WS 0
+#define CURLPROTO_WSS 0
+#endif
+
+/* This should be undefined once we need bit 32 or higher */
+#define PROTO_TYPE_SMALL
+
+#ifndef PROTO_TYPE_SMALL
+typedef curl_off_t curl_prot_t;
+#else
+typedef unsigned int curl_prot_t;
+#endif
+
+/* This mask is for all the old protocols that are provided and defined in the
+   public header and shall exclude protocols added since which are not exposed
+   in the API */
+#define CURLPROTO_MASK   (0x3ffffff)
+
 #define DICT_MATCH "/MATCH:"
 #define DICT_MATCH2 "/M:"
 #define DICT_MATCH3 "/FIND:"
@@ -64,7 +93,8 @@
 /* Convenience defines for checking protocols or their SSL based version. Each
    protocol handler should only ever have a single CURLPROTO_ in its protocol
    field. */
-#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS)
+#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_WS| \
+                           CURLPROTO_WSS)
 #define PROTO_FAMILY_FTP  (CURLPROTO_FTP|CURLPROTO_FTPS)
 #define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S)
 #define PROTO_FAMILY_SMB  (CURLPROTO_SMB|CURLPROTO_SMBS)
@@ -155,10 +185,10 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
 # endif
 #endif
 
-#ifdef HAVE_LIBSSH2_H
+#ifdef USE_LIBSSH2
 #include <libssh2.h>
 #include <libssh2_sftp.h>
-#endif /* HAVE_LIBSSH2_H */
+#endif /* USE_LIBSSH2 */
 
 #define READBUFFER_SIZE CURL_MAX_WRITE_SIZE
 #define READBUFFER_MAX  CURL_MAX_READ_SIZE
@@ -180,15 +210,6 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
 #define GOOD_EASY_HANDLE(x) \
   ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
 
-/* the type we use for storing a single boolean bit */
-#ifdef _MSC_VER
-typedef bool bit;
-#define BIT(x) bool x
-#else
-typedef unsigned int bit;
-#define BIT(x) bit x:1
-#endif
-
 #ifdef HAVE_GSSAPI
 /* Types needed for krb5-ftp connections */
 struct krb5buffer {
@@ -248,8 +269,6 @@ struct ssl_primary_config {
   char *CAfile;          /* certificate to verify peer against */
   char *issuercert;      /* optional issuer certificate filename */
   char *clientcert;
-  char *random_file;     /* path to file containing "random" data */
-  char *egdsocket;       /* path to file containing the EGD daemon socket */
   char *cipher_list;     /* list of ciphers to use */
   char *cipher_list13;   /* list of TLS 1.3 cipher suites to use */
   char *pinned_key;
@@ -327,11 +346,11 @@ struct digestdata {
   char *nonce;
   char *cnonce;
   char *realm;
-  int algo;
   char *opaque;
   char *qop;
   char *algorithm;
   int nc; /* nonce count */
+  unsigned char algo;
   BIT(stale); /* set true for re-negotiation */
   BIT(userhash);
 #endif
@@ -516,9 +535,7 @@ struct ConnectBits {
                  connection */
   BIT(multiplex); /* connection is multiplexed */
   BIT(tcp_fastopen); /* use TCP Fast Open */
-  BIT(tls_enable_npn);  /* TLS NPN extension? */
   BIT(tls_enable_alpn); /* TLS ALPN extension? */
-  BIT(connect_only);
 #ifndef CURL_DISABLE_DOH
   BIT(doh);
 #endif
@@ -563,7 +580,7 @@ struct Curl_async {
   struct Curl_dns_entry *dns;
   struct thread_data *tdata;
   void *resolver; /* resolver state, if it is used in the URL state -
-                     ares_channel f.e. */
+                     ares_channel e.g. */
   int port;
   int status; /* if done is TRUE, this is the status from the callback */
   BIT(done);  /* set TRUE when the lookup is complete */
@@ -584,8 +601,9 @@ enum expect100 {
 
 enum upgrade101 {
   UPGR101_INIT,               /* default state */
-  UPGR101_REQUESTED,          /* upgrade requested */
-  UPGR101_RECEIVED,           /* response received */
+  UPGR101_WS,                 /* upgrade to WebSockets requested */
+  UPGR101_H2,                 /* upgrade to HTTP/2 requested */
+  UPGR101_RECEIVED,           /* 101 response received */
   UPGR101_WORKING             /* talking upgraded protocol */
 };
 
@@ -607,23 +625,6 @@ enum doh_slots {
   DOH_PROBE_SLOTS
 };
 
-/* one of these for each DoH request */
-struct dnsprobe {
-  CURL *easy;
-  int dnstype;
-  unsigned char dohbuffer[512];
-  size_t dohlen;
-  struct dynbuf serverdoh;
-};
-
-struct dohdata {
-  struct curl_slist *headers;
-  struct dnsprobe probe[DOH_PROBE_SLOTS];
-  unsigned int pending; /* still outstanding requests */
-  int port;
-  const char *host;
-};
-
 /*
  * Request specific data in the easy handle (Curl_easy).  Previously,
  * these members were on the connectdata struct but since a conn struct may
@@ -707,6 +708,7 @@ struct SingleRequest {
 #ifndef CURL_DISABLE_DOH
   struct dohdata *doh; /* DoH specific data for this request */
 #endif
+  unsigned char setcookies;
   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
@@ -804,10 +806,10 @@ struct Curl_handler {
   void (*attach)(struct Curl_easy *data, struct connectdata *conn);
 
   int defport;            /* Default port. */
-  unsigned int protocol;  /* See CURLPROTO_* - this needs to be the single
-                             specific protocol bit */
-  unsigned int family;    /* single bit for protocol family; basically the
-                             non-TLS name of the protocol this is */
+  curl_prot_t protocol;  /* See CURLPROTO_* - this needs to be the single
+                            specific protocol bit */
+  curl_prot_t family;    /* single bit for protocol family; basically the
+                            non-TLS name of the protocol this is */
   unsigned int flags;     /* Extra particular characteristics, see PROTOPT_* */
 
 };
@@ -828,7 +830,7 @@ struct Curl_handler {
                                         url query strings (?foo=bar) ! */
 #define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per
                                           request instead of per connection */
-#define PROTOPT_ALPN_NPN (1<<8) /* set ALPN and/or NPN for this */
+#define PROTOPT_ALPN (1<<8) /* set ALPN for this */
 #define PROTOPT_STREAM (1<<9) /* a protocol with individual logical streams */
 #define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field
                                       of the URL */
@@ -864,7 +866,8 @@ struct postponed_data {
 struct proxy_info {
   struct hostname host;
   long port;
-  curl_proxytype proxytype; /* what kind of proxy that is in use */
+  unsigned char proxytype; /* curl_proxytype: what kind of proxy that is in
+                              use */
   char *user;    /* proxy user name string, allocated */
   char *passwd;  /* proxy password string, allocated */
 };
@@ -903,6 +906,11 @@ struct connstate {
   unsigned char *outp; /* send from this pointer */
 };
 
+#define TRNSPRT_TCP 3
+#define TRNSPRT_UDP 4
+#define TRNSPRT_QUIC 5
+#define TRNSPRT_UNIX 6
+
 /*
  * The connectdata struct contains all fields and variables that should be
  * unique for an entire connection.
@@ -940,15 +948,6 @@ struct connectdata {
      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_IPV6
-  unsigned int scope_id;  /* Scope id for IPv6 */
-#endif
-
-  enum {
-    TRNSPRT_TCP = 3,
-    TRNSPRT_UDP = 4,
-    TRNSPRT_QUIC = 5
-  } transport;
 
 #ifdef ENABLE_QUIC
   struct quicsocket hequic[2]; /* two, for happy eyeballs! */
@@ -964,13 +963,6 @@ struct connectdata {
   struct proxy_info socks_proxy;
   struct proxy_info http_proxy;
 #endif
-  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
-                       bits.conn_to_port is set */
-  unsigned short secondary_port; /* secondary socket remote port to connect to
-                                    (ftp) */
-
   /* 'primary_ip' and 'primary_port' get filled with peer's numerical
      ip address and port number whenever an outgoing connection is
      *attempted* from the primary socket to a remote address. When more
@@ -979,14 +971,11 @@ struct connectdata {
      these are updated with data which comes directly from the socket. */
 
   char primary_ip[MAX_IPADR_LEN];
-  unsigned char ip_version; /* copied from the Curl_easy at creation time */
-
   char *user;    /* user name string, allocated */
   char *passwd;  /* password string, allocated */
   char *options; /* options string, allocated */
   char *sasl_authzid;     /* authorization identity string, allocated */
   char *oauth_bearer; /* OAUTH2 bearer, allocated */
-  unsigned char httpversion; /* the HTTP version*10 reported by the server */
   struct curltime now;     /* "current" time */
   struct curltime created; /* creation time */
   struct curltime lastused; /* when returned to the connection cache */
@@ -1013,8 +1002,6 @@ struct connectdata {
 #endif
   struct ConnectBits bits;    /* various state-flags for this connection */
 
-  /* The field below gets set in Curl_connecthost */
-  int num_addr; /* number of addresses to try to connect to */
  /* 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. */
@@ -1042,9 +1029,9 @@ struct connectdata {
 
 #ifdef HAVE_GSSAPI
   BIT(sec_complete); /* if Kerberos is enabled for this connection */
-  enum protection_level command_prot;
-  enum protection_level data_prot;
-  enum protection_level request_data_prot;
+  unsigned char command_prot; /* enum protection_level */
+  unsigned char data_prot; /* enum protection_level */
+  unsigned char request_data_prot; /* enum protection_level */
   size_t buffer_size;
   struct krb5buffer in_buffer;
   void *app_data;
@@ -1091,18 +1078,38 @@ struct connectdata {
   struct dynbuf trailer;
 
   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
+#ifndef CURL_DISABLE_TFTP
     struct tftp_state_data *tftpc;
+#endif
+#ifndef CURL_DISABLE_IMAP
     struct imap_conn imapc;
+#endif
+#ifndef CURL_DISABLE_POP3
     struct pop3_conn pop3c;
+#endif
+#ifndef CURL_DISABLE_SMTP
     struct smtp_conn smtpc;
+#endif
+#ifndef CURL_DISABLE_RTSP
     struct rtsp_conn rtspc;
+#endif
+#ifndef CURL_DISABLE_SMB
     struct smb_conn smbc;
+#endif
     void *rtmp;
     struct ldapconninfo *ldapc;
+#ifndef CURL_DISABLE_MQTT
     struct mqtt_conn mqtt;
+#endif
   } proto;
 
   struct http_connect_state *connect_state; /* for HTTP CONNECT */
@@ -1123,12 +1130,27 @@ struct connectdata {
   int localportrange;
   int cselect_bits; /* bitmask of socket events */
   int waitfor;      /* current READ/WRITE bits to wait for */
-  int negnpn; /* APLN or NPN TLS negotiated protocol, CURL_HTTP_VERSION* */
-
 #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 */
+  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
+                       bits.conn_to_port is set */
+#ifdef ENABLE_IPV6
+  unsigned int scope_id;  /* Scope id for IPv6 */
+#endif
   unsigned short localport;
+  unsigned short secondary_port; /* secondary socket remote port to connect to
+                                    (ftp) */
+  unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
+                         value */
+  unsigned char transport; /* one of the TRNSPRT_* defines */
+  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;
 };
 
 /* The end of connectdata. */
@@ -1288,6 +1310,7 @@ typedef enum {
   EXPIRE_TIMEOUT,
   EXPIRE_TOOFAST,
   EXPIRE_QUIC,
+  EXPIRE_FTP_ACCEPT,
   EXPIRE_LAST /* not an actual timer, used as a marker only */
 } expire_id;
 
@@ -1341,7 +1364,7 @@ struct UrlState {
      This is strdup()ed data. */
   char *first_host;
   int first_remote_port;
-  unsigned int first_remote_protocol;
+  curl_prot_t first_remote_protocol;
 
   int retrycount; /* number of retries on a new connection */
   struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
@@ -1356,9 +1379,10 @@ struct UrlState {
   /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
   void (*prev_signal)(int sig);
 #endif
+#ifndef CURL_DISABLE_CRYPTO_AUTH
   struct digestdata digest;      /* state data for host Digest auth */
   struct digestdata proxydigest; /* state data for proxy Digest auth */
-
+#endif
   struct auth authhost;  /* auth details for host */
   struct auth authproxy; /* auth details for proxy */
 #ifdef USE_CURL_ASYNC
@@ -1374,7 +1398,7 @@ struct UrlState {
   struct Curl_llist timeoutlist; /* list of pending timeouts */
   struct time_node expires[EXPIRE_LAST]; /* nodes for each expire type */
 
-  /* a place to store the most recently set FTP entrypath */
+  /* a place to store the most recently set (S)FTP entrypath */
   char *most_recent_ftp_entrypath;
   unsigned char httpwant; /* when non-zero, a specific HTTP version requested
                              to be used in the library's request(s) */
@@ -1393,30 +1417,37 @@ struct UrlState {
                   this syntax. */
   curl_off_t resume_from; /* continue [ftp] transfer from here */
 
+#ifndef CURL_DISABLE_RTSP
   /* This RTSP state information survives requests and connections */
   long rtsp_next_client_CSeq; /* the session's next client CSeq */
   long rtsp_next_server_CSeq; /* the session's next server CSeq */
   long rtsp_CSeq_recv; /* most recent CSeq received */
+#endif
 
   curl_off_t infilesize; /* size of file to upload, -1 means unknown.
                             Copied from set.filesize at start of operation */
-
+#if defined(USE_HTTP2) || defined(USE_HTTP3)
   size_t drain; /* Increased when this stream has data to read, even if its
                    socket is not necessarily is readable. Decreased when
                    checked. */
+#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;
-  Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
+  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
@@ -1425,9 +1456,10 @@ struct UrlState {
                                  headers */
   struct Curl_llist httphdrs; /* received headers */
   struct curl_header headerout; /* for external purposes */
-#endif
+  struct Curl_header_store *prevhead; /* the latest added header */
   trailers_state trailers_state; /* whether we are sending trailers
-                                       and what stage are we at */
+                                    and what stage are we at */
+#endif
 #ifdef USE_HYPER
   bool hconnect;  /* set if a CONNECT request */
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
@@ -1471,7 +1503,6 @@ struct UrlState {
                       is always set TRUE when curl_easy_perform() is called. */
   BIT(authproblem); /* TRUE if there's some problem authenticating */
   /* set after initial USER failure, to prevent an authentication loop */
-  BIT(ftp_trying_alternative);
   BIT(wildcardmatch); /* enable wildcard matching */
   BIT(expect100header);  /* TRUE if we added Expect: 100-continue */
   BIT(disableexpect);    /* TRUE if Expect: is disabled due to a previous
@@ -1548,8 +1579,6 @@ enum dupstring {
   STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */
   STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */
   STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */
-  STRING_SSL_EGDSOCKET,   /* path to file containing the EGD daemon socket */
-  STRING_SSL_RANDOM_FILE, /* path to file containing "random" data */
   STRING_USERAGENT,       /* User-Agent string */
   STRING_SSL_CRLFILE,     /* crl file to check certificate */
   STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */
@@ -1632,16 +1661,15 @@ struct UserDefined {
   void *out;         /* CURLOPT_WRITEDATA */
   void *in_set;      /* CURLOPT_READDATA */
   void *writeheader; /* write the header to this if non-NULL */
-  void *rtp_out;     /* write RTP to this if non-NULL */
-  long use_port;     /* which port to use (when not using default) */
+  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 long socks5auth;/* kind of SOCKS5 authentication to use (bitmask) */
+#endif
   long maxredirs;    /* maximum no. of http(s) redirects to follow, set to -1
                         for infinity */
 
-  int keep_post;     /* keep POSTs as POSTs after a 30x request; each
-                        bit represents a request, from 301 to 303 */
   void *postfields;  /* if POST, set the fields' values here */
   curl_seek_callback seek_func;      /* function that seeks the input */
   curl_off_t postfieldsize; /* if POST, this might have a size to use instead
@@ -1679,16 +1707,17 @@ struct UserDefined {
 #endif
   void *progress_client; /* pointer to pass to the progress callback */
   void *ioctl_client;   /* pointer to pass to the ioctl callback */
-  long timeout;         /* in milliseconds, 0 means no timeout */
-  long connecttimeout;  /* in milliseconds, 0 means no timeout */
-  long accepttimeout;   /* in milliseconds, 0 means no timeout */
-  long happy_eyeballs_timeout; /* in milliseconds, 0 is a valid value */
-  long server_response_timeout; /* in milliseconds, 0 means no timeout */
+  unsigned int timeout;        /* ms, 0 means no timeout */
+  unsigned int connecttimeout; /* ms, 0 means no timeout */
+  unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */
+  unsigned int server_response_timeout; /* ms, 0 means no timeout */
   long maxage_conn;     /* in seconds, max idle time to allow a connection that
                            is to be reused */
   long maxlifetime_conn; /* in seconds, max time since creation to allow a
                             connection that is to be reused */
+#ifndef CURL_DISABLE_TFTP
   long tftp_blksize;    /* in bytes, 0 means use default */
+#endif
   curl_off_t filesize;  /* size of file to upload, -1 means unknown */
   long low_speed_limit; /* bytes/second */
   long low_speed_time;  /* number of seconds */
@@ -1708,15 +1737,17 @@ struct UserDefined {
                                           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
   struct curl_slist *resolve;     /* list of names to add/remove from
                                      DNS cache */
   struct curl_slist *connect_to; /* list of host:port mappings to override
                                     the hostname and port to connect to */
-  curl_TimeCond timecondition; /* kind of time/date comparison */
-  curl_proxytype proxytype; /* what kind of proxy that is in use */
   time_t timevalue;       /* what time to compare with */
-  Curl_HttpReq method;   /* what kind of HTTP request (if any) is this */
+  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 */
@@ -1724,8 +1755,8 @@ struct UserDefined {
   struct ssl_config_data proxy_ssl;  /* user defined SSL stuff for proxy */
 #endif
   struct ssl_general_config general_ssl; /* general user defined SSL stuff */
-  long dns_cache_timeout; /* DNS cache timeout */
-  long buffer_size;      /* size of receive buffer to use */
+  int dns_cache_timeout; /* DNS cache timeout (seconds) */
+  unsigned int buffer_size;      /* size of receive buffer to use */
   unsigned int upload_buffer_size; /* size of upload buffer to use,
                                       keep it >= CURL_MAX_WRITE_SIZE */
   void *private_data; /* application-private data */
@@ -1734,36 +1765,46 @@ struct UserDefined {
                           file 0 - whatever, 1 - v2, 2 - v6 */
   curl_off_t max_filesize; /* Maximum file size to download */
 #ifndef CURL_DISABLE_FTP
-  curl_ftpfile ftp_filemethod; /* how to get to a file when FTP is used  */
-  curl_ftpauth ftpsslauth; /* what AUTH XXX to be attempted */
-  curl_ftpccc ftp_ccc;   /* FTP CCC options */
+  unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile  */
+  unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */
+  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
+     1 - create directories that don't exist
+     2 - the same but also allow MKD to fail once
+  */
+  unsigned char ftp_create_missing_dirs;
+#ifdef USE_LIBSSH2
+  curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
+  void *ssh_hostkeyfunc_userp;         /* custom pointer to callback */
 #endif
-  int ftp_create_missing_dirs; /* 1 - create directories that don't exist
-                                  2 - the same but also allow MKD to fail once
-                               */
+
   curl_sshkeycallback ssh_keyfunc; /* key matching callback */
   void *ssh_keyfunc_userp;         /* custom pointer to callback */
 #ifndef CURL_DISABLE_NETRC
-  enum CURL_NETRC_OPTION
-       use_netrc;        /* defined in include/curl.h */
+  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! */
-  long new_file_perms;    /* Permissions to use when creating remote files */
-  long new_directory_perms; /* Permissions to use when creating remote dirs */
-  long ssh_auth_types;   /* allowed SSH auth types */
+  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
   unsigned int scope_id;  /* Scope id for IPv6 */
 #endif
-  long allowed_protocols;
-  long redir_protocols;
-  long mime_options;      /* Mime option flags. */
-  struct curl_slist *mail_rcpt; /* linked list of mail recipients */
+  curl_prot_t allowed_protocols;
+  curl_prot_t redir_protocols;
+  unsigned int mime_options;      /* Mime option flags. */
+
+#ifndef CURL_DISABLE_RTSP
+  void *rtp_out;     /* write RTP to this if non-NULL */
   /* Common RTSP header options */
   Curl_RtspReq rtspreq; /* RTSP request type */
-  long rtspversion; /* like httpversion, for RTSP */
+#endif
+#ifndef CURL_DISABLE_FTP
   curl_chunk_bgn_callback chunk_bgn; /* called before part of transfer
                                         starts */
   curl_chunk_end_callback chunk_end; /* called after part transferring
@@ -1771,38 +1812,48 @@ struct UserDefined {
   curl_fnmatch_callback fnmatch; /* callback to decide which file corresponds
                                     to pattern (e.g. if WILDCARDMATCH is on) */
   void *fnmatch_data;
-
+#endif
   long gssapi_delegation; /* GSS-API credential delegation, see the
                              documentation of CURLOPT_GSSAPI_DELEGATION */
 
-  long tcp_keepidle;     /* seconds in idle before sending keepalive probe */
-  long tcp_keepintvl;    /* seconds between TCP keepalive probes */
+  int tcp_keepidle;     /* seconds in idle before sending keepalive probe */
+  int tcp_keepintvl;    /* seconds between TCP keepalive probes */
 
   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;
-
+#endif
   curl_resolver_start_callback resolver_start; /* optional callback called
                                                   before resolver start */
   void *resolver_start_client; /* pointer to pass to resolver start callback */
   long upkeep_interval_ms;      /* Time between calls for connection upkeep. */
   multidone_func fmultidone;
+#ifndef CURL_DISABLE_DOH
   struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
+#endif
   CURLU *uh; /* URL handle for the current parsed URL */
   void *trailer_data; /* pointer to pass to trailer data callback */
   curl_trailer_callback trailer_callback; /* trailing data callback */
+  char keep_post;     /* keep POSTs as POSTs after a 30x request; each
+                         bit represents a request, from 301 to 303 */
+#ifndef CURL_DISABLE_SMTP
+  struct curl_slist *mail_rcpt; /* linked list of mail recipients */
+  BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
+                                recipients */
+#endif
+  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? */
-  BIT(is_fwrite_set); /* has write callback been set to non-NULL? */
-  BIT(free_referer); /* set TRUE if 'referer' points to a string we
-                        allocated */
+#ifndef CURL_DISABLE_TFTP
   BIT(tftp_no_options); /* do not send TFTP options requests */
+#endif
   BIT(sep_headers);     /* handle host and proxy headers separately */
   BIT(cookiesession);   /* new cookie session? */
   BIT(crlf);            /* convert crlf on ftp upload(?) */
-  BIT(strip_path_slash); /* strip off initial slash from path */
   BIT(ssh_compression);            /* enable SSH compression */
 
 /* Here follows boolean settings that define how to behave during
@@ -1820,6 +1871,7 @@ struct UserDefined {
   BIT(ftp_use_pret);     /* if PRET is to be used before PASV or not */
   BIT(ftp_skip_ip);      /* skip the IP address the FTP server passes on to
                             us */
+  BIT(wildcard_enabled); /* enable wildcard matching */
 #endif
   BIT(hide_progress);    /* don't use the progress meter */
   BIT(http_fail_on_error);  /* fail on HTTP error codes >= 400 */
@@ -1840,7 +1892,6 @@ struct UserDefined {
   BIT(no_signal);      /* do not use any signal/alarm handler */
   BIT(tcp_nodelay);    /* whether to enable TCP_NODELAY or not */
   BIT(ignorecl);       /* ignore content length */
-  BIT(connect_only);   /* make connection, let application use the socket */
   BIT(http_te_skip);   /* pass the raw body data to the user, even when
                           transfer-encoded (chunked, compressed) */
   BIT(http_ce_skip);   /* pass the raw body data to the user, even when
@@ -1851,10 +1902,8 @@ struct UserDefined {
   BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */
 #endif
   BIT(sasl_ir);         /* Enable/disable SASL initial response */
-  BIT(wildcard_enabled); /* enable wildcard matching */
   BIT(tcp_keepalive);  /* use TCP keepalives */
   BIT(tcp_fastopen);   /* use TCP Fast Open */
-  BIT(ssl_enable_npn); /* TLS NPN extension? */
   BIT(ssl_enable_alpn);/* TLS ALPN extension? */
   BIT(path_as_is);     /* allow dotdots? */
   BIT(pipewait);       /* wait for multiplex status before starting a new
@@ -1874,8 +1923,9 @@ struct UserDefined {
   BIT(doh_verifystatus);   /* DoH certificate status verification */
 #endif
   BIT(http09_allowed); /* allow HTTP/0.9 responses */
-  BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
-                                recipients */
+#ifdef USE_WEBSOCKETS
+  BIT(ws_raw_mode);
+#endif
 };
 
 struct Names {
@@ -1937,10 +1987,12 @@ struct Curl_easy {
 #endif
   struct SingleRequest req;    /* Request-specific data */
   struct UserDefined set;      /* values set by the libcurl user */
+#ifndef CURL_DISABLE_COOKIES
   struct CookieInfo *cookies;  /* the cookies, read from files and servers.
                                   NOTE that the 'cookie' field in the
                                   UserDefined struct defines if the "engine"
                                   is to be used or not. */
+#endif
 #ifndef CURL_DISABLE_HSTS
   struct hsts *hsts;
 #endif
index d17e16f..b82b171 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC4616 PLAIN authentication
  * Draft   LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
  *
index 9ddb0ac..475d31b 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2195 CRAM-MD5 authentication
  *
  ***************************************************************************/
index d461609..f945e8b 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2831 DIGEST-MD5 authentication
  * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
  *
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#define SESSION_ALGO 1 /* for algos with this bit set */
+
+#define ALGO_MD5 0
+#define ALGO_MD5SESS (ALGO_MD5 | SESSION_ALGO)
+#define ALGO_SHA256 2
+#define ALGO_SHA256SESS (ALGO_SHA256 | SESSION_ALGO)
+#define ALGO_SHA512_256 4
+#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
+
 #if !defined(USE_WINDOWS_SSPI)
 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
@@ -79,44 +90,50 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
   }
 
   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
-    switch(*str) {
-    case '\\':
-      if(!escape) {
-        /* possibly the start of an escaped quote */
-        escape = TRUE;
-        *content++ = '\\'; /* Even though this is an escape character, we still
-                              store it as-is in the target buffer */
-        continue;
-      }
-      break;
-
-    case ',':
-      if(!starts_with_quote) {
-        /* This signals the end of the content if we didn't get a starting
-           quote and then we do "sloppy" parsing */
-        c = 0; /* the end */
-        continue;
-      }
-      break;
-
-    case '\r':
-    case '\n':
-      /* end of string */
-      c = 0;
-      continue;
+    if(!escape) {
+      switch(*str) {
+      case '\\':
+        if(starts_with_quote) {
+          /* the start of an escaped quote */
+          escape = TRUE;
+          continue;
+        }
+        break;
+
+      case ',':
+        if(!starts_with_quote) {
+          /* This signals the end of the content if we didn't get a starting
+             quote and then we do "sloppy" parsing */
+          c = 0; /* the end */
+          continue;
+        }
+        break;
 
-    case '\"':
-      if(!escape && starts_with_quote) {
+      case '\r':
+      case '\n':
         /* end of string */
+        if(starts_with_quote)
+          return FALSE; /* No closing quote */
         c = 0;
         continue;
+
+      case '\"':
+        if(starts_with_quote) {
+          /* end of string */
+          c = 0;
+          continue;
+        }
+        else
+          return FALSE;
+        break;
       }
-      break;
     }
 
     escape = FALSE;
     *content++ = *str;
   }
+  if(escape)
+    return FALSE; /* No character after backslash */
 
   *content = 0;
   *endptr = str;
@@ -365,7 +382,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
     return CURLE_BAD_CONTENT_ENCODING;
 
-  /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
+  /* Generate 32 random hex chars, 32 bytes + 1 null-termination */
   result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
   if(result)
     return result;
@@ -504,7 +521,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
     char content[DIGEST_MAX_CONTENT_LENGTH];
 
     /* Pass all additional spaces here */
-    while(*chlg && ISSPACE(*chlg))
+    while(*chlg && ISBLANK(*chlg))
       chlg++;
 
     /* Extract a value=content pair */
@@ -543,6 +560,9 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
 
         token = strtok_r(tmp, ",", &tok_buf);
         while(token) {
+          /* Pass additional spaces here */
+          while(*token && ISBLANK(*token))
+            token++;
           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
             foundAuth = TRUE;
           }
@@ -575,17 +595,17 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
           return CURLE_OUT_OF_MEMORY;
 
         if(strcasecompare(content, "MD5-sess"))
-          digest->algo = CURLDIGESTALGO_MD5SESS;
+          digest->algo = ALGO_MD5SESS;
         else if(strcasecompare(content, "MD5"))
-          digest->algo = CURLDIGESTALGO_MD5;
+          digest->algo = ALGO_MD5;
         else if(strcasecompare(content, "SHA-256"))
-          digest->algo = CURLDIGESTALGO_SHA256;
+          digest->algo = ALGO_SHA256;
         else if(strcasecompare(content, "SHA-256-SESS"))
-          digest->algo = CURLDIGESTALGO_SHA256SESS;
+          digest->algo = ALGO_SHA256SESS;
         else if(strcasecompare(content, "SHA-512-256"))
-          digest->algo = CURLDIGESTALGO_SHA512_256;
+          digest->algo = ALGO_SHA512_256;
         else if(strcasecompare(content, "SHA-512-256-SESS"))
-          digest->algo = CURLDIGESTALGO_SHA512_256SESS;
+          digest->algo = ALGO_SHA512_256SESS;
         else
           return CURLE_BAD_CONTENT_ENCODING;
       }
@@ -602,7 +622,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
       break; /* We're done here */
 
     /* Pass all additional spaces here */
-    while(*chlg && ISSPACE(*chlg))
+    while(*chlg && ISBLANK(*chlg))
       chlg++;
 
     /* Allow the list to be comma-separated */
@@ -620,6 +640,10 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
   if(!digest->nonce)
     return CURLE_BAD_CONTENT_ENCODING;
 
+  /* "<algo>-sess" protocol versions require "auth" or "auth-int" qop */
+  if(!digest->qop && (digest->algo & SESSION_ALGO))
+    return CURLE_BAD_CONTENT_ENCODING;
+
   return CURLE_OK;
 }
 
@@ -664,6 +688,8 @@ static CURLcode auth_create_digest_http_message(
   char *cnonce = NULL;
   size_t cnonce_sz = 0;
   char *userp_quoted;
+  char *realm_quoted;
+  char *nonce_quoted;
   char *response = NULL;
   char *hashthis = NULL;
   char *tmp = NULL;
@@ -687,7 +713,7 @@ static CURLcode auth_create_digest_http_message(
   }
 
   if(digest->userhash) {
-    hashthis = aprintf("%s:%s", userp, digest->realm);
+    hashthis = aprintf("%s:%s", userp, digest->realm ? digest->realm : "");
     if(!hashthis)
       return CURLE_OUT_OF_MEMORY;
 
@@ -707,7 +733,8 @@ static CURLcode auth_create_digest_http_message(
            unq(nonce-value) ":" unq(cnonce-value)
   */
 
-  hashthis = aprintf("%s:%s:%s", userp, digest->realm, passwdp);
+  hashthis = aprintf("%s:%s:%s", userp, digest->realm ? digest->realm : "",
+                     passwdp);
   if(!hashthis)
     return CURLE_OUT_OF_MEMORY;
 
@@ -715,9 +742,7 @@ static CURLcode auth_create_digest_http_message(
   free(hashthis);
   convert_to_ascii(hashbuf, ha1);
 
-  if(digest->algo == CURLDIGESTALGO_MD5SESS ||
-     digest->algo == CURLDIGESTALGO_SHA256SESS ||
-     digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
+  if(digest->algo & SESSION_ALGO) {
     /* nonce and cnonce are OUTSIDE the hash */
     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
     if(!tmp)
@@ -786,16 +811,33 @@ static CURLcode auth_create_digest_http_message(
      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
 
      Digest parameters are all quoted strings.  Username which is provided by
-     the user will need double quotes and backslashes within it escaped.  For
-     the other fields, this shouldn't be an issue.  realm, nonce, and opaque
-     are copied as is from the server, escapes and all.  cnonce is generated
-     with web-safe characters.  uri is already percent encoded.  nc is 8 hex
+     the user will need double quotes and backslashes within it escaped.
+     realm, nonce, and opaque will need backslashes as well as they were
+     de-escaped when copied from request header.  cnonce is generated with
+     web-safe characters.  uri is already percent encoded.  nc is 8 hex
      characters.  algorithm and qop with standard values only contain web-safe
      characters.
   */
   userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
   if(!userp_quoted)
     return CURLE_OUT_OF_MEMORY;
+  if(digest->realm)
+    realm_quoted = auth_digest_string_quoted(digest->realm);
+  else {
+    realm_quoted = malloc(1);
+    if(realm_quoted)
+      realm_quoted[0] = 0;
+  }
+  if(!realm_quoted) {
+    free(userp_quoted);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  nonce_quoted = auth_digest_string_quoted(digest->nonce);
+  if(!nonce_quoted) {
+    free(realm_quoted);
+    free(userp_quoted);
+    return CURLE_OUT_OF_MEMORY;
+  }
 
   if(digest->qop) {
     response = aprintf("username=\"%s\", "
@@ -807,18 +849,16 @@ static CURLcode auth_create_digest_http_message(
                        "qop=%s, "
                        "response=\"%s\"",
                        userp_quoted,
-                       digest->realm,
-                       digest->nonce,
+                       realm_quoted,
+                       nonce_quoted,
                        uripath,
                        digest->cnonce,
                        digest->nc,
                        digest->qop,
                        request_digest);
 
-    if(strcasecompare(digest->qop, "auth"))
-      digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
-                       padded which tells to the server how many times you are
-                       using the same nonce in the qop=auth mode */
+    /* Increment nonce-count to use another nc value for the next request */
+    digest->nc++;
   }
   else {
     response = aprintf("username=\"%s\", "
@@ -827,20 +867,29 @@ static CURLcode auth_create_digest_http_message(
                        "uri=\"%s\", "
                        "response=\"%s\"",
                        userp_quoted,
-                       digest->realm,
-                       digest->nonce,
+                       realm_quoted,
+                       nonce_quoted,
                        uripath,
                        request_digest);
   }
+  free(nonce_quoted);
+  free(realm_quoted);
   free(userp_quoted);
   if(!response)
     return CURLE_OUT_OF_MEMORY;
 
   /* Add the optional fields */
   if(digest->opaque) {
+    char *opaque_quoted;
     /* Append the opaque */
-    tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
+    opaque_quoted = auth_digest_string_quoted(digest->opaque);
+    if(!opaque_quoted) {
+      free(response);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted);
     free(response);
+    free(opaque_quoted);
     if(!tmp)
       return CURLE_OUT_OF_MEMORY;
 
@@ -902,28 +951,18 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
                                               struct digestdata *digest,
                                               char **outptr, size_t *outlen)
 {
-  switch(digest->algo) {
-  case CURLDIGESTALGO_MD5:
-  case CURLDIGESTALGO_MD5SESS:
+  if(digest->algo <= ALGO_MD5SESS)
     return auth_create_digest_http_message(data, userp, passwdp,
                                            request, uripath, digest,
                                            outptr, outlen,
                                            auth_digest_md5_to_ascii,
                                            Curl_md5it);
-
-  case CURLDIGESTALGO_SHA256:
-  case CURLDIGESTALGO_SHA256SESS:
-  case CURLDIGESTALGO_SHA512_256:
-  case CURLDIGESTALGO_SHA512_256SESS:
-    return auth_create_digest_http_message(data, userp, passwdp,
-                                           request, uripath, digest,
-                                           outptr, outlen,
-                                           auth_digest_sha256_to_ascii,
-                                           Curl_sha256it);
-
-  default:
-    return CURLE_UNSUPPORTED_PROTOCOL;
-  }
+  DEBUGASSERT(digest->algo <= ALGO_SHA512_256SESS);
+  return auth_create_digest_http_message(data, userp, passwdp,
+                                         request, uripath, digest,
+                                         outptr, outlen,
+                                         auth_digest_sha256_to_ascii,
+                                         Curl_sha256it);
 }
 
 /*
@@ -946,7 +985,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest)
   Curl_safefree(digest->algorithm);
 
   digest->nc = 0;
-  digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
+  digest->algo = ALGO_MD5; /* default algorithm */
   digest->stale = FALSE; /* default means normal, not stale */
   digest->userhash = FALSE;
 }
index ee373cd..d785bdd 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
 #define DIGEST_MAX_VALUE_LENGTH           256
 #define DIGEST_MAX_CONTENT_LENGTH         1024
 
-enum {
-  CURLDIGESTALGO_MD5,
-  CURLDIGESTALGO_MD5SESS,
-  CURLDIGESTALGO_SHA256,
-  CURLDIGESTALGO_SHA256SESS,
-  CURLDIGESTALGO_SHA512_256,
-  CURLDIGESTALGO_SHA512_256SESS
-};
-
 /* This is used to extract the realm from a challenge message */
 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
                                const char **endptr);
index 94f8f8c..89a9db5 100644 (file)
@@ -6,7 +6,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2015 - 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
@@ -19,6 +19,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC2831 DIGEST-MD5 authentication
  *
  ***************************************************************************/
@@ -257,7 +259,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg,
       char content[DIGEST_MAX_CONTENT_LENGTH];
 
       /* Pass all additional spaces here */
-      while(*chlg && ISSPACE(*chlg))
+      while(*chlg && ISBLANK(*chlg))
         chlg++;
 
       /* Extract a value=content pair */
@@ -290,7 +292,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg,
         break; /* We're done here */
 
       /* Pass all additional spaces here */
-      while(*chlg && ISSPACE(*chlg))
+      while(*chlg && ISBLANK(*chlg))
         chlg++;
 
       /* Allow the list to be comma-separated */
@@ -331,7 +333,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
       char value[DIGEST_MAX_VALUE_LENGTH];
       char content[DIGEST_MAX_CONTENT_LENGTH];
 
-      while(*p && ISSPACE(*p))
+      while(*p && ISBLANK(*p))
         p++;
 
       if(!Curl_auth_digest_get_pair(p, value, content, &p))
@@ -343,7 +345,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
         break;
       }
 
-      while(*p && ISSPACE(*p))
+      while(*p && ISBLANK(*p))
         p++;
 
       if(',' == *p)
@@ -429,8 +431,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
      has changed then delete that context. */
   if((userp && !digest->user) || (!userp && digest->user) ||
      (passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
-     (userp && digest->user && strcmp(userp, digest->user)) ||
-     (passwdp && digest->passwd && strcmp(passwdp, digest->passwd))) {
+     (userp && digest->user && Curl_timestrcmp(userp, digest->user)) ||
+     (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) {
     if(digest->http_context) {
       s_pSecFn->DeleteSecurityContext(digest->http_context);
       Curl_safefree(digest->http_context);
index 40fef53..a73c644 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020 - 2021, Simon Josefsson, <simon@josefsson.org>, et al.
+ * Copyright (C) 2020 - 2022, 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC5802 SCRAM-SHA-1 authentication
  *
  ***************************************************************************/
@@ -34,7 +36,8 @@
 
 #include <gsasl.h>
 
-/* The last #include files should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
index 67d43bd..bac7804 100644 (file)
@@ -6,7 +6,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>.
- * Copyright (C) 2015 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2015 - 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
@@ -19,6 +19,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
  *
  ***************************************************************************/
index c652fd7..895b4a1 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2014 - 2021, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2022, 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
  *
  ***************************************************************************/
index 115f70b..c10fa6c 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -27,7 +29,7 @@
 /*
  * NTLM details:
  *
- * https://davenport.sourceforge.io/ntlm.html
+ * https://davenport.sourceforge.net/ntlm.html
  * https://www.innovation.ch/java/ntlm.html
  */
 
 /* "NTLMSSP" signature is always in ASCII regardless of the platform */
 #define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
 
+/* The fixed host name we provide, in order to not leak our real local host
+   name. Copy the name used by Firefox. */
+#define NTLM_HOSTNAME "WORKSTATION"
+
 #if DEBUG_ME
 # define DEBUG_OUT(x) x
 static void ntlm_print_flags(FILE *handle, unsigned long flags)
@@ -521,6 +527,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
 
   userlen = strlen(user);
 
+#ifndef NTLM_HOSTNAME
   /* Get the machine's un-qualified host name as NTLM doesn't like the fully
      qualified domain name */
   if(Curl_gethostname(host, sizeof(host))) {
@@ -530,6 +537,10 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
   else {
     hostlen = strlen(host);
   }
+#else
+  (void)msnprintf(host, sizeof(host), "%s", NTLM_HOSTNAME);
+  hostlen = sizeof(NTLM_HOSTNAME)-1;
+#endif
 
   if(ntlm->flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) {
     unsigned char ntbuffer[0x18];
@@ -589,7 +600,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
 
     /* A safer but less compatible alternative is:
      *   Curl_ntlm_core_lm_resp(ntbuffer, &ntlm->nonce[0], lmresp);
-     * See https://davenport.sourceforge.io/ntlm.html#ntlmVersion2 */
+     * See https://davenport.sourceforge.net/ntlm.html#ntlmVersion2 */
   }
 
   if(unicode) {
@@ -647,7 +658,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
                    /* LanManager response */
                    /* NT response */
 
-                   0,                /* zero termination */
+                   0,                /* null-termination */
                    0, 0, 0,          /* type-3 long, the 24 upper bits */
 
                    SHORTPAIR(0x18),  /* LanManager response length, twice */
index 8ec23ad..4dfda55 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
@@ -32,7 +34,8 @@
 /* Stuff only required for curl_ntlm_msgs.c */
 #ifdef BUILDING_CURL_NTLM_MSGS_C
 
-/* Flag bits definitions based on https://davenport.sourceforge.io/ntlm.html */
+/* Flag bits definitions based on
+   https://davenport.sourceforge.net/ntlm.html */
 
 #define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
 /* Indicates that Unicode strings are supported for use in security buffer
index 3e39dad..193576a 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index a5f16a0..1604b30 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC6749 OAuth 2.0 Authorization Framework
  *
  ***************************************************************************/
index 8c1a3ed..25dff96 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
  *
  ***************************************************************************/
index d219d8b..d845cac 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
  *
  ***************************************************************************/
index 3624fb0..58fe051 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2014 - 2021, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2014 - 2022, 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
@@ -18,6 +18,8 @@
  * 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"
@@ -25,6 +27,8 @@
 #include <curl/curl.h>
 
 #include "vauth.h"
+#include "urldata.h"
+#include "strcase.h"
 #include "curl_multibyte.h"
 #include "curl_printf.h"
 
@@ -142,3 +146,18 @@ bool Curl_auth_user_contains_domain(const char *user)
 
   return valid;
 }
+
+/*
+ * Curl_auth_ollowed_to_host() tells if authentication, cookies or other
+ * "sensitive data" can (still) be sent to this host.
+ */
+bool Curl_auth_allowed_to_host(struct Curl_easy *data)
+{
+  struct connectdata *conn = data->conn;
+  return (!data->state.this_is_a_follow ||
+          data->set.allow_auth_to_other_hosts ||
+          (data->state.first_host &&
+           strcasecompare(data->state.first_host, conn->host.name) &&
+           (data->state.first_remote_port == conn->remote_port) &&
+           (data->state.first_remote_protocol == conn->handler->protocol)));
+}
index 6e12378..af27f01 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
@@ -52,6 +54,12 @@ struct gsasldata;
 #define GSS_ERROR(status) ((status) & 0x80000000)
 #endif
 
+/*
+ * Curl_auth_allowed_to_host() tells if authentication, cookies or other
+ * "sensitive data" can (still) be sent to this host.
+ */
+bool Curl_auth_allowed_to_host(struct Curl_easy *data);
+
 /* This is used to build a SPN string */
 #if !defined(USE_WINDOWS_SSPI)
 char *Curl_auth_build_spn(const char *service, const char *host,
@@ -222,7 +230,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
 CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
                                          char **outptr, size_t *outlen);
 
-/* This is used to clean up the SPNEGO specifiec data */
+/* This is used to clean up the SPNEGO specific data */
 void Curl_auth_cleanup_spnego(struct negotiatedata *nego);
 
 #endif /* USE_SPNEGO */
index e37253d..cb44eb4 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -29,6 +31,7 @@
 #include "vssh/ssh.h"
 #include "quic.h"
 #include "curl_printf.h"
+#include "easy_lock.h"
 
 #ifdef USE_ARES
 #  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
@@ -50,8 +53,8 @@
 #include <librtmp/rtmp.h>
 #endif
 
-#ifdef HAVE_ZLIB_H
-#include <zlib.h>
+#ifdef HAVE_LIBZ
+#include <cm3p/zlib.h>
 #endif
 
 #ifdef HAVE_BROTLI
@@ -335,6 +338,11 @@ static const char * const protocols[] = {
 #endif
 #ifdef USE_LIBRTMP
   "rtmp",
+  "rtmpe",
+  "rtmps",
+  "rtmpt",
+  "rtmpte",
+  "rtmpts",
 #endif
 #ifndef CURL_DISABLE_RTSP
   "rtsp",
@@ -364,6 +372,12 @@ static const char * const protocols[] = {
 #ifndef CURL_DISABLE_TFTP
   "tftp",
 #endif
+#ifdef USE_WEBSOCKETS
+  "ws",
+#endif
+#if defined(USE_SSL) && defined(USE_WEBSOCKETS)
+  "wss",
+#endif
 
   NULL
 };
@@ -451,6 +465,9 @@ static curl_version_info_data version_info = {
 #if defined(USE_GSASL)
   | CURL_VERSION_GSASL
 #endif
+#if defined(GLOBAL_INIT_IS_THREADSAFE)
+  | CURL_VERSION_THREADSAFE
+#endif
   ,
   NULL, /* ssl_version */
   0,    /* ssl_version_num, this is kept at zero */
index afdb1d6..e8f14f9 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 38af87f..7a9a6a1 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2016 - 2022, 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
@@ -20,6 +20,8 @@
  * 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"
index f7bd315..c3e58e7 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -25,7 +27,6 @@
 #ifdef USE_MSH3
 
 #include "urldata.h"
-#include "curl_printf.h"
 #include "timeval.h"
 #include "multiif.h"
 #include "sendf.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
@@ -108,7 +114,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
                            socklen_t addrlen)
 {
   struct quicsocket *qs = &conn->hequic[sockindex];
-  bool unsecure = !conn->ssl_config.verifypeer;
+  bool insecure = !conn->ssl_config.verifypeer;
   memset(qs, 0, sizeof(*qs));
 
   (void)sockfd;
@@ -126,7 +132,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   qs->conn = MsH3ConnectionOpen(qs->api,
                                 conn->host.name,
                                 (uint16_t)conn->remote_port,
-                                unsecure);
+                                insecure);
   if(!qs->conn) {
     failf(data, "can't create msh3 connection");
     if(qs->api) {
@@ -220,13 +226,8 @@ static unsigned int msh3_conncheck(struct Curl_easy *data,
   return CONNRESULT_NONE;
 }
 
-static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
+static void disconnect(struct quicsocket *qs)
 {
-  if(stream && stream->recv_buf) {
-    free(stream->recv_buf);
-    stream->recv_buf = ZERO_NULL;
-    msh3_lock_uninitialize(&stream->recv_lock);
-  }
   if(qs->conn) {
     MsH3ConnectionClose(qs->conn);
     qs->conn = ZERO_NULL;
@@ -240,18 +241,20 @@ static void msh3_cleanup(struct quicsocket *qs, struct HTTP *stream)
 static CURLcode msh3_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead_connection)
 {
+  (void)data;
   (void)dead_connection;
   H3BUGF(infof(data, "disconnecting (msh3)"));
-  msh3_cleanup(conn->quic, data->req.p.http);
+  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 (curl)"));
-    msh3_cleanup(&conn->hequic[tempindex], data->req.p.http);
+    H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
+    disconnect(&conn->hequic[tempindex]);
   }
 }
 
@@ -286,7 +289,6 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
   struct HTTP *stream = IfContext;
   size_t total_len;
   (void)Request;
-  H3BUGF(printf("* msh3_header_received\n"));
 
   if(stream->recv_header_complete) {
     H3BUGF(printf("* ignoring header after data\n"));
@@ -379,9 +381,6 @@ static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
   (void)stream;
 }
 
-static_assert(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo),
-              "Sizes must match for cast below to work");
-
 static ssize_t msh3_stream_send(struct Curl_easy *data,
                                 int sockindex,
                                 const void *mem,
@@ -394,6 +393,9 @@ static ssize_t msh3_stream_send(struct Curl_easy *data,
   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) {
@@ -488,9 +490,19 @@ CURLcode Curl_quic_done_sending(struct Curl_easy *data)
 
 void Curl_quic_done(struct Curl_easy *data, bool premature)
 {
-  (void)data;
+  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)
@@ -500,4 +512,16 @@ bool Curl_quic_data_pending(const struct Curl_easy *data)
   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 */
index bacdcb1..ce884d9 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index f1a64ee..097cca4 100644 (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 <ngtcp2/ngtcp2_crypto.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"
@@ -96,17 +104,32 @@ struct h3out {
   "+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,
-                                   size_t datalen, void *user_data,
+                                   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();
@@ -187,91 +210,44 @@ static int keylog_callback(gnutls_session_t session, const char *label,
   Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
   return 0;
 }
-#endif
-
-static int init_ngh3_conn(struct quicsocket *qs);
-
-static int write_client_handshake(struct quicsocket *qs,
-                                  ngtcp2_crypto_level level,
-                                  const uint8_t *data, size_t len)
-{
-  int rv;
-
-  rv = ngtcp2_conn_submit_crypto_data(qs->qconn, level, data, len);
-  if(rv) {
-    H3BUGF(fprintf(stderr, "write_client_handshake failed\n"));
-  }
-  assert(0 == rv);
-
-  return 1;
-}
-
-#ifdef USE_OPENSSL
-static int quic_set_encryption_secrets(SSL *ssl,
-                                       OSSL_ENCRYPTION_LEVEL ossl_level,
-                                       const uint8_t *rx_secret,
-                                       const uint8_t *tx_secret,
-                                       size_t secretlen)
-{
-  struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  int level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
-
-  if(ngtcp2_crypto_derive_and_install_rx_key(
-       qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0)
-    return 0;
-
-  if(ngtcp2_crypto_derive_and_install_tx_key(
-       qs->qconn, NULL, NULL, NULL, level, tx_secret, secretlen) != 0)
-    return 0;
-
-  if(level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
-    if(init_ngh3_conn(qs) != CURLE_OK)
-      return 0;
-  }
-
-  return 1;
-}
-
-static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
-                                   const uint8_t *data, size_t len)
-{
-  struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  ngtcp2_crypto_level level =
-      ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
-
-  return write_client_handshake(qs, level, data, len);
-}
-
-static int quic_flush_flight(SSL *ssl)
+#elif defined(USE_WOLFSSL)
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
 {
   (void)ssl;
-  return 1;
-}
-
-static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level,
-                           uint8_t alert)
-{
-  struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  (void)level;
-
-  qs->tls_alert = alert;
-  return 1;
+  Curl_tls_keylog_write_line(line);
 }
+#endif
+#endif
 
-static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets,
-                                      quic_add_handshake_data,
-                                      quic_flush_flight, quic_send_alert};
+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());
 
-  SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
-  SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+#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));
@@ -283,8 +259,7 @@ static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
     failf(data, "SSL_CTX_set1_groups_list failed");
     return NULL;
   }
-
-  SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+#endif
 
   /* Open the file if a TLS or QUIC backend has not done this before. */
   Curl_tls_keylog_open();
@@ -353,7 +328,7 @@ static int quic_init_ssl(struct quicsocket *qs)
   DEBUGASSERT(!qs->ssl);
   qs->ssl = SSL_new(qs->sslctx);
 
-  SSL_set_app_data(qs->ssl, qs);
+  SSL_set_app_data(qs->ssl, &qs->conn_ref);
   SSL_set_connect_state(qs->ssl);
   SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
 
@@ -367,107 +342,6 @@ static int quic_init_ssl(struct quicsocket *qs)
   return 0;
 }
 #elif defined(USE_GNUTLS)
-static int secret_func(gnutls_session_t ssl,
-                       gnutls_record_encryption_level_t gtls_level,
-                       const void *rx_secret,
-                       const void *tx_secret, size_t secretlen)
-{
-  struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  int level =
-      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
-
-  if(level != NGTCP2_CRYPTO_LEVEL_EARLY &&
-     ngtcp2_crypto_derive_and_install_rx_key(
-       qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0)
-    return 0;
-
-  if(ngtcp2_crypto_derive_and_install_tx_key(
-       qs->qconn, NULL, NULL, NULL, level, tx_secret, secretlen) != 0)
-    return 0;
-
-  if(level == NGTCP2_CRYPTO_LEVEL_APPLICATION) {
-    if(init_ngh3_conn(qs) != CURLE_OK)
-      return -1;
-  }
-
-  return 0;
-}
-
-static int read_func(gnutls_session_t ssl,
-                     gnutls_record_encryption_level_t gtls_level,
-                     gnutls_handshake_description_t htype, const void *data,
-                     size_t len)
-{
-  struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  ngtcp2_crypto_level level =
-      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
-  int rv;
-
-  if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
-    return 0;
-
-  rv = write_client_handshake(qs, level, data, len);
-  if(rv == 0)
-    return -1;
-
-  return 0;
-}
-
-static int alert_read_func(gnutls_session_t ssl,
-                           gnutls_record_encryption_level_t gtls_level,
-                           gnutls_alert_level_t alert_level,
-                           gnutls_alert_description_t alert_desc)
-{
-  struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  (void)gtls_level;
-  (void)alert_level;
-
-  qs->tls_alert = alert_desc;
-  return 1;
-}
-
-static int tp_recv_func(gnutls_session_t ssl, const uint8_t *data,
-                        size_t data_size)
-{
-  struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  ngtcp2_transport_params params;
-
-  if(ngtcp2_decode_transport_params(
-       &params, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
-       data, data_size) != 0)
-    return -1;
-
-  if(ngtcp2_conn_set_remote_transport_params(qs->qconn, &params) != 0)
-    return -1;
-
-  return 0;
-}
-
-static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata)
-{
-  struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  uint8_t paramsbuf[64];
-  ngtcp2_transport_params params;
-  ssize_t nwrite;
-  int rc;
-
-  ngtcp2_conn_get_local_transport_params(qs->qconn, &params);
-  nwrite = ngtcp2_encode_transport_params(
-    paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
-    &params);
-  if(nwrite < 0) {
-    H3BUGF(fprintf(stderr, "ngtcp2_encode_transport_params: %s\n",
-                   ngtcp2_strerror((int)nwrite)));
-    return -1;
-  }
-
-  rc = gnutls_buffer_append_data(extdata, paramsbuf, nwrite);
-  if(rc < 0)
-    return rc;
-
-  return (int)nwrite;
-}
-
 static int quic_init_ssl(struct quicsocket *qs)
 {
   gnutls_datum_t alpn[2];
@@ -478,26 +352,17 @@ static int quic_init_ssl(struct quicsocket *qs)
   DEBUGASSERT(!qs->ssl);
 
   gnutls_init(&qs->ssl, GNUTLS_CLIENT);
-  gnutls_session_set_ptr(qs->ssl, qs);
+  gnutls_session_set_ptr(qs->ssl, &qs->conn_ref);
 
-  rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL);
-  if(rc < 0) {
-    H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
-                   gnutls_strerror(rc)));
+  if(ngtcp2_crypto_gnutls_configure_client_session(qs->ssl) != 0) {
+    H3BUGF(fprintf(stderr,
+                   "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
     return 1;
   }
 
-  gnutls_handshake_set_secret_function(qs->ssl, secret_func);
-  gnutls_handshake_set_read_function(qs->ssl, read_func);
-  gnutls_alert_set_read_function(qs->ssl, alert_read_func);
-
-  rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters",
-         NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS,
-         tp_recv_func, tp_send_func, NULL, NULL, NULL,
-         GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
-         GNUTLS_EXT_FLAG_EE);
+  rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL);
   if(rc < 0) {
-    H3BUGF(fprintf(stderr, "gnutls_session_ext_register failed: %s\n",
+    H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
                    gnutls_strerror(rc)));
     return 1;
   }
@@ -546,7 +411,105 @@ static int quic_init_ssl(struct quicsocket *qs)
   gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname));
   return 0;
 }
+#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 int quic_init_ssl(struct quicsocket *qs)
+{
+  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;
+
+  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 0;
+}
+#endif /* defined(USE_WOLFSSL) */
 
 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
 {
@@ -571,7 +534,7 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
                                void *user_data, void *stream_user_data)
 {
   struct quicsocket *qs = (struct quicsocket *)user_data;
-  ssize_t nconsumed;
+  nghttp3_ssize nconsumed;
   int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
   (void)offset;
   (void)stream_user_data;
@@ -579,6 +542,9 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
   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;
   }
 
@@ -629,6 +595,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
   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;
   }
 
@@ -735,6 +703,23 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
   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 */
@@ -773,6 +758,9 @@ static ngtcp2_callbacks ng_callbacks = {
   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 */
 };
 
 /*
@@ -818,6 +806,10 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   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
 
   if(quic_init_ssl(qs))
@@ -855,6 +847,29 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
 
   ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
 
+  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;
 }
 
@@ -899,18 +914,14 @@ static void qs_disconnect(struct quicsocket *qs)
   char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
   ngtcp2_tstamp ts;
   ngtcp2_ssize rc;
-  ngtcp2_connection_close_error errorcode;
 
   if(!qs->conn) /* already closed */
     return;
-  ngtcp2_connection_close_error_set_application_error(&errorcode,
-                                                      NGHTTP3_H3_NO_ERROR,
-                                                      NULL, 0);
   ts = timestamp();
   rc = ngtcp2_conn_write_connection_close(qs->qconn, NULL, /* path */
                                           NULL, /* pkt_info */
                                           (uint8_t *)buffer, sizeof(buffer),
-                                          &errorcode, ts);
+                                          &qs->last_error, ts);
   if(rc > 0) {
     while((send(qs->conn->sock[FIRSTSOCKET], buffer, rc, 0) == -1) &&
           SOCKERRNO == EINTR);
@@ -926,6 +937,8 @@ static void qs_disconnect(struct quicsocket *qs)
     SSL_free(qs->ssl);
 #elif defined(USE_GNUTLS)
     gnutls_deinit(qs->ssl);
+#elif defined(USE_WOLFSSL)
+    wolfSSL_free(qs->ssl);
 #endif
   qs->ssl = NULL;
 #ifdef USE_GNUTLS
@@ -934,10 +947,13 @@ static void qs_disconnect(struct quicsocket *qs)
     qs->cred = NULL;
   }
 #endif
+  free(qs->pktbuf);
   nghttp3_conn_del(qs->h3conn);
   ngtcp2_conn_del(qs->qconn);
 #ifdef USE_OPENSSL
   SSL_CTX_free(qs->sslctx);
+#elif defined(USE_WOLFSSL)
+  wolfSSL_CTX_free(qs->sslctx);
 #endif
 }
 
@@ -1006,6 +1022,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
   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. */
@@ -1117,6 +1134,10 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
       return -1;
     }
   }
+
+  if(stream->status_code / 100 != 1) {
+    stream->bodystarted = TRUE;
+  }
   return 0;
 }
 
@@ -1139,9 +1160,10 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
   if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
     char line[14]; /* status line is always 13 characters long */
     size_t ncopy;
-    int status = decode_status_code(h3val.base, h3val.len);
-    DEBUGASSERT(status != -1);
-    ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", status);
+    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;
@@ -1171,16 +1193,36 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
   return 0;
 }
 
-static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
-                                   uint64_t app_error_code,
-                                   void *user_data,
-                                   void *stream_user_data)
+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_id;
-  (void)app_error_code;
-  (void)user_data;
   (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;
 }
 
@@ -1195,9 +1237,9 @@ static nghttp3_callbacks ngh3_callbacks = {
   NULL, /* begin_trailers */
   cb_h3_recv_header,
   NULL, /* end_trailers */
-  cb_h3_send_stop_sending,
+  cb_h3_stop_sending,
   NULL, /* end_stream */
-  NULL, /* reset_stream */
+  cb_h3_reset_stream,
   NULL /* shutdown */
 };
 
@@ -1333,6 +1375,24 @@ static ssize_t ngh3_stream_recv(struct Curl_easy *data,
   }
 
   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;
   }
@@ -1344,7 +1404,7 @@ static ssize_t ngh3_stream_recv(struct Curl_easy *data,
 
 /* 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,
-                                   size_t datalen, void *user_data,
+                                   uint64_t datalen, void *user_data,
                                    void *stream_user_data)
 {
   struct Curl_easy *data = stream_user_data;
@@ -1368,10 +1428,10 @@ static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
   return 0;
 }
 
-static ssize_t 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)
+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;
@@ -1482,6 +1542,7 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem,
       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;
     }
   }
 
@@ -1550,6 +1611,11 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
   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) {
@@ -1626,12 +1692,23 @@ static CURLcode ng_has_connected(struct Curl_easy *data,
     if(result)
       return result;
     infof(data, "Verified certificate just fine");
-#else
+#elif defined(USE_GNUTLS)
     result = Curl_gtls_verifyserver(data, conn, conn->quic->ssl, FIRSTSOCKET);
+#elif defined(USE_WOLFSSL)
+    char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL);
+    if(!snihost ||
+       (wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE))
+      return CURLE_PEER_FAILED_VERIFICATION;
+    infof(data, "Verified certificate just fine");
 #endif
   }
   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;
 }
 
@@ -1704,7 +1781,17 @@ static CURLcode ng_process_ingress(struct Curl_easy *data,
 
     rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts);
     if(rv) {
-      /* TODO Send CONNECTION_CLOSE if possible */
+      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 */
@@ -1716,32 +1803,231 @@ static CURLcode ng_process_ingress(struct Curl_easy *data,
   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;
-  ssize_t sent;
-  ssize_t outlen;
-  uint8_t out[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
+  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;
-  ssize_t veccnt;
+  nghttp3_ssize veccnt;
   int fin;
   nghttp3_vec vec[16];
-  ssize_t ndatalen;
+  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(;;) {
@@ -1755,39 +2041,45 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
       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, out,
-                                       sizeof(out),
+    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);
-        rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
-        if(rv) {
-          failf(data, "nghttp3_conn_block_stream returned error: %s\n",
-                nghttp3_strerror(rv));
-          return CURLE_SEND_ERROR;
-        }
+        nghttp3_conn_block_stream(qs->h3conn, stream_id);
         continue;
       case NGTCP2_ERR_STREAM_SHUT_WR:
         assert(ndatalen == -1);
-        rv = nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
-        if(rv) {
-          failf(data,
-                "nghttp3_conn_shutdown_stream_write returned error: %s\n",
-                nghttp3_strerror(rv));
-          return CURLE_SEND_ERROR;
-        }
+        nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
         continue;
       case NGTCP2_ERR_WRITE_MORE:
         assert(ndatalen >= 0);
@@ -1802,6 +2094,8 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
         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;
       }
     }
@@ -1814,20 +2108,61 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
       }
     }
 
-    while((sent = send(sockfd, (const char *)out, outlen, 0)) == -1 &&
-          SOCKERRNO == EINTR)
-      ;
+    outpos += outlen;
 
-    if(sent == -1) {
-      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
-        /* TODO Cache packet */
-        break;
+    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;
       }
-      else {
-        failf(data, "send() returned %zd (errno %d)", sent,
-              SOCKERRNO);
-        return CURLE_SEND_ERROR;
+      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;
     }
   }
 
@@ -1894,4 +2229,26 @@ bool Curl_quic_data_pending(const struct Curl_easy *data)
   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
index 5014530..6539f5f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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>
+#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_GNUTLS)
 #include <gnutls/gnutls.h>
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
 #endif
 
+struct 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;
@@ -42,17 +58,29 @@ struct quicsocket {
   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)
   gnutls_certificate_credentials_t cred;
   gnutls_session_t ssl;
+#elif defined(USE_WOLFSSL)
+  WOLFSSL_CTX *sslctx;
+  WOLFSSL *ssl;
 #endif
-  /* the last TLS alert description generated by the local endpoint */
-  uint8_t tls_alert;
   struct sockaddr_storage local_addr;
   socklen_t local_addrlen;
+  bool no_gso;
+  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;
index bfdc966..a52a7e8 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -201,23 +203,31 @@ static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
 
   {
     struct connectdata *conn = data->conn;
-    const char * const ssl_cafile = conn->ssl_config.CAfile;
-    const char * const ssl_capath = conn->ssl_config.CApath;
-
     if(conn->ssl_config.verifypeer) {
-      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;
+      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);
       }
-      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
-      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+#endif
     }
   }
   return ssl_ctx;
@@ -248,6 +258,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   struct quicsocket *qs = &conn->hequic[sockindex];
   char ipbuf[40];
   int port;
+  int rv;
 
 #ifdef DEBUG_QUICHE
   /* initialize debug log callback only once */
@@ -293,8 +304,16 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   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, addr, addrlen,
+                                      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");
@@ -397,6 +416,10 @@ static CURLcode quiche_has_connected(struct Curl_easy *data,
     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);
@@ -468,6 +491,8 @@ static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
 
     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)
@@ -856,4 +881,15 @@ bool Curl_quic_data_pending(const struct Curl_easy *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
index 759a20b..2da65f5 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -47,6 +49,8 @@ struct quicsocket {
   SSL_CTX *sslctx;
   SSL *ssl;
   bool h3_recving; /* TRUE when in h3-body-reading state */
+  struct sockaddr_storage local_addr;
+  socklen_t local_addrlen;
 };
 
 #endif
index be2a65f..e52a4f3 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 3df138f..8f599a8 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index 7bf2b04..0105e40 100644 (file)
@@ -21,6 +21,8 @@
  * 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_memory.h"
 #include "memdebug.h"
 
+/* in 0.10.0 or later, ignore deprecated warnings */
+#if defined(__GNUC__) &&                        \
+  (LIBSSH_VERSION_MINOR >= 10) ||               \
+  (LIBSSH_VERSION_MAJOR > 0)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 /* A recent macro provided by libssh. Or make our own. */
 #ifndef SSH_STRING_FREE_CHAR
 #define SSH_STRING_FREE_CHAR(x)                 \
@@ -954,10 +963,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
       rc = sftp_init(sshc->sftp_session);
       if(rc != SSH_OK) {
-        rc = sftp_get_error(sshc->sftp_session);
         failf(data, "Failure initializing sftp session: %s",
               ssh_get_error(sshc->ssh_session));
-        MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(rc));
+        MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
         break;
       }
       state(data, SSH_SFTP_REALPATH);
@@ -1658,7 +1666,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
           if(from_t == CURL_OFFT_FLOW) {
             return CURLE_RANGE_ERROR;
           }
-          while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
+          while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
             ptr++;
           to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
           if(to_t == CURL_OFFT_FLOW) {
@@ -1970,10 +1978,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       }
 
       ssh_disconnect(sshc->ssh_session);
-      /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
-         explicitly mark it as closed with the memdebug macro: */
-      fake_sclose(conn->sock[FIRSTSOCKET]);
-      conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
+      if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
+        /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
+           explicitly mark it as closed with the memdebug macro. This libssh
+           bug is fixed in 0.10.0. */
+        fake_sclose(conn->sock[FIRSTSOCKET]);
+        conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
+      }
 
       SSH_STRING_FREE_CHAR(sshc->homedir);
       data->state.most_recent_ftp_entrypath = NULL;
@@ -2906,32 +2917,33 @@ static void sftp_quote_stat(struct Curl_easy *data)
     }
     sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
   }
-  else if(strncasecompare(cmd, "atime", 5)) {
+  else if(strncasecompare(cmd, "atime", 5) ||
+          strncasecompare(cmd, "mtime", 5)) {
     time_t date = Curl_getdate_capped(sshc->quote_path1);
+    bool fail = FALSE;
     if(date == -1) {
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-      failf(data, "Syntax error: incorrect access date format");
-      state(data, SSH_SFTP_CLOSE);
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_QUOTE_ERROR;
-      return;
+      failf(data, "incorrect date format for %.*s", 5, cmd);
+      fail = TRUE;
     }
-    sshc->quote_attrs->atime = (uint32_t)date;
-    sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
-  }
-  else if(strncasecompare(cmd, "mtime", 5)) {
-    time_t date = Curl_getdate_capped(sshc->quote_path1);
-    if(date == -1) {
+#if SIZEOF_TIME_T > 4
+    else if(date > 0xffffffff) {
+      failf(data, "date overflow");
+      fail = TRUE; /* avoid setting a capped time */
+    }
+#endif
+    if(fail) {
       Curl_safefree(sshc->quote_path1);
       Curl_safefree(sshc->quote_path2);
-      failf(data, "Syntax error: incorrect modification date format");
       state(data, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = CURLE_QUOTE_ERROR;
       return;
     }
-    sshc->quote_attrs->mtime = (uint32_t)date;
+    if(strncasecompare(cmd, "atime", 5))
+      sshc->quote_attrs->atime = (uint32_t)date;
+    else /* mtime */
+      sshc->quote_attrs->mtime = (uint32_t)date;
+
     sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
   }
 
@@ -2956,7 +2968,7 @@ void Curl_ssh_cleanup(void)
 
 void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  (void)msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
+  (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
 }
 
 #endif                          /* USE_LIBSSH */
index d269263..5a2c0f8 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* #define CURL_LIBSSH2_DEBUG */
@@ -437,9 +439,45 @@ static int sshkeycallback(struct Curl_easy *easy,
 #else
 #define session_startup(x,y) libssh2_session_startup(x, (int)y)
 #endif
+static int convert_ssh2_keytype(int sshkeytype)
+{
+  int keytype = CURLKHTYPE_UNKNOWN;
+  switch(sshkeytype) {
+  case LIBSSH2_HOSTKEY_TYPE_RSA:
+    keytype = CURLKHTYPE_RSA;
+    break;
+  case LIBSSH2_HOSTKEY_TYPE_DSS:
+    keytype = CURLKHTYPE_DSS;
+    break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+  case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+    keytype = CURLKHTYPE_ECDSA;
+    break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
+  case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+    keytype = CURLKHTYPE_ECDSA;
+    break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
+  case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+    keytype = CURLKHTYPE_ECDSA;
+    break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+  case LIBSSH2_HOSTKEY_TYPE_ED25519:
+    keytype = CURLKHTYPE_ED25519;
+    break;
+#endif
+  }
+  return keytype;
+}
 
 static CURLcode ssh_knownhost(struct Curl_easy *data)
 {
+  int sshkeytype = 0;
+  size_t keylen = 0;
+  int rc = 0;
   CURLcode result = CURLE_OK;
 
 #ifdef HAVE_LIBSSH2_KNOWNHOST_API
@@ -448,11 +486,8 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
     struct connectdata *conn = data->conn;
     struct ssh_conn *sshc = &conn->proto.sshc;
     struct libssh2_knownhost *host = NULL;
-    int rc;
-    int keytype;
-    size_t keylen;
     const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
-                                                    &keylen, &keytype);
+                                                    &keylen, &sshkeytype);
     int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE;
     int keybit = 0;
 
@@ -464,12 +499,12 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
        */
       enum curl_khmatch keymatch;
       curl_sshkeycallback func =
-        data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback;
+        data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback;
       struct curl_khkey knownkey;
       struct curl_khkey *knownkeyp = NULL;
       struct curl_khkey foundkey;
 
-      switch(keytype) {
+      switch(sshkeytype) {
       case LIBSSH2_HOSTKEY_TYPE_RSA:
         keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
         break;
@@ -533,16 +568,14 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
         if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
           knownkey.key = host->key;
           knownkey.len = 0;
-          knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
-            CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+          knownkey.keytype = convert_ssh2_keytype(sshkeytype);
           knownkeyp = &knownkey;
         }
 
         /* setup 'foundkey' */
         foundkey.key = remotekey;
         foundkey.len = keylen;
-        foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)?
-          CURLKHTYPE_RSA : CURLKHTYPE_DSS;
+        foundkey.keytype = convert_ssh2_keytype(sshkeytype);
 
         /*
          * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the
@@ -639,7 +672,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
 #ifdef LIBSSH2_HOSTKEY_HASH_SHA256
     /* The fingerprint points to static storage (!), don't free() it. */
     fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
-        LIBSSH2_HOSTKEY_HASH_SHA256);
+                                       LIBSSH2_HOSTKEY_HASH_SHA256);
 #else
     const char *hostkey;
     size_t len = 0;
@@ -654,8 +687,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
 
     if(!fingerprint) {
       failf(data,
-          "Denied establishing ssh session: sha256 fingerprint "
-          "not available");
+            "Denied establishing ssh session: sha256 fingerprint "
+            "not available");
       state(data, SSH_SESSION_FREE);
       sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
       return sshc->actualcode;
@@ -715,7 +748,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
     const char *fingerprint = NULL;
 
     fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
-        LIBSSH2_HOSTKEY_HASH_MD5);
+                                       LIBSSH2_HOSTKEY_HASH_MD5);
 
     if(fingerprint) {
       /* The fingerprint points to static storage (!), don't free() it. */
@@ -748,7 +781,31 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
   }
 
   if(!pubkey_md5 && !pubkey_sha256) {
-    return ssh_knownhost(data);
+    if(data->set.ssh_hostkeyfunc) {
+      size_t keylen = 0;
+      int sshkeytype = 0;
+      int rc = 0;
+      /* we handle the process to the callback*/
+      const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
+                                                      &keylen, &sshkeytype);
+      if(remotekey) {
+        int keytype = convert_ssh2_keytype(sshkeytype);
+        Curl_set_in_callback(data, true);
+        rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp,
+                                       keytype, remotekey, keylen);
+        Curl_set_in_callback(data, false);
+        if(rc!= CURLKHMATCH_OK) {
+          state(data, SSH_SESSION_FREE);
+        }
+      }
+      else {
+        state(data, SSH_SESSION_FREE);
+      }
+      return CURLE_OK;
+    }
+    else {
+      return ssh_knownhost(data);
+    }
   }
   else {
     /* as we already matched, we skip the check for known hosts */
@@ -1698,32 +1755,35 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
           break;
         }
       }
-      else if(strncasecompare(cmd, "atime", 5)) {
+      else if(strncasecompare(cmd, "atime", 5) ||
+              strncasecompare(cmd, "mtime", 5)) {
         time_t date = Curl_getdate_capped(sshc->quote_path1);
+        bool fail = FALSE;
+
         if(date == -1) {
-          Curl_safefree(sshc->quote_path1);
-          Curl_safefree(sshc->quote_path2);
-          failf(data, "Syntax error: incorrect access date format");
-          state(data, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = CURLE_QUOTE_ERROR;
-          break;
+          failf(data, "incorrect date format for %.*s", 5, cmd);
+          fail = TRUE;
         }
-        sshp->quote_attrs.atime = (unsigned long)date;
-        sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
-      }
-      else if(strncasecompare(cmd, "mtime", 5)) {
-        time_t date = Curl_getdate_capped(sshc->quote_path1);
-        if(date == -1) {
+#if SIZEOF_TIME_T > SIZEOF_LONG
+        if(date > 0xffffffff) {
+          /* if 'long' can't old >32bit, this date cannot be sent */
+          failf(data, "date overflow");
+          fail = TRUE;
+        }
+#endif
+        if(fail) {
           Curl_safefree(sshc->quote_path1);
           Curl_safefree(sshc->quote_path2);
-          failf(data, "Syntax error: incorrect modification date format");
           state(data, SSH_SFTP_CLOSE);
           sshc->nextstate = SSH_NO_STATE;
           sshc->actualcode = CURLE_QUOTE_ERROR;
           break;
         }
-        sshp->quote_attrs.mtime = (unsigned long)date;
+        if(strncasecompare(cmd, "atime", 5))
+          sshp->quote_attrs.atime = (unsigned long)date;
+        else /* mtime */
+          sshp->quote_attrs.mtime = (unsigned long)date;
+
         sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME;
       }
 
@@ -2278,7 +2338,8 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
                ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
                 LIBSSH2_SFTP_S_IFLNK)) {
               Curl_dyn_init(&sshp->readdir_link, PATH_MAX);
-              result = Curl_dyn_add(&sshp->readdir_link, sshp->path);
+              result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
+                                     sshp->readdir_filename);
               state(data, SSH_SFTP_READDIR_LINK);
               if(!result)
                 break;
@@ -2445,7 +2506,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block)
           from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from);
           if(from_t == CURL_OFFT_FLOW)
             return CURLE_RANGE_ERROR;
-          while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
+          while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
             ptr++;
           to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
           if(to_t == CURL_OFFT_FLOW)
index 30d82e5..13bb8aa 100644 (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(HAVE_LIBSSH2_H)
+#if defined(USE_LIBSSH2)
 #include <libssh2.h>
 #include <libssh2_sftp.h>
-#elif defined(HAVE_LIBSSH_LIBSSH_H)
+#elif defined(USE_LIBSSH)
 #include <libssh/libssh.h>
 #include <libssh/sftp.h>
 #elif defined(USE_WOLFSSH)
@@ -209,11 +211,7 @@ struct ssh_conn {
 #endif /* USE_LIBSSH */
 };
 
-#if defined(USE_LIBSSH)
-
-#define CURL_LIBSSH_VERSION ssh_version(0)
-
-#elif defined(USE_LIBSSH2)
+#if defined(USE_LIBSSH2)
 
 /* Feature detection based on version numbers to better work with
    non-configure platforms */
index 85f2941..c2f85f3 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 91f4416..1221ce8 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
 
@@ -74,9 +76,9 @@ struct cafile_parser {
 #define CAFILE_SOURCE_PATH 1
 #define CAFILE_SOURCE_BLOB 2
 struct cafile_source {
-  const int type;
-  const char * const data;
-  const size_t len;
+  int type;
+  const char *data;
+  size_t len;
 };
 
 static void append_dn(void *ctx, const void *buf, size_t len)
@@ -616,11 +618,11 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
   }
 
   if(ca_info_blob) {
-    struct cafile_source source = {
-      CAFILE_SOURCE_BLOB,
-      ca_info_blob->data,
-      ca_info_blob->len,
-    };
+    struct cafile_source source;
+    source.type = CAFILE_SOURCE_BLOB;
+    source.data = ca_info_blob->data;
+    source.len = ca_info_blob->len;
+
     ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
     if(ret != CURLE_OK) {
       if(verifypeer) {
@@ -633,11 +635,11 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data,
   }
 
   if(ssl_cafile) {
-    struct cafile_source source = {
-      CAFILE_SOURCE_PATH,
-      ssl_cafile,
-      0,
-    };
+    struct cafile_source source;
+    source.type = CAFILE_SOURCE_PATH;
+    source.data = ssl_cafile;
+    source.len = 0;
+
     ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
     if(ret != CURLE_OK) {
       if(verifypeer) {
@@ -873,14 +875,14 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data,
 
 #ifdef USE_HTTP2
       if(!strcmp(protocol, ALPN_H2))
-        conn->negnpn = CURL_HTTP_VERSION_2;
+        conn->alpn = CURL_HTTP_VERSION_2;
       else
 #endif
       if(!strcmp(protocol, ALPN_HTTP_1_1))
-        conn->negnpn = CURL_HTTP_VERSION_1_1;
+        conn->alpn = CURL_HTTP_VERSION_1_1;
       else
         infof(data, "ALPN, unrecognized protocol %s", protocol);
-      Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
     else
index d72b7d0..5125359 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Michael Forney, <mforney@mforney.org>
+ * Copyright (C) 2019 - 2022, 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
@@ -20,6 +20,8 @@
  * 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"
index 7a65f92..4ee4ede 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index 202df7e..cf923f6 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index dd82755..cf3dbc5 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -43,6 +45,7 @@
 #include "inet_pton.h"
 #include "gtls.h"
 #include "vtls.h"
+#include "vauth/vauth.h"
 #include "parsedate.h"
 #include "connect.h" /* for the connect timeout */
 #include "select.h"
@@ -446,7 +449,7 @@ gtls_connect_step1(struct Curl_easy *data,
 
 #ifdef USE_GNUTLS_SRP
   if((SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) &&
-     Curl_allow_auth_to_host(data)) {
+     Curl_auth_allowed_to_host(data)) {
     infof(data, "Using TLS-SRP username: %s",
           SSL_SET_OPTION(primary.username));
 
@@ -1272,19 +1275,19 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
       if(proto.size == ALPN_H2_LENGTH &&
          !memcmp(ALPN_H2, proto.data,
                  ALPN_H2_LENGTH)) {
-        conn->negnpn = CURL_HTTP_VERSION_2;
+        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)) {
-        conn->negnpn = CURL_HTTP_VERSION_1_1;
+        conn->alpn = CURL_HTTP_VERSION_1_1;
       }
     }
     else
       infof(data, VTLS_INFOF_NO_ALPN);
 
-    Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+    Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 
index 642d5f0..abade73 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
index 8dc97a2..2a648f2 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
index aa96640..d3c4eab 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 #include <curl/curl.h>
index a45945f..1952a69 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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 "keylog.h"
+#include <curl/curl.h>
 
 /* The last #include files should be: */
 #include "curl_memory.h"
index 63626da..5d3c675 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index b60b9ca..fbde897 100644 (file)
@@ -19,6 +19,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -819,19 +821,19 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
 #ifdef USE_HTTP2
       if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) &&
          !next_protocol[ALPN_H2_LENGTH]) {
-        conn->negnpn = CURL_HTTP_VERSION_2;
+        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]) {
-          conn->negnpn = CURL_HTTP_VERSION_1_1;
+          conn->alpn = CURL_HTTP_VERSION_1_1;
         }
     }
     else {
       infof(data, VTLS_INFOF_NO_ALPN);
     }
-    Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+    Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 #endif
index 1abd331..ec3b43b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -21,6 +21,8 @@
  * 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"
 
index 751755c..3971e69 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2013 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
  * 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"
 
index e40dfc8..3a50d03 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2013 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2013 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  * Copyright (C) 2010, Hoi-Ho Chan, <hoiho.chan@gmail.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -21,6 +21,8 @@
  * 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"
 
index cb0509f..12cf618 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -334,7 +336,7 @@ static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model,
     char name[MAX_CIPHER_LENGTH + 1];
     size_t len;
     bool found = FALSE;
-    while((*cipher) && (ISSPACE(*cipher)))
+    while((*cipher) && (ISBLANK(*cipher)))
       ++cipher;
 
     end = strpbrk(cipher, ":, ");
@@ -848,7 +850,7 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
   unsigned int buflen;
   SSLNextProtoState state;
 
-  if(!conn->bits.tls_enable_npn && !conn->bits.tls_enable_alpn) {
+  if(!conn->bits.tls_enable_alpn) {
     return;
   }
 
@@ -869,21 +871,21 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
       infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf);
       break;
 #endif
-    case SSL_NEXT_PROTO_NEGOTIATED:
-      infof(data, "NPN, server accepted to use %.*s", buflen, buf);
+    default:
+      /* ignore SSL_NEXT_PROTO_NEGOTIATED */
       break;
     }
 
 #ifdef USE_HTTP2
     if(buflen == ALPN_H2_LENGTH &&
        !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) {
-      conn->negnpn = CURL_HTTP_VERSION_2;
+      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)) {
-      conn->negnpn = CURL_HTTP_VERSION_1_1;
+      conn->alpn = CURL_HTTP_VERSION_1_1;
     }
 
     /* This callback might get called when PR_Recv() is used within
@@ -891,7 +893,7 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
      * be any "bundle" associated with the connection anymore.
      */
     if(conn->bundle)
-      Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 }
@@ -934,8 +936,8 @@ static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data,
   if(cipherInfo.symCipher != ssl_calg_aes_gcm)
     goto end;
 
-  /* Enforce ALPN or NPN to do False Start, as an indicator of server
-   * compatibility. */
+  /* Enforce ALPN to do False Start, as an indicator of server
+     compatibility. */
   rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn,
                                         &negotiatedExtension);
   if(rv != SECSuccess || !negotiatedExtension) {
@@ -2134,12 +2136,6 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
   }
 #endif
 
-#ifdef SSL_ENABLE_NPN
-  if(SSL_OptionSet(backend->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn
-                   ? PR_TRUE : PR_FALSE) != SECSuccess)
-    goto error;
-#endif
-
 #ifdef SSL_ENABLE_ALPN
   if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn
                    ? PR_TRUE : PR_FALSE) != SECSuccess)
@@ -2158,15 +2154,15 @@ static CURLcode nss_setup_connect(struct Curl_easy *data,
   }
 #endif
 
-#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN)
-  if(conn->bits.tls_enable_npn || conn->bits.tls_enable_alpn) {
+#if defined(SSL_ENABLE_ALPN)
+  if(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
-      && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+       && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
 #endif
       ) {
       protocols[cur++] = ALPN_H2_LENGTH;
index 37b3646..454a38f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index 635e9c1..0dc695d 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -27,7 +29,7 @@
 
 #include "curl_setup.h"
 
-#ifdef USE_OPENSSL
+#if defined(USE_QUICHE) || defined(USE_OPENSSL)
 
 #include <limits.h>
 
@@ -53,6 +55,7 @@
 #include "slist.h"
 #include "select.h"
 #include "vtls.h"
+#include "vauth/vauth.h"
 #include "keylog.h"
 #include "strcase.h"
 #include "hostcheck.h"
 #include <openssl/buffer.h>
 #include <openssl/pkcs12.h>
 
-#ifdef USE_AMISSL
-#include "amigaos.h"
-#endif
-
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
 #include <openssl/ocsp.h>
 #endif
  * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
  * LibreSSL: since 2.5.3 (April 12, 2017)
  */
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) ||  \
-  defined(OPENSSL_IS_BORINGSSL)
+#if ((OPENSSL_VERSION_NUMBER >= 0x10002000L) &&  \
+     !(defined(LIBRESSL_VERSION_NUMBER) &&       \
+      LIBRESSL_VERSION_NUMBER < 0x20503000L)) || \
+     defined(OPENSSL_IS_BORINGSSL)
 #define HAVE_SSL_CTX_SET_EC_CURVES
 #endif
 
@@ -273,6 +274,344 @@ struct ssl_backend_data {
 #endif
 };
 
+#define push_certinfo(_label, _num)             \
+do {                              \
+  long info_len = BIO_get_mem_data(mem, &ptr); \
+  Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
+  if(1 != BIO_reset(mem))                                        \
+    break;                                                       \
+} while(0)
+
+static void pubkey_show(struct Curl_easy *data,
+                        BIO *mem,
+                        int num,
+                        const char *type,
+                        const char *name,
+                        const BIGNUM *bn)
+{
+  char *ptr;
+  char namebuf[32];
+
+  msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+
+  if(bn)
+    BN_print(mem, bn);
+  push_certinfo(namebuf, num);
+}
+
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#define print_pubkey_BN(_type, _name, _num)              \
+  pubkey_show(data, mem, _num, #_type, #_name, _name)
+
+#else
+#define print_pubkey_BN(_type, _name, _num)    \
+do {                              \
+  if(_type->_name) { \
+    pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
+  } \
+} while(0)
+#endif
+
+static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
+{
+  int i, ilen;
+
+  ilen = (int)len;
+  if(ilen < 0)
+    return 1; /* buffer too big */
+
+  i = i2t_ASN1_OBJECT(buf, ilen, a);
+
+  if(i >= ilen)
+    return 1; /* buffer too small */
+
+  return 0;
+}
+
+static void X509V3_ext(struct Curl_easy *data,
+                      int certnum,
+                      CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+{
+  int i;
+
+  if((int)sk_X509_EXTENSION_num(exts) <= 0)
+    /* no extensions, bail out */
+    return;
+
+  for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
+    ASN1_OBJECT *obj;
+    X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+    BUF_MEM *biomem;
+    char namebuf[128];
+    BIO *bio_out = BIO_new(BIO_s_mem());
+
+    if(!bio_out)
+      return;
+
+    obj = X509_EXTENSION_get_object(ext);
+
+    asn1_object_dump(obj, namebuf, sizeof(namebuf));
+
+    if(!X509V3_EXT_print(bio_out, ext, 0, 0))
+      ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
+
+    BIO_get_mem_ptr(bio_out, &biomem);
+    Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+                               biomem->length);
+    BIO_free(bio_out);
+  }
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef size_t numcert_t;
+#else
+typedef int numcert_t;
+#endif
+
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl)
+{
+  CURLcode result;
+  STACK_OF(X509) *sk;
+  int i;
+  numcert_t numcerts;
+  BIO *mem;
+
+  DEBUGASSERT(ssl);
+
+  sk = SSL_get_peer_cert_chain(ssl);
+  if(!sk) {
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  numcerts = sk_X509_num(sk);
+
+  result = Curl_ssl_init_certinfo(data, (int)numcerts);
+  if(result) {
+    return result;
+  }
+
+  mem = BIO_new(BIO_s_mem());
+  if(!mem) {
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  for(i = 0; i < (int)numcerts; i++) {
+    ASN1_INTEGER *num;
+    X509 *x = sk_X509_value(sk, i);
+    EVP_PKEY *pubkey = NULL;
+    int j;
+    char *ptr;
+    const ASN1_BIT_STRING *psig = NULL;
+
+    X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
+    push_certinfo("Subject", i);
+
+    X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
+    push_certinfo("Issuer", i);
+
+    BIO_printf(mem, "%lx", X509_get_version(x));
+    push_certinfo("Version", i);
+
+    num = X509_get_serialNumber(x);
+    if(num->type == V_ASN1_NEG_INTEGER)
+      BIO_puts(mem, "-");
+    for(j = 0; j < num->length; j++)
+      BIO_printf(mem, "%02x", num->data[j]);
+    push_certinfo("Serial Number", i);
+
+#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+    {
+      const X509_ALGOR *sigalg = NULL;
+      X509_PUBKEY *xpubkey = NULL;
+      ASN1_OBJECT *pubkeyoid = NULL;
+
+      X509_get0_signature(&psig, &sigalg, x);
+      if(sigalg) {
+        i2a_ASN1_OBJECT(mem, sigalg->algorithm);
+        push_certinfo("Signature Algorithm", i);
+      }
+
+      xpubkey = X509_get_X509_PUBKEY(x);
+      if(xpubkey) {
+        X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
+        if(pubkeyoid) {
+          i2a_ASN1_OBJECT(mem, pubkeyoid);
+          push_certinfo("Public Key Algorithm", i);
+        }
+      }
+
+      X509V3_ext(data, i, X509_get0_extensions(x));
+    }
+#else
+    {
+      /* before OpenSSL 1.0.2 */
+      X509_CINF *cinf = x->cert_info;
+
+      i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
+      push_certinfo("Signature Algorithm", i);
+
+      i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+      push_certinfo("Public Key Algorithm", i);
+
+      X509V3_ext(data, i, cinf->extensions);
+
+      psig = x->signature;
+    }
+#endif
+
+    ASN1_TIME_print(mem, X509_get0_notBefore(x));
+    push_certinfo("Start date", i);
+
+    ASN1_TIME_print(mem, X509_get0_notAfter(x));
+    push_certinfo("Expire date", i);
+
+    pubkey = X509_get_pubkey(x);
+    if(!pubkey)
+      infof(data, "   Unable to load public key");
+    else {
+      int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+      pktype = EVP_PKEY_id(pubkey);
+#else
+      pktype = pubkey->type;
+#endif
+      switch(pktype) {
+      case EVP_PKEY_RSA:
+      {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        RSA *rsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+        rsa = EVP_PKEY_get0_RSA(pubkey);
+#else
+        rsa = pubkey->pkey.rsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+
+        {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(n);
+          DECLARE_PKEY_PARAM_BIGNUM(e);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
+#else
+          RSA_get0_key(rsa, &n, &e, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+          BIO_printf(mem, "%d", BN_num_bits(n));
+#else
+          BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+          push_certinfo("RSA Public Key", i);
+          print_pubkey_BN(rsa, n, i);
+          print_pubkey_BN(rsa, e, i);
+          FREE_PKEY_PARAM_BIGNUM(n);
+          FREE_PKEY_PARAM_BIGNUM(e);
+        }
+
+        break;
+      }
+      case EVP_PKEY_DSA:
+      {
+#ifndef OPENSSL_NO_DSA
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        DSA *dsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+        dsa = EVP_PKEY_get0_DSA(pubkey);
+#else
+        dsa = pubkey->pkey.dsa;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+        {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(p);
+          DECLARE_PKEY_PARAM_BIGNUM(q);
+          DECLARE_PKEY_PARAM_BIGNUM(g);
+          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+          DSA_get0_pqg(dsa, &p, &q, &g);
+          DSA_get0_key(dsa, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+          print_pubkey_BN(dsa, p, i);
+          print_pubkey_BN(dsa, q, i);
+          print_pubkey_BN(dsa, g, i);
+          print_pubkey_BN(dsa, pub_key, i);
+          FREE_PKEY_PARAM_BIGNUM(p);
+          FREE_PKEY_PARAM_BIGNUM(q);
+          FREE_PKEY_PARAM_BIGNUM(g);
+          FREE_PKEY_PARAM_BIGNUM(pub_key);
+        }
+#endif /* !OPENSSL_NO_DSA */
+        break;
+      }
+      case EVP_PKEY_DH:
+      {
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        DH *dh;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+        dh = EVP_PKEY_get0_DH(pubkey);
+#else
+        dh = pubkey->pkey.dh;
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+        {
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(p);
+          DECLARE_PKEY_PARAM_BIGNUM(q);
+          DECLARE_PKEY_PARAM_BIGNUM(g);
+          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
+          DH_get0_pqg(dh, &p, &q, &g);
+          DH_get0_key(dh, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+          print_pubkey_BN(dh, p, i);
+          print_pubkey_BN(dh, q, i);
+          print_pubkey_BN(dh, g, i);
+#else
+          print_pubkey_BN(dh, p, i);
+          print_pubkey_BN(dh, g, i);
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
+          print_pubkey_BN(dh, pub_key, i);
+          FREE_PKEY_PARAM_BIGNUM(p);
+          FREE_PKEY_PARAM_BIGNUM(q);
+          FREE_PKEY_PARAM_BIGNUM(g);
+          FREE_PKEY_PARAM_BIGNUM(pub_key);
+       }
+        break;
+      }
+      }
+      EVP_PKEY_free(pubkey);
+    }
+
+    if(psig) {
+      for(j = 0; j < psig->length; j++)
+        BIO_printf(mem, "%02x:", psig->data[j]);
+      push_certinfo("Signature", i);
+    }
+
+    PEM_write_bio_X509(mem, x);
+    push_certinfo("Cert", i);
+  }
+
+  BIO_free(mem);
+
+  return CURLE_OK;
+}
+
+#endif /* quiche or OpenSSL */
+
+#ifdef USE_OPENSSL
+
 static bool ossl_associate_connection(struct Curl_easy *data,
                                       struct connectdata *conn,
                                       int sockindex);
@@ -484,36 +823,19 @@ static CURLcode ossl_seed(struct Curl_easy *data)
   return CURLE_SSL_CONNECT_ERROR;
 #else
 
-#ifndef RANDOM_FILE
-  /* if RANDOM_FILE isn't defined, we only perform this if an option tells
-     us to! */
-  if(data->set.str[STRING_SSL_RANDOM_FILE])
-#define RANDOM_FILE "" /* doesn't matter won't be used */
+#ifdef RANDOM_FILE
+  RAND_load_file(RANDOM_FILE, RAND_LOAD_LENGTH);
+  if(rand_enough())
+    return CURLE_OK;
 #endif
-  {
-    /* let the option override the define */
-    RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
-                    data->set.str[STRING_SSL_RANDOM_FILE]:
-                    RANDOM_FILE),
-                   RAND_LOAD_LENGTH);
-    if(rand_enough())
-      return CURLE_OK;
-  }
 
-#if defined(HAVE_RAND_EGD)
-  /* only available in OpenSSL 0.9.5 and later */
+#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET)
+  /* available in OpenSSL 0.9.5 and later */
   /* EGD_SOCKET is set at configure time or not at all */
-#ifndef EGD_SOCKET
-  /* If we don't have the define set, we only do this if the egd-option
-     is set */
-  if(data->set.str[STRING_SSL_EGDSOCKET])
-#define EGD_SOCKET "" /* doesn't matter won't be used */
-#endif
   {
     /* If there's an option and a define, the option overrides the
        define */
-    int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
-                       data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
+    int ret = RAND_egd(EGD_SOCKET);
     if(-1 != ret) {
       if(rand_enough())
         return CURLE_OK;
@@ -812,9 +1134,10 @@ int cert_stuff(struct Curl_easy *data,
         SSL_CTX_use_certificate_chain_file(ctx, cert_file);
       if(cert_use_result != 1) {
         failf(data,
-              "could not load PEM client certificate, " OSSL_PACKAGE
+              "could not load PEM client certificate from %s, " OSSL_PACKAGE
               " error %s, "
               "(no key found, wrong pass phrase, or wrong file format?)",
+              (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
               ossl_strerror(ERR_get_error(), error_buffer,
                             sizeof(error_buffer)) );
         return 0;
@@ -832,9 +1155,10 @@ int cert_stuff(struct Curl_easy *data,
         SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
       if(cert_use_result != 1) {
         failf(data,
-              "could not load ASN1 client certificate, " OSSL_PACKAGE
+              "could not load ASN1 client certificate from %s, " OSSL_PACKAGE
               " error %s, "
               "(no key found, wrong pass phrase, or wrong file format?)",
+              (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file),
               ossl_strerror(ERR_get_error(), error_buffer,
                             sizeof(error_buffer)) );
         return 0;
@@ -887,8 +1211,9 @@ int cert_stuff(struct Curl_easy *data,
           }
 
           if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
-            failf(data, "unable to set client certificate");
-            X509_free(params.cert);
+            failf(data, "unable to set client certificate [%s]",
+                  ossl_strerror(ERR_get_error(), error_buffer,
+                                sizeof(error_buffer)));
             return 0;
           }
           X509_free(params.cert); /* we don't need the handle any more... */
@@ -1011,11 +1336,7 @@ int cert_stuff(struct Curl_easy *data,
   fail:
       EVP_PKEY_free(pri);
       X509_free(x509);
-#ifdef USE_AMISSL
-      sk_X509_pop_free(ca, Curl_amiga_X509_free);
-#else
       sk_X509_pop_free(ca, X509_free);
-#endif
       if(!cert_done)
         return 0; /* failure! */
       break;
@@ -2271,76 +2592,18 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
 #undef HAS_ALPN
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L \
     && !defined(OPENSSL_NO_TLSEXT)
-#  define HAS_ALPN 1
-#endif
-
-/* Check for OpenSSL 1.0.1 which has NPN support. */
-#undef HAS_NPN
-#if OPENSSL_VERSION_NUMBER >= 0x10001000L \
-    && !defined(OPENSSL_NO_TLSEXT) \
-    && !defined(OPENSSL_NO_NEXTPROTONEG)
-#  define HAS_NPN 1
-#endif
-
-#ifdef HAS_NPN
-
-/*
- * in is a list of length prefixed strings. this function has to select
- * the protocol we want to use from the list and write its string into out.
- */
-
-static int
-select_next_protocol(unsigned char **out, unsigned char *outlen,
-                     const unsigned char *in, unsigned int inlen,
-                     const char *key, unsigned int keylen)
-{
-  unsigned int i;
-  for(i = 0; i + keylen <= inlen; i += in[i] + 1) {
-    if(memcmp(&in[i + 1], key, keylen) == 0) {
-      *out = (unsigned char *) &in[i + 1];
-      *outlen = in[i];
-      return 0;
-    }
-  }
-  return -1;
-}
-
-static int
-select_next_proto_cb(SSL *ssl,
-                     unsigned char **out, unsigned char *outlen,
-                     const unsigned char *in, unsigned int inlen,
-                     void *arg)
-{
-  struct Curl_easy *data = (struct Curl_easy *)arg;
-  struct connectdata *conn = data->conn;
-  (void)ssl;
-
-#ifdef USE_HTTP2
-  if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
-     !select_next_protocol(out, outlen, in, inlen, ALPN_H2, ALPN_H2_LENGTH)) {
-    infof(data, "NPN, negotiated HTTP2 (%s)", ALPN_H2);
-    conn->negnpn = CURL_HTTP_VERSION_2;
-    return SSL_TLSEXT_ERR_OK;
-  }
-#endif
-
-  if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
-                           ALPN_HTTP_1_1_LENGTH)) {
-    infof(data, "NPN, negotiated HTTP1.1");
-    conn->negnpn = CURL_HTTP_VERSION_1_1;
-    return SSL_TLSEXT_ERR_OK;
-  }
-
-  infof(data, "NPN, no overlap, use HTTP1.1");
-  *out = (unsigned char *)ALPN_HTTP_1_1;
-  *outlen = ALPN_HTTP_1_1_LENGTH;
-  conn->negnpn = CURL_HTTP_VERSION_1_1;
+#  define HAS_ALPN 1
+#endif
 
-  return SSL_TLSEXT_ERR_OK;
-}
-#endif /* HAS_NPN */
+/* Check for OpenSSL 1.1.0 which has set_{min,max}_proto_version(). */
+#undef HAS_MODERN_SET_PROTO_VER
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
+    && !(defined(LIBRESSL_VERSION_NUMBER) && \
+      LIBRESSL_VERSION_NUMBER < 0x20600000L)
+#  define HAS_MODERN_SET_PROTO_VER 1
+#endif
 
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+#ifdef HAS_MODERN_SET_PROTO_VER
 static CURLcode
 set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
 {
@@ -2424,7 +2687,7 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
 
   return CURLE_OK;
 }
-#endif
+#endif /* HAS_MODERN_SET_PROTO_VER */
 
 #ifdef OPENSSL_IS_BORINGSSL
 typedef uint32_t ctx_option_t;
@@ -2434,7 +2697,7 @@ typedef uint64_t ctx_option_t;
 typedef long ctx_option_t;
 #endif
 
-#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */
+#if !defined(HAS_MODERN_SET_PROTO_VER)
 static CURLcode
 set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
                                struct Curl_easy *data,
@@ -2509,7 +2772,7 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
   }
   return CURLE_OK;
 }
-#endif
+#endif /* ! HAS_MODERN_SET_PROTO_VER */
 
 /* The "new session" callback must return zero if the session can be removed
  * or non-zero if the session has been put into the session cache.
@@ -2813,7 +3076,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
       ctx_options |= SSL_OP_NO_SSLv2;
       ctx_options |= SSL_OP_NO_SSLv3;
 
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+#if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */
       result = set_ssl_version_min_max(backend->ctx, conn);
 #else
       result = set_ssl_version_min_max_legacy(&ctx_options, data, conn,
@@ -2830,11 +3093,6 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
 
   SSL_CTX_set_options(backend->ctx, ctx_options);
 
-#ifdef HAS_NPN
-  if(conn->bits.tls_enable_npn)
-    SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, data);
-#endif
-
 #ifdef HAS_ALPN
   if(conn->bits.tls_enable_alpn) {
     int cur = 0;
@@ -2924,7 +3182,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
 
 #ifdef USE_OPENSSL_SRP
   if((ssl_authtype == CURL_TLSAUTH_SRP) &&
-     Curl_allow_auth_to_host(data)) {
+     Curl_auth_allowed_to_host(data)) {
     char * const ssl_username = SSL_SET_OPTION(primary.username);
     char * const ssl_password = SSL_SET_OPTION(primary.password);
     infof(data, "Using TLS-SRP username: %s", ssl_username);
@@ -3375,448 +3633,112 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data,
          the entry. */
       errdetail = ERR_get_error();
 
-      /* Extract which lib and reason */
-      lib = ERR_GET_LIB(errdetail);
-      reason = ERR_GET_REASON(errdetail);
-
-      if((lib == ERR_LIB_SSL) &&
-         ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
-          (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
-        result = CURLE_PEER_FAILED_VERIFICATION;
-
-        lerr = SSL_get_verify_result(backend->handle);
-        if(lerr != X509_V_OK) {
-          SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
-          msnprintf(error_buffer, sizeof(error_buffer),
-                    "SSL certificate problem: %s",
-                    X509_verify_cert_error_string(lerr));
-        }
-        else
-          /* strcpy() is fine here as long as the string fits within
-             error_buffer */
-          strcpy(error_buffer, "SSL certificate verification failed");
-      }
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
-    !defined(LIBRESSL_VERSION_NUMBER) && \
-    !defined(OPENSSL_IS_BORINGSSL))
-      /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
-         OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */
-      else if((lib == ERR_LIB_SSL) &&
-              (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
-          /* If client certificate is required, communicate the
-             error to client */
-          result = CURLE_SSL_CLIENTCERT;
-          ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
-      }
-#endif
-      else {
-        result = CURLE_SSL_CONNECT_ERROR;
-        ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
-      }
-
-      /* detail is already set to the SSL error above */
-
-      /* If we e.g. use SSLv2 request-method and the server doesn't like us
-       * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
-       * the SO_ERROR is also lost.
-       */
-      if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
-        const char * const hostname = SSL_HOST_NAME();
-        const long int port = SSL_HOST_PORT();
-        char extramsg[80]="";
-        int sockerr = SOCKERRNO;
-        if(sockerr && detail == SSL_ERROR_SYSCALL)
-          Curl_strerror(sockerr, extramsg, sizeof(extramsg));
-        failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ",
-              extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
-              hostname, port);
-        return result;
-      }
-
-      /* Could be a CERT problem */
-      failf(data, "%s", error_buffer);
-
-      return result;
-    }
-  }
-  else {
-    /* we connected fine, we're not waiting for anything else. */
-    connssl->connecting_state = ssl_connect_3;
-
-    /* Informational message */
-    infof(data, "SSL connection using %s / %s",
-          SSL_get_version(backend->handle),
-          SSL_get_cipher(backend->handle));
-
-#ifdef HAS_ALPN
-    /* Sets data and len to negotiated protocol, len is 0 if no protocol was
-     * negotiated
-     */
-    if(conn->bits.tls_enable_alpn) {
-      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)) {
-          conn->negnpn = 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)) {
-          conn->negnpn = CURL_HTTP_VERSION_1_1;
-        }
-      }
-      else
-        infof(data, VTLS_INFOF_NO_ALPN);
-
-      Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
-                          BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
-    }
-#endif
-
-    return CURLE_OK;
-  }
-}
-
-static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
-{
-  int i, ilen;
-
-  ilen = (int)len;
-  if(ilen < 0)
-    return 1; /* buffer too big */
-
-  i = i2t_ASN1_OBJECT(buf, ilen, a);
-
-  if(i >= ilen)
-    return 1; /* buffer too small */
-
-  return 0;
-}
-
-#define push_certinfo(_label, _num) \
-do {                              \
-  long info_len = BIO_get_mem_data(mem, &ptr); \
-  Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
-  if(1 != BIO_reset(mem))                                        \
-    break;                                                       \
-} while(0)
-
-static void pubkey_show(struct Curl_easy *data,
-                        BIO *mem,
-                        int num,
-                        const char *type,
-                        const char *name,
-                        const BIGNUM *bn)
-{
-  char *ptr;
-  char namebuf[32];
-
-  msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
-
-  if(bn)
-    BN_print(mem, bn);
-  push_certinfo(namebuf, num);
-}
-
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
-#define print_pubkey_BN(_type, _name, _num)              \
-  pubkey_show(data, mem, _num, #_type, #_name, _name)
-
-#else
-#define print_pubkey_BN(_type, _name, _num)    \
-do {                              \
-  if(_type->_name) { \
-    pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
-  } \
-} while(0)
-#endif
-
-static void X509V3_ext(struct Curl_easy *data,
-                      int certnum,
-                      CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
-{
-  int i;
-
-  if((int)sk_X509_EXTENSION_num(exts) <= 0)
-    /* no extensions, bail out */
-    return;
-
-  for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
-    ASN1_OBJECT *obj;
-    X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
-    BUF_MEM *biomem;
-    char namebuf[128];
-    BIO *bio_out = BIO_new(BIO_s_mem());
-
-    if(!bio_out)
-      return;
-
-    obj = X509_EXTENSION_get_object(ext);
-
-    asn1_object_dump(obj, namebuf, sizeof(namebuf));
-
-    if(!X509V3_EXT_print(bio_out, ext, 0, 0))
-      ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
-
-    BIO_get_mem_ptr(bio_out, &biomem);
-    Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
-                               biomem->length);
-    BIO_free(bio_out);
-  }
-}
-
-#ifdef OPENSSL_IS_BORINGSSL
-typedef size_t numcert_t;
-#else
-typedef int numcert_t;
-#endif
-
-static CURLcode get_cert_chain(struct Curl_easy *data,
-                               struct ssl_connect_data *connssl)
-{
-  CURLcode result;
-  STACK_OF(X509) *sk;
-  int i;
-  numcert_t numcerts;
-  BIO *mem;
-  struct ssl_backend_data *backend = connssl->backend;
-
-  DEBUGASSERT(backend);
-
-  sk = SSL_get_peer_cert_chain(backend->handle);
-  if(!sk) {
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  numcerts = sk_X509_num(sk);
-
-  result = Curl_ssl_init_certinfo(data, (int)numcerts);
-  if(result) {
-    return result;
-  }
-
-  mem = BIO_new(BIO_s_mem());
-  if(!mem) {
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  for(i = 0; i < (int)numcerts; i++) {
-    ASN1_INTEGER *num;
-    X509 *x = sk_X509_value(sk, i);
-    EVP_PKEY *pubkey = NULL;
-    int j;
-    char *ptr;
-    const ASN1_BIT_STRING *psig = NULL;
-
-    X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
-    push_certinfo("Subject", i);
-
-    X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
-    push_certinfo("Issuer", i);
-
-    BIO_printf(mem, "%lx", X509_get_version(x));
-    push_certinfo("Version", i);
-
-    num = X509_get_serialNumber(x);
-    if(num->type == V_ASN1_NEG_INTEGER)
-      BIO_puts(mem, "-");
-    for(j = 0; j < num->length; j++)
-      BIO_printf(mem, "%02x", num->data[j]);
-    push_certinfo("Serial Number", i);
-
-#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
-    {
-      const X509_ALGOR *sigalg = NULL;
-      X509_PUBKEY *xpubkey = NULL;
-      ASN1_OBJECT *pubkeyoid = NULL;
+      /* Extract which lib and reason */
+      lib = ERR_GET_LIB(errdetail);
+      reason = ERR_GET_REASON(errdetail);
 
-      X509_get0_signature(&psig, &sigalg, x);
-      if(sigalg) {
-        i2a_ASN1_OBJECT(mem, sigalg->algorithm);
-        push_certinfo("Signature Algorithm", i);
-      }
+      if((lib == ERR_LIB_SSL) &&
+         ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
+          (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+        result = CURLE_PEER_FAILED_VERIFICATION;
 
-      xpubkey = X509_get_X509_PUBKEY(x);
-      if(xpubkey) {
-        X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
-        if(pubkeyoid) {
-          i2a_ASN1_OBJECT(mem, pubkeyoid);
-          push_certinfo("Public Key Algorithm", i);
+        lerr = SSL_get_verify_result(backend->handle);
+        if(lerr != X509_V_OK) {
+          SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
+          msnprintf(error_buffer, sizeof(error_buffer),
+                    "SSL certificate problem: %s",
+                    X509_verify_cert_error_string(lerr));
         }
+        else
+          /* strcpy() is fine here as long as the string fits within
+             error_buffer */
+          strcpy(error_buffer, "SSL certificate verification failed");
+      }
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+    !defined(LIBRESSL_VERSION_NUMBER) && \
+    !defined(OPENSSL_IS_BORINGSSL))
+      /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
+         OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */
+      else if((lib == ERR_LIB_SSL) &&
+              (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
+          /* If client certificate is required, communicate the
+             error to client */
+          result = CURLE_SSL_CLIENTCERT;
+          ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
+      }
+#endif
+      else {
+        result = CURLE_SSL_CONNECT_ERROR;
+        ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
       }
 
-      X509V3_ext(data, i, X509_get0_extensions(x));
-    }
-#else
-    {
-      /* before OpenSSL 1.0.2 */
-      X509_CINF *cinf = x->cert_info;
-
-      i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
-      push_certinfo("Signature Algorithm", i);
+      /* detail is already set to the SSL error above */
 
-      i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
-      push_certinfo("Public Key Algorithm", i);
+      /* If we e.g. use SSLv2 request-method and the server doesn't like us
+       * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
+       * the SO_ERROR is also lost.
+       */
+      if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
+        const char * const hostname = SSL_HOST_NAME();
+        const long int port = SSL_HOST_PORT();
+        char extramsg[80]="";
+        int sockerr = SOCKERRNO;
+        if(sockerr && detail == SSL_ERROR_SYSCALL)
+          Curl_strerror(sockerr, extramsg, sizeof(extramsg));
+        failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ",
+              extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
+              hostname, port);
+        return result;
+      }
 
-      X509V3_ext(data, i, cinf->extensions);
+      /* Could be a CERT problem */
+      failf(data, "%s", error_buffer);
 
-      psig = x->signature;
+      return result;
     }
-#endif
-
-    ASN1_TIME_print(mem, X509_get0_notBefore(x));
-    push_certinfo("Start date", i);
+  }
+  else {
+    /* we connected fine, we're not waiting for anything else. */
+    connssl->connecting_state = ssl_connect_3;
 
-    ASN1_TIME_print(mem, X509_get0_notAfter(x));
-    push_certinfo("Expire date", i);
+    /* Informational message */
+    infof(data, "SSL connection using %s / %s",
+          SSL_get_version(backend->handle),
+          SSL_get_cipher(backend->handle));
 
-    pubkey = X509_get_pubkey(x);
-    if(!pubkey)
-      infof(data, "   Unable to load public key");
-    else {
-      int pktype;
-#ifdef HAVE_OPAQUE_EVP_PKEY
-      pktype = EVP_PKEY_id(pubkey);
-#else
-      pktype = pubkey->type;
-#endif
-      switch(pktype) {
-      case EVP_PKEY_RSA:
-      {
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
-        RSA *rsa;
-#ifdef HAVE_OPAQUE_EVP_PKEY
-        rsa = EVP_PKEY_get0_RSA(pubkey);
-#else
-        rsa = pubkey->pkey.rsa;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
+#ifdef HAS_ALPN
+    /* Sets data and len to negotiated protocol, len is 0 if no protocol was
+     * negotiated
+     */
+    if(conn->bits.tls_enable_alpn) {
+      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 HAVE_OPAQUE_RSA_DSA_DH
-          DECLARE_PKEY_PARAM_BIGNUM(n);
-          DECLARE_PKEY_PARAM_BIGNUM(e);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
-#else
-          RSA_get0_key(rsa, &n, &e, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
-          BIO_printf(mem, "%d", BN_num_bits(n));
-#else
-          BIO_printf(mem, "%d", BN_num_bits(rsa->n));
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
-          push_certinfo("RSA Public Key", i);
-          print_pubkey_BN(rsa, n, i);
-          print_pubkey_BN(rsa, e, i);
-          FREE_PKEY_PARAM_BIGNUM(n);
-          FREE_PKEY_PARAM_BIGNUM(e);
+#ifdef USE_HTTP2
+        if(len == ALPN_H2_LENGTH &&
+           !memcmp(ALPN_H2, neg_protocol, len)) {
+          conn->alpn = CURL_HTTP_VERSION_2;
         }
-
-        break;
-      }
-      case EVP_PKEY_DSA:
-      {
-#ifndef OPENSSL_NO_DSA
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
-        DSA *dsa;
-#ifdef HAVE_OPAQUE_EVP_PKEY
-        dsa = EVP_PKEY_get0_DSA(pubkey);
-#else
-        dsa = pubkey->pkey.dsa;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
-        {
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
-          DECLARE_PKEY_PARAM_BIGNUM(p);
-          DECLARE_PKEY_PARAM_BIGNUM(q);
-          DECLARE_PKEY_PARAM_BIGNUM(g);
-          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
-#else
-          DSA_get0_pqg(dsa, &p, &q, &g);
-          DSA_get0_key(dsa, &pub_key, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
-          print_pubkey_BN(dsa, p, i);
-          print_pubkey_BN(dsa, q, i);
-          print_pubkey_BN(dsa, g, i);
-          print_pubkey_BN(dsa, pub_key, i);
-          FREE_PKEY_PARAM_BIGNUM(p);
-          FREE_PKEY_PARAM_BIGNUM(q);
-          FREE_PKEY_PARAM_BIGNUM(g);
-          FREE_PKEY_PARAM_BIGNUM(pub_key);
+        else
+#endif
+        if(len == ALPN_HTTP_1_1_LENGTH &&
+           !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
+          conn->alpn = CURL_HTTP_VERSION_1_1;
         }
-#endif /* !OPENSSL_NO_DSA */
-        break;
-      }
-      case EVP_PKEY_DH:
-      {
-#ifndef HAVE_EVP_PKEY_GET_PARAMS
-        DH *dh;
-#ifdef HAVE_OPAQUE_EVP_PKEY
-        dh = EVP_PKEY_get0_DH(pubkey);
-#else
-        dh = pubkey->pkey.dh;
-#endif /* HAVE_OPAQUE_EVP_PKEY */
-#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
-        {
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
-          DECLARE_PKEY_PARAM_BIGNUM(p);
-          DECLARE_PKEY_PARAM_BIGNUM(q);
-          DECLARE_PKEY_PARAM_BIGNUM(g);
-          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
-#ifdef HAVE_EVP_PKEY_GET_PARAMS
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
-          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
-#else
-          DH_get0_pqg(dh, &p, &q, &g);
-          DH_get0_key(dh, &pub_key, NULL);
-#endif /* HAVE_EVP_PKEY_GET_PARAMS */
-          print_pubkey_BN(dh, p, i);
-          print_pubkey_BN(dh, q, i);
-          print_pubkey_BN(dh, g, i);
-#else
-          print_pubkey_BN(dh, p, i);
-          print_pubkey_BN(dh, g, i);
-#endif /* HAVE_OPAQUE_RSA_DSA_DH */
-          print_pubkey_BN(dh, pub_key, i);
-          FREE_PKEY_PARAM_BIGNUM(p);
-          FREE_PKEY_PARAM_BIGNUM(q);
-          FREE_PKEY_PARAM_BIGNUM(g);
-          FREE_PKEY_PARAM_BIGNUM(pub_key);
-       }
-        break;
       }
-      }
-      EVP_PKEY_free(pubkey);
-    }
+      else
+        infof(data, VTLS_INFOF_NO_ALPN);
 
-    if(psig) {
-      for(j = 0; j < psig->length; j++)
-        BIO_printf(mem, "%02x:", psig->data[j]);
-      push_certinfo("Signature", i);
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+                          BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
+#endif
 
-    PEM_write_bio_X509(mem, x);
-    push_certinfo("Cert", i);
+    return CURLE_OK;
   }
-
-  BIO_free(mem);
-
-  return CURLE_OK;
 }
 
 /*
@@ -3913,8 +3835,8 @@ static CURLcode servercert(struct Curl_easy *data,
   }
 
   if(data->set.ssl.certinfo)
-    /* we've been asked to gather certificate info! */
-    (void)get_cert_chain(data, connssl);
+    /* asked to gather certificate info */
+    (void)Curl_ossl_certchain(data, connssl->backend->handle);
 
   backend->server_cert = SSL_get1_peer_certificate(backend->handle);
   if(!backend->server_cert) {
@@ -4457,7 +4379,7 @@ static size_t ossl_version(char *buffer, size_t size)
   }
   count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver);
   for(p = buffer; *p; ++p) {
-    if(ISSPACE(*p))
+    if(ISBLANK(*p))
       *p = '_';
   }
   return count;
@@ -4469,7 +4391,13 @@ static size_t ossl_version(char *buffer, size_t size)
                    (LIBRESSL_VERSION_NUMBER>>12)&0xff);
 #endif
 #elif defined(OPENSSL_IS_BORINGSSL)
+#ifdef CURL_BORINGSSL_VERSION
+  return msnprintf(buffer, size, "%s/%s",
+                   OSSL_PACKAGE,
+                   CURL_BORINGSSL_VERSION);
+#else
   return msnprintf(buffer, size, OSSL_PACKAGE);
+#endif
 #elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING)
   return msnprintf(buffer, size, "%s/%s",
                    OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING));
index 0a7536e..9df4ecd 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
@@ -29,6 +31,7 @@
  * This header should only be needed to get included by vtls.c, openssl.c
  * and ngtcp2.c
  */
+#include <openssl/ssl.h>
 
 #include "urldata.h"
 
@@ -51,5 +54,7 @@ CURLcode Curl_ossl_set_client_cert(struct Curl_easy *data,
                                    const struct curl_blob *key_blob,
                                    const char *key_type, char *key_passwd);
 
+CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl);
+
 #endif /* USE_OPENSSL */
 #endif /* HEADER_CURL_SSLUSE_H */
index 16970b7..77a49f1 100644 (file)
@@ -19,6 +19,8 @@
  * 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"
 
@@ -413,20 +415,20 @@ cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
 #ifdef USE_HTTP2
   if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
     infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
-    conn->negnpn = CURL_HTTP_VERSION_2;
+    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);
-    conn->negnpn = CURL_HTTP_VERSION_1_1;
+    conn->alpn = CURL_HTTP_VERSION_1_1;
   }
   else {
     infof(data, "ALPN, negotiated an unrecognized protocol");
   }
 
-  Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+  Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                       BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
 }
 
index 056211d..6b393dd 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews,
+ * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
  * <github@hoffman-andrews.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -19,6 +19,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 #ifndef HEADER_CURL_RUSTLS_H
 #define HEADER_CURL_RUSTLS_H
index dfec66d..e022a2c 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -51,6 +53,7 @@
 #include "curl_printf.h"
 #include "multiif.h"
 #include "version_win32.h"
+#include "rand.h"
 
 /* The last #include file should be: */
 #include "curl_memory.h"
 #endif
 #endif
 
-#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)
-#define HAS_CLIENT_CERT_PATH
+#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
+#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_CCM
+#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
+#endif
+
+#ifndef BCRYPT_CHAIN_MODE_GCM
+#define BCRYPT_CHAIN_MODE_GCM L"ChainingModeGCM"
+#endif
+
+#ifndef BCRYPT_AES_ALGORITHM
+#define BCRYPT_AES_ALGORITHM L"AES"
+#endif
+
+#ifndef BCRYPT_SHA256_ALGORITHM
+#define BCRYPT_SHA256_ALGORITHM L"SHA256"
+#endif
+
+#ifndef BCRYPT_SHA384_ALGORITHM
+#define BCRYPT_SHA384_ALGORITHM L"SHA384"
+#endif
+
+/* Workaround broken compilers like MinGW.
+   Return the number of elements in a statically sized array.
+*/
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 #endif
 
 #ifdef HAS_CLIENT_CERT_PATH
 #define SP_PROT_TLS1_2_CLIENT           0x00000800
 #endif
 
+#ifndef SP_PROT_TLS1_3_CLIENT
+#define SP_PROT_TLS1_3_CLIENT           0x00002000
+#endif
+
 #ifndef SCH_USE_STRONG_CRYPTO
 #define SCH_USE_STRONG_CRYPTO           0x00400000
 #endif
 #define ALG_CLASS_DHASH ALG_CLASS_HASH
 #endif
 
+#ifndef PKCS12_NO_PERSIST_KEY
+#define PKCS12_NO_PERSIST_KEY 0x00008000
+#endif
+
 static Curl_recv schannel_recv;
 static Curl_send schannel_send;
 
@@ -171,7 +209,7 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
 }
 
 static CURLcode
-set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data,
+set_ssl_version_min_max(DWORD *enabled_protocols, struct Curl_easy *data,
                         struct connectdata *conn)
 {
   long ssl_version = SSL_CONN_CONFIG(version);
@@ -181,23 +219,51 @@ set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data,
   switch(ssl_version_max) {
   case CURL_SSLVERSION_MAX_NONE:
   case CURL_SSLVERSION_MAX_DEFAULT:
-    ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+
+#if 0 /* Disabled in CMake due to issue 24147 (curl issue 9431) */
+    /* Windows Server 2022 and newer (including Windows 11) support TLS 1.3
+       built-in. Previous builds of Windows 10 had broken TLS 1.3
+       implementations that could be enabled via registry.
+    */
+    if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+                                    VERSION_GREATER_THAN_EQUAL)) {
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+    }
+    else /* Windows 10 and older */
+#endif
+      ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
+
     break;
   }
+
   for(; i <= (ssl_version_max >> 16); ++i) {
     switch(i) {
     case CURL_SSLVERSION_TLSv1_0:
-      schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT;
+      (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_1:
-      schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT;
+      (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_2:
-      schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT;
+      (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT;
       break;
     case CURL_SSLVERSION_TLSv1_3:
+
+#if 0 /* Disabled in CMake due to issue 24147 (curl issue 9431) */
+      /* Windows Server 2022 and newer */
+      if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT,
+                                      VERSION_GREATER_THAN_EQUAL)) {
+        (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT;
+        break;
+      }
+      else { /* Windows 10 and older */
+        failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11");
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+#else
       failf(data, "schannel: TLS 1.3 is not yet supported");
       return CURLE_SSL_CONNECT_ERROR;
+#endif
     }
   }
   return CURLE_OK;
@@ -214,8 +280,12 @@ get_alg_id_by_name(char *name)
 {
   char tmp[LONGEST_ALG_ID] = { 0 };
   char *nameEnd = strchr(name, ':');
-  size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \
-    min(strlen(name), LONGEST_ALG_ID - 1);
+  size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
+
+  /* reject too-long alg names */
+  if(n > (LONGEST_ALG_ID - 1))
+    return 0;
+
   strncpy(tmp, name, n);
   tmp[n] = 0;
   CIPHEROPTION(CALG_MD2);
@@ -383,13 +453,13 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
   else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_USERS;
   else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"),
-                    store_name_len) == 0)
+                   store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY;
   else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"),
-                    store_name_len) == 0)
+                   store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY;
   else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"),
-                    store_name_len) == 0)
+                   store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE;
   else
     return CURLE_SSL_CERTPROBLEM;
@@ -419,49 +489,52 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
                                    int sockindex)
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  SCHANNEL_CRED schannel_cred;
-  ALG_ID algIds[NUM_CIPHERS];
+
+#ifdef HAS_CLIENT_CERT_PATH
   PCCERT_CONTEXT client_certs[1] = { NULL };
+  HCERTSTORE client_cert_store = NULL;
+#endif
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CURLcode result;
+
+  /* setup Schannel API options */
+  DWORD flags = 0;
+  DWORD enabled_protocols = 0;
+
   struct ssl_backend_data *backend = connssl->backend;
 
   DEBUGASSERT(backend);
 
-  /* setup Schannel API options */
-  memset(&schannel_cred, 0, sizeof(schannel_cred));
-  schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-
   if(conn->ssl_config.verifypeer) {
 #ifdef HAS_MANUAL_VERIFY_API
     if(backend->use_manual_cred_validation)
-      schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
+      flags = SCH_CRED_MANUAL_CRED_VALIDATION;
     else
 #endif
-      schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
+      flags = SCH_CRED_AUTO_CRED_VALIDATION;
 
     if(SSL_SET_OPTION(no_revoke)) {
-      schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+      flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
         SCH_CRED_IGNORE_REVOCATION_OFFLINE;
 
       DEBUGF(infof(data, "schannel: disabled server certificate revocation "
                    "checks"));
     }
     else if(SSL_SET_OPTION(revoke_best_effort)) {
-      schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+      flags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
         SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
 
       DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
     }
     else {
-      schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
+      flags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
 
       DEBUGF(infof(data,
                    "schannel: checking server certificate revocation"));
     }
   }
   else {
-    schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+    flags = SCH_CRED_MANUAL_CRED_VALIDATION |
       SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
       SCH_CRED_IGNORE_REVOCATION_OFFLINE;
     DEBUGF(infof(data,
@@ -469,15 +542,15 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
   }
 
   if(!conn->ssl_config.verifyhost) {
-    schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+    flags |= SCH_CRED_NO_SERVERNAME_CHECK;
     DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
                  "comparing the supplied target name with the subject "
                  "names in server certificates."));
   }
 
   if(!SSL_SET_OPTION(auto_client_cert)) {
-    schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS;
-    schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+    flags &= ~SCH_CRED_USE_DEFAULT_CREDS;
+    flags |= SCH_CRED_NO_DEFAULT_CREDS;
     infof(data, "schannel: disabled automatic use of client certificate");
   }
   else
@@ -491,7 +564,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
   case CURL_SSLVERSION_TLSv1_2:
   case CURL_SSLVERSION_TLSv1_3:
   {
-    result = set_ssl_version_min_max(&schannel_cred, data, conn);
+    result = set_ssl_version_min_max(&enabled_protocols, data, conn);
     if(result != CURLE_OK)
       return result;
     break;
@@ -505,16 +578,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(SSL_CONN_CONFIG(cipher_list)) {
-    result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
-                             algIds);
-    if(CURLE_OK != result) {
-      failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
-      return result;
-    }
-  }
-
-
 #ifdef HAS_CLIENT_CERT_PATH
   /* client certificate */
   if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
@@ -540,7 +603,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
         return CURLE_OUT_OF_MEMORY;
 
       result = get_cert_location(cert_path, &cert_store_name,
-        &cert_store_path, &cert_thumbprint_str);
+                                 &cert_store_path, &cert_thumbprint_str);
 
       if(result && (data->set.ssl.primary.clientcert[0]!='\0'))
         fInCert = fopen(data->set.ssl.primary.clientcert, "rb");
@@ -555,18 +618,18 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
     }
 
     if((fInCert || blob) && (data->set.ssl.cert_type) &&
-        (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
+       (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
       failf(data, "schannel: certificate format compatibility error "
-              " for %s",
-              blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
+            " for %s",
+            blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
       curlx_unicodefree(cert_path);
       return CURLE_SSL_CERTPROBLEM;
     }
 
     if(fInCert || blob) {
       /* Reading a .P12 or .pfx file, like the example at bottom of
-           https://social.msdn.microsoft.com/Forums/windowsdesktop/
-                          en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
+         https://social.msdn.microsoft.com/Forums/windowsdesktop/
+         en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
       */
       CRYPT_DATA_BLOB datablob;
       WCHAR* pszPassword;
@@ -594,7 +657,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
         fclose(fInCert);
         if(!continue_reading) {
           failf(data, "schannel: Failed to read cert file %s",
-              data->set.ssl.primary.clientcert);
+                data->set.ssl.primary.clientcert);
           free(certdata);
           return CURLE_SSL_CERTPROBLEM;
         }
@@ -610,16 +673,23 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
       if(pszPassword) {
         if(pwd_len > 0)
           str_w_len = MultiByteToWideChar(CP_UTF8,
-             MB_ERR_INVALID_CHARS,
-             data->set.ssl.key_passwd, (int)pwd_len,
-             pszPassword, (int)(pwd_len + 1));
+                                          MB_ERR_INVALID_CHARS,
+                                          data->set.ssl.key_passwd,
+                                          (int)pwd_len,
+                                          pszPassword, (int)(pwd_len + 1));
 
         if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
           pszPassword[str_w_len] = 0;
         else
           pszPassword[0] = 0;
 
-        cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+        if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+                                        VERSION_GREATER_THAN_EQUAL))
+          cert_store = PFXImportCertStore(&datablob, pszPassword,
+                                          PKCS12_NO_PERSIST_KEY);
+        else
+          cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+
         free(pszPassword);
       }
       if(!blob)
@@ -648,9 +718,6 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
         CertCloseStore(cert_store, 0);
         return CURLE_SSL_CERTPROBLEM;
       }
-
-      schannel_cred.cCreds = 1;
-      schannel_cred.paCred = client_certs;
     }
     else {
       cert_store =
@@ -688,17 +755,13 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
 
       curlx_unicodefree(cert_path);
 
-      if(client_certs[0]) {
-        schannel_cred.cCreds = 1;
-        schannel_cred.paCred = client_certs;
-      }
-      else {
+      if(!client_certs[0]) {
         /* CRYPT_E_NOT_FOUND / E_INVALIDARG */
         CertCloseStore(cert_store, 0);
         return CURLE_SSL_CERTPROBLEM;
       }
     }
-    CertCloseStore(cert_store, 0);
+    client_cert_store = cert_store;
   }
 #else
   if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
@@ -713,22 +776,279 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
   if(!backend->cred) {
     failf(data, "schannel: unable to allocate memory");
 
+#ifdef HAS_CLIENT_CERT_PATH
     if(client_certs[0])
       CertFreeCertificateContext(client_certs[0]);
+    if(client_cert_store)
+      CertCloseStore(client_cert_store, 0);
+#endif
 
     return CURLE_OUT_OF_MEMORY;
   }
   backend->cred->refcount = 1;
 
-  sspi_status =
-    s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
-                                       SECPKG_CRED_OUTBOUND, NULL,
-                                       &schannel_cred, NULL, NULL,
-                                       &backend->cred->cred_handle,
-                                       &backend->cred->time_stamp);
+#ifdef HAS_CLIENT_CERT_PATH
+  /* Since we did not persist the key, we need to extend the store's
+   * lifetime until the end of the connection
+   */
+  backend->cred->client_cert_store = client_cert_store;
+#endif
+
+  /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
+  if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
+                                  VERSION_GREATER_THAN_EQUAL)) {
+
+    char *ciphers13 = 0;
+
+    bool disable_aes_gcm_sha384 = FALSE;
+    bool disable_aes_gcm_sha256 = FALSE;
+    bool disable_chacha_poly = FALSE;
+    bool disable_aes_ccm_8_sha256 = FALSE;
+    bool disable_aes_ccm_sha256 = FALSE;
+
+    SCH_CREDENTIALS credentials = { 0 };
+    TLS_PARAMETERS tls_parameters = { 0 };
+    CRYPTO_SETTINGS crypto_settings[4] = { 0 };
+    UNICODE_STRING blocked_ccm_modes[1] = { 0 };
+    UNICODE_STRING blocked_gcm_modes[1] = { 0 };
 
+    int crypto_settings_idx = 0;
+
+
+    /* If TLS 1.3 ciphers are explicitly listed, then
+     * disable all the ciphers and re-enable which
+     * ciphers the user has provided.
+     */
+    ciphers13 = SSL_CONN_CONFIG(cipher_list13);
+    if(ciphers13) {
+      const int remaining_ciphers = 5;
+
+      /* detect which remaining ciphers to enable
+         and then disable everything else.
+      */
+
+      char *startCur = ciphers13;
+      int algCount = 0;
+      char tmp[LONGEST_ALG_ID] = { 0 };
+      char *nameEnd;
+      size_t n;
+
+      disable_aes_gcm_sha384 = TRUE;
+      disable_aes_gcm_sha256 = TRUE;
+      disable_chacha_poly = TRUE;
+      disable_aes_ccm_8_sha256 = TRUE;
+      disable_aes_ccm_sha256 = TRUE;
+
+      while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
+        nameEnd = strchr(startCur, ':');
+        n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
+
+        /* reject too-long cipher names */
+        if(n > (LONGEST_ALG_ID - 1)) {
+          failf(data, "Cipher name too long, not checked.");
+          return CURLE_SSL_CIPHER;
+        }
+
+        strncpy(tmp, startCur, n);
+        tmp[n] = 0;
+
+        if(disable_aes_gcm_sha384
+           && !strcmp("TLS_AES_256_GCM_SHA384", tmp)) {
+          disable_aes_gcm_sha384 = FALSE;
+        }
+        else if(disable_aes_gcm_sha256
+                && !strcmp("TLS_AES_128_GCM_SHA256", tmp)) {
+          disable_aes_gcm_sha256 = FALSE;
+        }
+        else if(disable_chacha_poly
+                && !strcmp("TLS_CHACHA20_POLY1305_SHA256", tmp)) {
+          disable_chacha_poly = FALSE;
+        }
+        else if(disable_aes_ccm_8_sha256
+                && !strcmp("TLS_AES_128_CCM_8_SHA256", tmp)) {
+          disable_aes_ccm_8_sha256 = FALSE;
+        }
+        else if(disable_aes_ccm_sha256
+                && !strcmp("TLS_AES_128_CCM_SHA256", tmp)) {
+          disable_aes_ccm_sha256 = FALSE;
+        }
+        else {
+          failf(data, "Passed in an unknown TLS 1.3 cipher.");
+          return CURLE_SSL_CIPHER;
+        }
+
+        startCur = nameEnd;
+        if(startCur)
+          startCur++;
+
+        algCount++;
+      }
+    }
+
+    if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
+       && disable_chacha_poly && disable_aes_ccm_8_sha256
+       && disable_aes_ccm_sha256) {
+      failf(data, "All available TLS 1.3 ciphers were disabled.");
+      return CURLE_SSL_CIPHER;
+    }
+
+    /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */
+    if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) {
+      /*
+        Disallow AES_CCM algorithm.
+      */
+      blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM);
+      blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM);
+      blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM;
+
+      crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+        TlsParametersCngAlgUsageCipher;
+      crypto_settings[crypto_settings_idx].rgstrChainingModes =
+        blocked_ccm_modes;
+      crypto_settings[crypto_settings_idx].cChainingModes =
+        ARRAYSIZE(blocked_ccm_modes);
+      crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+        sizeof(BCRYPT_AES_ALGORITHM);
+      crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+        sizeof(BCRYPT_AES_ALGORITHM);
+      crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+        (PWSTR)BCRYPT_AES_ALGORITHM;
+
+      /* only disabling one of the CCM modes */
+      if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) {
+        if(disable_aes_ccm_8_sha256)
+          crypto_settings[crypto_settings_idx].dwMinBitLength = 128;
+        else /* disable_aes_ccm_sha256 */
+          crypto_settings[crypto_settings_idx].dwMaxBitLength = 64;
+      }
+
+      crypto_settings_idx++;
+    }
+
+    /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */
+    if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) {
+
+      /*
+        Disallow AES_GCM algorithm
+      */
+      blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM);
+      blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM);
+      blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM;
+
+      /* if only one is disabled, then explicitly disable the
+         digest cipher suite (sha384 or sha256) */
+      if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) {
+        crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+          TlsParametersCngAlgUsageDigest;
+        crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+          sizeof(disable_aes_gcm_sha384 ?
+                 BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+        crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+          sizeof(disable_aes_gcm_sha384 ?
+                 BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+        crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+          (PWSTR)(disable_aes_gcm_sha384 ?
+                  BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
+      }
+      else { /* Disable both AES_GCM ciphers */
+        crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+          TlsParametersCngAlgUsageCipher;
+        crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+          sizeof(BCRYPT_AES_ALGORITHM);
+        crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+          sizeof(BCRYPT_AES_ALGORITHM);
+        crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+          (PWSTR)BCRYPT_AES_ALGORITHM;
+      }
+
+      crypto_settings[crypto_settings_idx].rgstrChainingModes =
+        blocked_gcm_modes;
+      crypto_settings[crypto_settings_idx].cChainingModes = 1;
+
+      crypto_settings_idx++;
+    }
+
+    /*
+      Disable ChaCha20-Poly1305.
+    */
+    if(disable_chacha_poly) {
+      crypto_settings[crypto_settings_idx].eAlgorithmUsage =
+        TlsParametersCngAlgUsageCipher;
+      crypto_settings[crypto_settings_idx].strCngAlgId.Length =
+        sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+      crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
+        sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
+      crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
+        (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM;
+      crypto_settings_idx++;
+    }
+
+    tls_parameters.pDisabledCrypto = crypto_settings;
+
+    /* The number of blocked suites */
+    tls_parameters.cDisabledCrypto = crypto_settings_idx;
+    credentials.pTlsParameters = &tls_parameters;
+    credentials.cTlsParameters = 1;
+
+    credentials.dwVersion = SCH_CREDENTIALS_VERSION;
+    credentials.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+
+    credentials.pTlsParameters->grbitDisabledProtocols =
+      (DWORD)~enabled_protocols;
+
+#ifdef HAS_CLIENT_CERT_PATH
+    if(client_certs[0]) {
+      credentials.cCreds = 1;
+      credentials.paCred = client_certs;
+    }
+#endif
+
+    sspi_status =
+      s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+                                         SECPKG_CRED_OUTBOUND, NULL,
+                                         &credentials, NULL, NULL,
+                                         &backend->cred->cred_handle,
+                                         &backend->cred->time_stamp);
+  }
+  else {
+    /* Pre-Windows 10 1809 */
+    ALG_ID algIds[NUM_CIPHERS];
+    char *ciphers = SSL_CONN_CONFIG(cipher_list);
+    SCHANNEL_CRED schannel_cred = { 0 };
+    schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+    schannel_cred.dwFlags = flags;
+    schannel_cred.grbitEnabledProtocols = enabled_protocols;
+
+    if(ciphers) {
+      result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
+      if(CURLE_OK != result) {
+        failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
+        return result;
+      }
+    }
+    else {
+      schannel_cred.dwFlags = flags | SCH_USE_STRONG_CRYPTO;
+    }
+
+#ifdef HAS_CLIENT_CERT_PATH
+    if(client_certs[0]) {
+      schannel_cred.cCreds = 1;
+      schannel_cred.paCred = client_certs;
+    }
+#endif
+
+    sspi_status =
+      s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
+                                         SECPKG_CRED_OUTBOUND, NULL,
+                                         &schannel_cred, NULL, NULL,
+                                         &backend->cred->cred_handle,
+                                         &backend->cred->time_stamp);
+  }
+
+#ifdef HAS_CLIENT_CERT_PATH
   if(client_certs[0])
     CertFreeCertificateContext(client_certs[0]);
+#endif
 
   if(sspi_status != SEC_E_OK) {
     char buffer[STRERROR_LEN];
@@ -1016,6 +1336,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   backend->recv_unrecoverable_err = CURLE_OK;
   backend->recv_sspi_close_notify = false;
   backend->recv_connection_closed = false;
+  backend->recv_renegotiating = false;
   backend->encdata_is_incomplete = false;
 
   /* continue to second handshake step */
@@ -1415,6 +1736,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 
     if(alpn_result.ProtoNegoStatus ==
        SecApplicationProtocolNegotiationStatus_Success) {
+      unsigned char alpn = 0;
 
       infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR,
             alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
@@ -1422,20 +1744,33 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 #ifdef USE_HTTP2
       if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH &&
          !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) {
-        conn->negnpn = CURL_HTTP_VERSION_2;
+        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)) {
-          conn->negnpn = CURL_HTTP_VERSION_1_1;
+          alpn = CURL_HTTP_VERSION_1_1;
         }
+      if(backend->recv_renegotiating) {
+        if(alpn != conn->alpn) {
+          failf(data, "schannel: server selected an ALPN protocol too late");
+          return CURLE_SSL_CONNECT_ERROR;
+        }
+      }
+      else
+        conn->alpn = alpn;
+    }
+    else {
+      if(!backend->recv_renegotiating)
+        infof(data, VTLS_INFOF_NO_ALPN);
+    }
+
+    if(!backend->recv_renegotiating) {
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
+                          BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
-    else
-      infof(data, VTLS_INFOF_NO_ALPN);
-    Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
-                        BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 #endif
 
@@ -1607,8 +1942,15 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn,
 
   if(ssl_connect_done == connssl->connecting_state) {
     connssl->state = ssl_connection_complete;
-    conn->recv[sockindex] = schannel_recv;
-    conn->send[sockindex] = schannel_send;
+    if(!connssl->backend->recv_renegotiating) {
+      /* On renegotiation, we don't want to reset the existing recv/send
+       * function pointers. They will have been set after the initial TLS
+       * handshake was completed. If they were subsequently modified, as
+       * is the case with HTTP/2, we don't want to override that change.
+       */
+      conn->recv[sockindex] = schannel_recv;
+      conn->send[sockindex] = schannel_send;
+    }
 
 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
     /* When SSPI is used in combination with Schannel
@@ -1990,17 +2332,14 @@ schannel_recv(struct Curl_easy *data, int sockindex,
           infof(data, "schannel: can't renegotiate, an error is pending");
           goto cleanup;
         }
-        if(backend->encdata_offset) {
-          *err = CURLE_RECV_ERROR;
-          infof(data, "schannel: can't renegotiate, "
-                "encrypted data available");
-          goto cleanup;
-        }
+
         /* begin renegotiation */
         infof(data, "schannel: renegotiating SSL/TLS connection");
         connssl->state = ssl_connection_negotiating;
         connssl->connecting_state = ssl_connect_2_writing;
+        backend->recv_renegotiating = true;
         *err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
+        backend->recv_renegotiating = false;
         if(*err) {
           infof(data, "schannel: renegotiation failed");
           goto cleanup;
@@ -2154,6 +2493,12 @@ static void schannel_session_free(void *ptr)
     if(cred->refcount == 0) {
       s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
       curlx_unicodefree(cred->sni_hostname);
+#ifdef HAS_CLIENT_CERT_PATH
+      if(cred->client_cert_store) {
+        CertCloseStore(cred->client_cert_store, 0);
+        cred->client_cert_store = NULL;
+      }
+#endif
       Curl_safefree(cred);
     }
   }
@@ -2296,21 +2641,9 @@ static size_t schannel_version(char *buffer, size_t size)
 static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM,
                                 unsigned char *entropy, size_t length)
 {
-  HCRYPTPROV hCryptProv = 0;
-
   (void)data;
 
-  if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
-                          CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
-    return CURLE_FAILED_INIT;
-
-  if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
-    CryptReleaseContext(hCryptProv, 0UL);
-    return CURLE_FAILED_INIT;
-  }
-
-  CryptReleaseContext(hCryptProv, 0UL);
-  return CURLE_OK;
+  return Curl_win32_random(entropy, length);
 }
 
 static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
@@ -2457,7 +2790,8 @@ const struct Curl_ssl Curl_ssl_schannel = {
 #ifdef HAS_MANUAL_VERIFY_API
   SSLSUPP_CAINFO_BLOB |
 #endif
-  SSLSUPP_PINNEDPUBKEY,
+  SSLSUPP_PINNEDPUBKEY |
+  SSLSUPP_TLS13_CIPHERSUITES,
 
   sizeof(struct ssl_backend_data),
 
index da60702..24d7eff 100644 (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_SCHANNEL
 
+#define SCHANNEL_USE_BLACKLISTS 1
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4201)
+#endif
+#include <subauth.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
 #include <schnlsp.h>
 #include <schannel.h>
 #include "curl_sspi.h"
@@ -59,22 +83,87 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
 /* structs to expose only in schannel.c and schannel_verify.c */
 #ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
 
+#include <wincrypt.h>
+
 #ifdef __MINGW32__
-#include <_mingw.h>
 #ifdef __MINGW64_VERSION_MAJOR
 #define HAS_MANUAL_VERIFY_API
 #endif
 #else
-#include <wincrypt.h>
 #ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
 #define HAS_MANUAL_VERIFY_API
 #endif
 #endif
 
+#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)   \
+  && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
+#define HAS_CLIENT_CERT_PATH
+#endif
+
+#ifndef SCH_CREDENTIALS_VERSION
+
+#define SCH_CREDENTIALS_VERSION  0x00000005
+
+typedef enum _eTlsAlgorithmUsage
+{
+    TlsParametersCngAlgUsageKeyExchange,
+    TlsParametersCngAlgUsageSignature,
+    TlsParametersCngAlgUsageCipher,
+    TlsParametersCngAlgUsageDigest,
+    TlsParametersCngAlgUsageCertSig
+} eTlsAlgorithmUsage;
+
+typedef struct _CRYPTO_SETTINGS
+{
+    eTlsAlgorithmUsage  eAlgorithmUsage;
+    UNICODE_STRING      strCngAlgId;
+    DWORD               cChainingModes;
+    PUNICODE_STRING     rgstrChainingModes;
+    DWORD               dwMinBitLength;
+    DWORD               dwMaxBitLength;
+} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
+
+typedef struct _TLS_PARAMETERS
+{
+    DWORD               cAlpnIds;
+    PUNICODE_STRING     rgstrAlpnIds;
+    DWORD               grbitDisabledProtocols;
+    DWORD               cDisabledCrypto;
+    PCRYPTO_SETTINGS    pDisabledCrypto;
+    DWORD               dwFlags;
+} TLS_PARAMETERS, * PTLS_PARAMETERS;
+
+typedef struct _SCH_CREDENTIALS
+{
+    DWORD               dwVersion;
+    DWORD               dwCredFormat;
+    DWORD               cCreds;
+    PCCERT_CONTEXT* paCred;
+    HCERTSTORE          hRootStore;
+
+    DWORD               cMappers;
+    struct _HMAPPER **aphMappers;
+
+    DWORD               dwSessionLifespan;
+    DWORD               dwFlags;
+    DWORD               cTlsParameters;
+    PTLS_PARAMETERS     pTlsParameters;
+} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
+
+#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
+#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
+#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
+#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
+
+#endif
+
 struct Curl_schannel_cred {
   CredHandle cred_handle;
   TimeStamp time_stamp;
   TCHAR *sni_hostname;
+#ifdef HAS_CLIENT_CERT_PATH
+  HCERTSTORE client_cert_store;
+#endif
   int refcount;
 };
 
@@ -99,6 +188,7 @@ struct ssl_backend_data {
   CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
   bool recv_sspi_close_notify; /* true if connection closed by close_notify */
   bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool recv_renegotiating;     /* true if recv is doing renegotiation */
   bool use_alpn; /* true if ALPN is used for this connection */
 #ifdef HAS_MANUAL_VERIFY_API
   bool use_manual_cred_validation; /* true if manual cred validation is used */
index 4dc2d14..1ac1d3e 100644 (file)
@@ -20,6 +20,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
index 2e57d83..c764e36 100644 (file)
@@ -19,6 +19,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -2845,18 +2847,18 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn,
 #ifdef USE_HTTP2
         if(chosenProtocol &&
            !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) {
-          conn->negnpn = CURL_HTTP_VERSION_2;
+          conn->alpn = CURL_HTTP_VERSION_2;
         }
         else
 #endif
         if(chosenProtocol &&
            !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) {
-          conn->negnpn = CURL_HTTP_VERSION_1_1;
+          conn->alpn = CURL_HTTP_VERSION_1_1;
         }
         else
           infof(data, VTLS_INFOF_NO_ALPN);
 
-        Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+        Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                             BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
 
         /* chosenProtocol is a reference to the string within alpnArr
@@ -2964,7 +2966,7 @@ collect_server_cert(struct Curl_easy *data,
      private API and doesn't work as expected. So we have to look for
      a different symbol to make sure this code is only executed under
      Lion or later. */
-  if(SecTrustEvaluateAsync) {
+  if(SecTrustCopyPublicKey) {
 #pragma unused(server_certs)
     err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
     /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
index 0febd66..2d53b7c 100644 (file)
@@ -8,7 +8,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
- * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 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
@@ -21,6 +21,8 @@
  * 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"
 
index e2d3438..9dee5aa 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /* This file is for implementing all "generic" SSL functions that all libcurl
@@ -143,11 +145,9 @@ Curl_ssl_config_matches(struct ssl_primary_config *data,
      Curl_safecmp(data->CAfile, needle->CAfile) &&
      Curl_safecmp(data->issuercert, needle->issuercert) &&
      Curl_safecmp(data->clientcert, needle->clientcert) &&
-     Curl_safecmp(data->random_file, needle->random_file) &&
-     Curl_safecmp(data->egdsocket, needle->egdsocket) &&
 #ifdef USE_TLS_SRP
-     Curl_safecmp(data->username, needle->username) &&
-     Curl_safecmp(data->password, needle->password) &&
+     !Curl_timestrcmp(data->username, needle->username) &&
+     !Curl_timestrcmp(data->password, needle->password) &&
      (data->authtype == needle->authtype) &&
 #endif
      Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) &&
@@ -182,8 +182,6 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
   CLONE_STRING(CAfile);
   CLONE_STRING(issuercert);
   CLONE_STRING(clientcert);
-  CLONE_STRING(random_file);
-  CLONE_STRING(egdsocket);
   CLONE_STRING(cipher_list);
   CLONE_STRING(cipher_list13);
   CLONE_STRING(pinned_key);
@@ -203,8 +201,6 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
   Curl_safefree(sslc->CAfile);
   Curl_safefree(sslc->issuercert);
   Curl_safefree(sslc->clientcert);
-  Curl_safefree(sslc->random_file);
-  Curl_safefree(sslc->egdsocket);
   Curl_safefree(sslc->cipher_list);
   Curl_safefree(sslc->cipher_list13);
   Curl_safefree(sslc->pinned_key);
@@ -223,13 +219,13 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
 static int multissl_setup(const struct Curl_ssl *backend);
 #endif
 
-int Curl_ssl_backend(void)
+curl_sslbackend Curl_ssl_backend(void)
 {
 #ifdef USE_SSL
   multissl_setup(NULL);
   return Curl_ssl->info.id;
 #else
-  return (int)CURLSSLBACKEND_NONE;
+  return CURLSSLBACKEND_NONE;
 #endif
 }
 
@@ -903,7 +899,7 @@ char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen)
   size_t len = strlen(host);
   if(len && (host[len-1] == '.'))
     len--;
-  if((long)len >= data->set.buffer_size)
+  if(len >= data->set.buffer_size)
     return NULL;
 
   Curl_strntolower(data->state.buffer, host, len);
@@ -1460,8 +1456,10 @@ static int multissl_setup(const struct Curl_ssl *backend)
   return 0;
 }
 
-CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
-                              const curl_ssl_backend ***avail)
+/* This function is used to select the SSL backend to use. It is called by
+   curl_global_sslset (easy.c) which uses the global init lock. */
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+                                   const curl_ssl_backend ***avail)
 {
   int i;
 
@@ -1490,8 +1488,8 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
 }
 
 #else /* USE_SSL */
-CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
-                              const curl_ssl_backend ***avail)
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+                                   const curl_ssl_backend ***avail)
 {
   (void)id;
   (void)name;
index 6bd1e0d..50c53b3 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
 
@@ -123,6 +125,9 @@ struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
 bool Curl_none_false_start(void);
 bool Curl_ssl_tls13_ciphersuites(void);
 
+CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
+                                   const curl_ssl_backend ***avail);
+
 #include "openssl.h"        /* OpenSSL versions */
 #include "gtls.h"           /* GnuTLS versions */
 #include "nssg.h"           /* NSS versions */
@@ -195,7 +200,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
    ssl_connect_2_writing. */
 int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks);
 
-int Curl_ssl_backend(void);
+curl_sslbackend Curl_ssl_backend(void);
 
 #ifdef USE_SSL
 int Curl_ssl_init(void);
index da8cb82..594c39a 100644 (file)
@@ -18,6 +18,8 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
+ * SPDX-License-Identifier: curl
+ *
  ***************************************************************************/
 
 /*
@@ -503,7 +505,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
     SSL_free(backend->handle);
   backend->handle = SSL_new(backend->ctx);
   if(!backend->handle) {
-    failf(data, "SSL: couldn't create a context");
+    failf(data, "SSL: couldn't create a handle");
     return CURLE_OUT_OF_MEMORY;
   }
 
@@ -761,17 +763,17 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn,
 
       if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
          !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
-        conn->negnpn = CURL_HTTP_VERSION_1_1;
+        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))
-        conn->negnpn = CURL_HTTP_VERSION_2;
+        conn->alpn = CURL_HTTP_VERSION_2;
 #endif
       else
         infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
               protocol);
-      Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
+      Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
     else if(rc == SSL_ALPN_NOT_FOUND)
@@ -809,8 +811,10 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 
   if(SSL_SET_OPTION(primary.sessionid)) {
     bool incache;
+    bool added = FALSE;
     void *old_ssl_sessionid = NULL;
-    SSL_SESSION *our_ssl_sessionid = SSL_get_session(backend->handle);
+    /* SSL_get1_session allocates memory that has to be freed. */
+    SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle);
     bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
 
     if(our_ssl_sessionid) {
@@ -830,11 +834,20 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn,
                                        0, sockindex, NULL);
         if(result) {
           Curl_ssl_sessionid_unlock(data);
+          SSL_SESSION_free(our_ssl_sessionid);
           failf(data, "failed to store ssl session");
           return result;
         }
+        else {
+          added = TRUE;
+        }
       }
       Curl_ssl_sessionid_unlock(data);
+
+      if(!added) {
+        /* If the session info wasn't added to the cache, free our copy. */
+        SSL_SESSION_free(our_ssl_sessionid);
+      }
     }
   }
 
@@ -954,8 +967,7 @@ static ssize_t wolfssl_recv(struct Curl_easy *data,
 
 static void wolfssl_session_free(void *ptr)
 {
-  (void)ptr;
-  /* wolfSSL reuses sessions on own, no free */
+  SSL_SESSION_free(ptr);
 }
 
 
index d411e69..b2e7c3f 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -20,6 +20,8 @@
  * 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"
 
index dfb9386..0cfcbe8 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -43,6 +45,7 @@
 #include <curl/curl.h>
 #include "urldata.h"
 #include "strcase.h"
+#include "curl_ctype.h"
 #include "hostcheck.h"
 #include "vtls/vtls.h"
 #include "sendf.h"
@@ -714,7 +717,7 @@ static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
       /* Encode delimiter.
          If attribute has a short uppercase name, delimiter is ", ". */
       if(l) {
-        for(p3 = str; isupper(*p3); p3++)
+        for(p3 = str; ISUPPER(*p3); p3++)
           ;
         for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) {
           if(l < buflen)
@@ -956,7 +959,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
       infof(data, "   ECC Public Key (%lu bits)", len);
     if(data->set.ssl.certinfo) {
       char q[sizeof(len) * 8 / 3 + 1];
-      msnprintf(q, sizeof(q), "%lu", len);
+      (void)msnprintf(q, sizeof(q), "%lu", len);
       if(Curl_ssl_push_certinfo(data, certnum, "ECC Public Key", q))
         return 1;
     }
index db7df0e..a18fa11 100644 (file)
@@ -21,6 +21,8 @@
  * 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"
index 0336a41..b00d7a5 100644 (file)
@@ -18,6 +18,8 @@
  * 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"
@@ -37,6 +39,8 @@
 
 #include "warnless.h"
 
+#include <limits.h>
+
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
 #define CURL_MASK_SCHAR   (CURL_MASK_UCHAR >> 1)
 
index 37ac5ba..4367099 100644 (file)
@@ -20,6 +20,8 @@
  * 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"
index 105bcce..a3e24b6 100644 (file)
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
@@ -18,6 +18,8 @@
  * 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"
index 081be9e..21e933b 100644 (file)
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2010 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * 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
  * 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 */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
new file mode 100644 (file)
index 0000000..a673446
--- /dev/null
@@ -0,0 +1,757 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+#include <curl/curl.h>
+
+#ifdef USE_WEBSOCKETS
+
+#include "urldata.h"
+#include "dynbuf.h"
+#include "rand.h"
+#include "curl_base64.h"
+#include "sendf.h"
+#include "multiif.h"
+#include "ws.h"
+#include "easyif.h"
+#include "transfer.h"
+#include "nonblock.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+struct wsfield {
+  const char *name;
+  const char *val;
+};
+
+CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
+{
+  unsigned int i;
+  CURLcode result = CURLE_OK;
+  unsigned char rand[16];
+  char *randstr;
+  size_t randlen;
+  char keyval[40];
+  struct SingleRequest *k = &data->req;
+  struct wsfield heads[]= {
+    {
+      /* The request MUST contain an |Upgrade| header field whose value
+         MUST include the "websocket" keyword. */
+      "Upgrade:", "websocket"
+    },
+    {
+      /* The request MUST contain a |Connection| header field whose value
+         MUST include the "Upgrade" token. */
+      "Connection:", "Upgrade",
+    },
+    {
+      /* The request MUST include a header field with the name
+         |Sec-WebSocket-Version|. The value of this header field MUST be
+         13. */
+      "Sec-WebSocket-Version:", "13",
+    },
+    {
+      /* The request MUST include a header field with the name
+         |Sec-WebSocket-Key|. The value of this header field MUST be a nonce
+         consisting of a randomly selected 16-byte value that has been
+         base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
+         selected randomly for each connection. */
+      "Sec-WebSocket-Key:", NULL,
+    }
+  };
+  heads[3].val = &keyval[0];
+
+  /* 16 bytes random */
+  result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
+  if(result)
+    return result;
+  result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
+  if(result)
+    return result;
+  DEBUGASSERT(randlen < sizeof(keyval));
+  if(randlen >= sizeof(keyval))
+    return CURLE_FAILED_INIT;
+  strcpy(keyval, randstr);
+  free(randstr);
+  for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
+    if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
+#ifdef USE_HYPER
+      char field[128];
+      msnprintf(field, sizeof(field), "%s %s", heads[i].name,
+                heads[i].val);
+      result = Curl_hyper_header(data, req, field);
+#else
+      (void)data;
+      result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
+                             heads[i].val);
+#endif
+    }
+  }
+  k->upgr101 = UPGR101_WS;
+  Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2);
+  return result;
+}
+
+CURLcode Curl_ws_accept(struct Curl_easy *data)
+{
+  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;
+  CURLcode result;
+
+  /* Verify the Sec-WebSocket-Accept response.
+
+     The sent value is the base64 encoded version of a SHA-1 hash done on the
+     |Sec-WebSocket-Key| header field concatenated with
+     the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
+  */
+
+  /* If the response includes a |Sec-WebSocket-Extensions| header field and
+     this header field indicates the use of an extension that was not present
+     in the client's handshake (the server has indicated an extension not
+     requested by the client), the client MUST Fail the WebSocket Connection.
+  */
+
+  /* If the response includes a |Sec-WebSocket-Protocol| header field
+     and this header field indicates the use of a subprotocol that was
+     not present in the client's handshake (the server has indicated a
+     subprotocol not requested by the client), the client MUST Fail
+     the WebSocket Connection. */
+
+  /* 4 bytes random */
+  result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
+  if(result)
+    return result;
+
+  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]);
+  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;
+}
+
+#define WSBIT_FIN 0x80
+#define WSBIT_OPCODE_CONT  0
+#define WSBIT_OPCODE_TEXT  (1)
+#define WSBIT_OPCODE_BIN   (2)
+#define WSBIT_OPCODE_CLOSE (8)
+#define WSBIT_OPCODE_PING  (9)
+#define WSBIT_OPCODE_PONG  (0xa)
+#define WSBIT_OPCODE_MASK  (0xf)
+
+#define WSBIT_MASK 0x80
+
+/* remove the spent bytes from the beginning of the buffer as that part has
+   now been delivered to the application */
+static void ws_decode_clear(struct Curl_easy *data)
+{
+  struct websocket *wsp = &data->req.p.http->ws;
+  size_t spent = wsp->usedbuf;
+  size_t len = Curl_dyn_len(&wsp->buf);
+  size_t keep = len - spent;
+  DEBUGASSERT(len >= spent);
+  Curl_dyn_tail(&wsp->buf, keep);
+}
+
+/* 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
+   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
+   stores the first part in the ->extra buffer to be used in the next call
+   when more data is provided.
+*/
+
+static CURLcode ws_decode(struct Curl_easy *data,
+                          unsigned char *wpkt, size_t ilen,
+                          unsigned char **out, size_t *olen,
+                          curl_off_t *oleft,
+                          bool *more,
+                          unsigned int *flags)
+{
+  bool fin;
+  unsigned char opcode;
+  curl_off_t total;
+  size_t dataindex = 2;
+  curl_off_t plen; /* size of data in the buffer */
+  curl_off_t payloadsize;
+  struct websocket *wsp = &data->req.p.http->ws;
+  unsigned char *p;
+  CURLcode result;
+
+  *olen = 0;
+
+  /* add the incoming bytes, if any */
+  if(wpkt) {
+    result = Curl_dyn_addn(&wsp->buf, wpkt, ilen);
+    if(result)
+      return result;
+  }
+
+  plen = Curl_dyn_len(&wsp->buf);
+  if(plen < 2) {
+    /* the smallest possible frame is two bytes */
+    infof(data, "WS: plen == %u, EAGAIN", (int)plen);
+    return CURLE_AGAIN;
+  }
+
+  p = Curl_dyn_uptr(&wsp->buf);
+
+  fin = p[0] & WSBIT_FIN;
+  opcode = p[0] & WSBIT_OPCODE_MASK;
+  infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
+  *flags = 0;
+  switch(opcode) {
+  case WSBIT_OPCODE_CONT:
+    if(!fin)
+      *flags |= CURLWS_CONT;
+    infof(data, "WS: received OPCODE CONT");
+    break;
+  case WSBIT_OPCODE_TEXT:
+    infof(data, "WS: received OPCODE TEXT");
+    *flags |= CURLWS_TEXT;
+    break;
+  case WSBIT_OPCODE_BIN:
+    infof(data, "WS: received OPCODE BINARY");
+    *flags |= CURLWS_BINARY;
+    break;
+  case WSBIT_OPCODE_CLOSE:
+    infof(data, "WS: received OPCODE CLOSE");
+    *flags |= CURLWS_CLOSE;
+    break;
+  case WSBIT_OPCODE_PING:
+    infof(data, "WS: received OPCODE PING");
+    *flags |= CURLWS_PING;
+    break;
+  case WSBIT_OPCODE_PONG:
+    infof(data, "WS: received OPCODE PONG");
+    *flags |= CURLWS_PONG;
+    break;
+  }
+
+  if(p[1] & WSBIT_MASK) {
+    /* A client MUST close a connection if it detects a masked frame. */
+    failf(data, "WS: masked input frame");
+    return CURLE_RECV_ERROR;
+  }
+  payloadsize = p[1];
+  if(payloadsize == 126) {
+    if(plen < 4) {
+      infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen);
+      return CURLE_AGAIN; /* not enough data available */
+    }
+    payloadsize = (p[2] << 8) | p[3];
+    dataindex += 2;
+  }
+  else if(payloadsize == 127) {
+    /* 64 bit payload size */
+    if(plen < 10)
+      return CURLE_AGAIN;
+    if(p[2] & 80) {
+      failf(data, "WS: too large frame");
+      return CURLE_RECV_ERROR;
+    }
+    dataindex += 8;
+    payloadsize = ((curl_off_t)p[2] << 56) |
+      (curl_off_t)p[3] << 48 |
+      (curl_off_t)p[4] << 40 |
+      (curl_off_t)p[5] << 32 |
+      (curl_off_t)p[6] << 24 |
+      (curl_off_t)p[7] << 16 |
+      (curl_off_t)p[8] << 8 |
+      p[9];
+  }
+
+  total = dataindex + payloadsize;
+  if(total > plen) {
+    /* deliver a partial frame */
+    *oleft = total - dataindex;
+    payloadsize = total - dataindex;
+  }
+  else {
+    *oleft = 0;
+    if(plen > total)
+      /* there is another fragment after */
+      *more = TRUE;
+  }
+
+  /* point to the payload */
+  *out = &p[dataindex];
+
+  /* return the payload length */
+  *olen = payloadsize;
+
+  /* number of bytes "used" from the buffer */
+  wsp->usedbuf = dataindex + payloadsize;
+  infof(data, "WS: received %zu bytes payload (%zu left)",
+        payloadsize, *oleft);
+  return CURLE_OK;
+}
+
+/* Curl_ws_writecb() is the write callback for websocket traffic. The
+   websocket data is provided to this raw, in chunks. This function should
+   handle/decode the data and call the "real" underlying callback accordingly.
+*/
+size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
+                       size_t nitems, void *userp)
+{
+  struct HTTP *ws = (struct HTTP *)userp;
+  struct Curl_easy *data = ws->ws.data;
+  void *writebody_ptr = data->set.out;
+  if(data->set.ws_raw_mode)
+    return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
+  else if(nitems) {
+    unsigned char *frame = NULL;
+    size_t flen = 0;
+    size_t wrote = 0;
+    CURLcode result;
+    bool more; /* there's is more to parse in the buffer */
+    curl_off_t oleft;
+
+    decode:
+    more = FALSE;
+    oleft = ws->ws.frame.bytesleft;
+    if(!oleft) {
+      unsigned int recvflags;
+      result = ws_decode(data, (unsigned char *)buffer, nitems,
+                         &frame, &flen, &oleft, &more, &recvflags);
+      if(result == CURLE_AGAIN)
+        /* insufficient amount of data, keep it for later */
+        return nitems;
+      else if(result) {
+        infof(data, "WS: decode error %d", (int)result);
+        return nitems - 1;
+      }
+      /* Store details about the frame to be reachable with curl_ws_meta()
+         from within the write callback */
+      ws->ws.frame.age = 0;
+      ws->ws.frame.offset = 0;
+      ws->ws.frame.flags = recvflags;
+      ws->ws.frame.bytesleft = oleft;
+    }
+    else {
+      if(nitems > (size_t)ws->ws.frame.bytesleft) {
+        nitems = ws->ws.frame.bytesleft;
+        more = TRUE;
+      }
+      else
+        more = FALSE;
+      ws->ws.frame.offset += nitems;
+      ws->ws.frame.bytesleft -= nitems;
+      frame = (unsigned char *)buffer;
+      flen = nitems;
+    }
+    if((ws->ws.frame.flags & CURLWS_PING) && !oleft) {
+      /* auto-respond to PINGs, only works for single-frame payloads atm */
+      size_t bytes;
+      infof(data, "WS: auto-respond to PING with a PONG");
+      DEBUGASSERT(frame);
+      /* send back the exact same content as a PONG */
+      result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG);
+      if(result)
+        return result;
+    }
+    else {
+      /* deliver the decoded frame to the user callback */
+      Curl_set_in_callback(data, true);
+      wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr);
+      Curl_set_in_callback(data, false);
+      if(wrote != flen)
+        return 0;
+    }
+    if(oleft)
+      ws->ws.frame.offset += flen;
+    /* the websocket frame has been delivered */
+    ws_decode_clear(data);
+    if(more) {
+      /* there's more websocket data to deal with in the buffer */
+      buffer = NULL; /* the buffer as been drained already */
+      goto decode;
+    }
+  }
+  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)
+{
+  size_t bytes;
+  CURLcode result;
+  struct websocket *wsp = &data->req.p.http->ws;
+
+  *nread = 0;
+  *metap = NULL;
+  /* get a download buffer */
+  result = Curl_preconnect(data);
+  if(result)
+    return result;
+
+  do {
+    bool drain = FALSE; /* if there is pending buffered data to drain */
+    char *inbuf = data->state.buffer;
+    bytes = wsp->stillbuffer;
+    if(!bytes) {
+      result = curl_easy_recv(data, data->state.buffer,
+                              data->set.buffer_size, &bytes);
+      if(result)
+        return result;
+    }
+    else {
+      /* the pending bytes can be found here */
+      inbuf = wsp->stillb;
+      drain = TRUE;
+    }
+    if(bytes) {
+      unsigned char *out;
+      size_t olen;
+      bool more;
+      unsigned int recvflags;
+      curl_off_t oleft = wsp->frame.bytesleft;
+
+      infof(data, "WS: got %u websocket bytes to decode", (int)bytes);
+      if(!oleft && !drain) {
+        result = ws_decode(data, (unsigned char *)inbuf, bytes,
+                           &out, &olen, &oleft, &more, &recvflags);
+        if(result == CURLE_AGAIN)
+          /* a packet fragment only */
+          break;
+        else if(result)
+          return result;
+        wsp->frame.offset = 0;
+        wsp->frame.bytesleft = oleft;
+        wsp->frame.flags = recvflags;
+      }
+      else {
+        olen = oleft;
+        out = (unsigned char *)wsp->stillb;
+        recvflags = wsp->frame.flags;
+        if((curl_off_t)buflen < oleft)
+          /* there is still data left after this */
+          wsp->frame.bytesleft -= buflen;
+        else
+          wsp->frame.bytesleft = 0;
+      }
+
+      /* auto-respond to PINGs */
+      if((recvflags & CURLWS_PING) && !oleft) {
+        infof(data, "WS: auto-respond to PING with a PONG");
+        /* send back the exact same content as a PONG */
+        result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG);
+        if(result)
+          return result;
+      }
+      else {
+        if(olen < buflen) {
+          /* copy the payload to the user buffer */
+          memcpy(buffer, out, olen);
+          *nread = olen;
+          if(!oleft)
+            /*  websocket frame has been delivered */
+            ws_decode_clear(data);
+        }
+        else {
+          /* copy a partial payload */
+          memcpy(buffer, out, buflen);
+          *nread = buflen;
+          /* remember what is left and where */
+          wsp->stillbuffer = olen - buflen;
+          wsp->stillb = (char *)buffer + buflen;
+        }
+        wsp->frame.offset += *nread;
+      }
+    }
+    else
+      *nread = bytes;
+    break;
+  } while(1);
+  *metap = &wsp->frame;
+  return CURLE_OK;
+}
+
+static void ws_xor(struct Curl_easy *data,
+                   const unsigned char *source,
+                   unsigned char *dest,
+                   size_t len)
+{
+  struct websocket *wsp = &data->req.p.http->ws;
+  size_t i;
+  /* append payload after the mask, XOR appropriately */
+  for(i = 0; i < len; i++) {
+    dest[i] = source[i] ^ wsp->mask[wsp->xori];
+    wsp->xori++;
+    wsp->xori &= 3;
+  }
+}
+
+/***
+    RFC 6455 Section 5.2
+
+      0                   1                   2                   3
+      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+     +-+-+-+-+-------+-+-------------+-------------------------------+
+     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
+     | |1|2|3|       |K|             |                               |
+     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+     |     Extended payload length continued, if payload len == 127  |
+     + - - - - - - - - - - - - - - - +-------------------------------+
+     |                               |Masking-key, if MASK set to 1  |
+     +-------------------------------+-------------------------------+
+     | Masking-key (continued)       |          Payload Data         |
+     +-------------------------------- - - - - - - - - - - - - - - - +
+     :                     Payload Data continued ...                :
+     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+     |                     Payload Data continued ...                |
+     +---------------------------------------------------------------+
+*/
+
+static size_t ws_packethead(struct Curl_easy *data,
+                            size_t len, unsigned int flags)
+{
+  struct HTTP *ws = data->req.p.http;
+  unsigned char *out = (unsigned char *)data->state.ulbuf;
+  unsigned char firstbyte = 0;
+  int outi;
+  unsigned char opcode;
+  if(flags & CURLWS_TEXT) {
+    opcode = WSBIT_OPCODE_TEXT;
+    infof(data, "WS: send OPCODE TEXT");
+  }
+  else if(flags & CURLWS_CLOSE) {
+    opcode = WSBIT_OPCODE_CLOSE;
+    infof(data, "WS: send OPCODE CLOSE");
+  }
+  else if(flags & CURLWS_PING) {
+    opcode = WSBIT_OPCODE_PING;
+    infof(data, "WS: send OPCODE PING");
+  }
+  else if(flags & CURLWS_PONG) {
+    opcode = WSBIT_OPCODE_PONG;
+    infof(data, "WS: send OPCODE PONG");
+  }
+  else {
+    opcode = WSBIT_OPCODE_BIN;
+    infof(data, "WS: send OPCODE BINARY");
+  }
+
+  if(!(flags & CURLWS_CONT)) {
+    /* if not marked as continuing, assume this is the final fragment */
+    firstbyte |= WSBIT_FIN | opcode;
+    ws->ws.contfragment = FALSE;
+  }
+  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;
+  }
+  else {
+    ws->ws.contfragment = TRUE;
+  }
+  out[0] = firstbyte;
+  if(len > 65535) {
+    out[1] = 127 | WSBIT_MASK;
+    out[2] = (len >> 8) & 0xff;
+    out[3] = len & 0xff;
+    outi = 10;
+  }
+  else if(len > 126) {
+    out[1] = 126 | WSBIT_MASK;
+    out[2] = (len >> 8) & 0xff;
+    out[3] = len & 0xff;
+    outi = 4;
+  }
+  else {
+    out[1] = (unsigned char)len | WSBIT_MASK;
+    outi = 2;
+  }
+
+  infof(data, "WS: send FIN bit %u (byte %02x)",
+        firstbyte & WSBIT_FIN ? 1 : 0,
+        firstbyte);
+  infof(data, "WS: send payload len %u", (int)len);
+
+  /* 4 bytes mask */
+  memcpy(&out[outi], &ws->ws.mask, 4);
+
+  if(data->set.upload_buffer_size < (len + 10))
+    return 0;
+
+  /* pass over the mask */
+  outi += 4;
+
+  ws->ws.xori = 0;
+  /* return packet size */
+  return outi;
+}
+
+CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
+                                  size_t buflen, size_t *sent,
+                                  curl_off_t totalsize,
+                                  unsigned int sendflags)
+{
+  CURLcode result;
+  size_t headlen;
+  char *out;
+  ssize_t written;
+  struct websocket *wsp = &data->req.p.http->ws;
+
+  if(!data->set.ws_raw_mode) {
+    result = Curl_get_upload_buffer(data);
+    if(result)
+      return result;
+  }
+  else {
+    if(totalsize || sendflags)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  if(data->set.ws_raw_mode) {
+    if(!buflen)
+      /* nothing to do */
+      return CURLE_OK;
+    /* raw mode sends exactly what was requested, and this is from within
+       the write callback */
+    if(Curl_is_in_callback(data))
+      result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
+                          &written);
+    else
+      result = Curl_senddata(data, buffer, buflen, &written);
+
+    infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
+          buflen, written);
+    *sent = written;
+    return result;
+  }
+
+  if(buflen > (data->set.upload_buffer_size - 10))
+    /* don't do more than this in one go */
+    buflen = data->set.upload_buffer_size - 10;
+
+  if(sendflags & CURLWS_OFFSET) {
+    if(totalsize) {
+      /* a frame series 'totalsize' bytes big, this is the first */
+      headlen = ws_packethead(data, totalsize, sendflags);
+      wsp->sleft = totalsize - buflen;
+    }
+    else {
+      headlen = 0;
+      if((curl_off_t)buflen > wsp->sleft) {
+        infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
+              buflen, wsp->sleft);
+        wsp->sleft = 0;
+      }
+      else
+        wsp->sleft -= buflen;
+    }
+  }
+  else
+    headlen = ws_packethead(data, buflen, sendflags);
+
+  /* headlen is the size of the frame header */
+  out = data->state.ulbuf;
+  if(buflen)
+    /* for PING and PONG etc there might not be a payload */
+    ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
+
+  if(data->set.connect_only)
+    result = Curl_senddata(data, out, buflen + headlen, &written);
+  else
+    result = Curl_write(data, data->conn->writesockfd, out,
+                        buflen + headlen, &written);
+
+  infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
+        headlen + buflen, written);
+  *sent = written;
+
+  return result;
+}
+
+void Curl_ws_done(struct Curl_easy *data)
+{
+  struct websocket *wsp = &data->req.p.http->ws;
+  DEBUGASSERT(wsp);
+  Curl_dyn_free(&wsp->buf);
+}
+
+CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+{
+  /* we only return something for websocket, called from within the callback
+     when not using raw mode */
+  if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
+     !data->set.ws_raw_mode)
+    return &data->req.p.http->ws.frame;
+  return NULL;
+}
+
+#else
+
+CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
+                                  size_t *nread,
+                                  struct curl_ws_frame **metap)
+{
+  (void)curl;
+  (void)buffer;
+  (void)buflen;
+  (void)nread;
+  (void)metap;
+  return CURLE_OK;
+}
+
+CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
+                                  size_t buflen, size_t *sent,
+                                  curl_off_t framesize,
+                                  unsigned int sendflags)
+{
+  (void)curl;
+  (void)buffer;
+  (void)buflen;
+  (void)sent;
+  (void)framesize;
+  (void)sendflags;
+  return CURLE_OK;
+}
+
+CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+{
+  (void)data;
+  return NULL;
+}
+#endif /* USE_WEBSOCKETS */
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h
new file mode 100644 (file)
index 0000000..341242e
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef HEADER_CURL_WS_H
+#define HEADER_CURL_WS_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 USE_WEBSOCKETS
+
+#ifdef USE_HYPER
+#define REQTYPE void
+#else
+#define REQTYPE struct dynbuf
+#endif
+
+/* this is the largest single fragment size we support */
+#define MAX_WS_SIZE 65535
+
+/* part of 'struct HTTP', when used in the 'struct SingleRequest' in the
+   Curl_easy struct */
+struct websocket {
+  bool contfragment; /* set TRUE if the previous fragment sent was not final */
+  unsigned char mask[4]; /* 32 bit mask for this connection */
+  struct Curl_easy *data; /* used for write callback handling */
+  struct dynbuf buf;
+  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 */
+  curl_off_t stillbuffer; /* number of bytes left in the buffer to deliver in
+                             the next curl_ws_recv() call */
+  char *stillb; /* the stillbuffer 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);
+
+size_t Curl_ws_writecb(char *buffer, size_t size, size_t nitems, void *userp);
+void Curl_ws_done(struct Curl_easy *data);
+
+#else
+#define Curl_ws_request(x,y) CURLE_OK
+#define Curl_ws_done(x) Curl_nop_stmt
+#endif
+
+#endif /* HEADER_CURL_WS_H */
index 9a62b79..81dfee3 100644 (file)
@@ -6,6 +6,12 @@ ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "PathScale")
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -woffall")
 ENDIF()
 
+# Activate POSIX APIs.
+if(CMAKE_SYSTEM_NAME MATCHES "^(Linux)$")
+  add_definitions(-D_DEFAULT_SOURCE -D_BSD_SOURCE)
+  string(APPEND CMAKE_REQUIRED_DEFINITIONS " -D_DEFAULT_SOURCE -D_BSD_SOURCE")
+endif()
+
 include(ConfigureChecks.cmake)
 configure_file(expat_config.h.cmake expat_config.h @ONLY)
 
index c384f4e..09579d2 100644 (file)
@@ -21,5 +21,5 @@ include_directories(
   )
 
 add_library(cmjsoncpp ${JSONCPP_SOURCES})
-target_link_libraries(cmjsoncpp ${CMake_KWIML_LIBRARIES})
+target_link_libraries(cmjsoncpp $<TARGET_NAME_IF_EXISTS:kwiml::kwiml>)
 set_property(TARGET cmjsoncpp PROPERTY CXX_INCLUDE_WHAT_YOU_USE "")
index eea472f..b38e653 100644 (file)
@@ -442,6 +442,8 @@ SET(ADDITIONAL_LIBS "")
 #
 IF(ENABLE_ZLIB)
   FIND_PACKAGE(ZLIB)
+  SET(ZLIB_INCLUDE_DIR "")
+  SET(ZLIB_LIBRARIES ZLIB::ZLIB)
 ELSE()
   SET(ZLIB_FOUND FALSE) # Override cached value
 ENDIF()
index b815a5c..ad3d433 100644 (file)
@@ -27,6 +27,8 @@ set(uv_sources
   src/queue.h
   src/strscpy.c
   src/strscpy.h
+  src/strtok.c
+  src/strtok.h
   src/threadpool.c
   src/timer.c
   src/uv-common.c
index 747095f..ffe34ec 100644 (file)
@@ -1150,8 +1150,8 @@ struct uv_interface_address_s {
 
 struct uv_passwd_s {
   char* username;
-  long uid;
-  long gid;
+  unsigned long uid;
+  unsigned long gid;
   char* shell;
   char* homedir;
 };
@@ -1259,6 +1259,7 @@ UV_EXTERN uv_pid_t uv_os_getppid(void);
 UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
 UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
 
+UV_EXTERN unsigned int uv_available_parallelism(void);
 UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
 UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
 UV_EXTERN int uv_cpumask_size(void);
index 1934f39..9c9d292 100644 (file)
  */
 
 #define UV_VERSION_MAJOR 1
-#define UV_VERSION_MINOR 43
-#define UV_VERSION_PATCH 1
-#define UV_VERSION_IS_RELEASE 0
-#define UV_VERSION_SUFFIX "dev"
+#define UV_VERSION_MINOR 44
+#define UV_VERSION_PATCH 2
+#define UV_VERSION_IS_RELEASE 1
+#define UV_VERSION_SUFFIX ""
 
 #define UV_VERSION_HEX  ((UV_VERSION_MAJOR << 16) | \
                          (UV_VERSION_MINOR <<  8) | \
index e3fe37d..2662b0a 100644 (file)
@@ -234,7 +234,7 @@ typedef struct _AFD_POLL_INFO {
   AFD_POLL_HANDLE_INFO Handles[1];
 } AFD_POLL_INFO, *PAFD_POLL_INFO;
 
-#define UV_MSAFD_PROVIDER_COUNT 3
+#define UV_MSAFD_PROVIDER_COUNT 4
 
 
 /**
@@ -388,6 +388,12 @@ typedef struct {
       OVERLAPPED overlapped;                                                  \
       size_t queued_bytes;                                                    \
     } io;                                                                     \
+    /* in v2, we can move these to the UV_CONNECT_PRIVATE_FIELDS */           \
+    struct {                                                                  \
+      ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\
+      HANDLE pipeHandle;                                                      \
+      DWORD duplex_flags;                                                     \
+    } connect;                                                                \
   } u;                                                                        \
   struct uv_req_s* next_req;
 
index 89864e2..1bac1c5 100644 (file)
@@ -25,7 +25,7 @@
 #ifdef _WIN32
 #include "win/internal.h"
 #include "win/handle-inl.h"
-#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h))
+#define uv__make_close_pending(h) uv__want_endgame((h)->loop, (h))
 #else
 #include "unix/internal.h"
 #endif
index b44cb16..93d982c 100644 (file)
@@ -21,6 +21,7 @@
 #include "idna.h"
 #include <assert.h>
 #include <string.h>
+#include <limits.h> /* UINT_MAX */
 
 static unsigned uv__utf8_decode1_slow(const char** p,
                                       const char* pe,
@@ -129,7 +130,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
   while (s < se) {
     c = uv__utf8_decode1(&s, se);
 
-    if (c == -1u)
+    if (c == UINT_MAX)
       return UV_EINVAL;
 
     if (c < 128)
@@ -151,7 +152,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
   s = ss;
   while (s < se) {
     c = uv__utf8_decode1(&s, se);
-    assert(c != -1u);
+    assert(c != UINT_MAX);
 
     if (c > 127)
       continue;
@@ -182,7 +183,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
 
     while (s < se) {
       c = uv__utf8_decode1(&s, se);
-      assert(c != -1u);
+      assert(c != UINT_MAX);
 
       if (c >= n)
         if (c < m)
@@ -201,7 +202,7 @@ static int uv__idna_toascii_label(const char* s, const char* se,
     s = ss;
     while (s < se) {
       c = uv__utf8_decode1(&s, se);
-      assert(c != -1u);
+      assert(c != UINT_MAX);
 
       if (c < n)
         if (++delta == 0)
@@ -280,7 +281,7 @@ long uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
     st = si;
     c = uv__utf8_decode1(&si, se);
 
-    if (c == -1u)
+    if (c == UINT_MAX)
       return UV_EINVAL;
 
     if (c != '.')
index cc78149..e8d4724 100644 (file)
@@ -28,7 +28,7 @@
  */
 #include "uv.h"
 
-/* Copies up to |n-1| bytes from |d| to |s| and always zero-terminates
+/* Copies up to |n-1| bytes from |s| to |d| and always zero-terminates
  * the result, except when |n==0|. Returns the number of bytes copied
  * or UV_E2BIG if |d| is too small.
  *
diff --git a/Utilities/cmlibuv/src/strtok.c b/Utilities/cmlibuv/src/strtok.c
new file mode 100644 (file)
index 0000000..45ddea5
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include "strtok.h"
+
+char* uv__strtok(char* str, const char* sep, char** itr) {
+  const char* sep_itr;
+  char* tmp;
+  char* start;
+
+  if (str == NULL)
+    start = tmp = *itr;
+  else
+    start = tmp = str;
+
+  if (tmp == NULL)
+    return NULL;
+
+  while (*tmp != '\0') {
+    sep_itr = sep;
+    while (*sep_itr != '\0') {
+      if (*tmp == *sep_itr) {
+        *itr = tmp + 1;
+        *tmp = '\0';
+        return start;
+      }
+      sep_itr++;
+    }
+    tmp++;
+  }
+  *itr = NULL;
+  return start;
+}
diff --git a/Utilities/cmlibuv/src/strtok.h b/Utilities/cmlibuv/src/strtok.h
new file mode 100644 (file)
index 0000000..3799ff5
--- /dev/null
@@ -0,0 +1,27 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef UV_STRTOK_H_
+#define UV_STRTOK_H_
+
+char* uv__strtok(char* str, const char* sep, char** itr);
+
+#endif  /* UV_STRTOK_H_ */
index 63d8268..2b58162 100644 (file)
@@ -43,12 +43,11 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
   __compare_and_swap((volatile int*)ptr, &oldval, newval);
   return oldval;
 #elif defined(__MVS__)
-  unsigned int op4;
-  if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
-                (unsigned int*) ptr, *ptr, &op4))
-    return oldval;
-  else
-    return op4;
+  /* Use hand-rolled assembly because codegen from builtin __plo_CSST results in
+   * a runtime bug.
+   */
+  __asm(" cs %0,%2,%1 \n " : "+r"(oldval), "+m"(*ptr) : "r"(newval) :);
+  return oldval;
 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
   return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
 #else
@@ -61,7 +60,9 @@ UV_UNUSED(static void cpu_relax(void)) {
   __asm__ __volatile__ ("rep; nop" ::: "memory");  /* a.k.a. PAUSE */
 #elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
   __asm__ __volatile__ ("yield" ::: "memory");
-#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__)
+#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__)
+  __asm volatile ("" : : : "memory");
+#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__))
   __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory");
 #endif
 }
index e48934b..11ca955 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <ifaddrs.h>
 #include <net/if.h>
-#if !defined(__CYGWIN__) && !defined(__MSYS__)
+#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
 #include <net/if_dl.h>
 #endif
 
@@ -40,7 +40,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
     return 1;
   if (ent->ifa_addr == NULL)
     return 1;
-#if !defined(__CYGWIN__) && !defined(__MSYS__)
+#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__)
   /*
    * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family`
    * equals `AF_LINK`. Otherwise, the result depends on the operating
@@ -69,7 +69,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
   struct ifaddrs* addrs;
   struct ifaddrs* ent;
   uv_interface_address_t* address;
-#if !(defined(__CYGWIN__) || defined(__MSYS__))
+#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
   int i;
 #endif
 
@@ -126,7 +126,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     address++;
   }
 
-#if !(defined(__CYGWIN__) || defined(__MSYS__))
+#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
   /* Fill in physical addresses for each interface */
   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
index 4f4e9e5..b0c01e2 100644 (file)
@@ -38,6 +38,7 @@ static void init_process_title_mutex_once(void) {
 
 
 void uv__process_title_cleanup(void) {
+  uv_once(&process_title_mutex_once, init_process_title_mutex_once);
   uv_mutex_destroy(&process_title_mutex);
 }
 
index 0f279d5..394231d 100644 (file)
@@ -135,7 +135,9 @@ int uv__statx(int dirfd,
   errno = ENOSYS;
   return -1;
 }
+#endif
 
+#if defined(__linux__) || defined(__FreeBSD__)
 ssize_t uv__fs_copy_file_range(int fd_in, off_t* off_in,
                                int fd_out, off_t* off_out,
                                size_t len, unsigned int flags)
index 0793922..d0b0e00 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "uv.h"
 #include "internal.h"
+#include "strtok.h"
 
 #include <stddef.h> /* NULL */
 #include <stdio.h> /* printf */
@@ -86,10 +87,12 @@ extern char** environ;
 #endif
 
 #if defined(__MVS__)
-#include <sys/ioctl.h>
+# include <sys/ioctl.h>
+# include "zos-sys-info.h"
 #endif
 
 #if defined(__linux__)
+# include <sched.h>
 # include <sys/syscall.h>
 # define uv__accept4 accept4
 #endif
@@ -98,7 +101,7 @@ extern char** environ;
 # include <sanitizer/linux_syscall_hooks.h>
 #endif
 
-static int uv__run_pending(uv_loop_t* loop);
+static void uv__run_pending(uv_loop_t* loop);
 
 /* Verify that uv_buf_t is ABI-compatible with struct iovec. */
 STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec));
@@ -164,6 +167,15 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
 
   case UV_FS_EVENT:
     uv__fs_event_close((uv_fs_event_t*)handle);
+#if defined(__sun) || defined(__MVS__)
+    /*
+     * On Solaris, illumos, and z/OS we will not be able to dissociate the
+     * watcher for an event which is pending delivery, so we cannot always call
+     * uv__make_close_pending() straight away. The backend will call the
+     * function once the event has cleared.
+     */
+    return;
+#endif
     break;
 
   case UV_POLL:
@@ -340,42 +352,43 @@ int uv_backend_fd(const uv_loop_t* loop) {
 }
 
 
-int uv_backend_timeout(const uv_loop_t* loop) {
-  if (loop->stop_flag != 0)
-    return 0;
-
-  if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))
-    return 0;
-
-  if (!QUEUE_EMPTY(&loop->idle_handles))
-    return 0;
-
-  if (!QUEUE_EMPTY(&loop->pending_queue))
-    return 0;
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         !QUEUE_EMPTY(&loop->pending_queue) ||
+         loop->closing_handles != NULL;
+}
 
-  if (loop->closing_handles)
-    return 0;
 
-  return uv__next_timeout(loop);
+static int uv__backend_timeout(const uv_loop_t* loop) {
+  if (loop->stop_flag == 0 &&
+      /* uv__loop_alive(loop) && */
+      (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) &&
+      QUEUE_EMPTY(&loop->pending_queue) &&
+      QUEUE_EMPTY(&loop->idle_handles) &&
+      loop->closing_handles == NULL)
+    return uv__next_timeout(loop);
+  return 0;
 }
 
 
-static int uv__loop_alive(const uv_loop_t* loop) {
-  return uv__has_active_handles(loop) ||
-         uv__has_active_reqs(loop) ||
-         loop->closing_handles != NULL;
+int uv_backend_timeout(const uv_loop_t* loop) {
+  if (QUEUE_EMPTY(&loop->watcher_queue))
+    return uv__backend_timeout(loop);
+  /* Need to call uv_run to update the backend fd state. */
+  return 0;
 }
 
 
 int uv_loop_alive(const uv_loop_t* loop) {
-    return uv__loop_alive(loop);
+  return uv__loop_alive(loop);
 }
 
 
 int uv_run(uv_loop_t* loop, uv_run_mode mode) {
   int timeout;
   int r;
-  int ran_pending;
+  int can_sleep;
 
   r = uv__loop_alive(loop);
   if (!r)
@@ -384,16 +397,25 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {
   while (r != 0 && loop->stop_flag == 0) {
     uv__update_time(loop);
     uv__run_timers(loop);
-    ran_pending = uv__run_pending(loop);
+
+    can_sleep =
+        QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);
+
+    uv__run_pending(loop);
     uv__run_idle(loop);
     uv__run_prepare(loop);
 
     timeout = 0;
-    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
-      timeout = uv_backend_timeout(loop);
+    if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
+      timeout = uv__backend_timeout(loop);
 
     uv__io_poll(loop, timeout);
 
+    /* Process immediate callbacks (e.g. write_cb) a small fixed number of
+     * times to avoid loop starvation.*/
+    for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)
+      uv__run_pending(loop);
+
     /* Run one final update on the provider_idle_time in case uv__io_poll
      * returned because the timeout expired, but no events were received. This
      * call will be ignored if the provider_entry_time was either never set (if
@@ -603,20 +625,6 @@ int uv__nonblock_ioctl(int fd, int set) {
 
   return 0;
 }
-
-
-int uv__cloexec_ioctl(int fd, int set) {
-  int r;
-
-  do
-    r = ioctl(fd, set ? FIOCLEX : FIONCLEX);
-  while (r == -1 && errno == EINTR);
-
-  if (r)
-    return UV__ERR(errno);
-
-  return 0;
-}
 #endif
 
 
@@ -651,25 +659,13 @@ int uv__nonblock_fcntl(int fd, int set) {
 }
 
 
-int uv__cloexec_fcntl(int fd, int set) {
+int uv__cloexec(int fd, int set) {
   int flags;
   int r;
 
-  do
-    r = fcntl(fd, F_GETFD);
-  while (r == -1 && errno == EINTR);
-
-  if (r == -1)
-    return UV__ERR(errno);
-
-  /* Bail out now if already set/clear. */
-  if (!!(r & FD_CLOEXEC) == !!set)
-    return 0;
-
+  flags = 0;
   if (set)
-    flags = r | FD_CLOEXEC;
-  else
-    flags = r & ~FD_CLOEXEC;
+    flags = FD_CLOEXEC;
 
   do
     r = fcntl(fd, F_SETFD, flags);
@@ -683,28 +679,23 @@ int uv__cloexec_fcntl(int fd, int set) {
 
 
 ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
-  struct cmsghdr* cmsg;
+#if defined(__ANDROID__)   || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__)   || \
+    defined(__NetBSD__)    || \
+    defined(__OpenBSD__)   || \
+    defined(__linux__)
   ssize_t rc;
+  rc = recvmsg(fd, msg, flags | MSG_CMSG_CLOEXEC);
+  if (rc == -1)
+    return UV__ERR(errno);
+  return rc;
+#else
+  struct cmsghdr* cmsg;
   int* pfd;
   int* end;
-#if defined(__linux__)
-  static int no_msg_cmsg_cloexec;
-  if (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) {
-    rc = recvmsg(fd, msg, flags | 0x40000000);  /* MSG_CMSG_CLOEXEC */
-    if (rc != -1)
-      return rc;
-    if (errno != EINVAL)
-      return UV__ERR(errno);
-    rc = recvmsg(fd, msg, flags);
-    if (rc == -1)
-      return UV__ERR(errno);
-    uv__store_relaxed(&no_msg_cmsg_cloexec, 1);
-  } else {
-    rc = recvmsg(fd, msg, flags);
-  }
-#else
+  ssize_t rc;
   rc = recvmsg(fd, msg, flags);
-#endif
   if (rc == -1)
     return UV__ERR(errno);
   if (msg->msg_controllen == 0)
@@ -717,6 +708,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
            pfd += 1)
         uv__cloexec(*pfd, 1);
   return rc;
+#endif
 }
 
 
@@ -809,14 +801,11 @@ int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
 }
 
 
-static int uv__run_pending(uv_loop_t* loop) {
+static void uv__run_pending(uv_loop_t* loop) {
   QUEUE* q;
   QUEUE pq;
   uv__io_t* w;
 
-  if (QUEUE_EMPTY(&loop->pending_queue))
-    return 0;
-
   QUEUE_MOVE(&loop->pending_queue, &pq);
 
   while (!QUEUE_EMPTY(&pq)) {
@@ -826,8 +815,6 @@ static int uv__run_pending(uv_loop_t* loop) {
     w = QUEUE_DATA(q, uv__io_t, pending_queue);
     w->cb(loop, w, POLLOUT);
   }
-
-  return 1;
 }
 
 
@@ -1042,6 +1029,32 @@ int uv__open_cloexec(const char* path, int flags) {
 }
 
 
+int uv__slurp(const char* filename, char* buf, size_t len) {
+  ssize_t n;
+  int fd;
+
+  assert(len > 0);
+
+  fd = uv__open_cloexec(filename, O_RDONLY);
+  if (fd < 0)
+    return fd;
+
+  do
+    n = read(fd, buf, len - 1);
+  while (n == -1 && errno == EINTR);
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  if (n < 0)
+    return UV__ERR(errno);
+
+  buf[n] = '\0';
+
+  return 0;
+}
+
+
 int uv__dup2_cloexec(int oldfd, int newfd) {
 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__)
   int r;
@@ -1166,24 +1179,17 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
   size_t name_size;
   size_t homedir_size;
   size_t shell_size;
-  long initsize;
   int r;
 
   if (pwd == NULL)
     return UV_EINVAL;
 
-  initsize = sysconf(_SC_GETPW_R_SIZE_MAX);
-
-  if (initsize <= 0)
-    bufsize = 4096;
-  else
-    bufsize = (size_t) initsize;
-
   uid = geteuid();
-  buf = NULL;
 
-  for (;;) {
-    uv__free(buf);
+  /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it
+   * is frequently 1024 or 4096, so we can just use that directly. The pwent
+   * will not usually be large. */
+  for (bufsize = 2000;; bufsize *= 2) {
     buf = uv__malloc(bufsize);
 
     if (buf == NULL)
@@ -1193,21 +1199,18 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
       r = getpwuid_r(uid, &pw, buf, bufsize, &result);
     while (r == EINTR);
 
+    if (r != 0 || result == NULL)
+      uv__free(buf);
+
     if (r != ERANGE)
       break;
-
-    bufsize *= 2;
   }
 
-  if (r != 0) {
-    uv__free(buf);
+  if (r != 0)
     return UV__ERR(r);
-  }
 
-  if (result == NULL) {
-    uv__free(buf);
+  if (result == NULL)
     return UV_ENOENT;
-  }
 
   /* Allocate memory for the username, shell, and home directory */
   name_size = strlen(pw.pw_name) + 1;
@@ -1569,6 +1572,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
   char* cloned_path;
   char* path_env;
   char* token;
+  char* itr;
 
   if (buf == NULL || buflen == NULL || *buflen == 0)
     return UV_EINVAL;
@@ -1610,7 +1614,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
   if (cloned_path == NULL)
     return UV_ENOMEM;
 
-  token = strtok(cloned_path, ":");
+  token = uv__strtok(cloned_path, ":", &itr);
   while (token != NULL) {
     snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog);
     if (realpath(trypath, abspath) == abspath) {
@@ -1629,10 +1633,50 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
         return 0;
       }
     }
-    token = strtok(NULL, ":");
+    token = uv__strtok(NULL, ":", &itr);
   }
   uv__free(cloned_path);
 
   /* Out of tokens (path entries), and no match found */
   return UV_EINVAL;
 }
+
+
+unsigned int uv_available_parallelism(void) {
+#ifdef __linux__
+  cpu_set_t set;
+  long rc;
+
+  memset(&set, 0, sizeof(set));
+
+  /* sysconf(_SC_NPROCESSORS_ONLN) in musl calls sched_getaffinity() but in
+   * glibc it's... complicated... so for consistency try sched_getaffinity()
+   * before falling back to sysconf(_SC_NPROCESSORS_ONLN).
+   */
+  if (0 == sched_getaffinity(0, sizeof(set), &set))
+    rc = CPU_COUNT(&set);
+  else
+    rc = sysconf(_SC_NPROCESSORS_ONLN);
+
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#elif defined(__MVS__)
+  int rc;
+
+  rc = __get_num_online_cpus();
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#else  /* __linux__ */
+  long rc;
+
+  rc = sysconf(_SC_NPROCESSORS_ONLN);
+  if (rc < 1)
+    rc = 1;
+
+  return (unsigned) rc;
+#endif  /* __linux__ */
+}
index 170b897..658ff26 100644 (file)
@@ -287,3 +287,18 @@ int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) {
   return errno = ENOSYS, -1;
 #endif
 }
+
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags)
+{
+#if __FreeBSD__ >= 13 && !defined(__DragonFly__)
+       return copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
+#else
+       return errno = ENOSYS, -1;
+#endif
+}
index 40448f1..e2db3ad 100644 (file)
@@ -247,7 +247,8 @@ UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) {
 static ssize_t uv__fs_futime(uv_fs_t* req) {
 #if defined(__linux__)                                                        \
     || defined(_AIX71)                                                        \
-    || defined(__HAIKU__)
+    || defined(__HAIKU__)                                                     \
+    || defined(__GNU__)
   struct timespec ts[2];
   ts[0] = uv__fs_to_timespec(req->atime);
   ts[1] = uv__fs_to_timespec(req->mtime);
@@ -1085,6 +1086,17 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
      */
 
 #if defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__FreeBSD__)
+    off_t off;
+
+    off = req->off;
+    r = uv__fs_copy_file_range(in_fd, &off, out_fd, NULL, req->bufsml[0].len, 0);
+    if (r >= 0) {
+        r = off - req->off;
+        req->off = off;
+        return r;
+    }
+#endif
     len = 0;
     r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0);
 #elif defined(__FreeBSD_kernel__)
@@ -1179,7 +1191,9 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) {
 #if defined(__linux__)            ||                                           \
     defined(_AIX71)               ||                                           \
     defined(__sun)                ||                                           \
-    defined(__HAIKU__)
+    defined(__HAIKU__)            ||                                           \
+    defined(__GNU__)              ||                                           \
+    defined(__OpenBSD__)
   struct timespec ts[2];
   ts[0] = uv__fs_to_timespec(req->atime);
   ts[1] = uv__fs_to_timespec(req->mtime);
diff --git a/Utilities/cmlibuv/src/unix/hurd.c b/Utilities/cmlibuv/src/unix/hurd.c
new file mode 100644 (file)
index 0000000..d19ea63
--- /dev/null
@@ -0,0 +1,167 @@
+/* Copyright libuv project contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define _GNU_SOURCE 1
+
+#include "uv.h"
+#include "internal.h"
+
+#include <hurd.h>
+#include <hurd/process.h>
+#include <mach/task_info.h>
+#include <mach/vm_statistics.h>
+#include <mach/vm_param.h>
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+
+int uv_exepath(char* buffer, size_t* size) {
+  kern_return_t err;
+  /* XXX in current Hurd, strings are char arrays of 1024 elements */
+  string_t exepath;
+  ssize_t copied;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  if (*size - 1 > 0) {
+    /* XXX limited length of buffer in current Hurd, this API will probably
+     * evolve in the future */
+    err = proc_get_exe(getproc(), getpid(), exepath);
+
+    if (err)
+      return UV__ERR(err);
+  }
+
+  copied = uv__strscpy(buffer, exepath, *size);
+
+  /* do not return error on UV_E2BIG failure */
+  *size = copied < 0 ? strlen(buffer) : (size_t) copied;
+
+  return 0;
+}
+
+int uv_resident_set_memory(size_t* rss) {
+  kern_return_t err;
+  struct task_basic_info bi;
+  mach_msg_type_number_t count;
+
+  count = TASK_BASIC_INFO_COUNT;
+  err = task_info(mach_task_self(), TASK_BASIC_INFO,
+                 (task_info_t) &bi, &count);
+
+  if (err)
+    return UV__ERR(err);
+
+  *rss = bi.resident_size;
+
+  return 0;
+}
+
+uint64_t uv_get_free_memory(void) {
+  kern_return_t err;
+  struct vm_statistics vmstats;
+  
+  err = vm_statistics(mach_task_self(), &vmstats);
+
+  if (err)
+    return 0;
+  
+  return vmstats.free_count * vm_page_size;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  kern_return_t err;
+  host_basic_info_data_t hbi;
+  mach_msg_type_number_t cnt;
+  
+  cnt = HOST_BASIC_INFO_COUNT;
+  err = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); 
+
+  if (err)
+    return 0;
+
+  return hbi.memory_size;
+}
+
+
+int uv_uptime(double* uptime) {
+  char buf[128];
+
+  /* Try /proc/uptime first */
+  if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
+    if (1 == sscanf(buf, "%lf", uptime))
+      return 0;
+
+  /* Reimplement here code from procfs to calculate uptime if not mounted? */
+
+  return UV__ERR(EIO);
+}
+
+void uv_loadavg(double avg[3]) {
+  char buf[128];  /* Large enough to hold all of /proc/loadavg. */
+
+  if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf)))
+    if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]))
+      return;
+
+  /* Reimplement here code from procfs to calculate loadavg if not mounted? */
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  kern_return_t err;
+  host_basic_info_data_t hbi;
+  mach_msg_type_number_t cnt;
+  
+  /* Get count of cpus  */
+  cnt = HOST_BASIC_INFO_COUNT;
+  err = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); 
+
+  if (err) {
+    err = UV__ERR(err);
+    goto abort;
+  }
+
+  /* XXX not implemented on the Hurd */
+  *cpu_infos = uv__calloc(hbi.avail_cpus, sizeof(**cpu_infos));
+  if (*cpu_infos == NULL) {
+    err = UV_ENOMEM;
+    goto abort;
+  }
+
+  *count = hbi.avail_cpus;
+
+  return 0;
+  
+ abort:
+  *cpu_infos = NULL;
+  *count = 0;
+  return err;
+}
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
index 586ae39..f41ee3c 100644 (file)
@@ -155,7 +155,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
 
 /* loop flags */
 enum {
-  UV_LOOP_BLOCK_SIGPROF = 1
+  UV_LOOP_BLOCK_SIGPROF = 0x1,
+  UV_LOOP_REAP_CHILDREN = 0x2
 };
 
 /* flags of excluding ifaddr */
@@ -184,11 +185,9 @@ struct uv__stream_queued_fds_s {
     defined(__linux__) || \
     defined(__OpenBSD__) || \
     defined(__NetBSD__)
-#define uv__cloexec uv__cloexec_ioctl
 #define uv__nonblock uv__nonblock_ioctl
 #define UV__NONBLOCK_IS_IOCTL 1
 #else
-#define uv__cloexec uv__cloexec_fcntl
 #define uv__nonblock uv__nonblock_fcntl
 #define UV__NONBLOCK_IS_IOCTL 0
 #endif
@@ -206,8 +205,7 @@ struct uv__stream_queued_fds_s {
 #endif
 
 /* core */
-int uv__cloexec_ioctl(int fd, int set);
-int uv__cloexec_fcntl(int fd, int set);
+int uv__cloexec(int fd, int set);
 int uv__nonblock_ioctl(int fd, int set);
 int uv__nonblock_fcntl(int fd, int set);
 int uv__close(int fd); /* preserves errno */
@@ -251,14 +249,15 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
 int uv__accept(int sockfd);
 int uv__dup2_cloexec(int oldfd, int newfd);
 int uv__open_cloexec(const char* path, int flags);
+int uv__slurp(const char* filename, char* buf, size_t len);
 
 /* tcp */
-int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
+int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
 int uv__tcp_nodelay(int fd, int on);
 int uv__tcp_keepalive(int fd, int on, unsigned int delay);
 
 /* pipe */
-int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
 
 /* signal */
 void uv__signal_close(uv_signal_t* handle);
@@ -288,10 +287,10 @@ void uv__tcp_close(uv_tcp_t* handle);
 size_t uv__thread_stack_size(void);
 void uv__udp_close(uv_udp_t* handle);
 void uv__udp_finish_close(uv_udp_t* handle);
-uv_handle_type uv__handle_type(int fd);
 FILE* uv__open_file(const char* path);
 int uv__getpwuid_r(uv_passwd_t* pwd);
 int uv__search_path(const char* prog, char* buf, size_t* buflen);
+void uv__wait_children(uv_loop_t* loop);
 
 /* random */
 int uv__random_devurandom(void* buf, size_t buflen);
@@ -366,5 +365,15 @@ size_t strnlen(const char* s, size_t maxlen);
 #endif
 #endif
 
+#if defined(__FreeBSD__)
+ssize_t
+uv__fs_copy_file_range(int fd_in,
+                       off_t* off_in,
+                       int fd_out,
+                       off_t* off_out,
+                       size_t len,
+                       unsigned int flags);
+#endif
+
 
 #endif /* UV_UNIX_INTERNAL_H_ */
index 75e9110..5dac76a 100644 (file)
@@ -117,6 +117,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
   unsigned int revents;
   QUEUE* q;
   uv__io_t* w;
+  uv_process_t* process;
   sigset_t* pset;
   sigset_t set;
   uint64_t base;
@@ -285,6 +286,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
     for (i = 0; i < nfds; i++) {
       ev = events + i;
       fd = ev->ident;
+
+      /* Handle kevent NOTE_EXIT results */
+      if (ev->filter == EVFILT_PROC) {
+        QUEUE_FOREACH(q, &loop->process_handles) {
+          process = QUEUE_DATA(q, uv_process_t, queue);
+          if (process->pid == fd) {
+            process->flags |= UV_HANDLE_REAP;
+            loop->flags |= UV_LOOP_REAP_CHILDREN;
+            break;
+          }
+        }
+        nevents++;
+        continue;
+      }
+
       /* Skip invalidated events, see uv__platform_invalidate_fd */
       if (fd == -1)
         continue;
@@ -377,6 +393,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
       nevents++;
     }
 
+    if (loop->flags & UV_LOOP_REAP_CHILDREN) {
+      loop->flags &= ~UV_LOOP_REAP_CHILDREN;
+      uv__wait_children(loop);
+    }
+
     if (reset_timeout != 0) {
       timeout = user_timeout;
       reset_timeout = 0;
@@ -435,7 +456,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
 
   /* Invalidate events with same file descriptor */
   for (i = 0; i < nfds; i++)
-    if ((int) events[i].ident == fd)
+    if ((int) events[i].ident == fd && events[i].filter != EVFILT_PROC)
       events[i].ident = -1;
 }
 
index 7b041e6..23a7daf 100644 (file)
@@ -211,31 +211,6 @@ err:
   return UV_EINVAL;
 }
 
-static int uv__slurp(const char* filename, char* buf, size_t len) {
-  ssize_t n;
-  int fd;
-
-  assert(len > 0);
-
-  fd = uv__open_cloexec(filename, O_RDONLY);
-  if (fd < 0)
-    return fd;
-
-  do
-    n = read(fd, buf, len - 1);
-  while (n == -1 && errno == EINTR);
-
-  if (uv__close_nocheckstdio(fd))
-    abort();
-
-  if (n < 0)
-    return UV__ERR(errno);
-
-  buf[n] = '\0';
-
-  return 0;
-}
-
 int uv_uptime(double* uptime) {
   static volatile int no_clock_boottime;
   char buf[128];
@@ -243,7 +218,7 @@ int uv_uptime(double* uptime) {
   int r;
 
   /* Try /proc/uptime first, then fallback to clock_gettime(). */
-  
+
   if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf)))
     if (1 == sscanf(buf, "%lf", uptime))
       return 0;
@@ -641,6 +616,7 @@ static uint64_t read_cpufreq(unsigned int cpunum) {
 }
 
 
+#ifdef HAVE_IFADDRS_H
 static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
   if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
     return 1;
@@ -654,6 +630,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
     return exclude_type;
   return !exclude_type;
 }
+#endif
 
 int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
 #ifndef HAVE_IFADDRS_H
index a741127..5861aaa 100644 (file)
@@ -284,6 +284,8 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
   nmsgsfds_t size;
   struct pollfd* pfds;
   int pollret;
+  int pollfdret;
+  int pollmsgret;
   int reventcount;
   int nevents;
   struct pollfd msg_fd;
@@ -304,24 +306,24 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
     return -1;
   }
 
-  if (lst->size > 0)
-    _SET_FDS_MSGS(size, 1, lst->size - 1);
-  else
-    _SET_FDS_MSGS(size, 0, 0);
+  assert(lst->size > 0);
+  _SET_FDS_MSGS(size, 1, lst->size - 1);
   pfds = lst->items;
   pollret = poll(pfds, size, timeout);
   if (pollret <= 0)
     return pollret;
 
-  assert(lst->size > 0);
-
-  pollret = _NFDS(pollret) + _NMSGS(pollret);
+  pollfdret = _NFDS(pollret);
+  pollmsgret = _NMSGS(pollret);
 
   reventcount = 0;
   nevents = 0;
-  msg_fd = pfds[lst->size - 1];
+  msg_fd = pfds[lst->size - 1]; /* message queue is always last entry */
+  maxevents = maxevents - pollmsgret; /* allow spot for message queue */
   for (i = 0;
-       i < lst->size && i < maxevents && reventcount < pollret; ++i) {
+       i < lst->size - 1 &&
+       nevents < maxevents &&
+       reventcount < pollfdret; ++i) {
     struct epoll_event ev;
     struct pollfd* pfd;
 
@@ -332,18 +334,18 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
     ev.fd = pfd->fd;
     ev.events = pfd->revents;
     ev.is_msg = 0;
-    if (pfd->revents & POLLIN && pfd->revents & POLLOUT)
-      reventcount += 2;
-    else if (pfd->revents & (POLLIN | POLLOUT))
-      ++reventcount;
 
-    pfd->revents = 0;
+    reventcount++;
     events[nevents++] = ev;
   }
 
-  if (msg_fd.revents != 0 && msg_fd.fd != -1)
-    if (i == lst->size)
-      events[nevents - 1].is_msg = 1;
+  if (pollmsgret > 0 && msg_fd.revents != 0 && msg_fd.fd != -1) {
+    struct epoll_event ev;
+    ev.fd = msg_fd.fd;
+    ev.events = msg_fd.revents;
+    ev.is_msg = 1;
+    events[nevents++] = ev;
+  }
 
   return nevents;
 }
index bf0448b..3b16318 100644 (file)
@@ -278,7 +278,9 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
   __net_ifconf6header_t ifc;
   __net_ifconf6entry_t* ifr;
   __net_ifconf6entry_t* p;
-  __net_ifconf6entry_t flg;
+  unsigned int i;
+  int count_names;
+  unsigned char netmask[16] = {0};
 
   *count = 0;
   /* Assume maximum buffer size allowable */
@@ -287,24 +289,33 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
   if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
     return UV__ERR(errno);
 
+  ifc.__nif6h_buffer = uv__calloc(1, maxsize);
+
+  if (ifc.__nif6h_buffer == NULL) {
+    uv__close(sockfd);
+    return UV_ENOMEM;
+  }
+
   ifc.__nif6h_version = 1;
   ifc.__nif6h_buflen = maxsize;
-  ifc.__nif6h_buffer = uv__calloc(1, maxsize);;
 
   if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) {
+    /* This will error on a system that does not support IPv6. However, we want
+     * to treat this as there being 0 interfaces so we can continue to get IPv4
+     * interfaces in uv_interface_addresses(). So return 0 instead of the error.
+     */
+    uv__free(ifc.__nif6h_buffer);
     uv__close(sockfd);
-    return UV__ERR(errno);
+    errno = 0;
+    return 0;
   }
 
-
-  *count = 0;
   ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
   while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
     p = ifr;
     ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
 
-    if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
-          p->__nif6e_addr.sin6_family == AF_INET))
+    if (!(p->__nif6e_addr.sin6_family == AF_INET6))
       continue;
 
     if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
@@ -313,21 +324,28 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
     ++(*count);
   }
 
+  if ((*count) == 0) {
+    uv__free(ifc.__nif6h_buffer);
+    uv__close(sockfd);
+    return 0;
+  }
+
   /* Alloc the return interface structs */
-  *addresses = uv__malloc(*count * sizeof(uv_interface_address_t));
+  *addresses = uv__calloc(1, *count * sizeof(uv_interface_address_t));
   if (!(*addresses)) {
+    uv__free(ifc.__nif6h_buffer);
     uv__close(sockfd);
     return UV_ENOMEM;
   }
   address = *addresses;
 
+  count_names = 0;
   ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
   while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
     p = ifr;
     ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
 
-    if (!(p->__nif6e_addr.sin6_family == AF_INET6 ||
-          p->__nif6e_addr.sin6_family == AF_INET))
+    if (!(p->__nif6e_addr.sin6_family == AF_INET6))
       continue;
 
     if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
@@ -335,20 +353,41 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
 
     /* All conditions above must match count loop */
 
-    address->name = uv__strdup(p->__nif6e_name);
+    i = 0;
+    /* Ignore EBCDIC space (0x40) padding in name */
+    while (i < ARRAY_SIZE(p->__nif6e_name) &&
+           p->__nif6e_name[i] != 0x40 &&
+           p->__nif6e_name[i] != 0)
+      ++i;
+    address->name = uv__malloc(i + 1);
+    if (address->name == NULL) {
+      uv_free_interface_addresses(*addresses, count_names);
+      uv__free(ifc.__nif6h_buffer);
+      uv__close(sockfd);
+      return UV_ENOMEM;
+    }
+    memcpy(address->name, p->__nif6e_name, i);
+    address->name[i] = '\0';
+    __e2a_s(address->name);
+    count_names++;
 
-    if (p->__nif6e_addr.sin6_family == AF_INET6)
-      address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
-    else
-      address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr);
+    address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
 
-    /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */
+    for (i = 0; i < (p->__nif6e_prefixlen / 8); i++)
+      netmask[i] = 0xFF;
 
-    address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0;
-    memset(address->phys_addr, 0, sizeof(address->phys_addr));
+    if (p->__nif6e_prefixlen % 8)
+      netmask[i] = 0xFF << (8 - (p->__nif6e_prefixlen % 8));
+
+    address->netmask.netmask6.sin6_len = p->__nif6e_prefixlen;
+    memcpy(&(address->netmask.netmask6.sin6_addr), netmask, 16);
+    address->netmask.netmask6.sin6_family = AF_INET6;
+
+    address->is_internal = p->__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0;
     address++;
   }
 
+  uv__free(ifc.__nif6h_buffer);
   uv__close(sockfd);
   return 0;
 }
@@ -362,14 +401,18 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
   struct ifreq flg;
   struct ifreq* ifr;
   struct ifreq* p;
+  uv_interface_address_t* addresses_v6;
   int count_v6;
+  unsigned int i;
+  int rc;
+  int count_names;
 
   *count = 0;
   *addresses = NULL;
 
   /* get the ipv6 addresses first */
-  uv_interface_address_t* addresses_v6;
-  uv__interface_addresses_v6(&addresses_v6, &count_v6);
+  if ((rc = uv__interface_addresses_v6(&addresses_v6, &count_v6)) != 0)
+    return rc;
 
   /* now get the ipv4 addresses */
 
@@ -377,12 +420,27 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
   maxsize = 16384;
 
   sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-  if (0 > sockfd)
+  if (0 > sockfd) {
+    if (count_v6)
+      uv_free_interface_addresses(addresses_v6, count_v6);
     return UV__ERR(errno);
+  }
 
   ifc.ifc_req = uv__calloc(1, maxsize);
+
+  if (ifc.ifc_req == NULL) {
+    if (count_v6)
+      uv_free_interface_addresses(addresses_v6, count_v6);
+    uv__close(sockfd);
+    return UV_ENOMEM;
+  }
+
   ifc.ifc_len = maxsize;
+
   if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
+    if (count_v6)
+      uv_free_interface_addresses(addresses_v6, count_v6);
+    uv__free(ifc.ifc_req);
     uv__close(sockfd);
     return UV__ERR(errno);
   }
@@ -403,6 +461,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
 
     memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
     if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
+      if (count_v6)
+        uv_free_interface_addresses(addresses_v6, count_v6);
+      uv__free(ifc.ifc_req);
       uv__close(sockfd);
       return UV__ERR(errno);
     }
@@ -413,27 +474,35 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     (*count)++;
   }
 
-  if (*count == 0) {
+  if (*count == 0 && count_v6 == 0) {
+    uv__free(ifc.ifc_req);
     uv__close(sockfd);
     return 0;
   }
 
   /* Alloc the return interface structs */
-  *addresses = uv__malloc((*count + count_v6) *
+  *addresses = uv__calloc(1, (*count + count_v6) *
                           sizeof(uv_interface_address_t));
 
   if (!(*addresses)) {
+    if (count_v6)
+      uv_free_interface_addresses(addresses_v6, count_v6);
+    uv__free(ifc.ifc_req);
     uv__close(sockfd);
     return UV_ENOMEM;
   }
   address = *addresses;
 
-  /* copy over the ipv6 addresses */
-  memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t));
-  address += count_v6;
-  *count += count_v6;
-  uv__free(addresses_v6);
+  /* copy over the ipv6 addresses if any are found */
+  if (count_v6) {
+    memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t));
+    address += count_v6;
+    *count += count_v6;
+    /* free ipv6 addresses, but keep address names */
+    uv__free(addresses_v6);
+  }
 
+  count_names = *count;
   ifr = ifc.ifc_req;
   while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
     p = ifr;
@@ -446,6 +515,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
 
     memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
     if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
+      uv_free_interface_addresses(*addresses, count_names);
+      uv__free(ifc.ifc_req);
       uv__close(sockfd);
       return UV_ENOSYS;
     }
@@ -455,22 +526,43 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
 
     /* All conditions above must match count loop */
 
-    address->name = uv__strdup(p->ifr_name);
+    i = 0;
+    /* Ignore EBCDIC space (0x40) padding in name */
+    while (i < ARRAY_SIZE(p->ifr_name) &&
+           p->ifr_name[i] != 0x40 &&
+           p->ifr_name[i] != 0)
+      ++i;
+    address->name = uv__malloc(i + 1);
+    if (address->name == NULL) {
+      uv_free_interface_addresses(*addresses, count_names);
+      uv__free(ifc.ifc_req);
+      uv__close(sockfd);
+      return UV_ENOMEM;
+    }
+    memcpy(address->name, p->ifr_name, i);
+    address->name[i] = '\0';
+    __e2a_s(address->name);
+    count_names++;
+
+    address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
 
-    if (p->ifr_addr.sa_family == AF_INET6) {
-      address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
-    } else {
-      address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
+    if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) {
+      uv_free_interface_addresses(*addresses, count_names);
+      uv__free(ifc.ifc_req);
+      uv__close(sockfd);
+      return UV__ERR(errno);
     }
 
+    address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr);
+    address->netmask.netmask4.sin_family = AF_INET;
     address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
-    memset(address->phys_addr, 0, sizeof(address->phys_addr));
     address++;
   }
 
 #undef ADDR_SIZE
 #undef MAX
 
+  uv__free(ifc.ifc_req);
   uv__close(sockfd);
   return 0;
 }
@@ -529,27 +621,17 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) {
 }
 
 
-void uv__fs_event_close(uv_fs_event_t* handle) {
-  uv_fs_event_stop(handle);
-}
-
-
 int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
   uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
   return 0;
 }
 
 
-int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
-                      const char* filename, unsigned int flags) {
+static int os390_regfileint(uv_fs_event_t* handle, char* path) {
   uv__os390_epoll* ep;
   _RFIS reg_struct;
-  char* path;
   int rc;
 
-  if (uv__is_active(handle))
-    return UV_EINVAL;
-
   ep = handle->loop->ep;
   assert(ep->msg_queue != -1);
 
@@ -558,25 +640,44 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
   reg_struct.__rfis_type = 1;
   memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle));
 
+  rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), &reg_struct);
+  if (rc != 0)
+    return UV__ERR(errno);
+
+  memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok,
+         sizeof(handle->rfis_rftok));
+
+  return 0;
+}
+
+
+int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
+                      const char* filename, unsigned int flags) {
+  char* path;
+  int rc;
+
+  if (uv__is_active(handle))
+    return UV_EINVAL;
+
   path = uv__strdup(filename);
   if (path == NULL)
     return UV_ENOMEM;
 
-  rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), &reg_struct);
-  if (rc != 0)
-    return UV__ERR(errno);
+  rc = os390_regfileint(handle, path);
+  if (rc != 0) {
+    uv__free(path);
+    return rc;
+  }
 
   uv__handle_start(handle);
   handle->path = path;
   handle->cb = cb;
-  memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok,
-         sizeof(handle->rfis_rftok));
 
   return 0;
 }
 
 
-int uv_fs_event_stop(uv_fs_event_t* handle) {
+int uv__fs_event_stop(uv_fs_event_t* handle) {
   uv__os390_epoll* ep;
   _RFIS reg_struct;
   int rc;
@@ -602,12 +703,40 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
   if (rc != 0 && errno != EALREADY && errno != ENOENT)
     abort();
 
+  if (handle->path != NULL) {
+    uv__free(handle->path);
+    handle->path = NULL;
+  }
+
+  if (rc != 0 && errno == EALREADY)
+    return -1;
+
   uv__handle_stop(handle);
 
   return 0;
 }
 
 
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  uv__fs_event_stop(handle);
+  return 0;
+}
+
+
+void uv__fs_event_close(uv_fs_event_t* handle) {
+  /*
+   * If we were unable to unregister file interest here, then it is most likely
+   * that there is a pending queued change notification. When this happens, we
+   * don't want to complete the close as it will free the underlying memory for
+   * the handle, causing a use-after-free problem when the event is processed.
+   * We defer the final cleanup until after the event is consumed in
+   * os390_message_queue_handler().
+   */
+  if (uv__fs_event_stop(handle) == 0)
+    uv__make_close_pending((uv_handle_t*) handle);
+}
+
+
 static int os390_message_queue_handler(uv__os390_epoll* ep) {
   uv_fs_event_t* handle;
   int msglen;
@@ -628,7 +757,15 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) {
   events = 0;
   if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE)
     events = UV_CHANGE;
-  else if (msg.__rfim_event == _RFIM_RENAME)
+  else if (msg.__rfim_event == _RFIM_RENAME || msg.__rfim_event == _RFIM_UNLINK)
+    events = UV_RENAME;
+  else if (msg.__rfim_event == 156)
+    /* TODO(gabylb): zos - this event should not happen, need to investigate.
+     *
+     * This event seems to occur when the watched file is [re]moved, or an
+     * editor (like vim) renames then creates the file on save (for vim, that's
+     * when backupcopy=no|auto).
+     */
     events = UV_RENAME;
   else
     /* Some event that we are not interested in. */
@@ -639,6 +776,26 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) {
    */
   __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok));
   handle = *(uv_fs_event_t**)(msg.__rfim_utok);
+  assert(handle != NULL);
+
+  assert((handle->flags & UV_HANDLE_CLOSED) == 0);
+  if (uv__is_closing(handle)) {
+    uv__handle_stop(handle);
+    uv__make_close_pending((uv_handle_t*) handle);
+    return 0;
+  } else if (handle->path == NULL) {
+    /* _RFIS_UNREG returned EALREADY. */
+    uv__handle_stop(handle);
+    return 0;
+  }
+
+  /* The file is implicitly unregistered when the change notification is
+   * sent, only one notification is sent per registration. So we need to
+   * re-register interest in a file after each change notification we
+   * receive.
+   */
+  assert(handle->path != NULL);
+  os390_regfileint(handle, handle->path);
   handle->cb(handle, uv__basename_r(handle->path), events, 0);
   return 1;
 }
@@ -650,6 +807,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
   struct epoll_event* pe;
   struct epoll_event e;
   uv__os390_epoll* ep;
+  int have_signals;
   int real_timeout;
   QUEUE* q;
   uv__io_t* w;
@@ -712,6 +870,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
   count = 48; /* Benchmarks suggest this gives the best throughput. */
   real_timeout = timeout;
   int nevents = 0;
+  have_signals = 0;
 
   if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
     reset_timeout = 1;
@@ -796,6 +955,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
       ep = loop->ep;
       if (pe->is_msg) {
         os390_message_queue_handler(ep);
+        nevents++;
         continue;
       }
 
@@ -825,19 +985,35 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
         pe->events |= w->pevents & (POLLIN | POLLOUT);
 
       if (pe->events != 0) {
-        uv__metrics_update_idle_time(loop);
-        w->cb(loop, w, pe->events);
+        /* Run signal watchers last.  This also affects child process watchers
+         * because those are implemented in terms of signal watchers.
+         */
+        if (w == &loop->signal_io_watcher) {
+          have_signals = 1;
+        } else {
+          uv__metrics_update_idle_time(loop);
+          w->cb(loop, w, pe->events);
+        }
         nevents++;
       }
     }
-    loop->watchers[loop->nwatchers] = NULL;
-    loop->watchers[loop->nwatchers + 1] = NULL;
 
     if (reset_timeout != 0) {
       timeout = user_timeout;
       reset_timeout = 0;
     }
 
+    if (have_signals != 0) {
+      uv__metrics_update_idle_time(loop);
+      loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
+    }
+
+    loop->watchers[loop->nwatchers] = NULL;
+    loop->watchers[loop->nwatchers + 1] = NULL;
+
+    if (have_signals != 0)
+      return;  /* Event loop should cycle now so don't poll again. */
+
     if (nevents != 0) {
       if (nfds == ARRAY_SIZE(events) && --count != 0) {
         /* Poll for more events but don't block this time. */
@@ -872,6 +1048,5 @@ int uv__io_fork(uv_loop_t* loop) {
   */
   loop->ep = NULL;
 
-  uv__platform_loop_delete(loop);
   return uv__platform_loop_init(loop);
 }
index fc7c4ea..e9b88c1 100644 (file)
@@ -49,7 +49,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
   /* Already bound? */
   if (uv__stream_fd(handle) >= 0)
     return UV_EINVAL;
-
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
   /* Make a copy of the file name, it outlives this function's scope. */
   pipe_fname = uv__strdup(name);
   if (pipe_fname == NULL)
@@ -89,7 +91,7 @@ err_socket:
 }
 
 
-int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
   if (uv__stream_fd(handle) == -1)
     return UV_EINVAL;
 
@@ -317,7 +319,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
   if (handle->accepted_fd == -1)
     return UV_UNKNOWN_HANDLE;
   else
-    return uv__handle_type(handle->accepted_fd);
+    return uv_guess_handle(handle->accepted_fd);
 }
 
 
index 2af2088..0de5c46 100644 (file)
@@ -27,6 +27,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <signal.h>
+#include <string.h>
 
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <poll.h>
 #include <sched.h>
 
-#if defined(__APPLE__) && !TARGET_OS_IPHONE
+#if defined(__APPLE__)
+# include <spawn.h>
+# include <paths.h>
+# include <sys/kauth.h>
+# include <sys/types.h>
+# include <sys/sysctl.h>
+# include <dlfcn.h>
 # include <crt_externs.h>
+# include <xlocale.h>
 # define environ (*_NSGetEnviron())
+
+/* macOS 10.14 back does not define this constant */
+# ifndef POSIX_SPAWN_SETSID
+#  define POSIX_SPAWN_SETSID 1024
+# endif
+
 #else
 extern char **environ;
 #endif
@@ -61,21 +75,35 @@ extern char **environ;
 #endif
 #endif
 
+#if defined(__APPLE__) || \
+    defined(__DragonFly__) || \
+    defined(__FreeBSD__) || \
+    defined(__NetBSD__) || \
+    defined(__OpenBSD__)
+#include <sys/event.h>
+#else
+#define UV_USE_SIGCHLD
+#endif
+
+#ifdef UV_USE_SIGCHLD
 static void uv__chld(uv_signal_t* handle, int signum) {
+  assert(signum == SIGCHLD);
+  uv__wait_children(handle->loop);
+}
+#endif
+
+void uv__wait_children(uv_loop_t* loop) {
   uv_process_t* process;
-  uv_loop_t* loop;
   int exit_status;
   int term_signal;
   int status;
+  int options;
   pid_t pid;
   QUEUE pending;
   QUEUE* q;
   QUEUE* h;
 
-  assert(signum == SIGCHLD);
-
   QUEUE_INIT(&pending);
-  loop = handle->loop;
 
   h = &loop->process_handles;
   q = QUEUE_HEAD(h);
@@ -83,19 +111,33 @@ static void uv__chld(uv_signal_t* handle, int signum) {
     process = QUEUE_DATA(q, uv_process_t, queue);
     q = QUEUE_NEXT(q);
 
+#ifndef UV_USE_SIGCHLD
+    if ((process->flags & UV_HANDLE_REAP) == 0)
+      continue;
+    options = 0;
+    process->flags &= ~UV_HANDLE_REAP;
+#else
+    options = WNOHANG;
+#endif
+
     do
-      pid = waitpid(process->pid, &status, WNOHANG);
+      pid = waitpid(process->pid, &status, options);
     while (pid == -1 && errno == EINTR);
 
-    if (pid == 0)
+#ifdef UV_USE_SIGCHLD
+    if (pid == 0) /* Not yet exited */
       continue;
+#endif
 
     if (pid == -1) {
       if (errno != ECHILD)
         abort();
+      /* The child died, and we missed it. This probably means someone else
+       * stole the waitpid from us. Handle this by not handling it at all. */
       continue;
     }
 
+    assert(pid == process->pid);
     process->status = status;
     QUEUE_REMOVE(&process->queue);
     QUEUE_INSERT_TAIL(&pending, &process->queue);
@@ -206,16 +248,14 @@ static void uv__write_int(int fd, int val) {
     n = write(fd, &val, sizeof(val));
   while (n == -1 && errno == EINTR);
 
-  if (n == -1 && errno == EPIPE)
-    return; /* parent process has quit */
-
-  assert(n == sizeof(val));
+  /* The write might have failed (e.g. if the parent process has died),
+   * but we have nothing left but to _exit ourself now too. */
+  _exit(127);
 }
 
 
 static void uv__write_errno(int error_fd) {
   uv__write_int(error_fd, UV__ERR(errno));
-  _exit(127);
 }
 
 
@@ -273,22 +313,31 @@ static void uv__process_child_init(const uv_process_options_t* options,
     use_fd = pipes[fd][1];
     if (use_fd < 0 || use_fd >= fd)
       continue;
+#ifdef F_DUPFD_CLOEXEC /* POSIX 2008 */
+    pipes[fd][1] = fcntl(use_fd, F_DUPFD_CLOEXEC, stdio_count);
+#else
     pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count);
+#endif
     if (pipes[fd][1] == -1)
       uv__write_errno(error_fd);
+#ifndef F_DUPFD_CLOEXEC /* POSIX 2008 */
+    n = uv__cloexec(pipes[fd][1], 1);
+    if (n)
+      uv__write_int(error_fd, n);
+#endif
   }
 
   for (fd = 0; fd < stdio_count; fd++) {
-    close_fd = pipes[fd][0];
+    close_fd = -1;
     use_fd = pipes[fd][1];
 
     if (use_fd < 0) {
       if (fd >= 3)
         continue;
       else {
-        /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
-         * set
-         */
+        /* Redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is
+         * set. */
+        uv__close_nocheckstdio(fd); /* Free up fd, if it happens to be open. */
         use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
         close_fd = use_fd;
 
@@ -297,28 +346,27 @@ static void uv__process_child_init(const uv_process_options_t* options,
       }
     }
 
-    if (fd == use_fd)
-      uv__cloexec_fcntl(use_fd, 0);
-    else
+    if (fd == use_fd) {
+      if (close_fd == -1) {
+        n = uv__cloexec(use_fd, 0);
+        if (n)
+          uv__write_int(error_fd, n);
+      }
+    }
+    else {
       fd = dup2(use_fd, fd);
+    }
 
     if (fd == -1)
       uv__write_errno(error_fd);
 
-    if (fd <= 2)
+    if (fd <= 2 && close_fd == -1)
       uv__nonblock_fcntl(fd, 0);
 
     if (close_fd >= stdio_count)
       uv__close(close_fd);
   }
 
-  for (fd = 0; fd < stdio_count; fd++) {
-    use_fd = pipes[fd][1];
-
-    if (use_fd >= stdio_count)
-      uv__close(use_fd);
-  }
-
   if (options->cwd != NULL && chdir(options->cwd))
     uv__write_errno(error_fd);
 
@@ -361,9 +409,8 @@ static void uv__process_child_init(const uv_process_options_t* options,
 #endif
 #endif
 
-  if (options->env != NULL) {
+  if (options->env != NULL)
     environ = options->env;
-  }
 
   /* Reset signal mask just before exec. */
   sigemptyset(&signewset);
@@ -377,11 +424,555 @@ static void uv__process_child_init(const uv_process_options_t* options,
 #endif
 
   uv__write_errno(error_fd);
-  abort();
 }
 #endif
 
 
+#if defined(__APPLE__)
+typedef struct uv__posix_spawn_fncs_tag {
+  struct {
+    int (*addchdir_np)(const posix_spawn_file_actions_t *, const char *);
+  } file_actions;
+} uv__posix_spawn_fncs_t;
+
+
+static uv_once_t posix_spawn_init_once = UV_ONCE_INIT;
+static uv__posix_spawn_fncs_t posix_spawn_fncs;
+static int posix_spawn_can_use_setsid;
+
+
+static void uv__spawn_init_posix_spawn_fncs(void) {
+  /* Try to locate all non-portable functions at runtime */
+  posix_spawn_fncs.file_actions.addchdir_np =
+    dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np");
+}
+
+
+static void uv__spawn_init_can_use_setsid(void) {
+  int which[] = {CTL_KERN, KERN_OSRELEASE};
+  unsigned major;
+  unsigned minor;
+  unsigned patch;
+  char buf[256];
+  size_t len;
+
+  len = sizeof(buf);
+  if (sysctl(which, ARRAY_SIZE(which), buf, &len, NULL, 0))
+    return;
+
+  /* NULL specifies to use LC_C_LOCALE */
+  if (3 != sscanf_l(buf, NULL, "%u.%u.%u", &major, &minor, &patch))
+    return;
+
+  posix_spawn_can_use_setsid = (major >= 19);  /* macOS Catalina */
+}
+
+
+static void uv__spawn_init_posix_spawn(void) {
+  /* Init handles to all potentially non-defined functions */
+  uv__spawn_init_posix_spawn_fncs();
+
+  /* Init feature detection for POSIX_SPAWN_SETSID flag */
+  uv__spawn_init_can_use_setsid();
+}
+
+
+static int uv__spawn_set_posix_spawn_attrs(
+    posix_spawnattr_t* attrs,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs,
+    const uv_process_options_t* options) {
+  int err;
+  unsigned int flags;
+  sigset_t signal_set;
+
+  err = posix_spawnattr_init(attrs);
+  if (err != 0) {
+    /* If initialization fails, no need to de-init, just return */
+    return err;
+  }
+
+  if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) {
+    /* kauth_cred_issuser currently requires exactly uid == 0 for these
+     * posixspawn_attrs (set_groups_np, setuid_np, setgid_np), which deviates
+     * from the normal specification of setuid (which also uses euid), and they
+     * are also undocumented syscalls, so we do not use them. */
+    err = ENOSYS;
+    goto error;
+  }
+
+  /* Set flags for spawn behavior
+   * 1) POSIX_SPAWN_CLOEXEC_DEFAULT: (Apple Extension) All descriptors in the
+   *    parent will be treated as if they had been created with O_CLOEXEC. The
+   *    only fds that will be passed on to the child are those manipulated by
+   *    the file actions
+   * 2) POSIX_SPAWN_SETSIGDEF: Signals mentioned in spawn-sigdefault in the
+   *    spawn attributes will be reset to behave as their default
+   * 3) POSIX_SPAWN_SETSIGMASK: Signal mask will be set to the value of
+   *    spawn-sigmask in attributes
+   * 4) POSIX_SPAWN_SETSID: Make the process a new session leader if a detached
+   *    session was requested. */
+  flags = POSIX_SPAWN_CLOEXEC_DEFAULT |
+          POSIX_SPAWN_SETSIGDEF |
+          POSIX_SPAWN_SETSIGMASK;
+  if (options->flags & UV_PROCESS_DETACHED) {
+    /* If running on a version of macOS where this flag is not supported,
+     * revert back to the fork/exec flow. Otherwise posix_spawn will
+     * silently ignore the flag. */
+    if (!posix_spawn_can_use_setsid) {
+      err = ENOSYS;
+      goto error;
+    }
+
+    flags |= POSIX_SPAWN_SETSID;
+  }
+  err = posix_spawnattr_setflags(attrs, flags);
+  if (err != 0)
+    goto error;
+
+  /* Reset all signal the child to their default behavior */
+  sigfillset(&signal_set);
+  err = posix_spawnattr_setsigdefault(attrs, &signal_set);
+  if (err != 0)
+    goto error;
+
+  /* Reset the signal mask for all signals */
+  sigemptyset(&signal_set);
+  err = posix_spawnattr_setsigmask(attrs, &signal_set);
+  if (err != 0)
+    goto error;
+
+  return err;
+
+error:
+  (void) posix_spawnattr_destroy(attrs);
+  return err;
+}
+
+
+static int uv__spawn_set_posix_spawn_file_actions(
+    posix_spawn_file_actions_t* actions,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs,
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2]) {
+  int fd;
+  int fd2;
+  int use_fd;
+  int err;
+
+  err = posix_spawn_file_actions_init(actions);
+  if (err != 0) {
+    /* If initialization fails, no need to de-init, just return */
+    return err;
+  }
+
+  /* Set the current working directory if requested */
+  if (options->cwd != NULL) {
+    if (posix_spawn_fncs->file_actions.addchdir_np == NULL) {
+      err = ENOSYS;
+      goto error;
+    }
+
+    err = posix_spawn_fncs->file_actions.addchdir_np(actions, options->cwd);
+    if (err != 0)
+      goto error;
+  }
+
+  /* Do not return ENOSYS after this point, as we may mutate pipes. */
+
+  /* First duplicate low numbered fds, since it's not safe to duplicate them,
+   * they could get replaced. Example: swapping stdout and stderr; without
+   * this fd 2 (stderr) would be duplicated into fd 1, thus making both
+   * stdout and stderr go to the same fd, which was not the intention. */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0 || use_fd >= fd)
+      continue;
+    use_fd = stdio_count;
+    for (fd2 = 0; fd2 < stdio_count; fd2++) {
+      /* If we were not setting POSIX_SPAWN_CLOEXEC_DEFAULT, we would need to
+       * also consider whether fcntl(fd, F_GETFD) returned without the
+       * FD_CLOEXEC flag set. */
+      if (pipes[fd2][1] == use_fd) {
+        use_fd++;
+        fd2 = 0;
+      }
+    }
+    err = posix_spawn_file_actions_adddup2(
+      actions,
+      pipes[fd][1],
+      use_fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+    pipes[fd][1] = use_fd;
+  }
+
+  /* Second, move the descriptors into their respective places */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < 0) {
+      if (fd >= 3)
+        continue;
+      else {
+        /* If ignored, redirect to (or from) /dev/null, */
+        err = posix_spawn_file_actions_addopen(
+          actions,
+          fd,
+          "/dev/null",
+          fd == 0 ? O_RDONLY : O_RDWR,
+          0);
+        assert(err != ENOSYS);
+        if (err != 0)
+          goto error;
+        continue;
+      }
+    }
+
+    if (fd == use_fd)
+        err = posix_spawn_file_actions_addinherit_np(actions, fd);
+    else
+        err = posix_spawn_file_actions_adddup2(actions, use_fd, fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+
+    /* Make sure the fd is marked as non-blocking (state shared between child
+     * and parent). */
+    uv__nonblock_fcntl(use_fd, 0);
+  }
+
+  /* Finally, close all the superfluous descriptors */
+  for (fd = 0; fd < stdio_count; fd++) {
+    use_fd = pipes[fd][1];
+    if (use_fd < stdio_count)
+      continue;
+
+    /* Check if we already closed this. */
+    for (fd2 = 0; fd2 < fd; fd2++) {
+      if (pipes[fd2][1] == use_fd)
+          break;
+    }
+    if (fd2 < fd)
+      continue;
+
+    err = posix_spawn_file_actions_addclose(actions, use_fd);
+    assert(err != ENOSYS);
+    if (err != 0)
+      goto error;
+  }
+
+  return 0;
+
+error:
+  (void) posix_spawn_file_actions_destroy(actions);
+  return err;
+}
+
+char* uv__spawn_find_path_in_env(char** env) {
+  char** env_iterator;
+  const char path_var[] = "PATH=";
+
+  /* Look for an environment variable called PATH in the
+   * provided env array, and return its value if found */
+  for (env_iterator = env; *env_iterator != NULL; env_iterator++) {
+    if (strncmp(*env_iterator, path_var, sizeof(path_var) - 1) == 0) {
+      /* Found "PATH=" at the beginning of the string */
+      return *env_iterator + sizeof(path_var) - 1;
+    }
+  }
+
+  return NULL;
+}
+
+
+static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
+                                       posix_spawnattr_t* attrs,
+                                       posix_spawn_file_actions_t* actions,
+                                       pid_t* pid) {
+  const char *p;
+  const char *z;
+  const char *path;
+  size_t l;
+  size_t k;
+  int err;
+  int seen_eacces;
+
+  path = NULL;
+  err = -1;
+  seen_eacces = 0;
+
+  /* Short circuit for erroneous case */
+  if (options->file == NULL)
+    return ENOENT;
+
+  /* The environment for the child process is that of the parent unless overriden
+   * by options->env */
+  char** env = environ;
+  if (options->env != NULL)
+    env = options->env;
+
+  /* If options->file contains a slash, posix_spawn/posix_spawnp should behave
+   * the same, and do not involve PATH resolution at all. The libc
+   * `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it
+   * here, per https://github.com/libuv/libuv/pull/3583. */
+  if (strchr(options->file, '/') != NULL) {
+    do
+      err = posix_spawn(pid, options->file, actions, attrs, options->args, env);
+    while (err == EINTR);
+    return err;
+  }
+
+  /* Look for the definition of PATH in the provided env */
+  path = uv__spawn_find_path_in_env(env);
+
+  /* The following resolution logic (execvpe emulation) is copied from
+   * https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c
+   * and adapted to work for our specific usage */
+
+  /* If no path was provided in env, use the default value
+   * to look for the executable */
+  if (path == NULL)
+    path = _PATH_DEFPATH;
+
+  k = strnlen(options->file, NAME_MAX + 1);
+  if (k > NAME_MAX)
+    return ENAMETOOLONG;
+
+  l = strnlen(path, PATH_MAX - 1) + 1;
+
+  for (p = path;; p = z) {
+    /* Compose the new process file from the entry in the PATH
+     * environment variable and the actual file name */
+    char b[PATH_MAX + NAME_MAX];
+    z = strchr(p, ':');
+    if (!z)
+      z = p + strlen(p);
+    if ((size_t)(z - p) >= l) {
+      if (!*z++)
+        break;
+
+      continue;
+    }
+    memcpy(b, p, z - p);
+    b[z - p] = '/';
+    memcpy(b + (z - p) + (z > p), options->file, k + 1);
+
+    /* Try to spawn the new process file. If it fails with ENOENT, the
+     * new process file is not in this PATH entry, continue with the next
+     * PATH entry. */
+    do
+      err = posix_spawn(pid, b, actions, attrs, options->args, env);
+    while (err == EINTR);
+
+    switch (err) {
+    case EACCES:
+      seen_eacces = 1;
+      break; /* continue search */
+    case ENOENT:
+    case ENOTDIR:
+      break; /* continue search */
+    default:
+      return err;
+    }
+
+    if (!*z++)
+      break;
+  }
+
+  if (seen_eacces)
+    return EACCES;
+  return err;
+}
+
+
+static int uv__spawn_and_init_child_posix_spawn(
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2],
+    pid_t* pid,
+    const uv__posix_spawn_fncs_t* posix_spawn_fncs) {
+  int err;
+  posix_spawnattr_t attrs;
+  posix_spawn_file_actions_t actions;
+
+  err = uv__spawn_set_posix_spawn_attrs(&attrs, posix_spawn_fncs, options);
+  if (err != 0)
+    goto error;
+
+  /* This may mutate pipes. */
+  err = uv__spawn_set_posix_spawn_file_actions(&actions,
+                                               posix_spawn_fncs,
+                                               options,
+                                               stdio_count,
+                                               pipes);
+  if (err != 0) {
+    (void) posix_spawnattr_destroy(&attrs);
+    goto error;
+  }
+
+  /* Try to spawn options->file resolving in the provided environment
+   * if any */
+  err = uv__spawn_resolve_and_spawn(options, &attrs, &actions, pid);
+  assert(err != ENOSYS);
+
+  /* Destroy the actions/attributes */
+  (void) posix_spawn_file_actions_destroy(&actions);
+  (void) posix_spawnattr_destroy(&attrs);
+
+error:
+  /* In an error situation, the attributes and file actions are
+   * already destroyed, only the happy path requires cleanup */
+  return UV__ERR(err);
+}
+#endif
+
+static int uv__spawn_and_init_child_fork(const uv_process_options_t* options,
+                                         int stdio_count,
+                                         int (*pipes)[2],
+                                         int error_fd,
+                                         pid_t* pid) {
+  sigset_t signewset;
+  sigset_t sigoldset;
+
+  /* Start the child with most signals blocked, to avoid any issues before we
+   * can reset them, but allow program failures to exit (and not hang). */
+  sigfillset(&signewset);
+  sigdelset(&signewset, SIGKILL);
+  sigdelset(&signewset, SIGSTOP);
+  sigdelset(&signewset, SIGTRAP);
+  sigdelset(&signewset, SIGSEGV);
+  sigdelset(&signewset, SIGBUS);
+  sigdelset(&signewset, SIGILL);
+  sigdelset(&signewset, SIGSYS);
+  sigdelset(&signewset, SIGABRT);
+  if (pthread_sigmask(SIG_BLOCK, &signewset, &sigoldset) != 0)
+    abort();
+
+  *pid = fork();
+
+  if (*pid == 0) {
+    /* Fork succeeded, in the child process */
+    uv__process_child_init(options, stdio_count, pipes, error_fd);
+    abort();
+  }
+
+  if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0)
+    abort();
+
+  if (*pid == -1)
+    /* Failed to fork */
+    return UV__ERR(errno);
+
+  /* Fork succeeded, in the parent process */
+  return 0;
+}
+
+static int uv__spawn_and_init_child(
+    uv_loop_t* loop,
+    const uv_process_options_t* options,
+    int stdio_count,
+    int (*pipes)[2],
+    pid_t* pid) {
+  int signal_pipe[2] = { -1, -1 };
+  int status;
+  int err;
+  int exec_errorno;
+  ssize_t r;
+
+#if defined(__APPLE__)
+  uv_once(&posix_spawn_init_once, uv__spawn_init_posix_spawn);
+
+  /* Special child process spawn case for macOS Big Sur (11.0) onwards
+   *
+   * Big Sur introduced a significant performance degradation on a call to
+   * fork/exec when the process has many pages mmaped in with MAP_JIT, like, say
+   * a javascript interpreter. Electron-based applications, for example,
+   * are impacted; though the magnitude of the impact depends on how much the
+   * app relies on subprocesses.
+   *
+   * On macOS, though, posix_spawn is implemented in a way that does not
+   * exhibit the problem. This block implements the forking and preparation
+   * logic with posix_spawn and its related primitives. It also takes advantage of
+   * the macOS extension POSIX_SPAWN_CLOEXEC_DEFAULT that makes impossible to
+   * leak descriptors to the child process. */
+  err = uv__spawn_and_init_child_posix_spawn(options,
+                                             stdio_count,
+                                             pipes,
+                                             pid,
+                                             &posix_spawn_fncs);
+
+  /* The posix_spawn flow will return UV_ENOSYS if any of the posix_spawn_x_np
+   * non-standard functions is both _needed_ and _undefined_. In those cases,
+   * default back to the fork/execve strategy. For all other errors, just fail. */
+  if (err != UV_ENOSYS)
+    return err;
+
+#endif
+
+  /* This pipe is used by the parent to wait until
+   * the child has called `execve()`. We need this
+   * to avoid the following race condition:
+   *
+   *    if ((pid = fork()) > 0) {
+   *      kill(pid, SIGTERM);
+   *    }
+   *    else if (pid == 0) {
+   *      execve("/bin/cat", argp, envp);
+   *    }
+   *
+   * The parent sends a signal immediately after forking.
+   * Since the child may not have called `execve()` yet,
+   * there is no telling what process receives the signal,
+   * our fork or /bin/cat.
+   *
+   * To avoid ambiguity, we create a pipe with both ends
+   * marked close-on-exec. Then, after the call to `fork()`,
+   * the parent polls the read end until it EOFs or errors with EPIPE.
+   */
+  err = uv__make_pipe(signal_pipe, 0);
+  if (err)
+    return err;
+
+  /* Acquire write lock to prevent opening new fds in worker threads */
+  uv_rwlock_wrlock(&loop->cloexec_lock);
+
+  err = uv__spawn_and_init_child_fork(options, stdio_count, pipes, signal_pipe[1], pid);
+
+  /* Release lock in parent process */
+  uv_rwlock_wrunlock(&loop->cloexec_lock);
+
+  uv__close(signal_pipe[1]);
+
+  if (err == 0) {
+    do
+      r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
+    while (r == -1 && errno == EINTR);
+
+    if (r == 0)
+      ; /* okay, EOF */
+    else if (r == sizeof(exec_errorno)) {
+      do
+        err = waitpid(*pid, &status, 0); /* okay, read errorno */
+      while (err == -1 && errno == EINTR);
+      assert(err == *pid);
+      err = exec_errorno;
+    } else if (r == -1 && errno == EPIPE) {
+      /* Something unknown happened to our child before spawn */
+      do
+        err = waitpid(*pid, &status, 0); /* okay, got EPIPE */
+      while (err == -1 && errno == EINTR);
+      assert(err == *pid);
+      err = UV_EPIPE;
+    } else
+      abort();
+  }
+
+  uv__close_nocheckstdio(signal_pipe[0]);
+
+  return err;
+}
+
 int uv_spawn(uv_loop_t* loop,
              uv_process_t* process,
              const uv_process_options_t* options) {
@@ -389,18 +980,13 @@ int uv_spawn(uv_loop_t* loop,
   /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */
   return UV_ENOSYS;
 #else
-  sigset_t signewset;
-  sigset_t sigoldset;
-  int signal_pipe[2] = { -1, -1 };
   int pipes_storage[8][2];
   int (*pipes)[2];
   int stdio_count;
-  ssize_t r;
   pid_t pid;
   int err;
   int exec_errorno;
   int i;
-  int status;
 
   if (options->cpumask != NULL) {
 #ifndef CMAKE_BOOTSTRAP
@@ -427,6 +1013,7 @@ int uv_spawn(uv_loop_t* loop,
 
   uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
   QUEUE_INIT(&process->queue);
+  process->status = 0;
 
   stdio_count = options->stdio_count;
   if (stdio_count < 3)
@@ -451,92 +1038,43 @@ int uv_spawn(uv_loop_t* loop,
       goto error;
   }
 
-  /* This pipe is used by the parent to wait until
-   * the child has called `execve()`. We need this
-   * to avoid the following race condition:
-   *
-   *    if ((pid = fork()) > 0) {
-   *      kill(pid, SIGTERM);
-   *    }
-   *    else if (pid == 0) {
-   *      execve("/bin/cat", argp, envp);
-   *    }
-   *
-   * The parent sends a signal immediately after forking.
-   * Since the child may not have called `execve()` yet,
-   * there is no telling what process receives the signal,
-   * our fork or /bin/cat.
-   *
-   * To avoid ambiguity, we create a pipe with both ends
-   * marked close-on-exec. Then, after the call to `fork()`,
-   * the parent polls the read end until it EOFs or errors with EPIPE.
-   */
-  err = uv__make_pipe(signal_pipe, 0);
-  if (err)
-    goto error;
-
+#ifdef UV_USE_SIGCHLD
   uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD);
+#endif
 
-  /* Acquire write lock to prevent opening new fds in worker threads */
-  uv_rwlock_wrlock(&loop->cloexec_lock);
-
-  /* Start the child with most signals blocked, to avoid any issues before we
-   * can reset them, but allow program failures to exit (and not hang). */
-  sigfillset(&signewset);
-  sigdelset(&signewset, SIGKILL);
-  sigdelset(&signewset, SIGSTOP);
-  sigdelset(&signewset, SIGTRAP);
-  sigdelset(&signewset, SIGSEGV);
-  sigdelset(&signewset, SIGBUS);
-  sigdelset(&signewset, SIGILL);
-  sigdelset(&signewset, SIGSYS);
-  sigdelset(&signewset, SIGABRT);
-  if (pthread_sigmask(SIG_BLOCK, &signewset, &sigoldset) != 0)
-    abort();
-
-  pid = fork();
-  if (pid == -1)
-    err = UV__ERR(errno);
-
-  if (pid == 0)
-    uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]);
-
-  if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0)
-    abort();
+  /* Spawn the child */
+  exec_errorno = uv__spawn_and_init_child(loop, options, stdio_count, pipes, &pid);
 
-  /* Release lock in parent process */
-  uv_rwlock_wrunlock(&loop->cloexec_lock);
+#if 0
+  /* This runs into a nodejs issue (it expects initialized streams, even if the
+   * exec failed).
+   * See https://github.com/libuv/libuv/pull/3107#issuecomment-782482608 */
+  if (exec_errorno != 0)
+      goto error;
+#endif
 
-  uv__close(signal_pipe[1]);
+  /* Activate this handle if exec() happened successfully, even if we later
+   * fail to open a stdio handle. This ensures we can eventually reap the child
+   * with waitpid. */
+  if (exec_errorno == 0) {
+#ifndef UV_USE_SIGCHLD
+    struct kevent event;
+    EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0);
+    if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) {
+      if (errno != ESRCH)
+        abort();
+      /* Process already exited. Call waitpid on the next loop iteration. */
+      process->flags |= UV_HANDLE_REAP;
+      loop->flags |= UV_LOOP_REAP_CHILDREN;
+    }
+#endif
 
-  if (pid == -1) {
-    uv__close(signal_pipe[0]);
-    goto error;
+    process->pid = pid;
+    process->exit_cb = options->exit_cb;
+    QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
+    uv__handle_start(process);
   }
 
-  process->status = 0;
-  exec_errorno = 0;
-  do
-    r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
-  while (r == -1 && errno == EINTR);
-
-  if (r == 0)
-    ; /* okay, EOF */
-  else if (r == sizeof(exec_errorno)) {
-    do
-      err = waitpid(pid, &status, 0); /* okay, read errorno */
-    while (err == -1 && errno == EINTR);
-    assert(err == pid);
-  } else if (r == -1 && errno == EPIPE) {
-    do
-      err = waitpid(pid, &status, 0); /* okay, got EPIPE */
-    while (err == -1 && errno == EINTR);
-    assert(err == pid);
-  } else
-    abort();
-
-  uv__close_nocheckstdio(signal_pipe[0]);
-
   for (i = 0; i < options->stdio_count; i++) {
     err = uv__process_open_stream(options->stdio + i, pipes[i]);
     if (err == 0)
@@ -548,15 +1086,6 @@ int uv_spawn(uv_loop_t* loop,
     goto error;
   }
 
-  /* Only activate this handle if exec() happened successfully */
-  if (exec_errorno == 0) {
-    QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue);
-    uv__handle_start(process);
-  }
-
-  process->pid = pid;
-  process->exit_cb = options->exit_cb;
-
   if (pipes != pipes_storage)
     uv__free(pipes);
 
@@ -589,9 +1118,16 @@ int uv_process_kill(uv_process_t* process, int signum) {
 
 
 int uv_kill(int pid, int signum) {
-  if (kill(pid, signum))
+  if (kill(pid, signum)) {
+#if defined(__MVS__)
+    /* EPERM is returned if the process is a zombie. */
+    siginfo_t infop;
+    if (errno == EPERM &&
+        waitid(P_PID, pid, &infop, WNOHANG | WNOWAIT | WEXITED) == 0)
+      return 0;
+#endif
     return UV__ERR(errno);
-  else
+  else
     return 0;
 }
 
index 52e2b9a..1ce7525 100644 (file)
@@ -66,6 +66,7 @@ static void uv__read(uv_stream_t* stream);
 static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
 static void uv__write_callbacks(uv_stream_t* stream);
 static size_t uv__write_req_size(uv_write_t* req);
+static void uv__drain(uv_stream_t* stream);
 
 
 void uv__stream_init(uv_loop_t* loop,
@@ -453,17 +454,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
 
   uv__stream_flush_write_queue(stream, UV_ECANCELED);
   uv__write_callbacks(stream);
-
-  if (stream->shutdown_req) {
-    /* The ECANCELED error code is a lie, the shutdown(2) syscall is a
-     * fait accompli at this point. Maybe we should revisit this in v0.11.
-     * A possible reason for leaving it unchanged is that it informs the
-     * callee that the handle has been destroyed.
-     */
-    uv__req_unregister(stream->loop, stream->shutdown_req);
-    stream->shutdown_req->cb(stream->shutdown_req, UV_ECANCELED);
-    stream->shutdown_req = NULL;
-  }
+  uv__drain(stream);
 
   assert(stream->write_queue_size == 0);
 }
@@ -641,14 +632,16 @@ done:
 
 int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
   int err;
-
+  if (uv__is_closing(stream)) {
+    return UV_EINVAL;
+  }
   switch (stream->type) {
   case UV_TCP:
-    err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
+    err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb);
     break;
 
   case UV_NAMED_PIPE:
-    err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
+    err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb);
     break;
 
   default:
@@ -667,25 +660,30 @@ static void uv__drain(uv_stream_t* stream) {
   int err;
 
   assert(QUEUE_EMPTY(&stream->write_queue));
-  uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
-  uv__stream_osx_interrupt_select(stream);
+  if (!(stream->flags & UV_HANDLE_CLOSING)) {
+    uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
+    uv__stream_osx_interrupt_select(stream);
+  }
 
-  /* Shutdown? */
-  if ((stream->flags & UV_HANDLE_SHUTTING) &&
-      !(stream->flags & UV_HANDLE_CLOSING) &&
-      !(stream->flags & UV_HANDLE_SHUT)) {
-    assert(stream->shutdown_req);
+  if (!(stream->flags & UV_HANDLE_SHUTTING))
+    return;
 
-    req = stream->shutdown_req;
+  req = stream->shutdown_req;
+  assert(req);
+
+  if ((stream->flags & UV_HANDLE_CLOSING) ||
+      !(stream->flags & UV_HANDLE_SHUT)) {
     stream->shutdown_req = NULL;
     stream->flags &= ~UV_HANDLE_SHUTTING;
     uv__req_unregister(stream->loop, req);
 
     err = 0;
-    if (shutdown(uv__stream_fd(stream), SHUT_WR))
+    if (stream->flags & UV_HANDLE_CLOSING)
+      /* The user destroyed the stream before we got to do the shutdown. */
+      err = UV_ECANCELED;
+    else if (shutdown(uv__stream_fd(stream), SHUT_WR))
       err = UV__ERR(errno);
-
-    if (err == 0)
+    else /* Success. */
       stream->flags |= UV_HANDLE_SHUT;
 
     if (req->cb != NULL)
@@ -926,7 +924,6 @@ static void uv__write(uv_stream_t* stream) {
   }
 
   req->error = n;
-  /* XXX(jwn): this must call uv__stream_flush_write_queue(stream, n) here, since we won't generate any more events */
   uv__write_req_finish(req);
   uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
   uv__stream_osx_interrupt_select(stream);
@@ -964,49 +961,6 @@ static void uv__write_callbacks(uv_stream_t* stream) {
 }
 
 
-uv_handle_type uv__handle_type(int fd) {
-  struct sockaddr_storage ss;
-  socklen_t sslen;
-  socklen_t len;
-  int type;
-
-  memset(&ss, 0, sizeof(ss));
-  sslen = sizeof(ss);
-
-  if (getsockname(fd, (struct sockaddr*)&ss, &sslen))
-    return UV_UNKNOWN_HANDLE;
-
-  len = sizeof type;
-
-  if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len))
-    return UV_UNKNOWN_HANDLE;
-
-  if (type == SOCK_STREAM) {
-#if defined(_AIX) || defined(__DragonFly__)
-    /* on AIX/DragonFly the getsockname call returns an empty sa structure
-     * for sockets of type AF_UNIX.  For all other types it will
-     * return a properly filled in structure.
-     */
-    if (sslen == 0)
-      return UV_NAMED_PIPE;
-#endif
-    switch (ss.ss_family) {
-      case AF_UNIX:
-        return UV_NAMED_PIPE;
-      case AF_INET:
-      case AF_INET6:
-        return UV_TCP;
-      }
-  }
-
-  if (type == SOCK_DGRAM &&
-      (ss.ss_family == AF_INET || ss.ss_family == AF_INET6))
-    return UV_UDP;
-
-  return UV_UNKNOWN_HANDLE;
-}
-
-
 static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) {
   stream->flags |= UV_HANDLE_READ_EOF;
   stream->flags &= ~UV_HANDLE_READING;
@@ -1278,7 +1232,8 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
 
   assert(uv__stream_fd(stream) >= 0);
 
-  /* Initialize request */
+  /* Initialize request. The `shutdown(2)` call will always be deferred until
+   * `uv__drain`, just before the callback is run. */
   uv__req_init(stream->loop, req, UV_SHUTDOWN);
   req->handle = stream;
   req->cb = cb;
@@ -1286,8 +1241,8 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
   stream->flags |= UV_HANDLE_SHUTTING;
   stream->flags &= ~UV_HANDLE_WRITABLE;
 
-  uv__io_start(stream->loop, &stream->io_watcher, POLLOUT);
-  uv__stream_osx_interrupt_select(stream);
+  if (QUEUE_EMPTY(&stream->write_queue))
+    uv__io_feed(stream->loop, &stream->io_watcher);
 
   return 0;
 }
index eab2e40..85e4d60 100644 (file)
@@ -158,7 +158,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
   sigset_t set;
   uint64_t base;
   uint64_t diff;
-  uint64_t idle_poll;
   unsigned int nfds;
   unsigned int i;
   int saved_errno;
@@ -428,7 +427,7 @@ void uv_loadavg(double avg[3]) {
 #if defined(PORT_SOURCE_FILE)
 
 static int uv__fs_event_rearm(uv_fs_event_t *handle) {
-  if (handle->fd == -1)
+  if (handle->fd == PORT_DELETED)
     return UV_EBADF;
 
   if (port_associate(handle->loop->fs_fd,
@@ -479,6 +478,12 @@ static void uv__fs_event_read(uv_loop_t* loop,
     handle = (uv_fs_event_t*) pe.portev_user;
     assert((r == 0) && "unexpected port_get() error");
 
+    if (uv__is_closing(handle)) {
+      uv__handle_stop(handle);
+      uv__make_close_pending((uv_handle_t*) handle);
+      break;
+    }
+
     events = 0;
     if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED))
       events |= UV_CHANGE;
@@ -546,12 +551,14 @@ int uv_fs_event_start(uv_fs_event_t* handle,
 }
 
 
-int uv_fs_event_stop(uv_fs_event_t* handle) {
+static int uv__fs_event_stop(uv_fs_event_t* handle) {
+  int ret = 0;
+
   if (!uv__is_active(handle))
     return 0;
 
-  if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) {
-    port_dissociate(handle->loop->fs_fd,
+  if (handle->fd == PORT_LOADED) {
+    ret = port_dissociate(handle->loop->fs_fd,
                     PORT_SOURCE_FILE,
                     (uintptr_t) &handle->fo);
   }
@@ -560,13 +567,28 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
   uv__free(handle->path);
   handle->path = NULL;
   handle->fo.fo_name = NULL;
-  uv__handle_stop(handle);
+  if (ret == 0)
+    uv__handle_stop(handle);
+
+  return ret;
+}
 
+int uv_fs_event_stop(uv_fs_event_t* handle) {
+  (void) uv__fs_event_stop(handle);
   return 0;
 }
 
 void uv__fs_event_close(uv_fs_event_t* handle) {
-  uv_fs_event_stop(handle);
+  /*
+   * If we were unable to dissociate the port here, then it is most likely
+   * that there is a pending queued event. When this happens, we don't want
+   * to complete the close as it will free the underlying memory for the
+   * handle, causing a use-after-free problem when the event is processed.
+   * We defer the final cleanup until after the event is consumed in
+   * uv__fs_event_read().
+   */
+  if (uv__fs_event_stop(handle) == 0)
+    uv__make_close_pending((uv_handle_t*) handle);
 }
 
 #else /* !defined(PORT_SOURCE_FILE) */
index bc0fb66..73fc657 100644 (file)
@@ -184,14 +184,15 @@ int uv__tcp_bind(uv_tcp_t* tcp,
 #endif
 
   errno = 0;
-  if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
+  err = bind(tcp->io_watcher.fd, addr, addrlen);
+  if (err == -1 && errno != EADDRINUSE) {
     if (errno == EAFNOSUPPORT)
       /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
        * socket created with AF_INET to an AF_INET6 address or vice versa. */
       return UV_EINVAL;
     return UV__ERR(errno);
   }
-  tcp->delayed_error = UV__ERR(errno);
+  tcp->delayed_error = (err == -1) ? UV__ERR(errno) : 0;
 
   tcp->flags |= UV_HANDLE_BOUND;
   if (addr->sa_family == AF_INET6)
@@ -320,15 +321,23 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
     return UV_EINVAL;
 
   fd = uv__stream_fd(handle);
-  if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)))
-    return UV__ERR(errno);
+  if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) {
+    if (errno == EINVAL) {
+      /* Open Group Specifications Issue 7, 2018 edition states that
+       * EINVAL may mean the socket has been shut down already.
+       * Behavior observed on Solaris, illumos and macOS. */
+      errno = 0;
+    } else {
+      return UV__ERR(errno);
+    }
+  }
 
   uv_close((uv_handle_t*) handle, close_cb);
   return 0;
 }
 
 
-int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
+int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
   static int single_accept_cached = -1;
   unsigned long flags;
   int single_accept;
index cbddf17..5a07b02 100644 (file)
@@ -162,12 +162,46 @@ void uv_barrier_destroy(uv_barrier_t* barrier) {
 #endif
 
 
-/* On MacOS, threads other than the main thread are created with a reduced
- * stack size by default.  Adjust to RLIMIT_STACK aligned to the page size.
+/* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
+ * too small to safely receive signals on.
+ *
+ * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
+ * the largest MINSIGSTKSZ of the architectures that musl supports) so
+ * let's use that as a lower bound.
  *
- * On Linux, threads created by musl have a much smaller stack than threads
+ * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
+ * is between 28 and 133 KB when compiling against glibc, depending
+ * on the architecture.
+ */
+static size_t uv__min_stack_size(void) {
+  static const size_t min = 8192;
+
+#ifdef PTHREAD_STACK_MIN  /* Not defined on NetBSD. */
+  if (min < (size_t) PTHREAD_STACK_MIN)
+    return PTHREAD_STACK_MIN;
+#endif  /* PTHREAD_STACK_MIN */
+
+  return min;
+}
+
+
+/* On Linux, threads created by musl have a much smaller stack than threads
  * created by glibc (80 vs. 2048 or 4096 kB.)  Follow glibc for consistency.
  */
+static size_t uv__default_stack_size(void) {
+#if !defined(__linux__)
+  return 0;
+#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)
+  return 4 << 20;  /* glibc default. */
+#else
+  return 2 << 20;  /* glibc default. */
+#endif
+}
+
+
+/* On MacOS, threads other than the main thread are created with a reduced
+ * stack size by default.  Adjust to RLIMIT_STACK aligned to the page size.
+ */
 size_t uv__thread_stack_size(void) {
 #if defined(__APPLE__) || defined(__linux__)
   struct rlimit lim;
@@ -176,34 +210,20 @@ size_t uv__thread_stack_size(void) {
    * the system call wrapper invokes the wrong system call. Don't treat
    * that as fatal, just use the default stack size instead.
    */
-  if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) {
-    /* pthread_attr_setstacksize() expects page-aligned values. */
-    lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
-
-    /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
-     * too small to safely receive signals on.
-     *
-     * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
-     * the largest MINSIGSTKSZ of the architectures that musl supports) so
-     * let's use that as a lower bound.
-     *
-     * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
-     * is between 28 and 133 KB when compiling against glibc, depending
-     * on the architecture.
-     */
-    if (lim.rlim_cur >= 8192)
-      if (lim.rlim_cur >= PTHREAD_STACK_MIN)
-        return lim.rlim_cur;
-  }
-#endif
+  if (getrlimit(RLIMIT_STACK, &lim))
+    return uv__default_stack_size();
 
-#if !defined(__linux__)
-  return 0;
-#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)
-  return 4 << 20;  /* glibc default. */
-#else
-  return 2 << 20;  /* glibc default. */
+  if (lim.rlim_cur == RLIM_INFINITY)
+    return uv__default_stack_size();
+
+  /* pthread_attr_setstacksize() expects page-aligned values. */
+  lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
+
+  if (lim.rlim_cur >= (rlim_t) uv__min_stack_size())
+    return lim.rlim_cur;
 #endif
+
+  return uv__default_stack_size();
 }
 
 
@@ -222,6 +242,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
   pthread_attr_t attr_storage;
   size_t pagesize;
   size_t stack_size;
+  size_t min_stack_size;
 
   /* Used to squelch a -Wcast-function-type warning. */
   union {
@@ -239,10 +260,9 @@ int uv_thread_create_ex(uv_thread_t* tid,
     pagesize = (size_t)getpagesize();
     /* Round up to the nearest page boundary. */
     stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
-#ifdef PTHREAD_STACK_MIN
-    if (stack_size < PTHREAD_STACK_MIN)
-      stack_size = PTHREAD_STACK_MIN;
-#endif
+    min_stack_size = uv__min_stack_size();
+    if (stack_size < min_stack_size)
+      stack_size = min_stack_size;
   }
 
   if (stack_size > 0) {
index 8cefc15..44fdb9c 100644 (file)
@@ -62,10 +62,25 @@ static int isreallyatty(int file) {
 #define isatty(fd) isreallyatty(fd)
 #endif
 
+#if !defined(CMAKE_BOOTSTRAP)
+
 static int orig_termios_fd = -1;
 static struct termios orig_termios;
 static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
 
+int uv__tcsetattr(int fd, int how, const struct termios *term) {
+  int rc;
+
+  do
+    rc = tcsetattr(fd, how, term);
+  while (rc == -1 && errno == EINTR);
+
+  if (rc == -1)
+    return UV__ERR(errno);
+
+  return 0;
+}
+
 static int uv__tty_is_slave(const int fd) {
   int result;
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -265,13 +280,18 @@ static void uv__tty_make_raw(struct termios* tio) {
 int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
   struct termios tmp;
   int fd;
+  int rc;
 
   if (tty->mode == (int) mode)
     return 0;
 
   fd = uv__stream_fd(tty);
   if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
-    if (tcgetattr(fd, &tty->orig_termios))
+    do
+      rc = tcgetattr(fd, &tty->orig_termios);
+    while (rc == -1 && errno == EINTR);
+
+    if (rc == -1)
       return UV__ERR(errno);
 
     /* This is used for uv_tty_reset_mode() */
@@ -301,11 +321,11 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
   }
 
   /* Apply changes after draining */
-  if (tcsetattr(fd, TCSADRAIN, &tmp))
-    return UV__ERR(errno);
+  rc = uv__tcsetattr(fd, TCSADRAIN, &tmp);
+  if (rc == 0)
+    tty->mode = mode;
 
-  tty->mode = mode;
-  return 0;
+  return rc;
 }
 
 
@@ -326,9 +346,10 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
   return 0;
 }
 
+#endif
 
 uv_handle_type uv_guess_handle(uv_file file) {
-  struct sockaddr sa;
+  struct sockaddr_storage ss;
   struct stat s;
   socklen_t len;
   int type;
@@ -339,8 +360,24 @@ uv_handle_type uv_guess_handle(uv_file file) {
   if (isatty(file))
     return UV_TTY;
 
-  if (fstat(file, &s))
+  if (fstat(file, &s)) {
+#if defined(__PASE__)
+    /* On ibmi receiving RST from TCP instead of FIN immediately puts fd into
+     * an error state. fstat will return EINVAL, getsockname will also return
+     * EINVAL, even if sockaddr_storage is valid. (If file does not refer to a
+     * socket, ENOTSOCK is returned instead.)
+     * In such cases, we will permit the user to open the connection as uv_tcp
+     * still, so that the user can get immediately notified of the error in
+     * their read callback and close this fd.
+     */
+    len = sizeof(ss);
+    if (getsockname(file, (struct sockaddr*) &ss, &len)) {
+      if (errno == EINVAL)
+        return UV_TCP;
+    }
+#endif
     return UV_UNKNOWN_HANDLE;
+  }
 
   if (S_ISREG(s.st_mode))
     return UV_FILE;
@@ -354,16 +391,29 @@ uv_handle_type uv_guess_handle(uv_file file) {
   if (!S_ISSOCK(s.st_mode))
     return UV_UNKNOWN_HANDLE;
 
-  len = sizeof(type);
-  if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
+  len = sizeof(ss);
+  if (getsockname(file, (struct sockaddr*) &ss, &len)) {
+#if defined(_AIX)
+    /* On aix receiving RST from TCP instead of FIN immediately puts fd into
+     * an error state. In such case getsockname will return EINVAL, even if
+     * sockaddr_storage is valid.
+     * In such cases, we will permit the user to open the connection as uv_tcp
+     * still, so that the user can get immediately notified of the error in
+     * their read callback and close this fd.
+     */
+    if (errno == EINVAL) {
+      return UV_TCP;
+    }
+#endif
     return UV_UNKNOWN_HANDLE;
+  }
 
-  len = sizeof(sa);
-  if (getsockname(file, &sa, &len))
+  len = sizeof(type);
+  if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
     return UV_UNKNOWN_HANDLE;
 
   if (type == SOCK_DGRAM)
-    if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
       return UV_UDP;
 
   if (type == SOCK_STREAM) {
@@ -376,15 +426,16 @@ uv_handle_type uv_guess_handle(uv_file file) {
       return UV_NAMED_PIPE;
 #endif /* defined(_AIX) || defined(__DragonFly__) */
 
-    if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6)
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
       return UV_TCP;
-    if (sa.sa_family == AF_UNIX)
+    if (ss.ss_family == AF_UNIX)
       return UV_NAMED_PIPE;
   }
 
   return UV_UNKNOWN_HANDLE;
 }
 
+#if !defined(CMAKE_BOOTSTRAP)
 
 /* This function is async signal-safe, meaning that it's safe to call from
  * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s
@@ -400,8 +451,7 @@ int uv_tty_reset_mode(void) {
 
   err = 0;
   if (orig_termios_fd != -1)
-    if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios))
-      err = UV__ERR(errno);
+    err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
 
   uv_spinlock_unlock(&termios_spinlock);
   errno = saved_errno;
@@ -415,3 +465,5 @@ void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
 int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
   return UV_ENOTSUP;
 }
+
+#endif
index aee8d63..4d985b8 100644 (file)
@@ -201,6 +201,7 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
   for (k = 0; k < chunks; ++k) {
     iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE;
     iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE;
+    memset(&msgs[k].msg_hdr, 0, sizeof(msgs[k].msg_hdr));
     msgs[k].msg_hdr.msg_iov = iov + k;
     msgs[k].msg_hdr.msg_iovlen = 1;
     msgs[k].msg_hdr.msg_name = peers + k;
@@ -494,7 +495,7 @@ static int uv__set_reuse(int fd) {
     if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
        return UV__ERR(errno);
   }
-#elif defined(SO_REUSEPORT) && !defined(__linux__)
+#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__)
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #else
@@ -655,16 +656,16 @@ int uv__udp_connect(uv_udp_t* handle,
 }
 
 /* From https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html
- * Any of uv supported UNIXs kernel should be standardized, but the kernel 
+ * Any of uv supported UNIXs kernel should be standardized, but the kernel
  * implementation logic not same, let's use pseudocode to explain the udp
  * disconnect behaviors:
- * 
+ *
  * Predefined stubs for pseudocode:
  *   1. sodisconnect: The function to perform the real udp disconnect
  *   2. pru_connect: The function to perform the real udp connect
  *   3. so: The kernel object match with socket fd
  *   4. addr: The sockaddr parameter from user space
- * 
+ *
  * BSDs:
  *   if(sodisconnect(so) == 0) { // udp disconnect succeed
  *     if (addr->sa_len != so->addr->sa_len) return EINVAL;
@@ -694,16 +695,25 @@ int uv__udp_disconnect(uv_udp_t* handle) {
 #endif
 
     memset(&addr, 0, sizeof(addr));
-    
+
 #if defined(__MVS__)
     addr.ss_family = AF_UNSPEC;
 #else
     addr.sa_family = AF_UNSPEC;
 #endif
-    
+
     do {
       errno = 0;
+#ifdef __PASE__
+      /* On IBMi a connectionless transport socket can be disconnected by
+       * either setting the addr parameter to NULL or setting the
+       * addr_length parameter to zero, and issuing another connect().
+       * https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/connec.htm
+       */
+      r = connect(handle->io_watcher.fd, (struct sockaddr*) NULL, 0);
+#else
       r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr));
+#endif
     } while (r == -1 && errno == EINTR);
 
     if (r == -1) {
@@ -927,7 +937,8 @@ static int uv__udp_set_membership6(uv_udp_t* handle,
     !defined(__NetBSD__) &&                                         \
     !defined(__ANDROID__) &&                                        \
     !defined(__DragonFly__) &&                                      \
-    !defined(__QNX__)
+    !defined(__QNX__) &&                                            \
+    !defined(__GNU__)
 static int uv__udp_set_source_membership4(uv_udp_t* handle,
                                           const struct sockaddr_in* multicast_addr,
                                           const char* interface_addr,
@@ -1119,7 +1130,8 @@ int uv_udp_set_source_membership(uv_udp_t* handle,
     !defined(__NetBSD__) &&                                         \
     !defined(__ANDROID__) &&                                        \
     !defined(__DragonFly__) &&                                      \
-    !defined(__QNX__)
+    !defined(__QNX__) &&                                            \
+    !defined(__GNU__)
   int err;
   union uv__sockaddr mcast_addr;
   union uv__sockaddr src_addr;
index e88347a..d8f7b0e 100644 (file)
@@ -296,7 +296,9 @@ int uv_tcp_bind(uv_tcp_t* handle,
 
   if (handle->type != UV_TCP)
     return UV_EINVAL;
-
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
   if (addr->sa_family == AF_INET)
     addrlen = sizeof(struct sockaddr_in);
   else if (addr->sa_family == AF_INET6)
index 8a190bf..6001b0c 100644 (file)
@@ -130,7 +130,10 @@ enum {
   UV_SIGNAL_ONE_SHOT                    = 0x02000000,
 
   /* Only used by uv_poll_t handles. */
-  UV_HANDLE_POLL_SLOW                   = 0x01000000
+  UV_HANDLE_POLL_SLOW                   = 0x01000000,
+
+  /* Only used by uv_process_t handles. */
+  UV_HANDLE_REAP                        = 0x10000000
 };
 
 int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
index d787f66..b904676 100644 (file)
@@ -28,7 +28,7 @@
 #include "req-inl.h"
 
 
-void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) {
+void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       !handle->async_sent) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
@@ -54,9 +54,9 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
 }
 
 
-void uv_async_close(uv_loop_t* loop, uv_async_t* handle) {
+void uv__async_close(uv_loop_t* loop, uv_async_t* handle) {
   if (!((uv_async_t*)handle)->async_sent) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 
   uv__handle_closing(handle);
@@ -83,7 +83,7 @@ int uv_async_send(uv_async_t* handle) {
 }
 
 
-void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
     uv_req_t* req) {
   assert(handle->type == UV_ASYNC);
   assert(req->type == UV_WAKEUP);
@@ -91,7 +91,7 @@ void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
   handle->async_sent = 0;
 
   if (handle->flags & UV_HANDLE_CLOSING) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
+    uv__want_endgame(loop, (uv_handle_t*)handle);
   } else if (handle->async_cb != NULL) {
     handle->async_cb(handle);
   }
index e53a0f8..67af93e 100644 (file)
@@ -84,10 +84,12 @@ static int uv__loops_capacity;
 #define UV__LOOPS_CHUNK_SIZE 8
 static uv_mutex_t uv__loops_lock;
 
+
 static void uv__loops_init(void) {
   uv_mutex_init(&uv__loops_lock);
 }
 
+
 static int uv__loops_add(uv_loop_t* loop) {
   uv_loop_t** new_loops;
   int new_capacity, i;
@@ -115,6 +117,7 @@ failed_loops_realloc:
   return ERROR_OUTOFMEMORY;
 }
 
+
 static void uv__loops_remove(uv_loop_t* loop) {
   int loop_index;
   int smaller_capacity;
@@ -173,7 +176,7 @@ void uv__wake_all_loops(void) {
   uv_mutex_unlock(&uv__loops_lock);
 }
 
-static void uv_init(void) {
+static void uv__init(void) {
   /* Tell Windows that we will handle critical errors. */
   SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
                SEM_NOOPENFILEERRORBOX);
@@ -199,19 +202,19 @@ static void uv_init(void) {
   /* Fetch winapi function pointers. This must be done first because other
    * initialization code might need these function pointers to be loaded.
    */
-  uv_winapi_init();
+  uv__winapi_init();
 
   /* Initialize winsock */
-  uv_winsock_init();
+  uv__winsock_init();
 
   /* Initialize FS */
-  uv_fs_init();
+  uv__fs_init();
 
   /* Initialize signal stuff */
-  uv_signals_init();
+  uv__signals_init();
 
   /* Initialize console */
-  uv_console_init();
+  uv__console_init();
 
   /* Initialize utilities */
   uv__util_init();
@@ -327,7 +330,7 @@ void uv_update_time(uv_loop_t* loop) {
 
 
 void uv__once_init(void) {
-  uv_once(&uv_init_guard_, uv_init);
+  uv_once(&uv_init_guard_, uv__init);
 }
 
 
@@ -395,23 +398,28 @@ int uv_loop_fork(uv_loop_t* loop) {
 }
 
 
-int uv_backend_timeout(const uv_loop_t* loop) {
-  if (loop->stop_flag != 0)
-    return 0;
-
-  if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))
-    return 0;
+static int uv__loop_alive(const uv_loop_t* loop) {
+  return uv__has_active_handles(loop) ||
+         uv__has_active_reqs(loop) ||
+         loop->pending_reqs_tail != NULL ||
+         loop->endgame_handles != NULL;
+}
 
-  if (loop->pending_reqs_tail)
-    return 0;
 
-  if (loop->endgame_handles)
-    return 0;
+int uv_loop_alive(const uv_loop_t* loop) {
+  return uv__loop_alive(loop);
+}
 
-  if (loop->idle_handles)
-    return 0;
 
-  return uv__next_timeout(loop);
+int uv_backend_timeout(const uv_loop_t* loop) {
+  if (loop->stop_flag == 0 &&
+      /* uv__loop_alive(loop) && */
+      (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) &&
+      loop->pending_reqs_tail == NULL &&
+      loop->idle_handles == NULL &&
+      loop->endgame_handles == NULL)
+    return uv__next_timeout(loop);
+  return 0;
 }
 
 
@@ -462,8 +470,8 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
 
     if (overlapped) {
       /* Package was dequeued */
-      req = uv_overlapped_to_req(overlapped);
-      uv_insert_pending_req(loop, req);
+      req = uv__overlapped_to_req(overlapped);
+      uv__insert_pending_req(loop, req);
 
       /* Some time might have passed waiting for I/O,
        * so update the loop time here.
@@ -547,8 +555,8 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
          * meant only to wake us up.
          */
         if (overlappeds[i].lpOverlapped) {
-          req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
-          uv_insert_pending_req(loop, req);
+          req = uv__overlapped_to_req(overlappeds[i].lpOverlapped);
+          uv__insert_pending_req(loop, req);
         }
       }
 
@@ -581,22 +589,10 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
 }
 
 
-static int uv__loop_alive(const uv_loop_t* loop) {
-  return uv__has_active_handles(loop) ||
-         uv__has_active_reqs(loop) ||
-         loop->endgame_handles != NULL;
-}
-
-
-int uv_loop_alive(const uv_loop_t* loop) {
-    return uv__loop_alive(loop);
-}
-
-
 int uv_run(uv_loop_t *loop, uv_run_mode mode) {
   DWORD timeout;
   int r;
-  int ran_pending;
+  int can_sleep;
 
   r = uv__loop_alive(loop);
   if (!r)
@@ -606,12 +602,14 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
     uv_update_time(loop);
     uv__run_timers(loop);
 
-    ran_pending = uv_process_reqs(loop);
-    uv_idle_invoke(loop);
-    uv_prepare_invoke(loop);
+    can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL;
+
+    uv__process_reqs(loop);
+    uv__idle_invoke(loop);
+    uv__prepare_invoke(loop);
 
     timeout = 0;
-    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
+    if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
       timeout = uv_backend_timeout(loop);
 
     if (pGetQueuedCompletionStatusEx)
@@ -619,6 +617,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
     else
       uv__poll_wine(loop, timeout);
 
+    /* Process immediate callbacks (e.g. write_cb) a small fixed number of
+     * times to avoid loop starvation.*/
+    for (r = 0; r < 8 && loop->pending_reqs_tail != NULL; r++)
+      uv__process_reqs(loop);
+
     /* Run one final update on the provider_idle_time in case uv__poll*
      * returned because the timeout expired, but no events were received. This
      * call will be ignored if the provider_entry_time was either never set (if
@@ -626,8 +629,8 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
      */
     uv__metrics_update_idle_time(loop);
 
-    uv_check_invoke(loop);
-    uv_process_endgames(loop);
+    uv__check_invoke(loop);
+    uv__process_endgames(loop);
 
     if (mode == UV_RUN_ONCE) {
       /* UV_RUN_ONCE implies forward progress: at least one callback must have
@@ -638,6 +641,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
        * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
        * the check.
        */
+      uv_update_time(loop);
       uv__run_timers(loop);
     }
 
index 76da077..6758c7c 100644 (file)
@@ -33,7 +33,7 @@
 const unsigned int uv_directory_watcher_buffer_size = 4096;
 
 
-static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
+static void uv__fs_event_queue_readdirchanges(uv_loop_t* loop,
     uv_fs_event_t* handle) {
   assert(handle->dir_handle != INVALID_HANDLE_VALUE);
   assert(!handle->req_pending);
@@ -57,15 +57,15 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop,
                              NULL)) {
     /* Make this req pending reporting an error. */
     SET_REQ_ERROR(&handle->req, GetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)&handle->req);
+    uv__insert_pending_req(loop, (uv_req_t*)&handle->req);
   }
 
   handle->req_pending = 1;
 }
 
-static void uv_relative_path(const WCHAR* filename,
-                             const WCHAR* dir,
-                             WCHAR** relpath) {
+static void uv__relative_path(const WCHAR* filename,
+                              const WCHAR* dir,
+                              WCHAR** relpath) {
   size_t relpathlen;
   size_t filenamelen = wcslen(filename);
   size_t dirlen = wcslen(dir);
@@ -80,7 +80,7 @@ static void uv_relative_path(const WCHAR* filename,
   (*relpath)[relpathlen] = L'\0';
 }
 
-static int uv_split_path(const WCHAR* filename, WCHAR** dir,
+static int uv__split_path(const WCHAR* filename, WCHAR** dir,
     WCHAR** file) {
   size_t len, i;
   DWORD dir_len;
@@ -255,12 +255,12 @@ int uv_fs_event_start(uv_fs_event_t* handle,
 short_path_done:
     short_path = short_path_buffer;
 
-    if (uv_split_path(pathw, &dir, &handle->filew) != 0) {
+    if (uv__split_path(pathw, &dir, &handle->filew) != 0) {
       last_error = GetLastError();
       goto error;
     }
 
-    if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) {
+    if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) {
       last_error = GetLastError();
       goto error;
     }
@@ -423,7 +423,7 @@ static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) {
 }
 
 
-void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
     uv_fs_event_t* handle) {
   FILE_NOTIFY_INFORMATION* file_info;
   int err, sizew, size;
@@ -442,7 +442,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
    */
   if (!uv__is_active(handle)) {
     if (handle->flags & UV_HANDLE_CLOSING) {
-      uv_want_endgame(loop, (uv_handle_t*) handle);
+      uv__want_endgame(loop, (uv_handle_t*) handle);
     }
     return;
   }
@@ -515,9 +515,9 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
 
               if (long_filenamew) {
                 /* Get the file name out of the long path. */
-                uv_relative_path(long_filenamew,
-                                 handle->dirw,
-                                 &filenamew);
+                uv__relative_path(long_filenamew,
+                                  handle->dirw,
+                                  &filenamew);
                 uv__free(long_filenamew);
                 long_filenamew = filenamew;
                 sizew = -1;
@@ -575,26 +575,26 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
   }
 
   if (handle->flags & UV_HANDLE_CLOSING) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
+    uv__want_endgame(loop, (uv_handle_t*)handle);
   } else if (uv__is_active(handle)) {
-    uv_fs_event_queue_readdirchanges(loop, handle);
+    uv__fs_event_queue_readdirchanges(loop, handle);
   }
 }
 
 
-void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
+void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) {
   uv_fs_event_stop(handle);
 
   uv__handle_closing(handle);
 
   if (!handle->req_pending) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
+    uv__want_endgame(loop, (uv_handle_t*)handle);
   }
 
 }
 
 
-void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
+void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) {
   if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
 
index 9037641..7923079 100644 (file)
@@ -46,7 +46,7 @@
   do {                                                                        \
     if (req == NULL)                                                          \
       return UV_EINVAL;                                                       \
-    uv_fs_req_init(loop, req, subtype, cb);                                   \
+    uv__fs_req_init(loop, req, subtype, cb);                                  \
   }                                                                           \
   while (0)
 
@@ -132,7 +132,7 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE
 static DWORD uv__allocation_granularity;
 
 
-void uv_fs_init(void) {
+void uv__fs_init(void) {
   SYSTEM_INFO system_info;
 
   GetSystemInfo(&system_info);
@@ -241,7 +241,7 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path,
 
 
 
-INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req,
+INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req,
     uv_fs_type fs_type, const uv_fs_cb cb) {
   uv__once_init();
   UV_REQ_INIT(req, UV_FS);
@@ -912,12 +912,11 @@ void fs__read(uv_fs_t* req) {
     SET_REQ_RESULT(req, bytes);
   } else {
     error = GetLastError();
-
     if (error == ERROR_ACCESS_DENIED) {
       error = ERROR_INVALID_FLAGS;
     }
 
-    if (error == ERROR_HANDLE_EOF) {
+    if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
       SET_REQ_RESULT(req, bytes);
     } else {
       SET_REQ_WIN32_ERROR(req, error);
@@ -1881,8 +1880,9 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
                        NULL);
 
   if (handle == INVALID_HANDLE_VALUE)
-    ret = GetLastError();
-  else if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
+    return GetLastError();
+
+  if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
     ret = GetLastError();
   else
     ret = 0;
@@ -2300,13 +2300,13 @@ INLINE static DWORD fs__utime_impl_from_path(WCHAR* path,
                        flags,
                        NULL);
 
-  if (handle == INVALID_HANDLE_VALUE) {
-    ret = GetLastError();
-  } else if (fs__utime_handle(handle, atime, mtime) != 0) {
+  if (handle == INVALID_HANDLE_VALUE)
+    return GetLastError();
+
+  if (fs__utime_handle(handle, atime, mtime) != 0)
     ret = GetLastError();
-  } else {
+  else
     ret = 0;
-  }
 
   CloseHandle(handle);
   return ret;
index 82c657d..5c843c2 100644 (file)
@@ -55,7 +55,7 @@
                                                                         \
     if (handle->flags & UV_HANDLE_CLOSING &&                            \
         handle->reqs_pending == 0) {                                    \
-      uv_want_endgame(loop, (uv_handle_t*)handle);                      \
+      uv__want_endgame(loop, (uv_handle_t*)handle);                     \
     }                                                                   \
   } while (0)
 
@@ -85,7 +85,7 @@
   } while (0)
 
 
-INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+INLINE static void uv__want_endgame(uv_loop_t* loop, uv_handle_t* handle) {
   if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) {
     handle->flags |= UV_HANDLE_ENDGAME_QUEUED;
 
@@ -95,7 +95,7 @@ INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) {
 }
 
 
-INLINE static void uv_process_endgames(uv_loop_t* loop) {
+INLINE static void uv__process_endgames(uv_loop_t* loop) {
   uv_handle_t* handle;
 
   while (loop->endgame_handles) {
@@ -106,23 +106,23 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) {
 
     switch (handle->type) {
       case UV_TCP:
-        uv_tcp_endgame(loop, (uv_tcp_t*) handle);
+        uv__tcp_endgame(loop, (uv_tcp_t*) handle);
         break;
 
       case UV_NAMED_PIPE:
-        uv_pipe_endgame(loop, (uv_pipe_t*) handle);
+        uv__pipe_endgame(loop, (uv_pipe_t*) handle);
         break;
 
       case UV_TTY:
-        uv_tty_endgame(loop, (uv_tty_t*) handle);
+        uv__tty_endgame(loop, (uv_tty_t*) handle);
         break;
 
       case UV_UDP:
-        uv_udp_endgame(loop, (uv_udp_t*) handle);
+        uv__udp_endgame(loop, (uv_udp_t*) handle);
         break;
 
       case UV_POLL:
-        uv_poll_endgame(loop, (uv_poll_t*) handle);
+        uv__poll_endgame(loop, (uv_poll_t*) handle);
         break;
 
       case UV_TIMER:
@@ -133,23 +133,23 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) {
       case UV_PREPARE:
       case UV_CHECK:
       case UV_IDLE:
-        uv_loop_watcher_endgame(loop, handle);
+        uv__loop_watcher_endgame(loop, handle);
         break;
 
       case UV_ASYNC:
-        uv_async_endgame(loop, (uv_async_t*) handle);
+        uv__async_endgame(loop, (uv_async_t*) handle);
         break;
 
       case UV_SIGNAL:
-        uv_signal_endgame(loop, (uv_signal_t*) handle);
+        uv__signal_endgame(loop, (uv_signal_t*) handle);
         break;
 
       case UV_PROCESS:
-        uv_process_endgame(loop, (uv_process_t*) handle);
+        uv__process_endgame(loop, (uv_process_t*) handle);
         break;
 
       case UV_FS_EVENT:
-        uv_fs_event_endgame(loop, (uv_fs_event_t*) handle);
+        uv__fs_event_endgame(loop, (uv_fs_event_t*) handle);
         break;
 
       case UV_FS_POLL:
index 61e4df6..53a81fd 100644 (file)
@@ -77,63 +77,63 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) {
   /* Handle-specific close actions */
   switch (handle->type) {
     case UV_TCP:
-      uv_tcp_close(loop, (uv_tcp_t*)handle);
+      uv__tcp_close(loop, (uv_tcp_t*)handle);
       return;
 
     case UV_NAMED_PIPE:
-      uv_pipe_close(loop, (uv_pipe_t*) handle);
+      uv__pipe_close(loop, (uv_pipe_t*) handle);
       return;
 
     case UV_TTY:
-      uv_tty_close((uv_tty_t*) handle);
+      uv__tty_close((uv_tty_t*) handle);
       return;
 
     case UV_UDP:
-      uv_udp_close(loop, (uv_udp_t*) handle);
+      uv__udp_close(loop, (uv_udp_t*) handle);
       return;
 
     case UV_POLL:
-      uv_poll_close(loop, (uv_poll_t*) handle);
+      uv__poll_close(loop, (uv_poll_t*) handle);
       return;
 
     case UV_TIMER:
       uv_timer_stop((uv_timer_t*)handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
+      uv__want_endgame(loop, handle);
       return;
 
     case UV_PREPARE:
       uv_prepare_stop((uv_prepare_t*)handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
+      uv__want_endgame(loop, handle);
       return;
 
     case UV_CHECK:
       uv_check_stop((uv_check_t*)handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
+      uv__want_endgame(loop, handle);
       return;
 
     case UV_IDLE:
       uv_idle_stop((uv_idle_t*)handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
+      uv__want_endgame(loop, handle);
       return;
 
     case UV_ASYNC:
-      uv_async_close(loop, (uv_async_t*) handle);
+      uv__async_close(loop, (uv_async_t*) handle);
       return;
 
     case UV_SIGNAL:
-      uv_signal_close(loop, (uv_signal_t*) handle);
+      uv__signal_close(loop, (uv_signal_t*) handle);
       return;
 
     case UV_PROCESS:
-      uv_process_close(loop, (uv_process_t*) handle);
+      uv__process_close(loop, (uv_process_t*) handle);
       return;
 
     case UV_FS_EVENT:
-      uv_fs_event_close(loop, (uv_fs_event_t*) handle);
+      uv__fs_event_close(loop, (uv_fs_event_t*) handle);
       return;
 
     case UV_FS_POLL:
index 7e1aef4..3e92a66 100644 (file)
@@ -76,25 +76,28 @@ typedef struct {
   uint32_t delayed_error;
 } uv__ipc_socket_xfer_info_t;
 
-int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);
-int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client);
-int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb);
+int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client);
+int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb);
-int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
+int uv__tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
     const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
 int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[],
     unsigned int nbufs);
 
-void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req);
-void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req);
+void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_write_t* req);
-void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_req_t* req);
-void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_connect_t* req);
+void uv__process_tcp_shutdown_req(uv_loop_t* loop,
+                                  uv_tcp_t* stream,
+                                  uv_shutdown_t* req);
 
-void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
-void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
+void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
+void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
 
 int uv__tcp_xfer_export(uv_tcp_t* handle,
                         int pid,
@@ -108,12 +111,12 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp,
 /*
  * UDP
  */
-void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req);
-void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req);
+void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
     uv_udp_send_t* req);
 
-void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle);
-void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
+void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle);
+void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
 
 
 /*
@@ -122,9 +125,9 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle);
 int uv__create_stdio_pipe_pair(uv_loop_t* loop,
     uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags);
 
-int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
-int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client);
-int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb,
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
+int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client);
+int uv__pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb);
 void uv__pipe_read_stop(uv_pipe_t* handle);
 int uv__pipe_write(uv_loop_t* loop,
@@ -134,75 +137,77 @@ int uv__pipe_write(uv_loop_t* loop,
                    size_t nbufs,
                    uv_stream_t* send_handle,
                    uv_write_cb cb);
+void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req);
 
-void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_req_t* req);
-void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_write_t* req);
-void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_req_t* raw_req);
-void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_connect_t* req);
-void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_shutdown_t* req);
 
-void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
-void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle);
-void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
+void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
+void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
 
 
 /*
  * TTY
  */
-void uv_console_init(void);
+void uv__console_init(void);
 
-int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb);
-int uv_tty_read_stop(uv_tty_t* handle);
-int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
+int uv__tty_read_stop(uv_tty_t* handle);
+int uv__tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
     const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
 int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[],
     unsigned int nbufs);
-void uv_tty_close(uv_tty_t* handle);
+void uv__tty_close(uv_tty_t* handle);
 
-void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_req_t* req);
-void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_write_t* req);
 /*
- * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
  * TODO: find a way to remove it
  */
-void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_req_t* raw_req);
 /*
- * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
  * TODO: find a way to remove it
  */
-void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_connect_t* req);
-
-void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle);
+void uv__process_tty_shutdown_req(uv_loop_t* loop,
+                                  uv_tty_t* stream,
+                                  uv_shutdown_t* req);
+void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle);
 
 
 /*
  * Poll watchers
  */
-void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
+void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
     uv_req_t* req);
 
-int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle);
-void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle);
+int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle);
+void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle);
 
 
 /*
  * Loop watchers
  */
-void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle);
+void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle);
 
-void uv_prepare_invoke(uv_loop_t* loop);
-void uv_check_invoke(uv_loop_t* loop);
-void uv_idle_invoke(uv_loop_t* loop);
+void uv__prepare_invoke(uv_loop_t* loop);
+void uv__check_invoke(uv_loop_t* loop);
+void uv__idle_invoke(uv_loop_t* loop);
 
 void uv__once_init(void);
 
@@ -210,53 +215,47 @@ void uv__once_init(void);
 /*
  * Async watcher
  */
-void uv_async_close(uv_loop_t* loop, uv_async_t* handle);
-void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle);
+void uv__async_close(uv_loop_t* loop, uv_async_t* handle);
+void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle);
 
-void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
+void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle,
     uv_req_t* req);
 
 
 /*
  * Signal watcher
  */
-void uv_signals_init(void);
+void uv__signals_init(void);
 int uv__signal_dispatch(int signum);
 
-void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle);
-void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
+void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle);
+void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle);
 
-void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
     uv_req_t* req);
 
 
 /*
  * Spawn
  */
-void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
-void uv_process_close(uv_loop_t* loop, uv_process_t* handle);
-void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle);
-
-
-/*
- * Error
- */
-int uv_translate_sys_error(int sys_errno);
+void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle);
+void uv__process_close(uv_loop_t* loop, uv_process_t* handle);
+void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle);
 
 
 /*
  * FS
  */
-void uv_fs_init(void);
+void uv__fs_init(void);
 
 
 /*
  * FS Event
  */
-void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
+void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
     uv_fs_event_t* handle);
-void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
-void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
+void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle);
+void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle);
 
 
 /*
@@ -303,28 +302,28 @@ HANDLE uv__stdio_handle(BYTE* buffer, int fd);
 /*
  * Winapi and ntapi utility functions
  */
-void uv_winapi_init(void);
+void uv__winapi_init(void);
 
 
 /*
  * Winsock utility functions
  */
-void uv_winsock_init(void);
+void uv__winsock_init(void);
 
-int uv_ntstatus_to_winsock_error(NTSTATUS status);
+int uv__ntstatus_to_winsock_error(NTSTATUS status);
 
-BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
-BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);
+BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target);
+BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target);
 
-int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,
     DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
     LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
-int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
     DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
     int* addr_len, WSAOVERLAPPED *overlapped,
     LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine);
 
-int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
     AFD_POLL_INFO* info_out, OVERLAPPED* overlapped);
 
 /* Whether there are any non-IFS LSPs stacked on TCP */
index ad7fbea..fad9e8a 100644 (file)
@@ -26,7 +26,7 @@
 #include "handle-inl.h"
 
 
-void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
+void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
     handle->flags |= UV_HANDLE_CLOSED;
@@ -104,7 +104,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) {
   }                                                                           \
                                                                               \
                                                                               \
-  void uv_##name##_invoke(uv_loop_t* loop) {                                  \
+  void uv__##name##_invoke(uv_loop_t* loop) {                                 \
     uv_##name##_t* handle;                                                    \
                                                                               \
     (loop)->next_##name##_handle = (loop)->name##_handles;                    \
index 984b766..9984618 100644 (file)
@@ -98,13 +98,13 @@ static void eof_timer_destroy(uv_pipe_t* pipe);
 static void eof_timer_close_cb(uv_handle_t* handle);
 
 
-static void uv_unique_pipe_name(char* ptr, char* name, size_t size) {
+static void uv__unique_pipe_name(char* ptr, char* name, size_t size) {
   snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
 }
 
 
 int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
-  uv_stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
+  uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
 
   handle->reqs_pending = 0;
   handle->handle = INVALID_HANDLE_VALUE;
@@ -120,15 +120,11 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
 }
 
 
-static void uv_pipe_connection_init(uv_pipe_t* handle) {
-  uv_connection_init((uv_stream_t*) handle);
+static void uv__pipe_connection_init(uv_pipe_t* handle) {
+  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
+  uv__connection_init((uv_stream_t*) handle);
   handle->read_req.data = handle;
   handle->pipe.conn.eof_timer = NULL;
-  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
-  if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
-    handle->pipe.conn.readfile_thread_handle = NULL;
-    InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
-  }
 }
 
 
@@ -209,7 +205,7 @@ static int uv__pipe_server(
   int err;
 
   for (;;) {
-    uv_unique_pipe_name(random, name, nameSize);
+    uv__unique_pipe_name(random, name, nameSize);
 
     pipeHandle = CreateNamedPipeA(name,
       access | FILE_FLAG_FIRST_PIPE_INSTANCE,
@@ -393,6 +389,8 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
   unsigned int client_flags;
   int err;
 
+  uv__pipe_connection_init(parent_pipe);
+
   server_pipe = INVALID_HANDLE_VALUE;
   client_pipe = INVALID_HANDLE_VALUE;
 
@@ -427,7 +425,6 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
     goto error;
   }
 
-  uv_pipe_connection_init(parent_pipe);
   parent_pipe->handle = server_pipe;
   *child_pipe_ptr = client_pipe;
 
@@ -450,11 +447,11 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
 }
 
 
-static int uv_set_pipe_handle(uv_loop_t* loop,
-                              uv_pipe_t* handle,
-                              HANDLE pipeHandle,
-                              int fd,
-                              DWORD duplex_flags) {
+static int uv__set_pipe_handle(uv_loop_t* loop,
+                               uv_pipe_t* handle,
+                               HANDLE pipeHandle,
+                               int fd,
+                               DWORD duplex_flags) {
   NTSTATUS nt_status;
   IO_STATUS_BLOCK io_status;
   FILE_MODE_INFORMATION mode_info;
@@ -462,7 +459,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop,
   DWORD current_mode = 0;
   DWORD err = 0;
 
-  if (handle->flags & UV_HANDLE_PIPESERVER)
+  assert(handle->flags & UV_HANDLE_CONNECTION);
+  assert(!(handle->flags & UV_HANDLE_PIPESERVER));
+  if (handle->flags & UV_HANDLE_CLOSING)
     return UV_EINVAL;
   if (handle->handle != INVALID_HANDLE_VALUE)
     return UV_EBUSY;
@@ -478,18 +477,17 @@ static int uv_set_pipe_handle(uv_loop_t* loop,
        */
       if (!GetNamedPipeHandleState(pipeHandle, &current_mode, NULL, NULL,
                                    NULL, NULL, 0)) {
-        return -1;
+        return uv_translate_sys_error(GetLastError());
       } else if (current_mode & PIPE_NOWAIT) {
-        SetLastError(ERROR_ACCESS_DENIED);
-        return -1;
+        return UV_EACCES;
       }
     } else {
       /* If this returns ERROR_INVALID_PARAMETER we probably opened
        * something that is not a pipe. */
       if (err == ERROR_INVALID_PARAMETER) {
-        SetLastError(WSAENOTSOCK);
+        return UV_ENOTSOCK;
       }
-      return -1;
+      return uv_translate_sys_error(err);
     }
   }
 
@@ -500,13 +498,15 @@ static int uv_set_pipe_handle(uv_loop_t* loop,
                                       sizeof(mode_info),
                                       FileModeInformation);
   if (nt_status != STATUS_SUCCESS) {
-    return -1;
+    return uv_translate_sys_error(err);
   }
 
   if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT ||
       mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) {
     /* Non-overlapped pipe. */
     handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE;
+    handle->pipe.conn.readfile_thread_handle = NULL;
+    InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
   } else {
     /* Overlapped pipe.  Try to associate with IOCP. */
     if (CreateIoCompletionPort(pipeHandle,
@@ -578,135 +578,109 @@ static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) {
 }
 
 
-void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
-  int err;
+void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t *req) {
   DWORD result;
-  uv_shutdown_t* req;
   NTSTATUS nt_status;
   IO_STATUS_BLOCK io_status;
   FILE_PIPE_LOCAL_INFORMATION pipe_info;
-  uv__ipc_xfer_queue_item_t* xfer_queue_item;
-
-  if ((handle->flags & UV_HANDLE_CONNECTION) &&
-      handle->stream.conn.shutdown_req != NULL &&
-      handle->stream.conn.write_reqs_pending == 0) {
-    req = handle->stream.conn.shutdown_req;
-
-    /* Clear the shutdown_req field so we don't go here again. */
-    handle->stream.conn.shutdown_req = NULL;
-
-    if (handle->flags & UV_HANDLE_CLOSING) {
-      UNREGISTER_HANDLE_REQ(loop, handle, req);
-
-      /* Already closing. Cancel the shutdown. */
-      if (req->cb) {
-        req->cb(req, UV_ECANCELED);
-      }
-
-      DECREASE_PENDING_REQ_COUNT(handle);
-      return;
-    }
 
-    /* Try to avoid flushing the pipe buffer in the thread pool. */
-    nt_status = pNtQueryInformationFile(handle->handle,
-                                        &io_status,
-                                        &pipe_info,
-                                        sizeof pipe_info,
-                                        FilePipeLocalInformation);
-
-    if (nt_status != STATUS_SUCCESS) {
-      /* Failure */
-      UNREGISTER_HANDLE_REQ(loop, handle, req);
-
-      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
-      if (req->cb) {
-        err = pRtlNtStatusToDosError(nt_status);
-        req->cb(req, uv_translate_sys_error(err));
-      }
-
-      DECREASE_PENDING_REQ_COUNT(handle);
-      return;
-    }
+  assert(handle->flags & UV_HANDLE_CONNECTION);
+  assert(req != NULL);
+  assert(handle->stream.conn.write_reqs_pending == 0);
+  SET_REQ_SUCCESS(req);
 
-    if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) {
-      /* Short-circuit, no need to call FlushFileBuffers. */
-      uv_insert_pending_req(loop, (uv_req_t*) req);
-      return;
-    }
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
 
-    /* Run FlushFileBuffers in the thread pool. */
-    result = QueueUserWorkItem(pipe_shutdown_thread_proc,
-                               req,
-                               WT_EXECUTELONGFUNCTION);
-    if (result) {
-      return;
+  /* Try to avoid flushing the pipe buffer in the thread pool. */
+  nt_status = pNtQueryInformationFile(handle->handle,
+                                      &io_status,
+                                      &pipe_info,
+                                      sizeof pipe_info,
+                                      FilePipeLocalInformation);
 
-    } else {
-      /* Failure. */
-      UNREGISTER_HANDLE_REQ(loop, handle, req);
+  if (nt_status != STATUS_SUCCESS) {
+    SET_REQ_ERROR(req, pRtlNtStatusToDosError(nt_status));
+    handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
 
-      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
-      if (req->cb) {
-        err = GetLastError();
-        req->cb(req, uv_translate_sys_error(err));
-      }
+  if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) {
+    /* Short-circuit, no need to call FlushFileBuffers:
+     * all writes have been read. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
+  }
 
-      DECREASE_PENDING_REQ_COUNT(handle);
-      return;
-    }
+  /* Run FlushFileBuffers in the thread pool. */
+  result = QueueUserWorkItem(pipe_shutdown_thread_proc,
+                             req,
+                             WT_EXECUTELONGFUNCTION);
+  if (!result) {
+    SET_REQ_ERROR(req, GetLastError());
+    handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
+    uv__insert_pending_req(loop, (uv_req_t*) req);
+    return;
   }
+}
 
-  if (handle->flags & UV_HANDLE_CLOSING &&
-      handle->reqs_pending == 0) {
-    assert(!(handle->flags & UV_HANDLE_CLOSED));
 
-    if (handle->flags & UV_HANDLE_CONNECTION) {
-      /* Free pending sockets */
-      while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
-        QUEUE* q;
-        SOCKET socket;
+void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
+  uv__ipc_xfer_queue_item_t* xfer_queue_item;
 
-        q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
-        QUEUE_REMOVE(q);
-        xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+  assert(handle->reqs_pending == 0);
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
 
-        /* Materialize socket and close it */
-        socket = WSASocketW(FROM_PROTOCOL_INFO,
-                            FROM_PROTOCOL_INFO,
-                            FROM_PROTOCOL_INFO,
-                            &xfer_queue_item->xfer_info.socket_info,
-                            0,
-                            WSA_FLAG_OVERLAPPED);
-        uv__free(xfer_queue_item);
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    /* Free pending sockets */
+    while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
+      QUEUE* q;
+      SOCKET socket;
+
+      q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
+      QUEUE_REMOVE(q);
+      xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
+
+      /* Materialize socket and close it */
+      socket = WSASocketW(FROM_PROTOCOL_INFO,
+                          FROM_PROTOCOL_INFO,
+                          FROM_PROTOCOL_INFO,
+                          &xfer_queue_item->xfer_info.socket_info,
+                          0,
+                          WSA_FLAG_OVERLAPPED);
+      uv__free(xfer_queue_item);
+
+      if (socket != INVALID_SOCKET)
+        closesocket(socket);
+    }
+    handle->pipe.conn.ipc_xfer_queue_length = 0;
 
-        if (socket != INVALID_SOCKET)
-          closesocket(socket);
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+        UnregisterWait(handle->read_req.wait_handle);
+        handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
       }
-      handle->pipe.conn.ipc_xfer_queue_length = 0;
-
-      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
-        if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
-          UnregisterWait(handle->read_req.wait_handle);
-          handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
-        }
-        if (handle->read_req.event_handle != NULL) {
-          CloseHandle(handle->read_req.event_handle);
-          handle->read_req.event_handle = NULL;
-        }
+      if (handle->read_req.event_handle != NULL) {
+        CloseHandle(handle->read_req.event_handle);
+        handle->read_req.event_handle = NULL;
       }
-
-      if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
-        DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
     }
 
-    if (handle->flags & UV_HANDLE_PIPESERVER) {
-      assert(handle->pipe.serv.accept_reqs);
-      uv__free(handle->pipe.serv.accept_reqs);
-      handle->pipe.serv.accept_reqs = NULL;
-    }
+    if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
+      DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
+  }
 
-    uv__handle_close(handle);
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    assert(handle->pipe.serv.accept_reqs);
+    uv__free(handle->pipe.serv.accept_reqs);
+    handle->pipe.serv.accept_reqs = NULL;
   }
+
+  uv__handle_close(handle);
 }
 
 
@@ -731,7 +705,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
   if (!name) {
     return UV_EINVAL;
   }
-
+  if (uv__is_closing(handle)) {
+    return UV_EINVAL;
+  }
   if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
     handle->pipe.serv.pending_instances = default_pending_pipe_instances;
   }
@@ -815,7 +791,7 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
   assert(loop);
 
   /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
-   * for the pipe to become available with WaitNamedPipe. */
+   * up to 30 seconds for the pipe to become available with WaitNamedPipe. */
   while (WaitNamedPipeW(handle->name, 30000)) {
     /* The pipe is now available, try to connect. */
     pipeHandle = open_named_pipe(handle->name, &duplex_flags);
@@ -825,9 +801,10 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
     SwitchToThread();
   }
 
-  if (pipeHandle != INVALID_HANDLE_VALUE &&
-      !uv_set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) {
+  if (pipeHandle != INVALID_HANDLE_VALUE) {
     SET_REQ_SUCCESS(req);
+    req->u.connect.pipeHandle = pipeHandle;
+    req->u.connect.duplex_flags = duplex_flags;
   } else {
     SET_REQ_ERROR(req, GetLastError());
   }
@@ -849,6 +826,18 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
   UV_REQ_INIT(req, UV_CONNECT);
   req->handle = (uv_stream_t*) handle;
   req->cb = cb;
+  req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
+  req->u.connect.duplex_flags = 0;
+
+  if (handle->flags & UV_HANDLE_PIPESERVER) {
+    err = ERROR_INVALID_PARAMETER;
+    goto error;
+  }
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    err = ERROR_PIPE_BUSY;
+    goto error;
+  }
+  uv__pipe_connection_init(handle);
 
   /* Convert name to UTF16. */
   nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
@@ -888,19 +877,10 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
     goto error;
   }
 
-  assert(pipeHandle != INVALID_HANDLE_VALUE);
-
-  if (uv_set_pipe_handle(loop,
-                         (uv_pipe_t*) req->handle,
-                         pipeHandle,
-                         -1,
-                         duplex_flags)) {
-    err = GetLastError();
-    goto error;
-  }
-
+  req->u.connect.pipeHandle = pipeHandle;
+  req->u.connect.duplex_flags = duplex_flags;
   SET_REQ_SUCCESS(req);
-  uv_insert_pending_req(loop, (uv_req_t*) req);
+  uv__insert_pending_req(loop, (uv_req_t*) req);
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
   return;
@@ -916,7 +896,7 @@ error:
 
   /* Make this req pending reporting an error. */
   SET_REQ_ERROR(req, err);
-  uv_insert_pending_req(loop, (uv_req_t*) req);
+  uv__insert_pending_req(loop, (uv_req_t*) req);
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
   return;
@@ -937,7 +917,7 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) {
     /* Cancel asynchronous read. */
     r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped);
     assert(r || GetLastError() == ERROR_NOT_FOUND);
-
+    (void) r;
   } else {
     /* Cancel synchronous read (which is happening in the thread pool). */
     HANDLE thread;
@@ -973,17 +953,30 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) {
 void uv__pipe_read_stop(uv_pipe_t* handle) {
   handle->flags &= ~UV_HANDLE_READING;
   DECREASE_ACTIVE_COUNT(handle->loop, handle);
-
   uv__pipe_interrupt_read(handle);
 }
 
 
 /* Cleans up uv_pipe_t (server or connection) and all resources associated with
  * it. */
-void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) {
+void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
   int i;
   HANDLE pipeHandle;
 
+  if (handle->flags & UV_HANDLE_READING) {
+    handle->flags &= ~UV_HANDLE_READING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  if (handle->flags & UV_HANDLE_LISTENING) {
+    handle->flags &= ~UV_HANDLE_LISTENING;
+    DECREASE_ACTIVE_COUNT(loop, handle);
+  }
+
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+
+  uv__handle_closing(handle);
+
   uv__pipe_interrupt_read(handle);
 
   if (handle->name) {
@@ -1003,45 +996,27 @@ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) {
   }
 
   if (handle->flags & UV_HANDLE_CONNECTION) {
-    handle->flags &= ~UV_HANDLE_WRITABLE;
     eof_timer_destroy(handle);
   }
 
   if ((handle->flags & UV_HANDLE_CONNECTION)
-      && handle->handle != INVALID_HANDLE_VALUE)
+      && handle->handle != INVALID_HANDLE_VALUE) {
+    /* This will eventually destroy the write queue for us too. */
     close_pipe(handle);
-}
-
-
-void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
-  if (handle->flags & UV_HANDLE_READING) {
-    handle->flags &= ~UV_HANDLE_READING;
-    DECREASE_ACTIVE_COUNT(loop, handle);
-  }
-
-  if (handle->flags & UV_HANDLE_LISTENING) {
-    handle->flags &= ~UV_HANDLE_LISTENING;
-    DECREASE_ACTIVE_COUNT(loop, handle);
   }
 
-  uv_pipe_cleanup(loop, handle);
-
-  if (handle->reqs_pending == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
-  }
-
-  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
-  uv__handle_closing(handle);
+  if (handle->reqs_pending == 0)
+    uv__want_endgame(loop, (uv_handle_t*) handle);
 }
 
 
-static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
+static void uv__pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
     uv_pipe_accept_t* req, BOOL firstInstance) {
   assert(handle->flags & UV_HANDLE_LISTENING);
 
   if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) {
     SET_REQ_ERROR(req, GetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*) req);
+    uv__insert_pending_req(loop, (uv_req_t*) req);
     handle->reqs_pending++;
     return;
   }
@@ -1061,7 +1036,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
       /* Make this req pending reporting an error. */
       SET_REQ_ERROR(req, GetLastError());
     }
-    uv_insert_pending_req(loop, (uv_req_t*) req);
+    uv__insert_pending_req(loop, (uv_req_t*) req);
     handle->reqs_pending++;
     return;
   }
@@ -1071,7 +1046,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle,
 }
 
 
-int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
+int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
   uv_loop_t* loop = server->loop;
   uv_pipe_t* pipe_client;
   uv_pipe_accept_t* req;
@@ -1099,6 +1074,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
 
   } else {
     pipe_client = (uv_pipe_t*) client;
+    uv__pipe_connection_init(pipe_client);
 
     /* Find a connection instance that has been connected, but not yet
      * accepted. */
@@ -1110,7 +1086,6 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
     }
 
     /* Initialize the client handle and copy the pipeHandle to the client */
-    uv_pipe_connection_init(pipe_client);
     pipe_client->handle = req->pipeHandle;
     pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
 
@@ -1121,7 +1096,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
 
     server->handle = INVALID_HANDLE_VALUE;
     if (!(server->flags & UV_HANDLE_CLOSING)) {
-      uv_pipe_queue_accept(loop, server, req, FALSE);
+      uv__pipe_queue_accept(loop, server, req, FALSE);
     }
   }
 
@@ -1130,7 +1105,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
 
 
 /* Starts listening for connections for the given pipe. */
-int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
+int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
   uv_loop_t* loop = handle->loop;
   int i;
 
@@ -1162,7 +1137,7 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) {
   assert(handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE);
 
   for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
-    uv_pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0);
+    uv__pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0);
   }
 
   return 0;
@@ -1306,7 +1281,7 @@ static void CALLBACK post_completion_write_wait(void* context, BOOLEAN timed_out
 }
 
 
-static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
+static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
   uv_read_t* req;
   int result;
 
@@ -1365,15 +1340,15 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
   return;
 
 error:
-  uv_insert_pending_req(loop, (uv_req_t*)req);
+  uv__insert_pending_req(loop, (uv_req_t*)req);
   handle->flags |= UV_HANDLE_READ_PENDING;
   handle->reqs_pending++;
 }
 
 
-int uv_pipe_read_start(uv_pipe_t* handle,
-                       uv_alloc_cb alloc_cb,
-                       uv_read_cb read_cb) {
+int uv__pipe_read_start(uv_pipe_t* handle,
+                        uv_alloc_cb alloc_cb,
+                        uv_read_cb read_cb) {
   uv_loop_t* loop = handle->loop;
 
   handle->flags |= UV_HANDLE_READING;
@@ -1391,14 +1366,14 @@ int uv_pipe_read_start(uv_pipe_t* handle,
         uv_fatal_error(GetLastError(), "CreateEvent");
       }
     }
-    uv_pipe_queue_read(loop, handle);
+    uv__pipe_queue_read(loop, handle);
   }
 
   return 0;
 }
 
 
-static void uv_insert_non_overlapped_write_req(uv_pipe_t* handle,
+static void uv__insert_non_overlapped_write_req(uv_pipe_t* handle,
     uv_write_t* req) {
   req->next_req = NULL;
   if (handle->pipe.conn.non_overlapped_writes_tail) {
@@ -1434,7 +1409,7 @@ static uv_write_t* uv_remove_non_overlapped_write_req(uv_pipe_t* handle) {
 }
 
 
-static void uv_queue_non_overlapped_write(uv_pipe_t* handle) {
+static void uv__queue_non_overlapped_write(uv_pipe_t* handle) {
   uv_write_t* req = uv_remove_non_overlapped_write_req(handle);
   if (req) {
     if (!QueueUserWorkItem(&uv_pipe_writefile_thread_proc,
@@ -1575,9 +1550,9 @@ static int uv__pipe_write_data(uv_loop_t* loop,
     return 0;
   } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
     req->write_buffer = write_buf;
-    uv_insert_non_overlapped_write_req(handle, req);
+    uv__insert_non_overlapped_write_req(handle, req);
     if (handle->stream.conn.write_reqs_pending == 0) {
-      uv_queue_non_overlapped_write(handle);
+      uv__queue_non_overlapped_write(handle);
     }
 
     /* Request queued by the kernel. */
@@ -1790,7 +1765,7 @@ int uv__pipe_write(uv_loop_t* loop,
 }
 
 
-static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
+static void uv__pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
     uv_buf_t buf) {
   /* If there is an eof timer running, we don't need it any more, so discard
    * it. */
@@ -1802,7 +1777,7 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
 }
 
 
-static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
+static void uv__pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
     uv_buf_t buf) {
   /* If there is an eof timer running, we don't need it any more, so discard
    * it. */
@@ -1814,12 +1789,12 @@ static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
 }
 
 
-static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
+static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
     int error, uv_buf_t buf) {
   if (error == ERROR_BROKEN_PIPE) {
-    uv_pipe_read_eof(loop, handle, buf);
+    uv__pipe_read_eof(loop, handle, buf);
   } else {
-    uv_pipe_read_error(loop, handle, error, buf);
+    uv__pipe_read_error(loop, handle, error, buf);
   }
 }
 
@@ -1890,7 +1865,7 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop,
 
   /* Read into the user buffer. */
   if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
-    uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
+    uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
     return 0; /* Break out of read loop. */
   }
 
@@ -1977,14 +1952,14 @@ invalid:
   err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
 
 error:
-  uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+  uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
   return 0; /* Break out of read loop. */
 }
 
 
-void uv_process_pipe_read_req(uv_loop_t* loop,
-                              uv_pipe_t* handle,
-                              uv_req_t* req) {
+void uv__process_pipe_read_req(uv_loop_t* loop,
+                               uv_pipe_t* handle,
+                               uv_req_t* req) {
   assert(handle->type == UV_NAMED_PIPE);
 
   handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
@@ -2005,7 +1980,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop,
      * indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
      * the user; we'll start a new zero-read at the end of this function. */
     if (err != ERROR_OPERATION_ABORTED)
-      uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
+      uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_);
 
   } else {
     /* The zero-read completed without error, indicating there is data
@@ -2015,7 +1990,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop,
     /* Get the number of bytes available. */
     avail = 0;
     if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
-      uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
+      uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
 
     /* Read until we've either read all the bytes available, or the 'reading'
      * flag is cleared. */
@@ -2044,12 +2019,12 @@ void uv_process_pipe_read_req(uv_loop_t* loop,
   /* Start another zero-read request if necessary. */
   if ((handle->flags & UV_HANDLE_READING) &&
       !(handle->flags & UV_HANDLE_READ_PENDING)) {
-    uv_pipe_queue_read(loop, handle);
+    uv__pipe_queue_read(loop, handle);
   }
 }
 
 
-void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_write_t* req) {
   int err;
 
@@ -2091,26 +2066,25 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
   if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE &&
       handle->pipe.conn.non_overlapped_writes_tail) {
     assert(handle->stream.conn.write_reqs_pending > 0);
-    uv_queue_non_overlapped_write(handle);
+    uv__queue_non_overlapped_write(handle);
   }
 
-  if (handle->stream.conn.shutdown_req != NULL &&
-      handle->stream.conn.write_reqs_pending == 0) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
-  }
+  if (handle->stream.conn.write_reqs_pending == 0)
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_req_t* raw_req) {
   uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req;
 
   assert(handle->type == UV_NAMED_PIPE);
 
   if (handle->flags & UV_HANDLE_CLOSING) {
-    /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */
+    /* The req->pipeHandle should be freed already in uv__pipe_close(). */
     assert(req->pipeHandle == INVALID_HANDLE_VALUE);
     DECREASE_PENDING_REQ_COUNT(handle);
     return;
@@ -2130,7 +2104,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
       req->pipeHandle = INVALID_HANDLE_VALUE;
     }
     if (!(handle->flags & UV_HANDLE_CLOSING)) {
-      uv_pipe_queue_accept(loop, handle, req, FALSE);
+      uv__pipe_queue_accept(loop, handle, req, FALSE);
     }
   }
 
@@ -2138,54 +2112,74 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
 }
 
 
-void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
+void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_connect_t* req) {
+  HANDLE pipeHandle;
+  DWORD duplex_flags;
   int err;
 
   assert(handle->type == UV_NAMED_PIPE);
 
   UNREGISTER_HANDLE_REQ(loop, handle, req);
 
-  if (req->cb) {
-    err = 0;
-    if (REQ_SUCCESS(req)) {
-      uv_pipe_connection_init(handle);
-    } else {
-      err = GET_REQ_ERROR(req);
-    }
-    req->cb(req, uv_translate_sys_error(err));
+  err = 0;
+  if (REQ_SUCCESS(req)) {
+    pipeHandle = req->u.connect.pipeHandle;
+    duplex_flags = req->u.connect.duplex_flags;
+    err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
+    if (err)
+      CloseHandle(pipeHandle);
+  } else {
+    err = uv_translate_sys_error(GET_REQ_ERROR(req));
   }
 
+  if (req->cb)
+    req->cb(req, err);
+
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
+
+void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_shutdown_t* req) {
+  int err;
+
   assert(handle->type == UV_NAMED_PIPE);
 
+  /* Clear the shutdown_req field so we don't go here again. */
+  handle->stream.conn.shutdown_req = NULL;
+  handle->flags &= ~UV_HANDLE_SHUTTING;
   UNREGISTER_HANDLE_REQ(loop, handle, req);
 
-  if (handle->flags & UV_HANDLE_READABLE) {
-    /* Initialize and optionally start the eof timer. Only do this if the pipe
-     * is readable and we haven't seen EOF come in ourselves. */
-    eof_timer_init(handle);
+  if (handle->flags & UV_HANDLE_CLOSING) {
+    /* Already closing. Cancel the shutdown. */
+    err = UV_ECANCELED;
+  } else if (!REQ_SUCCESS(req)) {
+    /* An error occurred in trying to shutdown gracefully. */
+    err = uv_translate_sys_error(GET_REQ_ERROR(req));
+  } else {
+    if (handle->flags & UV_HANDLE_READABLE) {
+      /* Initialize and optionally start the eof timer. Only do this if the pipe
+       * is readable and we haven't seen EOF come in ourselves. */
+      eof_timer_init(handle);
+
+      /* If reading start the timer right now. Otherwise uv__pipe_queue_read will
+       * start it. */
+      if (handle->flags & UV_HANDLE_READ_PENDING) {
+        eof_timer_start(handle);
+      }
 
-    /* If reading start the timer right now. Otherwise uv_pipe_queue_read will
-     * start it. */
-    if (handle->flags & UV_HANDLE_READ_PENDING) {
-      eof_timer_start(handle);
+    } else {
+      /* This pipe is not readable. We can just close it to let the other end
+       * know that we're done writing. */
+      close_pipe(handle);
     }
-
-  } else {
-    /* This pipe is not readable. We can just close it to let the other end
-     * know that we're done writing. */
-    close_pipe(handle);
+    err = 0;
   }
 
-  if (req->cb) {
-    req->cb(req, 0);
-  }
+  if (req->cb)
+    req->cb(req, err);
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
@@ -2200,7 +2194,8 @@ static void eof_timer_init(uv_pipe_t* pipe) {
   pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer);
 
   r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer);
-  assert(r == 0); /* timers can't fail */
+  assert(r == 0);  /* timers can't fail */
+  (void) r;
   pipe->pipe.conn.eof_timer->data = pipe;
   uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer);
 }
@@ -2231,9 +2226,9 @@ static void eof_timer_cb(uv_timer_t* timer) {
   assert(pipe->type == UV_NAMED_PIPE);
 
   /* This should always be true, since we start the timer only in
-   * uv_pipe_queue_read after successfully calling ReadFile, or in
-   * uv_process_pipe_shutdown_req if a read is pending, and we always
-   * immediately stop the timer in uv_process_pipe_read_req. */
+   * uv__pipe_queue_read after successfully calling ReadFile, or in
+   * uv__process_pipe_shutdown_req if a read is pending, and we always
+   * immediately stop the timer in uv__process_pipe_read_req. */
   assert(pipe->flags & UV_HANDLE_READ_PENDING);
 
   /* If there are many packets coming off the iocp then the timer callback may
@@ -2254,7 +2249,7 @@ static void eof_timer_cb(uv_timer_t* timer) {
 
   /* Report the eof and update flags. This will get reported even if the user
    * stopped reading in the meantime. TODO: is that okay? */
-  uv_pipe_read_eof(loop, pipe, uv_null_buf_);
+  uv__pipe_read_eof(loop, pipe, uv_null_buf_);
 }
 
 
@@ -2280,10 +2275,16 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
   IO_STATUS_BLOCK io_status;
   FILE_ACCESS_INFORMATION access;
   DWORD duplex_flags = 0;
+  int err;
 
   if (os_handle == INVALID_HANDLE_VALUE)
     return UV_EBADF;
+  if (pipe->flags & UV_HANDLE_PIPESERVER)
+    return UV_EINVAL;
+  if (pipe->flags & UV_HANDLE_CONNECTION)
+    return UV_EBUSY;
 
+  uv__pipe_connection_init(pipe);
   uv__once_init();
   /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
    * underlying OS handle and forget about the original fd.
@@ -2300,6 +2301,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
                          FALSE,
                          DUPLICATE_SAME_ACCESS))
       return uv_translate_sys_error(GetLastError());
+    assert(os_handle != INVALID_HANDLE_VALUE);
     file = -1;
   }
 
@@ -2327,17 +2329,17 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
   if (access.AccessFlags & FILE_READ_DATA)
     duplex_flags |= UV_HANDLE_READABLE;
 
-  if (os_handle == INVALID_HANDLE_VALUE ||
-      uv_set_pipe_handle(pipe->loop,
-                         pipe,
-                         os_handle,
-                         file,
-                         duplex_flags) == -1) {
-    return UV_EINVAL;
+  err = uv__set_pipe_handle(pipe->loop,
+                            pipe,
+                            os_handle,
+                            file,
+                            duplex_flags);
+  if (err) {
+    if (file == -1)
+      CloseHandle(os_handle);
+    return err;
   }
 
-  uv_pipe_connection_init(pipe);
-
   if (pipe->ipc) {
     assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
     pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
@@ -2361,6 +2363,51 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size)
   uv__once_init();
   name_info = NULL;
 
+  if (handle->name != NULL) {
+    /* The user might try to query the name before we are connected,
+     * and this is just easier to return the cached value if we have it. */
+    name_buf = handle->name;
+    name_len = wcslen(name_buf);
+
+    /* check how much space we need */
+    addrlen = WideCharToMultiByte(CP_UTF8,
+                                  0,
+                                  name_buf,
+                                  name_len,
+                                  NULL,
+                                  0,
+                                  NULL,
+                                  NULL);
+    if (!addrlen) {
+      *size = 0;
+      err = uv_translate_sys_error(GetLastError());
+      return err;
+    } else if (addrlen >= *size) {
+      *size = addrlen + 1;
+      err = UV_ENOBUFS;
+      goto error;
+    }
+
+    addrlen = WideCharToMultiByte(CP_UTF8,
+                                  0,
+                                  name_buf,
+                                  name_len,
+                                  buffer,
+                                  addrlen,
+                                  NULL,
+                                  NULL);
+    if (!addrlen) {
+      *size = 0;
+      err = uv_translate_sys_error(GetLastError());
+      return err;
+    }
+
+    *size = addrlen;
+    buffer[addrlen] = '\0';
+
+    return 0;
+  }
+
   if (handle->handle == INVALID_HANDLE_VALUE) {
     *size = 0;
     return UV_EINVAL;
@@ -2498,6 +2545,11 @@ int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
   if (handle->handle != INVALID_HANDLE_VALUE)
     return uv__pipe_getname(handle, buffer, size);
 
+  if (handle->flags & UV_HANDLE_CONNECTION) {
+    if (handle->name != NULL)
+      return uv__pipe_getname(handle, buffer, size);
+  }
+
   return UV_EBADF;
 }
 
index 9d37759..bd531b0 100644 (file)
@@ -34,7 +34,9 @@ static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = {
   {0xf9eab0c0, 0x26d4, 0x11d0,
       {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}},
   {0x9fc48064, 0x7298, 0x43e4,
-      {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}
+      {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}},
+  {0xa00943d9, 0x9c2e, 0x4633,
+      {0x9b, 0x59, 0x00, 0x57, 0xa3, 0x16, 0x09, 0x94}}
 };
 
 typedef struct uv_single_fd_set_s {
@@ -122,14 +124,14 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
 
   memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped);
 
-  result = uv_msafd_poll((SOCKET) handle->peer_socket,
-                         afd_poll_info,
-                         afd_poll_info,
-                         &req->u.io.overlapped);
+  result = uv__msafd_poll((SOCKET) handle->peer_socket,
+                          afd_poll_info,
+                          afd_poll_info,
+                          &req->u.io.overlapped);
   if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) {
     /* Queue this req, reporting an error. */
     SET_REQ_ERROR(req, WSAGetLastError());
-    uv_insert_pending_req(loop, req);
+    uv__insert_pending_req(loop, req);
   }
 }
 
@@ -195,7 +197,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
   } else if ((handle->flags & UV_HANDLE_CLOSING) &&
              handle->submitted_events_1 == 0 &&
              handle->submitted_events_2 == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 }
 
@@ -357,7 +359,7 @@ static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) {
                          WT_EXECUTELONGFUNCTION)) {
     /* Make this req pending, reporting an error. */
     SET_REQ_ERROR(req, GetLastError());
-    uv_insert_pending_req(loop, req);
+    uv__insert_pending_req(loop, req);
   }
 }
 
@@ -400,7 +402,7 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle,
   } else if ((handle->flags & UV_HANDLE_CLOSING) &&
              handle->submitted_events_1 == 0 &&
              handle->submitted_events_2 == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 }
 
@@ -524,7 +526,7 @@ int uv_poll_stop(uv_poll_t* handle) {
 }
 
 
-void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
+void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
   if (!(handle->flags & UV_HANDLE_POLL_SLOW)) {
     uv__fast_poll_process_poll_req(loop, handle, req);
   } else {
@@ -533,7 +535,7 @@ void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) {
 }
 
 
-int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
+int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) {
   AFD_POLL_INFO afd_poll_info;
   DWORD error;
   int result;
@@ -543,7 +545,7 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
 
   if (handle->submitted_events_1 == 0 &&
       handle->submitted_events_2 == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
     return 0;
   }
 
@@ -559,10 +561,10 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
   afd_poll_info.Handles[0].Status = 0;
   afd_poll_info.Handles[0].Events = AFD_POLL_ALL;
 
-  result = uv_msafd_poll(handle->socket,
-                         &afd_poll_info,
-                         uv__get_afd_poll_info_dummy(),
-                         uv__get_overlapped_dummy());
+  result = uv__msafd_poll(handle->socket,
+                          &afd_poll_info,
+                          uv__get_afd_poll_info_dummy(),
+                          uv__get_overlapped_dummy());
 
   if (result == SOCKET_ERROR) {
     error = WSAGetLastError();
@@ -574,7 +576,7 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) {
 }
 
 
-void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
+void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle) {
   assert(handle->flags & UV_HANDLE_CLOSING);
   assert(!(handle->flags & UV_HANDLE_CLOSED));
 
index 9ca4122..248b7ea 100644 (file)
@@ -105,7 +105,7 @@ static void uv__init_global_job_handle(void) {
 }
 
 
-static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
+static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
   int ws_len, r;
   WCHAR* ws;
 
@@ -137,7 +137,7 @@ static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) {
 }
 
 
-static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
+static void uv__process_init(uv_loop_t* loop, uv_process_t* handle) {
   uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
   handle->exit_cb = NULL;
   handle->pid = 0;
@@ -864,7 +864,7 @@ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) {
 
 
 /* Called on main thread after a child process has exited. */
-void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
+void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
   int64_t exit_code;
   DWORD status;
 
@@ -874,7 +874,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
   /* If we're closing, don't call the exit callback. Just schedule a close
    * callback now. */
   if (handle->flags & UV_HANDLE_CLOSING) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
     return;
   }
 
@@ -902,7 +902,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
 }
 
 
-void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
+void uv__process_close(uv_loop_t* loop, uv_process_t* handle) {
   uv__handle_closing(handle);
 
   if (handle->wait_handle != INVALID_HANDLE_VALUE) {
@@ -918,12 +918,12 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) {
   }
 
   if (!handle->exit_cb_pending) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
+    uv__want_endgame(loop, (uv_handle_t*)handle);
   }
 }
 
 
-void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) {
+void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) {
   assert(!handle->exit_cb_pending);
   assert(handle->flags & UV_HANDLE_CLOSING);
   assert(!(handle->flags & UV_HANDLE_CLOSED));
@@ -948,7 +948,7 @@ int uv_spawn(uv_loop_t* loop,
   PROCESS_INFORMATION info;
   DWORD process_flags;
 
-  uv_process_init(loop, process);
+  uv__process_init(loop, process);
   process->exit_cb = options->exit_cb;
 
   if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) {
@@ -975,7 +975,7 @@ int uv_spawn(uv_loop_t* loop,
                               UV_PROCESS_WINDOWS_HIDE_GUI |
                               UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
 
-  err = uv_utf8_to_utf16_alloc(options->file, &application);
+  err = uv__utf8_to_utf16_alloc(options->file, &application);
   if (err)
     goto done;
 
@@ -994,7 +994,7 @@ int uv_spawn(uv_loop_t* loop,
 
   if (options->cwd) {
     /* Explicit cwd */
-    err = uv_utf8_to_utf16_alloc(options->cwd, &cwd);
+    err = uv__utf8_to_utf16_alloc(options->cwd, &cwd);
     if (err)
       goto done;
 
index f2513b7..9e20759 100644 (file)
@@ -50,7 +50,7 @@
   (pRtlNtStatusToDosError(GET_REQ_STATUS((req))))
 
 #define GET_REQ_SOCK_ERROR(req)                                         \
-  (uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
+  (uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
 
 
 #define REGISTER_HANDLE_REQ(loop, handle, req)                          \
   }
 
 
-INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) {
+INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
   return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
 }
 
 
-INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
+INLINE static void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
   req->next_req = NULL;
   if (loop->pending_reqs_tail) {
 #ifdef _DEBUG
@@ -115,19 +115,19 @@ INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
   do {                                                                        \
     switch (((uv_handle_t*) (req)->handle_at)->type) {                        \
       case UV_TCP:                                                            \
-        uv_process_tcp_##method##_req(loop,                                   \
+        uv__process_tcp_##method##_req(loop,                                  \
                                       (uv_tcp_t*) ((req)->handle_at),         \
                                       req);                                   \
         break;                                                                \
                                                                               \
       case UV_NAMED_PIPE:                                                     \
-        uv_process_pipe_##method##_req(loop,                                  \
+        uv__process_pipe_##method##_req(loop,                                 \
                                        (uv_pipe_t*) ((req)->handle_at),       \
                                        req);                                  \
         break;                                                                \
                                                                               \
       case UV_TTY:                                                            \
-        uv_process_tty_##method##_req(loop,                                   \
+        uv__process_tty_##method##_req(loop,                                  \
                                       (uv_tty_t*) ((req)->handle_at),         \
                                       req);                                   \
         break;                                                                \
@@ -138,13 +138,13 @@ INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
   } while (0)
 
 
-INLINE static int uv_process_reqs(uv_loop_t* loop) {
+INLINE static void uv__process_reqs(uv_loop_t* loop) {
   uv_req_t* req;
   uv_req_t* first;
   uv_req_t* next;
 
   if (loop->pending_reqs_tail == NULL)
-    return 0;
+    return;
 
   first = loop->pending_reqs_tail->next_req;
   next = first;
@@ -172,50 +172,43 @@ INLINE static int uv_process_reqs(uv_loop_t* loop) {
         break;
 
       case UV_SHUTDOWN:
-        /* Tcp shutdown requests don't come here. */
-        assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE);
-        uv_process_pipe_shutdown_req(
-            loop,
-            (uv_pipe_t*) ((uv_shutdown_t*) req)->handle,
-            (uv_shutdown_t*) req);
+        DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle);
         break;
 
       case UV_UDP_RECV:
-        uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
+        uv__process_udp_recv_req(loop, (uv_udp_t*) req->data, req);
         break;
 
       case UV_UDP_SEND:
-        uv_process_udp_send_req(loop,
-                                ((uv_udp_send_t*) req)->handle,
-                                (uv_udp_send_t*) req);
+        uv__process_udp_send_req(loop,
+                                 ((uv_udp_send_t*) req)->handle,
+                                 (uv_udp_send_t*) req);
         break;
 
       case UV_WAKEUP:
-        uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
+        uv__process_async_wakeup_req(loop, (uv_async_t*) req->data, req);
         break;
 
       case UV_SIGNAL_REQ:
-        uv_process_signal_req(loop, (uv_signal_t*) req->data, req);
+        uv__process_signal_req(loop, (uv_signal_t*) req->data, req);
         break;
 
       case UV_POLL_REQ:
-        uv_process_poll_req(loop, (uv_poll_t*) req->data, req);
+        uv__process_poll_req(loop, (uv_poll_t*) req->data, req);
         break;
 
       case UV_PROCESS_EXIT:
-        uv_process_proc_exit(loop, (uv_process_t*) req->data);
+        uv__process_proc_exit(loop, (uv_process_t*) req->data);
         break;
 
       case UV_FS_EVENT_REQ:
-        uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
+        uv__process_fs_event_req(loop, req, (uv_fs_event_t*) req->data);
         break;
 
       default:
         assert(0);
     }
   }
-
-  return 1;
 }
 
 #endif /* UV_WIN_REQ_INL_H_ */
index 3d9f92c..8c79871 100644 (file)
@@ -39,7 +39,7 @@ int uv__signal_start(uv_signal_t* handle,
                      int signum,
                      int oneshot);
 
-void uv_signals_init(void) {
+void uv__signals_init(void) {
   InitializeCriticalSection(&uv__signal_lock);
   if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE))
     abort();
@@ -231,7 +231,7 @@ int uv__signal_start(uv_signal_t* handle,
 }
 
 
-void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
+void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
     uv_req_t* req) {
   long dispatched_signum;
 
@@ -254,22 +254,22 @@ void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle,
   if (handle->flags & UV_HANDLE_CLOSING) {
     /* When it is closing, it must be stopped at this point. */
     assert(handle->signum == 0);
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 }
 
 
-void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) {
+void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle) {
   uv_signal_stop(handle);
   uv__handle_closing(handle);
 
   if (handle->pending_signum == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 }
 
 
-void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
+void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle) {
   assert(handle->flags & UV_HANDLE_CLOSING);
   assert(!(handle->flags & UV_HANDLE_CLOSED));
 
index 40f5ddd..91b1e78 100644 (file)
@@ -30,9 +30,9 @@
 #include "req-inl.h"
 
 
-INLINE static void uv_stream_init(uv_loop_t* loop,
-                                  uv_stream_t* handle,
-                                  uv_handle_type type) {
+INLINE static void uv__stream_init(uv_loop_t* loop,
+                                   uv_stream_t* handle,
+                                   uv_handle_type type) {
   uv__handle_init(loop, (uv_handle_t*) handle, type);
   handle->write_queue_size = 0;
   handle->activecnt = 0;
@@ -46,7 +46,7 @@ INLINE static void uv_stream_init(uv_loop_t* loop,
 }
 
 
-INLINE static void uv_connection_init(uv_stream_t* handle) {
+INLINE static void uv__connection_init(uv_stream_t* handle) {
   handle->flags |= UV_HANDLE_CONNECTION;
 }
 
index abf477f..292bf58 100644 (file)
 
 int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
   int err;
-
+  if (uv__is_closing(stream)) {
+    return UV_EINVAL;
+  }
   err = ERROR_INVALID_PARAMETER;
   switch (stream->type) {
     case UV_TCP:
-      err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb);
+      err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb);
       break;
     case UV_NAMED_PIPE:
-      err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb);
+      err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb);
       break;
     default:
       assert(0);
@@ -52,10 +54,10 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) {
   err = ERROR_INVALID_PARAMETER;
   switch (server->type) {
     case UV_TCP:
-      err = uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client);
+      err = uv__tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client);
       break;
     case UV_NAMED_PIPE:
-      err = uv_pipe_accept((uv_pipe_t*)server, client);
+      err = uv__pipe_accept((uv_pipe_t*)server, client);
       break;
     default:
       assert(0);
@@ -73,13 +75,13 @@ int uv__read_start(uv_stream_t* handle,
   err = ERROR_INVALID_PARAMETER;
   switch (handle->type) {
     case UV_TCP:
-      err = uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
+      err = uv__tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
       break;
     case UV_NAMED_PIPE:
-      err = uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb);
+      err = uv__pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb);
       break;
     case UV_TTY:
-      err = uv_tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb);
+      err = uv__tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb);
       break;
     default:
       assert(0);
@@ -97,7 +99,7 @@ int uv_read_stop(uv_stream_t* handle) {
 
   err = 0;
   if (handle->type == UV_TTY) {
-    err = uv_tty_read_stop((uv_tty_t*) handle);
+    err = uv__tty_read_stop((uv_tty_t*) handle);
   } else if (handle->type == UV_NAMED_PIPE) {
     uv__pipe_read_stop((uv_pipe_t*) handle);
   } else {
@@ -124,14 +126,14 @@ int uv_write(uv_write_t* req,
   err = ERROR_INVALID_PARAMETER;
   switch (handle->type) {
     case UV_TCP:
-      err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb);
+      err = uv__tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb);
       break;
     case UV_NAMED_PIPE:
       err = uv__pipe_write(
           loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
       break;
     case UV_TTY:
-      err = uv_tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
+      err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
       break;
     default:
       assert(0);
@@ -217,7 +219,12 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
 
-  uv_want_endgame(loop, (uv_handle_t*)handle);
+  if (handle->stream.conn.write_reqs_pending == 0) {
+    if (handle->type == UV_NAMED_PIPE)
+      uv__pipe_shutdown(loop, (uv_pipe_t*) handle, req);
+    else
+      uv__insert_pending_req(loop, (uv_req_t*) req);
+  }
 
   return 0;
 }
index 6ca11e0..b6aa4c5 100644 (file)
@@ -78,11 +78,11 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign
 }
 
 
-static int uv_tcp_set_socket(uv_loop_t* loop,
-                             uv_tcp_t* handle,
-                             SOCKET socket,
-                             int family,
-                             int imported) {
+static int uv__tcp_set_socket(uv_loop_t* loop,
+                              uv_tcp_t* handle,
+                              SOCKET socket,
+                              int family,
+                              int imported) {
   DWORD yes = 1;
   int non_ifs_lsp;
   int err;
@@ -162,7 +162,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) {
   if (flags & ~0xFF)
     return UV_EINVAL;
 
-  uv_stream_init(loop, (uv_stream_t*) handle, UV_TCP);
+  uv__stream_init(loop, (uv_stream_t*) handle, UV_TCP);
   handle->tcp.serv.accept_reqs = NULL;
   handle->tcp.serv.pending_accepts = NULL;
   handle->socket = INVALID_SOCKET;
@@ -173,7 +173,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) {
   handle->delayed_error = 0;
 
   /* If anything fails beyond this point we need to remove the handle from
-   * the handle queue, since it was added by uv__handle_init in uv_stream_init.
+   * the handle queue, since it was added by uv__handle_init in uv__stream_init.
    */
 
   if (domain != AF_UNSPEC) {
@@ -187,7 +187,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) {
       return uv_translate_sys_error(err);
     }
 
-    err = uv_tcp_set_socket(handle->loop, handle, sock, domain, 0);
+    err = uv__tcp_set_socket(handle->loop, handle, sock, domain, 0);
     if (err) {
       closesocket(sock);
       QUEUE_REMOVE(&handle->handle_queue);
@@ -205,73 +205,76 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) {
 }
 
 
-void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
+void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown_t *req) {
   int err;
-  unsigned int i;
-  uv_tcp_accept_t* req;
 
-  if (handle->flags & UV_HANDLE_CONNECTION &&
-      handle->stream.conn.shutdown_req != NULL &&
-      handle->stream.conn.write_reqs_pending == 0) {
+  assert(req);
+  assert(stream->stream.conn.write_reqs_pending == 0);
+  assert(!(stream->flags & UV_HANDLE_SHUT));
+  assert(stream->flags & UV_HANDLE_CONNECTION);
 
-    UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
+  stream->stream.conn.shutdown_req = NULL;
+  stream->flags &= ~UV_HANDLE_SHUTTING;
+  UNREGISTER_HANDLE_REQ(loop, stream, req);
 
-    err = 0;
-    if (handle->flags & UV_HANDLE_CLOSING) {
-      err = ERROR_OPERATION_ABORTED;
-    } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) {
-      err = WSAGetLastError();
-    }
+  err = 0;
+  if (stream->flags & UV_HANDLE_CLOSING)
+   /* The user destroyed the stream before we got to do the shutdown. */
+    err = UV_ECANCELED;
+  else if (shutdown(stream->socket, SD_SEND) == SOCKET_ERROR)
+    err = uv_translate_sys_error(WSAGetLastError());
+  else /* Success. */
+    stream->flags |= UV_HANDLE_SHUT;
+
+  if (req->cb)
+    req->cb(req, err);
 
-    if (handle->stream.conn.shutdown_req->cb) {
-      handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req,
-                               uv_translate_sys_error(err));
-    }
+  DECREASE_PENDING_REQ_COUNT(stream);
+}
 
-    handle->stream.conn.shutdown_req = NULL;
-    DECREASE_PENDING_REQ_COUNT(handle);
-    return;
-  }
 
-  if (handle->flags & UV_HANDLE_CLOSING &&
-      handle->reqs_pending == 0) {
-    assert(!(handle->flags & UV_HANDLE_CLOSED));
-    assert(handle->socket == INVALID_SOCKET);
+void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
+  unsigned int i;
+  uv_tcp_accept_t* req;
+
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(handle->reqs_pending == 0);
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  assert(handle->socket == INVALID_SOCKET);
 
-    if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
-      if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
-        for (i = 0; i < uv_simultaneous_server_accepts; i++) {
-          req = &handle->tcp.serv.accept_reqs[i];
-          if (req->wait_handle != INVALID_HANDLE_VALUE) {
-            UnregisterWait(req->wait_handle);
-            req->wait_handle = INVALID_HANDLE_VALUE;
-          }
-          if (req->event_handle != NULL) {
-            CloseHandle(req->event_handle);
-            req->event_handle = NULL;
-          }
+  if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
+    if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
+      for (i = 0; i < uv_simultaneous_server_accepts; i++) {
+        req = &handle->tcp.serv.accept_reqs[i];
+        if (req->wait_handle != INVALID_HANDLE_VALUE) {
+          UnregisterWait(req->wait_handle);
+          req->wait_handle = INVALID_HANDLE_VALUE;
+        }
+        if (req->event_handle != NULL) {
+          CloseHandle(req->event_handle);
+          req->event_handle = NULL;
         }
       }
-
-      uv__free(handle->tcp.serv.accept_reqs);
-      handle->tcp.serv.accept_reqs = NULL;
     }
 
-    if (handle->flags & UV_HANDLE_CONNECTION &&
-        handle->flags & UV_HANDLE_EMULATE_IOCP) {
-      if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
-        UnregisterWait(handle->read_req.wait_handle);
-        handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
-      }
-      if (handle->read_req.event_handle != NULL) {
-        CloseHandle(handle->read_req.event_handle);
-        handle->read_req.event_handle = NULL;
-      }
-    }
+    uv__free(handle->tcp.serv.accept_reqs);
+    handle->tcp.serv.accept_reqs = NULL;
+  }
 
-    uv__handle_close(handle);
-    loop->active_tcp_streams--;
+  if (handle->flags & UV_HANDLE_CONNECTION &&
+      handle->flags & UV_HANDLE_EMULATE_IOCP) {
+    if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
+      UnregisterWait(handle->read_req.wait_handle);
+      handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
+    }
+    if (handle->read_req.event_handle != NULL) {
+      CloseHandle(handle->read_req.event_handle);
+      handle->read_req.event_handle = NULL;
+    }
   }
+
+  uv__handle_close(handle);
+  loop->active_tcp_streams--;
 }
 
 
@@ -286,10 +289,10 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
  * See issue #1360.
  *
  */
-static int uv_tcp_try_bind(uv_tcp_t* handle,
-                           const struct sockaddr* addr,
-                           unsigned int addrlen,
-                           unsigned int flags) {
+static int uv__tcp_try_bind(uv_tcp_t* handle,
+                            const struct sockaddr* addr,
+                            unsigned int addrlen,
+                            unsigned int flags) {
   DWORD err;
   int r;
 
@@ -305,7 +308,7 @@ static int uv_tcp_try_bind(uv_tcp_t* handle,
       return WSAGetLastError();
     }
 
-    err = uv_tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0);
+    err = uv__tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0);
     if (err) {
       closesocket(sock);
       return err;
@@ -385,7 +388,7 @@ static void CALLBACK post_write_completion(void* context, BOOLEAN timed_out) {
 }
 
 
-static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
+static void uv__tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
   uv_loop_t* loop = handle->loop;
   BOOL success;
   DWORD bytes;
@@ -406,7 +409,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
   accept_socket = socket(family, SOCK_STREAM, 0);
   if (accept_socket == INVALID_SOCKET) {
     SET_REQ_ERROR(req, WSAGetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
     handle->reqs_pending++;
     return;
   }
@@ -414,7 +417,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
   /* Make the socket non-inheritable */
   if (!SetHandleInformation((HANDLE) accept_socket, HANDLE_FLAG_INHERIT, 0)) {
     SET_REQ_ERROR(req, GetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
     handle->reqs_pending++;
     closesocket(accept_socket);
     return;
@@ -440,7 +443,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
     /* Process the req without IOCP. */
     req->accept_socket = accept_socket;
     handle->reqs_pending++;
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
     /* The req will be processed with IOCP. */
     req->accept_socket = accept_socket;
@@ -451,12 +454,12 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
           req->event_handle, post_completion, (void*) req,
           INFINITE, WT_EXECUTEINWAITTHREAD)) {
       SET_REQ_ERROR(req, GetLastError());
-      uv_insert_pending_req(loop, (uv_req_t*)req);
+      uv__insert_pending_req(loop, (uv_req_t*)req);
     }
   } else {
     /* Make this req pending reporting an error. */
     SET_REQ_ERROR(req, WSAGetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
     handle->reqs_pending++;
     /* Destroy the preallocated client socket. */
     closesocket(accept_socket);
@@ -469,7 +472,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) {
 }
 
 
-static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
+static void uv__tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
   uv_read_t* req;
   uv_buf_t buf;
   int result;
@@ -524,7 +527,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
   if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) {
     /* Process the req without IOCP. */
     req->u.io.overlapped.InternalHigh = bytes;
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
     /* The req will be processed with IOCP. */
     if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
@@ -533,12 +536,12 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) {
           req->event_handle, post_completion, (void*) req,
           INFINITE, WT_EXECUTEINWAITTHREAD)) {
       SET_REQ_ERROR(req, GetLastError());
-      uv_insert_pending_req(loop, (uv_req_t*)req);
+      uv__insert_pending_req(loop, (uv_req_t*)req);
     }
   } else {
     /* Make this req pending reporting an error. */
     SET_REQ_ERROR(req, WSAGetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   }
 }
 
@@ -558,7 +561,7 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
 }
 
 
-int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
+int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
   unsigned int i, simultaneous_accepts;
   uv_tcp_accept_t* req;
   int err;
@@ -578,10 +581,10 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
   }
 
   if (!(handle->flags & UV_HANDLE_BOUND)) {
-    err = uv_tcp_try_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip4_any_,
-                          sizeof(uv_addr_ip4_any_),
-                          0);
+    err = uv__tcp_try_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           0);
     if (err)
       return err;
     if (handle->delayed_error)
@@ -589,7 +592,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
   }
 
   if (!handle->tcp.serv.func_acceptex) {
-    if (!uv_get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) {
+    if (!uv__get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) {
       return WSAEAFNOSUPPORT;
     }
   }
@@ -630,7 +633,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
         req->event_handle = NULL;
       }
 
-      uv_tcp_queue_accept(handle, req);
+      uv__tcp_queue_accept(handle, req);
     }
 
     /* Initialize other unused requests too, because uv_tcp_endgame doesn't
@@ -650,7 +653,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) {
 }
 
 
-int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
+int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
   uv_loop_t* loop = server->loop;
   int err = 0;
   int family;
@@ -672,7 +675,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
     family = AF_INET;
   }
 
-  err = uv_tcp_set_socket(client->loop,
+  err = uv__tcp_set_socket(client->loop,
                           client,
                           req->accept_socket,
                           family,
@@ -680,7 +683,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
   if (err) {
     closesocket(req->accept_socket);
   } else {
-    uv_connection_init((uv_stream_t*) client);
+    uv__connection_init((uv_stream_t*) client);
     /* AcceptEx() implicitly binds the accepted socket. */
     client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
   }
@@ -693,7 +696,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
   if (!(server->flags & UV_HANDLE_CLOSING)) {
     /* Check if we're in a middle of changing the number of pending accepts. */
     if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) {
-      uv_tcp_queue_accept(server, req);
+      uv__tcp_queue_accept(server, req);
     } else {
       /* We better be switching to a single pending accept. */
       assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT);
@@ -706,7 +709,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
          * All previously queued accept requests are now processed.
          * We now switch to queueing just a single accept.
          */
-        uv_tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]);
+        uv__tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]);
         server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING;
         server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
       }
@@ -719,7 +722,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
 }
 
 
-int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
+int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb) {
   uv_loop_t* loop = handle->loop;
 
@@ -738,7 +741,7 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
         uv_fatal_error(GetLastError(), "CreateEvent");
       }
     }
-    uv_tcp_queue_read(loop, handle);
+    uv__tcp_queue_read(loop, handle);
   }
 
   return 0;
@@ -779,7 +782,7 @@ static int uv__is_fast_loopback_fail_supported(void) {
   return os_info.dwBuildNumber >= 16299;
 }
 
-static int uv_tcp_try_connect(uv_connect_t* req,
+static int uv__tcp_try_connect(uv_connect_t* req,
                               uv_tcp_t* handle,
                               const struct sockaddr* addr,
                               unsigned int addrlen,
@@ -807,7 +810,7 @@ static int uv_tcp_try_connect(uv_connect_t* req,
     } else {
       abort();
     }
-    err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0);
+    err = uv__tcp_try_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return err;
     if (handle->delayed_error != 0)
@@ -815,7 +818,7 @@ static int uv_tcp_try_connect(uv_connect_t* req,
   }
 
   if (!handle->tcp.conn.func_connectex) {
-    if (!uv_get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) {
+    if (!uv__get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) {
       return WSAEAFNOSUPPORT;
     }
   }
@@ -850,7 +853,7 @@ out:
     /* Process the req without IOCP. */
     handle->reqs_pending++;
     REGISTER_HANDLE_REQ(loop, handle, req);
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
     return 0;
   }
 
@@ -866,7 +869,7 @@ out:
     /* Process the req without IOCP. */
     handle->reqs_pending++;
     REGISTER_HANDLE_REQ(loop, handle, req);
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   } else if (UV_SUCCEEDED_WITH_IOCP(success)) {
     /* The req will be processed with IOCP. */
     handle->reqs_pending++;
@@ -903,7 +906,7 @@ int uv_tcp_getpeername(const uv_tcp_t* handle,
 }
 
 
-int uv_tcp_write(uv_loop_t* loop,
+int uv__tcp_write(uv_loop_t* loop,
                  uv_write_t* req,
                  uv_tcp_t* handle,
                  const uv_buf_t bufs[],
@@ -941,7 +944,7 @@ int uv_tcp_write(uv_loop_t* loop,
     handle->reqs_pending++;
     handle->stream.conn.write_reqs_pending++;
     REGISTER_HANDLE_REQ(loop, handle, req);
-    uv_insert_pending_req(loop, (uv_req_t*) req);
+    uv__insert_pending_req(loop, (uv_req_t*) req);
   } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
     /* Request queued by the kernel. */
     req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
@@ -954,7 +957,7 @@ int uv_tcp_write(uv_loop_t* loop,
           req->event_handle, post_write_completion, (void*) req,
           INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
       SET_REQ_ERROR(req, GetLastError());
-      uv_insert_pending_req(loop, (uv_req_t*)req);
+      uv__insert_pending_req(loop, (uv_req_t*)req);
     }
   } else {
     /* Send failed due to an error, report it later */
@@ -963,7 +966,7 @@ int uv_tcp_write(uv_loop_t* loop,
     handle->stream.conn.write_reqs_pending++;
     REGISTER_HANDLE_REQ(loop, handle, req);
     SET_REQ_ERROR(req, WSAGetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*) req);
+    uv__insert_pending_req(loop, (uv_req_t*) req);
   }
 
   return 0;
@@ -994,7 +997,7 @@ int uv__tcp_try_write(uv_tcp_t* handle,
 }
 
 
-void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_req_t* req) {
   DWORD bytes, flags, err;
   uv_buf_t buf;
@@ -1115,7 +1118,7 @@ done:
     /* Post another read if still reading and not closing. */
     if ((handle->flags & UV_HANDLE_READING) &&
         !(handle->flags & UV_HANDLE_READ_PENDING)) {
-      uv_tcp_queue_read(loop, handle);
+      uv__tcp_queue_read(loop, handle);
     }
   }
 
@@ -1123,7 +1126,7 @@ done:
 }
 
 
-void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_write_t* req) {
   int err;
 
@@ -1160,16 +1163,17 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
       closesocket(handle->socket);
       handle->socket = INVALID_SOCKET;
     }
-    if (handle->stream.conn.shutdown_req != NULL) {
-      uv_want_endgame(loop, (uv_handle_t*)handle);
-    }
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__process_tcp_shutdown_req(loop,
+                                   handle,
+                                   handle->stream.conn.shutdown_req);
   }
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_req_t* raw_req) {
   uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req;
   int err;
@@ -1209,7 +1213,7 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
     closesocket(req->accept_socket);
     req->accept_socket = INVALID_SOCKET;
     if (handle->flags & UV_HANDLE_LISTENING) {
-      uv_tcp_queue_accept(handle, req);
+      uv__tcp_queue_accept(handle, req);
     }
   }
 
@@ -1217,7 +1221,7 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
 }
 
 
-void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
+void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
     uv_connect_t* req) {
   int err;
 
@@ -1242,7 +1246,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
                           SO_UPDATE_CONNECT_CONTEXT,
                           NULL,
                           0) == 0) {
-      uv_connection_init((uv_stream_t*)handle);
+      uv__connection_init((uv_stream_t*)handle);
       handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
       loop->active_tcp_streams++;
     } else {
@@ -1312,7 +1316,7 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp,
     return WSAGetLastError();
   }
 
-  err = uv_tcp_set_socket(
+  err = uv__tcp_set_socket(
       tcp->loop, tcp, socket, xfer_info->socket_info.iAddressFamily, 1);
   if (err) {
     closesocket(socket);
@@ -1323,7 +1327,7 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp,
   tcp->flags |= UV_HANDLE_BOUND | UV_HANDLE_SHARED_TCP_SOCKET;
 
   if (xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION) {
-    uv_connection_init((uv_stream_t*)tcp);
+    uv__connection_init((uv_stream_t*)tcp);
     tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
   }
 
@@ -1404,14 +1408,14 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
 }
 
 
-static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) {
+static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) {
   SOCKET socket;
   int non_ifs_lsp;
   int reading;
   int writing;
 
   socket = tcp->socket;
-  reading = tcp->flags & UV_HANDLE_READING;
+  reading = tcp->flags & UV_HANDLE_READ_PENDING;
   writing = tcp->stream.conn.write_reqs_pending > 0;
   if (!reading && !writing)
     return;
@@ -1456,12 +1460,12 @@ static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) {
 }
 
 
-void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
+void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
   if (tcp->flags & UV_HANDLE_CONNECTION) {
-    uv_tcp_try_cancel_reqs(tcp);
     if (tcp->flags & UV_HANDLE_READING) {
       uv_read_stop((uv_stream_t*) tcp);
     }
+    uv__tcp_try_cancel_reqs(tcp);
   } else {
     if (tcp->tcp.serv.accept_reqs != NULL) {
       /* First close the incoming sockets to cancel the accept operations before
@@ -1483,6 +1487,9 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
     DECREASE_ACTIVE_COUNT(loop, tcp);
   }
 
+  tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
+  uv__handle_closing(tcp);
+
   /* If any overlapped req failed to cancel, calling `closesocket` now would
    * cause Win32 to send an RST packet. Try to avoid that for writes, if
    * possibly applicable, by waiting to process the completion notifications
@@ -1494,12 +1501,8 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
     tcp->socket = INVALID_SOCKET;
   }
 
-  tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
-  uv__handle_closing(tcp);
-
-  if (tcp->reqs_pending == 0) {
-    uv_want_endgame(tcp->loop, (uv_handle_t*)tcp);
-  }
+  if (tcp->reqs_pending == 0)
+    uv__want_endgame(loop, (uv_handle_t*) tcp);
 }
 
 
@@ -1520,7 +1523,7 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
     return uv_translate_sys_error(GetLastError());
   }
 
-  err = uv_tcp_set_socket(handle->loop,
+  err = uv__tcp_set_socket(handle->loop,
                           handle,
                           sock,
                           protocol_info.iAddressFamily,
@@ -1537,7 +1540,7 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
     saddr_len = sizeof(saddr);
     if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) {
       /* Socket is already connected. */
-      uv_connection_init((uv_stream_t*) handle);
+      uv__connection_init((uv_stream_t*) handle);
       handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
     }
   }
@@ -1555,7 +1558,7 @@ int uv__tcp_bind(uv_tcp_t* handle,
                  unsigned int flags) {
   int err;
 
-  err = uv_tcp_try_bind(handle, addr, addrlen, flags);
+  err = uv__tcp_try_bind(handle, addr, addrlen, flags);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -1573,7 +1576,7 @@ int uv__tcp_connect(uv_connect_t* req,
                     uv_connect_cb cb) {
   int err;
 
-  err = uv_tcp_try_connect(req, handle, addr, addrlen, cb);
+  err = uv__tcp_try_connect(req, handle, addr, addrlen, cb);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -1634,7 +1637,7 @@ int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int f
     goto wsaerror;
   if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0))
     goto error;
-  if (!uv_get_acceptex_function(server, &func_acceptex)) {
+  if (!uv__get_acceptex_function(server, &func_acceptex)) {
     err = WSAEAFNOSUPPORT;
     goto cleanup;
   }
index ea5dc04..d3b1c96 100644 (file)
@@ -182,8 +182,9 @@ int uv_thread_create_ex(uv_thread_t* tid,
 
 
 uv_thread_t uv_thread_self(void) {
+  uv_thread_t key;
   uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key);
-  uv_thread_t key = uv_key_get(&uv__current_thread_key);
+  key = uv_key_get(&uv__current_thread_key);
   if (key == NULL) {
       /* If the thread wasn't started by uv_thread_create (such as the main
        * thread), we assign an id to it now. */
index 1b9d4f8..267ca64 100644 (file)
 #define CURSOR_SIZE_SMALL     25
 #define CURSOR_SIZE_LARGE     100
 
-static void uv_tty_capture_initial_style(
+static void uv__tty_capture_initial_style(
     CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
     CONSOLE_CURSOR_INFO* cursor_info);
-static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
+static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
 static int uv__cancel_read_console(uv_tty_t* handle);
 
 
@@ -163,7 +163,7 @@ static BOOL uv__need_check_vterm_state = TRUE;
 static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
 static void uv__determine_vterm_state(HANDLE handle);
 
-void uv_console_init(void) {
+void uv__console_init(void) {
   if (uv_sem_init(&uv_tty_output_lock, 1))
     abort();
   uv__tty_console_handle = CreateFileW(L"CONOUT$",
@@ -238,16 +238,16 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
       uv__determine_vterm_state(handle);
 
     /* Remember the original console text attributes and cursor info. */
-    uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info);
+    uv__tty_capture_initial_style(&screen_buffer_info, &cursor_info);
 
-    uv_tty_update_virtual_window(&screen_buffer_info);
+    uv__tty_update_virtual_window(&screen_buffer_info);
 
     uv_sem_post(&uv_tty_output_lock);
   }
 
 
-  uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY);
-  uv_connection_init((uv_stream_t*) tty);
+  uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
+  uv__connection_init((uv_stream_t*) tty);
 
   tty->handle = handle;
   tty->u.fd = fd;
@@ -289,7 +289,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
 /* Set the default console text attributes based on how the console was
  * configured when libuv started.
  */
-static void uv_tty_capture_initial_style(
+static void uv__tty_capture_initial_style(
     CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
     CONSOLE_CURSOR_INFO* cursor_info) {
   static int style_captured = 0;
@@ -380,7 +380,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
     was_reading = 1;
     alloc_cb = tty->alloc_cb;
     read_cb = tty->read_cb;
-    err = uv_tty_read_stop(tty);
+    err = uv__tty_read_stop(tty);
     if (err) {
       return uv_translate_sys_error(err);
     }
@@ -404,7 +404,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
 
   /* If we just stopped reading, restart. */
   if (was_reading) {
-    err = uv_tty_read_start(tty, alloc_cb, read_cb);
+    err = uv__tty_read_start(tty, alloc_cb, read_cb);
     if (err) {
       return uv_translate_sys_error(err);
     }
@@ -422,7 +422,7 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
   }
 
   uv_sem_wait(&uv_tty_output_lock);
-  uv_tty_update_virtual_window(&info);
+  uv__tty_update_virtual_window(&info);
   uv_sem_post(&uv_tty_output_lock);
 
   *width = uv_tty_virtual_width;
@@ -452,7 +452,7 @@ static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
 }
 
 
-static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
+static void uv__tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
   uv_read_t* req;
   BOOL r;
 
@@ -475,7 +475,7 @@ static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
   if (!r) {
     handle->tty.rd.read_raw_wait = NULL;
     SET_REQ_ERROR(req, GetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   }
 
   handle->flags |= UV_HANDLE_READ_PENDING;
@@ -579,7 +579,7 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
 }
 
 
-static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
+static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
   uv_read_t* req;
   BOOL r;
 
@@ -611,7 +611,7 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
                         WT_EXECUTELONGFUNCTION);
   if (!r) {
     SET_REQ_ERROR(req, GetLastError());
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   }
 
   handle->flags |= UV_HANDLE_READ_PENDING;
@@ -619,11 +619,11 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
 }
 
 
-static void uv_tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
+static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
   if (handle->flags & UV_HANDLE_TTY_RAW) {
-    uv_tty_queue_read_raw(loop, handle);
+    uv__tty_queue_read_raw(loop, handle);
   } else {
-    uv_tty_queue_read_line(loop, handle);
+    uv__tty_queue_read_line(loop, handle);
   }
 }
 
@@ -947,7 +947,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
   /* Wait for more input events. */
   if ((handle->flags & UV_HANDLE_READING) &&
       !(handle->flags & UV_HANDLE_READ_PENDING)) {
-    uv_tty_queue_read(loop, handle);
+    uv__tty_queue_read(loop, handle);
   }
 
   DECREASE_PENDING_REQ_COUNT(handle);
@@ -992,14 +992,14 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
   /* Wait for more input events. */
   if ((handle->flags & UV_HANDLE_READING) &&
       !(handle->flags & UV_HANDLE_READ_PENDING)) {
-    uv_tty_queue_read(loop, handle);
+    uv__tty_queue_read(loop, handle);
   }
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_req_t* req) {
   assert(handle->type == UV_TTY);
   assert(handle->flags & UV_HANDLE_TTY_READABLE);
@@ -1015,7 +1015,7 @@ void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
 }
 
 
-int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
+int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb) {
   uv_loop_t* loop = handle->loop;
 
@@ -1038,20 +1038,20 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
    * Short-circuit if this could be the case. */
   if (handle->tty.rd.last_key_len > 0) {
     SET_REQ_SUCCESS(&handle->read_req);
-    uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
+    uv__insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
     /* Make sure no attempt is made to insert it again until it's handled. */
     handle->flags |= UV_HANDLE_READ_PENDING;
     handle->reqs_pending++;
     return 0;
   }
 
-  uv_tty_queue_read(loop, handle);
+  uv__tty_queue_read(loop, handle);
 
   return 0;
 }
 
 
-int uv_tty_read_stop(uv_tty_t* handle) {
+int uv__tty_read_stop(uv_tty_t* handle) {
   INPUT_RECORD record;
   DWORD written, err;
 
@@ -1137,7 +1137,7 @@ static int uv__cancel_read_console(uv_tty_t* handle) {
 }
 
 
-static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
+static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
   uv_tty_virtual_width = info->dwSize.X;
   uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1;
 
@@ -1160,12 +1160,12 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
 }
 
 
-static COORD uv_tty_make_real_coord(uv_tty_t* handle,
+static COORD uv__tty_make_real_coord(uv_tty_t* handle,
     CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y,
     unsigned char y_relative) {
   COORD result;
 
-  uv_tty_update_virtual_window(info);
+  uv__tty_update_virtual_window(info);
 
   /* Adjust y position */
   if (y_relative) {
@@ -1197,7 +1197,7 @@ static COORD uv_tty_make_real_coord(uv_tty_t* handle,
 }
 
 
-static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
+static int uv__tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
     DWORD* error) {
   DWORD written;
 
@@ -1218,7 +1218,7 @@ static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
 }
 
 
-static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
+static int uv__tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
     int y, unsigned char y_relative, DWORD* error) {
   CONSOLE_SCREEN_BUFFER_INFO info;
   COORD pos;
@@ -1232,7 +1232,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
     *error = GetLastError();
   }
 
-  pos = uv_tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
+  pos = uv__tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
 
   if (!SetConsoleCursorPosition(handle->handle, pos)) {
     if (GetLastError() == ERROR_INVALID_PARAMETER) {
@@ -1248,7 +1248,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
 }
 
 
-static int uv_tty_reset(uv_tty_t* handle, DWORD* error) {
+static int uv__tty_reset(uv_tty_t* handle, DWORD* error) {
   const COORD origin = {0, 0};
   const WORD char_attrs = uv_tty_default_text_attributes;
   CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
@@ -1300,7 +1300,7 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) {
 
   /* Move the virtual window up to the top. */
   uv_tty_virtual_offset = 0;
-  uv_tty_update_virtual_window(&screen_buffer_info);
+  uv__tty_update_virtual_window(&screen_buffer_info);
 
   /* Reset the cursor size and the cursor state. */
   if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) {
@@ -1312,7 +1312,7 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) {
 }
 
 
-static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
+static int uv__tty_clear(uv_tty_t* handle, int dir, char entire_screen,
     DWORD* error) {
   CONSOLE_SCREEN_BUFFER_INFO info;
   COORD start, end;
@@ -1341,7 +1341,7 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
     x2r = 1;
   } else {
     /* Clear to end of row. We pretend the console is 65536 characters wide,
-     * uv_tty_make_real_coord will clip it to the actual console width. */
+     * uv__tty_make_real_coord will clip it to the actual console width. */
     x2 = 0xffff;
     x2r = 0;
   }
@@ -1364,8 +1364,8 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
     return -1;
   }
 
-  start = uv_tty_make_real_coord(handle, &info, x1, x1r, y1, y1r);
-  end = uv_tty_make_real_coord(handle, &info, x2, x2r, y2, y2r);
+  start = uv__tty_make_real_coord(handle, &info, x1, x1r, y1, y1r);
+  end = uv__tty_make_real_coord(handle, &info, x2, x2r, y2, y2r);
   count = (end.Y * info.dwSize.X + end.X) -
           (start.Y * info.dwSize.X + start.X) + 1;
 
@@ -1400,7 +1400,7 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
       info.wAttributes |= bg >> 4;                                            \
     } while (0)
 
-static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) {
+static int uv__tty_set_style(uv_tty_t* handle, DWORD* error) {
   unsigned short argc = handle->tty.wr.ansi_csi_argc;
   unsigned short* argv = handle->tty.wr.ansi_csi_argv;
   int i;
@@ -1556,7 +1556,7 @@ static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) {
 }
 
 
-static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
+static int uv__tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
     DWORD* error) {
   CONSOLE_SCREEN_BUFFER_INFO info;
 
@@ -1569,10 +1569,11 @@ static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
     return -1;
   }
 
-  uv_tty_update_virtual_window(&info);
+  uv__tty_update_virtual_window(&info);
 
   handle->tty.wr.saved_position.X = info.dwCursorPosition.X;
-  handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - uv_tty_virtual_offset;
+  handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y -
+        uv_tty_virtual_offset;
   handle->flags |= UV_HANDLE_TTY_SAVED_POSITION;
 
   if (save_attributes) {
@@ -1585,7 +1586,7 @@ static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
 }
 
 
-static int uv_tty_restore_state(uv_tty_t* handle,
+static int uv__tty_restore_state(uv_tty_t* handle,
     unsigned char restore_attributes, DWORD* error) {
   CONSOLE_SCREEN_BUFFER_INFO info;
   WORD new_attributes;
@@ -1595,7 +1596,7 @@ static int uv_tty_restore_state(uv_tty_t* handle,
   }
 
   if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
-    if (uv_tty_move_caret(handle,
+    if (uv__tty_move_caret(handle,
                           handle->tty.wr.saved_position.X,
                           0,
                           handle->tty.wr.saved_position.Y,
@@ -1625,7 +1626,7 @@ static int uv_tty_restore_state(uv_tty_t* handle,
   return 0;
 }
 
-static int uv_tty_set_cursor_visibility(uv_tty_t* handle,
+static int uv__tty_set_cursor_visibility(uv_tty_t* handle,
                                         BOOL visible,
                                         DWORD* error) {
   CONSOLE_CURSOR_INFO cursor_info;
@@ -1645,7 +1646,7 @@ static int uv_tty_set_cursor_visibility(uv_tty_t* handle,
   return 0;
 }
 
-static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
+static int uv__tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
   CONSOLE_CURSOR_INFO cursor_info;
 
   if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
@@ -1670,7 +1671,7 @@ static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
 }
 
 
-static int uv_tty_write_bufs(uv_tty_t* handle,
+static int uv__tty_write_bufs(uv_tty_t* handle,
                              const uv_buf_t bufs[],
                              unsigned int nbufs,
                              DWORD* error) {
@@ -1683,7 +1684,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
 #define FLUSH_TEXT()                                                \
   do {                                                              \
     if (utf16_buf_used > 0) {                                       \
-      uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error);   \
+      uv__tty_emit_text(handle, utf16_buf, utf16_buf_used, error);  \
       utf16_buf_used = 0;                                           \
     }                                                               \
   } while (0)
@@ -1802,21 +1803,21 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
           case 'c':
             /* Full console reset. */
             FLUSH_TEXT();
-            uv_tty_reset(handle, error);
+            uv__tty_reset(handle, error);
             ansi_parser_state = ANSI_NORMAL;
             continue;
 
           case '7':
             /* Save the cursor position and text attributes. */
             FLUSH_TEXT();
-            uv_tty_save_state(handle, 1, error);
+            uv__tty_save_state(handle, 1, error);
             ansi_parser_state = ANSI_NORMAL;
             continue;
 
           case '8':
             /* Restore the cursor position and text attributes */
             FLUSH_TEXT();
-            uv_tty_restore_state(handle, 1, error);
+            uv__tty_restore_state(handle, 1, error);
             ansi_parser_state = ANSI_NORMAL;
             continue;
 
@@ -1849,7 +1850,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
               ? handle->tty.wr.ansi_csi_argv[0] : 1;
             if (style >= 0 && style <= 6) {
               FLUSH_TEXT();
-              uv_tty_set_cursor_shape(handle, style, error);
+              uv__tty_set_cursor_shape(handle, style, error);
             }
           }
 
@@ -1947,7 +1948,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 if (handle->tty.wr.ansi_csi_argc == 1 &&
                     handle->tty.wr.ansi_csi_argv[0] == 25) {
                   FLUSH_TEXT();
-                  uv_tty_set_cursor_visibility(handle, 0, error);
+                  uv__tty_set_cursor_visibility(handle, 0, error);
                 }
                 break;
 
@@ -1956,7 +1957,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 if (handle->tty.wr.ansi_csi_argc == 1 &&
                     handle->tty.wr.ansi_csi_argv[0] == 25) {
                   FLUSH_TEXT();
-                  uv_tty_set_cursor_visibility(handle, 1, error);
+                  uv__tty_set_cursor_visibility(handle, 1, error);
                 }
                 break;
             }
@@ -1970,7 +1971,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 y = -(handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
-                uv_tty_move_caret(handle, 0, 1, y, 1, error);
+                uv__tty_move_caret(handle, 0, 1, y, 1, error);
                 break;
 
               case 'B':
@@ -1978,7 +1979,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 y = handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
-                uv_tty_move_caret(handle, 0, 1, y, 1, error);
+                uv__tty_move_caret(handle, 0, 1, y, 1, error);
                 break;
 
               case 'C':
@@ -1986,7 +1987,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 x = handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
-                uv_tty_move_caret(handle, x, 1, 0, 1, error);
+                uv__tty_move_caret(handle, x, 1, 0, 1, error);
                 break;
 
               case 'D':
@@ -1994,7 +1995,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 x = -(handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
-                uv_tty_move_caret(handle, x, 1, 0, 1, error);
+                uv__tty_move_caret(handle, x, 1, 0, 1, error);
                 break;
 
               case 'E':
@@ -2002,7 +2003,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 y = handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
-                uv_tty_move_caret(handle, 0, 0, y, 1, error);
+                uv__tty_move_caret(handle, 0, 0, y, 1, error);
                 break;
 
               case 'F':
@@ -2010,7 +2011,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 FLUSH_TEXT();
                 y = -(handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
-                uv_tty_move_caret(handle, 0, 0, y, 1, error);
+                uv__tty_move_caret(handle, 0, 0, y, 1, error);
                 break;
 
               case 'G':
@@ -2019,7 +2020,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 x = (handle->tty.wr.ansi_csi_argc >= 1 &&
                      handle->tty.wr.ansi_csi_argv[0])
                   ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
-                uv_tty_move_caret(handle, x, 0, 0, 1, error);
+                uv__tty_move_caret(handle, x, 0, 0, 1, error);
                 break;
 
               case 'H':
@@ -2032,7 +2033,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 x = (handle->tty.wr.ansi_csi_argc >= 2 &&
                      handle->tty.wr.ansi_csi_argv[1])
                   ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0;
-                uv_tty_move_caret(handle, x, 0, y, 0, error);
+                uv__tty_move_caret(handle, x, 0, y, 0, error);
                 break;
 
               case 'J':
@@ -2041,7 +2042,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 d = handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 0;
                 if (d >= 0 && d <= 2) {
-                  uv_tty_clear(handle, d, 1, error);
+                  uv__tty_clear(handle, d, 1, error);
                 }
                 break;
 
@@ -2051,26 +2052,26 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
                 d = handle->tty.wr.ansi_csi_argc
                   ? handle->tty.wr.ansi_csi_argv[0] : 0;
                 if (d >= 0 && d <= 2) {
-                  uv_tty_clear(handle, d, 0, error);
+                  uv__tty_clear(handle, d, 0, error);
                 }
                 break;
 
               case 'm':
                 /* Set style */
                 FLUSH_TEXT();
-                uv_tty_set_style(handle, error);
+                uv__tty_set_style(handle, error);
                 break;
 
               case 's':
                 /* Save the cursor position. */
                 FLUSH_TEXT();
-                uv_tty_save_state(handle, 0, error);
+                uv__tty_save_state(handle, 0, error);
                 break;
 
               case 'u':
                 /* Restore the cursor position */
                 FLUSH_TEXT();
-                uv_tty_restore_state(handle, 0, error);
+                uv__tty_restore_state(handle, 0, error);
                 break;
             }
           }
@@ -2179,7 +2180,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle,
 }
 
 
-int uv_tty_write(uv_loop_t* loop,
+int uv__tty_write(uv_loop_t* loop,
                  uv_write_t* req,
                  uv_tty_t* handle,
                  const uv_buf_t bufs[],
@@ -2197,13 +2198,13 @@ int uv_tty_write(uv_loop_t* loop,
 
   req->u.io.queued_bytes = 0;
 
-  if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) {
+  if (!uv__tty_write_bufs(handle, bufs, nbufs, &error)) {
     SET_REQ_SUCCESS(req);
   } else {
     SET_REQ_ERROR(req, error);
   }
 
-  uv_insert_pending_req(loop, (uv_req_t*) req);
+  uv__insert_pending_req(loop, (uv_req_t*) req);
 
   return 0;
 }
@@ -2217,14 +2218,14 @@ int uv__tty_try_write(uv_tty_t* handle,
   if (handle->stream.conn.write_reqs_pending > 0)
     return UV_EAGAIN;
 
-  if (uv_tty_write_bufs(handle, bufs, nbufs, &error))
+  if (uv__tty_write_bufs(handle, bufs, nbufs, &error))
     return uv_translate_sys_error(error);
 
   return uv__count_bufs(bufs, nbufs);
 }
 
 
-void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
   uv_write_t* req) {
   int err;
 
@@ -2236,20 +2237,22 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
     req->cb(req, uv_translate_sys_error(err));
   }
 
+
   handle->stream.conn.write_reqs_pending--;
-  if (handle->stream.conn.shutdown_req != NULL &&
-      handle->stream.conn.write_reqs_pending == 0) {
-    uv_want_endgame(loop, (uv_handle_t*)handle);
-  }
+  if (handle->stream.conn.write_reqs_pending == 0)
+    if (handle->flags & UV_HANDLE_SHUTTING)
+      uv__process_tty_shutdown_req(loop,
+                                   handle,
+                                   handle->stream.conn.shutdown_req);
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_tty_close(uv_tty_t* handle) {
+void uv__tty_close(uv_tty_t* handle) {
   assert(handle->u.fd == -1 || handle->u.fd > 2);
   if (handle->flags & UV_HANDLE_READING)
-    uv_tty_read_stop(handle);
+    uv__tty_read_stop(handle);
 
   if (handle->u.fd == -1)
     CloseHandle(handle->handle);
@@ -2261,61 +2264,61 @@ void uv_tty_close(uv_tty_t* handle) {
   handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   uv__handle_closing(handle);
 
-  if (handle->reqs_pending == 0) {
-    uv_want_endgame(handle->loop, (uv_handle_t*) handle);
-  }
+  if (handle->reqs_pending == 0)
+    uv__want_endgame(handle->loop, (uv_handle_t*) handle);
 }
 
 
-void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
-  if (!(handle->flags & UV_HANDLE_TTY_READABLE) &&
-      handle->stream.conn.shutdown_req != NULL &&
-      handle->stream.conn.write_reqs_pending == 0) {
-    UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
+void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) {
+  assert(stream->stream.conn.write_reqs_pending == 0);
+  assert(req);
 
-    /* TTY shutdown is really just a no-op */
-    if (handle->stream.conn.shutdown_req->cb) {
-      if (handle->flags & UV_HANDLE_CLOSING) {
-        handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED);
-      } else {
-        handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0);
-      }
+  stream->stream.conn.shutdown_req = NULL;
+  stream->flags &= ~UV_HANDLE_SHUTTING;
+  UNREGISTER_HANDLE_REQ(loop, stream, req);
+
+  /* TTY shutdown is really just a no-op */
+  if (req->cb) {
+    if (stream->flags & UV_HANDLE_CLOSING) {
+      req->cb(req, UV_ECANCELED);
+    } else {
+      req->cb(req, 0);
     }
+  }
 
-    handle->stream.conn.shutdown_req = NULL;
+  DECREASE_PENDING_REQ_COUNT(stream);
+}
 
-    DECREASE_PENDING_REQ_COUNT(handle);
-    return;
-  }
 
-  if (handle->flags & UV_HANDLE_CLOSING &&
-      handle->reqs_pending == 0) {
-    /* The wait handle used for raw reading should be unregistered when the
-     * wait callback runs. */
-    assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
-           handle->tty.rd.read_raw_wait == NULL);
+void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
+  assert(handle->flags & UV_HANDLE_CLOSING);
+  assert(handle->reqs_pending == 0);
 
-    assert(!(handle->flags & UV_HANDLE_CLOSED));
-    uv__handle_close(handle);
-  }
+  /* The wait handle used for raw reading should be unregistered when the
+   * wait callback runs. */
+  assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
+         handle->tty.rd.read_raw_wait == NULL);
+
+  assert(!(handle->flags & UV_HANDLE_CLOSED));
+  uv__handle_close(handle);
 }
 
 
 /*
- * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * uv__process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
  * TODO: find a way to remove it
  */
-void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_req_t* raw_req) {
   abort();
 }
 
 
 /*
- * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
+ * uv__process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
  * TODO: find a way to remove it
  */
-void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
+void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
     uv_connect_t* req) {
   abort();
 }
index 7b5efa2..eaebc1e 100644 (file)
@@ -60,7 +60,7 @@ int uv_udp_getsockname(const uv_udp_t* handle,
 }
 
 
-static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
+static int uv__udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
     int family) {
   DWORD yes = 1;
   WSAPROTOCOL_INFOW info;
@@ -106,8 +106,8 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket,
             FILE_SKIP_SET_EVENT_ON_HANDLE |
                 FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
       handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP;
-      handle->func_wsarecv = uv_wsarecv_workaround;
-      handle->func_wsarecvfrom = uv_wsarecvfrom_workaround;
+      handle->func_wsarecv = uv__wsarecv_workaround;
+      handle->func_wsarecvfrom = uv__wsarecvfrom_workaround;
     } else if (GetLastError() != ERROR_INVALID_FUNCTION) {
       return GetLastError();
     }
@@ -155,7 +155,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
       return uv_translate_sys_error(err);
     }
 
-    err = uv_udp_set_socket(handle->loop, handle, sock, domain);
+    err = uv__udp_set_socket(handle->loop, handle, sock, domain);
     if (err) {
       closesocket(sock);
       QUEUE_REMOVE(&handle->handle_queue);
@@ -167,7 +167,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
 }
 
 
-void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) {
+void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle) {
   uv_udp_recv_stop(handle);
   closesocket(handle->socket);
   handle->socket = INVALID_SOCKET;
@@ -175,12 +175,12 @@ void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) {
   uv__handle_closing(handle);
 
   if (handle->reqs_pending == 0) {
-    uv_want_endgame(loop, (uv_handle_t*) handle);
+    uv__want_endgame(loop, (uv_handle_t*) handle);
   }
 }
 
 
-void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
+void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle) {
   if (handle->flags & UV_HANDLE_CLOSING &&
       handle->reqs_pending == 0) {
     assert(!(handle->flags & UV_HANDLE_CLOSED));
@@ -194,10 +194,10 @@ int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
 }
 
 
-static int uv_udp_maybe_bind(uv_udp_t* handle,
-                             const struct sockaddr* addr,
-                             unsigned int addrlen,
-                             unsigned int flags) {
+static int uv__udp_maybe_bind(uv_udp_t* handle,
+                              const struct sockaddr* addr,
+                              unsigned int addrlen,
+                              unsigned int flags) {
   int r;
   int err;
   DWORD no = 0;
@@ -216,7 +216,7 @@ static int uv_udp_maybe_bind(uv_udp_t* handle,
       return WSAGetLastError();
     }
 
-    err = uv_udp_set_socket(handle->loop, handle, sock, addr->sa_family);
+    err = uv__udp_set_socket(handle->loop, handle, sock, addr->sa_family);
     if (err) {
       closesocket(sock);
       return err;
@@ -264,7 +264,7 @@ static int uv_udp_maybe_bind(uv_udp_t* handle,
 }
 
 
-static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
+static void uv__udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
   uv_req_t* req;
   uv_buf_t buf;
   DWORD bytes, flags;
@@ -311,7 +311,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
       handle->flags |= UV_HANDLE_READ_PENDING;
       req->u.io.overlapped.InternalHigh = bytes;
       handle->reqs_pending++;
-      uv_insert_pending_req(loop, req);
+      uv__insert_pending_req(loop, req);
     } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
       /* The req will be processed with IOCP. */
       handle->flags |= UV_HANDLE_READ_PENDING;
@@ -319,7 +319,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
     } else {
       /* Make this req pending reporting an error. */
       SET_REQ_ERROR(req, WSAGetLastError());
-      uv_insert_pending_req(loop, req);
+      uv__insert_pending_req(loop, req);
       handle->reqs_pending++;
     }
 
@@ -343,7 +343,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
       handle->flags |= UV_HANDLE_READ_PENDING;
       req->u.io.overlapped.InternalHigh = bytes;
       handle->reqs_pending++;
-      uv_insert_pending_req(loop, req);
+      uv__insert_pending_req(loop, req);
     } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
       /* The req will be processed with IOCP. */
       handle->flags |= UV_HANDLE_READ_PENDING;
@@ -351,7 +351,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) {
     } else {
       /* Make this req pending reporting an error. */
       SET_REQ_ERROR(req, WSAGetLastError());
-      uv_insert_pending_req(loop, req);
+      uv__insert_pending_req(loop, req);
       handle->reqs_pending++;
     }
   }
@@ -367,10 +367,10 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
     return UV_EALREADY;
   }
 
-  err = uv_udp_maybe_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip4_any_,
-                          sizeof(uv_addr_ip4_any_),
-                          0);
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           0);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -384,7 +384,7 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb,
   /* If reading was stopped and then started again, there could still be a recv
    * request pending. */
   if (!(handle->flags & UV_HANDLE_READ_PENDING))
-    uv_udp_queue_recv(loop, handle);
+    uv__udp_queue_recv(loop, handle);
 
   return 0;
 }
@@ -433,7 +433,7 @@ static int uv__send(uv_udp_send_t* req,
     handle->send_queue_size += req->u.io.queued_bytes;
     handle->send_queue_count++;
     REGISTER_HANDLE_REQ(loop, handle, req);
-    uv_insert_pending_req(loop, (uv_req_t*)req);
+    uv__insert_pending_req(loop, (uv_req_t*)req);
   } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
     /* Request queued by the kernel. */
     req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
@@ -450,7 +450,7 @@ static int uv__send(uv_udp_send_t* req,
 }
 
 
-void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
+void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle,
     uv_req_t* req) {
   uv_buf_t buf;
   int partial;
@@ -554,14 +554,14 @@ done:
   /* Post another read if still reading and not closing. */
   if ((handle->flags & UV_HANDLE_READING) &&
       !(handle->flags & UV_HANDLE_READ_PENDING)) {
-    uv_udp_queue_recv(loop, handle);
+    uv__udp_queue_recv(loop, handle);
   }
 
   DECREASE_PENDING_REQ_COUNT(handle);
 }
 
 
-void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
+void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
     uv_udp_send_t* req) {
   int err;
 
@@ -598,10 +598,10 @@ static int uv__udp_set_membership4(uv_udp_t* handle,
     return UV_EINVAL;
 
   /* If the socket is unbound, bind to inaddr_any. */
-  err = uv_udp_maybe_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip4_any_,
-                          sizeof(uv_addr_ip4_any_),
-                          UV_UDP_REUSEADDR);
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           UV_UDP_REUSEADDR);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -652,10 +652,10 @@ int uv__udp_set_membership6(uv_udp_t* handle,
   if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
     return UV_EINVAL;
 
-  err = uv_udp_maybe_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip6_any_,
-                          sizeof(uv_addr_ip6_any_),
-                          UV_UDP_REUSEADDR);
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip6_any_,
+                           sizeof(uv_addr_ip6_any_),
+                           UV_UDP_REUSEADDR);
 
   if (err)
     return uv_translate_sys_error(err);
@@ -708,10 +708,10 @@ static int uv__udp_set_source_membership4(uv_udp_t* handle,
     return UV_EINVAL;
 
   /* If the socket is unbound, bind to inaddr_any. */
-  err = uv_udp_maybe_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip4_any_,
-                          sizeof(uv_addr_ip4_any_),
-                          UV_UDP_REUSEADDR);
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip4_any_,
+                           sizeof(uv_addr_ip4_any_),
+                           UV_UDP_REUSEADDR);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -763,10 +763,10 @@ int uv__udp_set_source_membership6(uv_udp_t* handle,
   if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6))
     return UV_EINVAL;
 
-  err = uv_udp_maybe_bind(handle,
-                          (const struct sockaddr*) &uv_addr_ip6_any_,
-                          sizeof(uv_addr_ip6_any_),
-                          UV_UDP_REUSEADDR);
+  err = uv__udp_maybe_bind(handle,
+                           (const struct sockaddr*) &uv_addr_ip6_any_,
+                           sizeof(uv_addr_ip6_any_),
+                           UV_UDP_REUSEADDR);
 
   if (err)
     return uv_translate_sys_error(err);
@@ -962,10 +962,10 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
     return uv_translate_sys_error(GetLastError());
   }
 
-  err = uv_udp_set_socket(handle->loop,
-                          handle,
-                          sock,
-                          protocol_info.iAddressFamily);
+  err = uv__udp_set_socket(handle->loop,
+                           handle,
+                           sock,
+                           protocol_info.iAddressFamily);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -1044,7 +1044,7 @@ int uv__udp_bind(uv_udp_t* handle,
                  unsigned int flags) {
   int err;
 
-  err = uv_udp_maybe_bind(handle, addr, addrlen, flags);
+  err = uv__udp_maybe_bind(handle, addr, addrlen, flags);
   if (err)
     return uv_translate_sys_error(err);
 
@@ -1066,7 +1066,7 @@ int uv__udp_connect(uv_udp_t* handle,
     else
       return UV_EINVAL;
 
-    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return uv_translate_sys_error(err);
   }
@@ -1117,7 +1117,7 @@ int uv__udp_send(uv_udp_send_t* req,
     else
       return UV_EINVAL;
 
-    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return uv_translate_sys_error(err);
   }
@@ -1146,6 +1146,7 @@ int uv__udp_try_send(uv_udp_t* handle,
     err = uv__convert_to_localhost_if_unspecified(addr, &converted);
     if (err)
       return err;
+    addr = (const struct sockaddr*) &converted;
   }
 
   /* Already sending a message.*/
@@ -1159,7 +1160,7 @@ int uv__udp_try_send(uv_udp_t* handle,
       bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
     else
       return UV_EINVAL;
-    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return uv_translate_sys_error(err);
   }
@@ -1169,7 +1170,7 @@ int uv__udp_try_send(uv_udp_t* handle,
                   nbufs,
                   &bytes,
                   0,
-                  (const struct sockaddr*) &converted,
+                  addr,
                   addrlen,
                   NULL,
                   NULL);
index 33e874a..9943205 100644 (file)
@@ -531,103 +531,25 @@ int uv_resident_set_memory(size_t* rss) {
 
 
 int uv_uptime(double* uptime) {
-  BYTE stack_buffer[4096];
-  BYTE* malloced_buffer = NULL;
-  BYTE* buffer = (BYTE*) stack_buffer;
-  size_t buffer_size = sizeof(stack_buffer);
-  DWORD data_size;
-
-  PERF_DATA_BLOCK* data_block;
-  PERF_OBJECT_TYPE* object_type;
-  PERF_COUNTER_DEFINITION* counter_definition;
-
-  DWORD i;
-
-  for (;;) {
-    LONG result;
-
-    data_size = (DWORD) buffer_size;
-    result = RegQueryValueExW(HKEY_PERFORMANCE_DATA,
-                              L"2",
-                              NULL,
-                              NULL,
-                              buffer,
-                              &data_size);
-    if (result == ERROR_SUCCESS) {
-      break;
-    } else if (result != ERROR_MORE_DATA) {
-      *uptime = 0;
-      return uv_translate_sys_error(result);
-    }
-
-    buffer_size *= 2;
-    /* Don't let the buffer grow infinitely. */
-    if (buffer_size > 1 << 20) {
-      goto internalError;
-    }
-
-    uv__free(malloced_buffer);
-
-    buffer = malloced_buffer = (BYTE*) uv__malloc(buffer_size);
-    if (malloced_buffer == NULL) {
-      *uptime = 0;
-      return UV_ENOMEM;
-    }
-  }
-
-  if (data_size < sizeof(*data_block))
-    goto internalError;
-
-  data_block = (PERF_DATA_BLOCK*) buffer;
-
-  if (wmemcmp(data_block->Signature, L"PERF", 4) != 0)
-    goto internalError;
-
-  if (data_size < data_block->HeaderLength + sizeof(*object_type))
-    goto internalError;
-
-  object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength);
-
-  if (object_type->NumInstances != PERF_NO_INSTANCES)
-    goto internalError;
+  *uptime = GetTickCount64() / 1000.0;
+  return 0;
+}
 
-  counter_definition = (PERF_COUNTER_DEFINITION*) (buffer +
-      data_block->HeaderLength + object_type->HeaderLength);
-  for (i = 0; i < object_type->NumCounters; i++) {
-    if ((BYTE*) counter_definition + sizeof(*counter_definition) >
-        buffer + data_size) {
-      break;
-    }
 
-    if (counter_definition->CounterNameTitleIndex == 674 &&
-        counter_definition->CounterSize == sizeof(uint64_t)) {
-      if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size ||
-          !(counter_definition->CounterType & PERF_OBJECT_TIMER)) {
-        goto internalError;
-      } else {
-        BYTE* address = (BYTE*) object_type + object_type->DefinitionLength +
-                        counter_definition->CounterOffset;
-        uint64_t value = *((uint64_t*) address);
-        *uptime = floor((double) (object_type->PerfTime.QuadPart - value) /
-                        (double) object_type->PerfFreq.QuadPart);
-        uv__free(malloced_buffer);
-        return 0;
-      }
-    }
+unsigned int uv_available_parallelism(void) {
+  SYSTEM_INFO info;
+  unsigned rc;
 
-    counter_definition = (PERF_COUNTER_DEFINITION*)
-        ((BYTE*) counter_definition + counter_definition->ByteLength);
-  }
+  /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
+   * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
+   */
+  GetSystemInfo(&info);
 
-  /* If we get here, the uptime value was not found. */
-  uv__free(malloced_buffer);
-  *uptime = 0;
-  return UV_ENOSYS;
+  rc = info.dwNumberOfProcessors;
+  if (rc < 1)
+    rc = 1;
 
- internalError:
-  uv__free(malloced_buffer);
-  *uptime = 0;
-  return UV_EIO;
+  return rc;
 }
 
 
index bf306cd..53147b8 100644 (file)
@@ -48,7 +48,7 @@ sSetWinEventHook pSetWinEventHook;
 /* ws2_32.dll function pointer */
 uv_sGetHostNameW pGetHostNameW;
 
-void uv_winapi_init(void) {
+void uv__winapi_init(void) {
   HMODULE ntdll_module;
   HMODULE powrprof_module;
   HMODULE user32_module;
@@ -126,19 +126,19 @@ void uv_winapi_init(void) {
       kernel32_module,
       "GetQueuedCompletionStatusEx");
 
-  powrprof_module = LoadLibraryA("powrprof.dll");
+  powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
   if (powrprof_module != NULL) {
     pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
       GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
   }
 
-  user32_module = LoadLibraryA("user32.dll");
+  user32_module = GetModuleHandleA("user32.dll");
   if (user32_module != NULL) {
     pSetWinEventHook = (sSetWinEventHook)
       GetProcAddress(user32_module, "SetWinEventHook");
   }
 
-  ws2_32_module = LoadLibraryA("ws2_32.dll");
+  ws2_32_module = GetModuleHandleA("ws2_32.dll");
   if (ws2_32_module != NULL) {
     pGetHostNameW = (uv_sGetHostNameW) GetProcAddress(
         ws2_32_module,
index 4cf6e6b..a68b095 100644 (file)
@@ -38,7 +38,7 @@ struct sockaddr_in6 uv_addr_ip6_any_;
 /*
  * Retrieves the pointer to a winsock extension function.
  */
-static BOOL uv_get_extension_function(SOCKET socket, GUID guid,
+static BOOL uv__get_extension_function(SOCKET socket, GUID guid,
     void **target) {
   int result;
   DWORD bytes;
@@ -62,20 +62,20 @@ static BOOL uv_get_extension_function(SOCKET socket, GUID guid,
 }
 
 
-BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
+BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) {
   const GUID wsaid_acceptex = WSAID_ACCEPTEX;
-  return uv_get_extension_function(socket, wsaid_acceptex, (void**)target);
+  return uv__get_extension_function(socket, wsaid_acceptex, (void**)target);
 }
 
 
-BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
+BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) {
   const GUID wsaid_connectex = WSAID_CONNECTEX;
-  return uv_get_extension_function(socket, wsaid_connectex, (void**)target);
+  return uv__get_extension_function(socket, wsaid_connectex, (void**)target);
 }
 
 
 
-void uv_winsock_init(void) {
+void uv__winsock_init(void) {
   WSADATA wsa_data;
   int errorno;
   SOCKET dummy;
@@ -134,7 +134,7 @@ void uv_winsock_init(void) {
 }
 
 
-int uv_ntstatus_to_winsock_error(NTSTATUS status) {
+int uv__ntstatus_to_winsock_error(NTSTATUS status) {
   switch (status) {
     case STATUS_SUCCESS:
       return ERROR_SUCCESS;
@@ -267,7 +267,7 @@ int uv_ntstatus_to_winsock_error(NTSTATUS status) {
  * the user to use the default msafd driver, doesn't work when other LSPs are
  * stacked on top of it.
  */
-int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
+int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers,
     DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped,
     LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
   NTSTATUS status;
@@ -346,7 +346,7 @@ int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
       break;
 
     default:
-      error = uv_ntstatus_to_winsock_error(status);
+      error = uv__ntstatus_to_winsock_error(status);
       break;
   }
 
@@ -360,8 +360,8 @@ int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers,
 }
 
 
-/* See description of uv_wsarecv_workaround. */
-int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
+/* See description of uv__wsarecv_workaround. */
+int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
     DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr,
     int* addr_len, WSAOVERLAPPED *overlapped,
     LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) {
@@ -444,7 +444,7 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
       break;
 
     default:
-      error = uv_ntstatus_to_winsock_error(status);
+      error = uv__ntstatus_to_winsock_error(status);
       break;
   }
 
@@ -458,7 +458,7 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers,
 }
 
 
-int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
+int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
     AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) {
   IO_STATUS_BLOCK iosb;
   IO_STATUS_BLOCK* iosb_ptr;
@@ -531,7 +531,7 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in,
       break;
 
     default:
-      error = uv_ntstatus_to_winsock_error(status);
+      error = uv__ntstatus_to_winsock_error(status);
       break;
   }
 
index 4225b82..d7b8b39 100644 (file)
@@ -146,6 +146,7 @@ public:
   {
     this->Set.reset();
     this->insert(list);
+    return *this;
   }
 
   // Iterators
@@ -298,17 +299,15 @@ public:
   {
     if (this->contains(e)) {
       return iterator(this, static_cast<size_type>(e));
-    } else {
-      return this->end();
     }
+    return this->end();
   }
   const_iterator find(key_type e) const
   {
     if (this->contains(e)) {
       return const_iterator(this, static_cast<size_type>(e));
-    } else {
-      return this->end();
     }
+    return this->end();
   }
 
   bool contains(key_type e) const
@@ -317,6 +316,10 @@ public:
   }
 
 private:
+  template <typename E>
+  friend inline bool operator==(const enum_set<E>& lhs,
+                                const enum_set<E>& rhs) noexcept;
+
   template <typename E, typename Predicate>
   friend inline void erase_if(enum_set<E>& set, Predicate pred);
 
@@ -369,7 +372,7 @@ template <typename Enum>
 inline bool operator==(const enum_set<Enum>& lhs,
                        const enum_set<Enum>& rhs) noexcept
 {
-  return lhs == rhs;
+  return lhs.Set == rhs.Set;
 }
 
 template <typename Enum>
index 9a87413..ea9816c 100755 (executable)
--- a/bootstrap
+++ b/bootstrap
@@ -281,6 +281,7 @@ CMAKE_UNUSED_SOURCES="\
 "
 
 CMAKE_CXX_SOURCES="\
+  cmAddCompileDefinitionsCommand \
   cmAddCustomCommandCommand \
   cmAddCustomTargetCommand \
   cmAddDefinitionsCommand \
@@ -301,6 +302,7 @@ CMAKE_CXX_SOURCES="\
   cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool \
   cmBinUtilsWindowsPELinker \
   cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool \
+  cmBlockCommand \
   cmBreakCommand \
   cmBuildCommand \
   cmCMakeLanguageCommand \
@@ -328,6 +330,7 @@ CMAKE_CXX_SOURCES="\
   cmCustomCommand \
   cmCustomCommandGenerator \
   cmCustomCommandLines \
+  cmCxxModuleMapper \
   cmDefinePropertyCommand \
   cmDefinitions \
   cmDocumentationFormatter \
@@ -337,6 +340,7 @@ CMAKE_CXX_SOURCES="\
   cmExecProgramCommand \
   cmExecuteProcessCommand \
   cmExpandedCommandArgument \
+  cmExperimental \
   cmExportBuildFileGenerator \
   cmExportFileGenerator \
   cmExportInstallFileGenerator \
@@ -391,6 +395,7 @@ CMAKE_CXX_SOURCES="\
   cmIncludeRegularExpressionCommand \
   cmInstallCommand \
   cmInstallCommandArguments \
+  cmInstallCxxModuleBmiGenerator \
   cmInstallDirectoryGenerator \
   cmInstallExportGenerator \
   cmInstallFileSetGenerator \
@@ -588,6 +593,7 @@ if ${cmake_system_mingw}; then
     src/inet.c \
     src/threadpool.c \
     src/strscpy.c \
+    src/strtok.c \
     src/timer.c \
     src/uv-common.c \
     src/win/async.c \
@@ -618,6 +624,7 @@ if ${cmake_system_mingw}; then
 else
   LIBUV_C_SOURCES="\
     src/strscpy.c \
+    src/strtok.c \
     src/timer.c \
     src/uv-common.c \
     src/unix/cmake-bootstrap.c \
@@ -634,6 +641,7 @@ else
     src/unix/signal.c \
     src/unix/stream.c \
     src/unix/tcp.c \
+    src/unix/tty.c \
     "
 fi